These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / gpu / drm / tegra / dpaux.c
index a43a836..6aecb66 100644 (file)
@@ -56,15 +56,14 @@ static inline struct tegra_dpaux *work_to_dpaux(struct work_struct *work)
        return container_of(work, struct tegra_dpaux, work);
 }
 
-static inline unsigned long tegra_dpaux_readl(struct tegra_dpaux *dpaux,
-                                             unsigned long offset)
+static inline u32 tegra_dpaux_readl(struct tegra_dpaux *dpaux,
+                                   unsigned long offset)
 {
        return readl(dpaux->regs + (offset << 2));
 }
 
 static inline void tegra_dpaux_writel(struct tegra_dpaux *dpaux,
-                                     unsigned long value,
-                                     unsigned long offset)
+                                     u32 value, unsigned long offset)
 {
        writel(value, dpaux->regs + (offset << 2));
 }
@@ -76,7 +75,7 @@ static void tegra_dpaux_write_fifo(struct tegra_dpaux *dpaux, const u8 *buffer,
 
        for (i = 0; i < DIV_ROUND_UP(size, 4); i++) {
                size_t num = min_t(size_t, size - i * 4, 4);
-               unsigned long value = 0;
+               u32 value = 0;
 
                for (j = 0; j < num; j++)
                        value |= buffer[i * 4 + j] << (j * 8);
@@ -92,7 +91,7 @@ static void tegra_dpaux_read_fifo(struct tegra_dpaux *dpaux, u8 *buffer,
 
        for (i = 0; i < DIV_ROUND_UP(size, 4); i++) {
                size_t num = min_t(size_t, size - i * 4, 4);
-               unsigned long value;
+               u32 value;
 
                value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXDATA_READ(i));
 
@@ -120,6 +119,7 @@ static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
         */
        if (msg->size < 1) {
                switch (msg->request & ~DP_AUX_I2C_MOT) {
+               case DP_AUX_I2C_WRITE_STATUS_UPDATE:
                case DP_AUX_I2C_WRITE:
                case DP_AUX_I2C_READ:
                        value = DPAUX_DP_AUXCTL_CMD_ADDRESS_ONLY;
@@ -150,7 +150,7 @@ static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
 
                break;
 
-       case DP_AUX_I2C_STATUS:
+       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
                if (msg->request & DP_AUX_I2C_MOT)
                        value |= DPAUX_DP_AUXCTL_CMD_MOT_RQ;
                else
@@ -248,7 +248,7 @@ static irqreturn_t tegra_dpaux_irq(int irq, void *data)
 {
        struct tegra_dpaux *dpaux = data;
        irqreturn_t ret = IRQ_HANDLED;
-       unsigned long value;
+       u32 value;
 
        /* clear interrupts */
        value = tegra_dpaux_readl(dpaux, DPAUX_INTR_AUX);
@@ -271,7 +271,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
 {
        struct tegra_dpaux *dpaux;
        struct resource *regs;
-       unsigned long value;
+       u32 value;
        int err;
 
        dpaux = devm_kzalloc(&pdev->dev, sizeof(*dpaux), GFP_KERNEL);
@@ -295,26 +295,41 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
        }
 
        dpaux->rst = devm_reset_control_get(&pdev->dev, "dpaux");
-       if (IS_ERR(dpaux->rst))
+       if (IS_ERR(dpaux->rst)) {
+               dev_err(&pdev->dev, "failed to get reset control: %ld\n",
+                       PTR_ERR(dpaux->rst));
                return PTR_ERR(dpaux->rst);
+       }
 
        dpaux->clk = devm_clk_get(&pdev->dev, NULL);
-       if (IS_ERR(dpaux->clk))
+       if (IS_ERR(dpaux->clk)) {
+               dev_err(&pdev->dev, "failed to get module clock: %ld\n",
+                       PTR_ERR(dpaux->clk));
                return PTR_ERR(dpaux->clk);
+       }
 
        err = clk_prepare_enable(dpaux->clk);
-       if (err < 0)
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to enable module clock: %d\n",
+                       err);
                return err;
+       }
 
        reset_control_deassert(dpaux->rst);
 
        dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent");
-       if (IS_ERR(dpaux->clk_parent))
+       if (IS_ERR(dpaux->clk_parent)) {
+               dev_err(&pdev->dev, "failed to get parent clock: %ld\n",
+                       PTR_ERR(dpaux->clk_parent));
                return PTR_ERR(dpaux->clk_parent);
+       }
 
        err = clk_prepare_enable(dpaux->clk_parent);
-       if (err < 0)
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to enable parent clock: %d\n",
+                       err);
                return err;
+       }
 
        err = clk_set_rate(dpaux->clk_parent, 270000000);
        if (err < 0) {
@@ -324,8 +339,11 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
        }
 
        dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd");
-       if (IS_ERR(dpaux->vdd))
+       if (IS_ERR(dpaux->vdd)) {
+               dev_err(&pdev->dev, "failed to get VDD supply: %ld\n",
+                       PTR_ERR(dpaux->vdd));
                return PTR_ERR(dpaux->vdd);
+       }
 
        err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0,
                               dev_name(dpaux->dev), dpaux);
@@ -335,6 +353,8 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
                return err;
        }
 
+       disable_irq(dpaux->irq);
+
        dpaux->aux.transfer = tegra_dpaux_transfer;
        dpaux->aux.dev = &pdev->dev;
 
@@ -342,6 +362,24 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
        if (err < 0)
                return err;
 
+       /*
+        * Assume that by default the DPAUX/I2C pads will be used for HDMI,
+        * so power them up and configure them in I2C mode.
+        *
+        * The DPAUX code paths reconfigure the pads in AUX mode, but there
+        * is no possibility to perform the I2C mode configuration in the
+        * HDMI path.
+        */
+       value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
+       value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
+       tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
+
+       value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_PADCTL);
+       value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV |
+               DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV |
+               DPAUX_HYBRID_PADCTL_MODE_I2C;
+       tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL);
+
        /* enable and clear all interrupts */
        value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT |
                DPAUX_INTR_UNPLUG_EVENT | DPAUX_INTR_PLUG_EVENT;
@@ -360,6 +398,12 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
 static int tegra_dpaux_remove(struct platform_device *pdev)
 {
        struct tegra_dpaux *dpaux = platform_get_drvdata(pdev);
+       u32 value;
+
+       /* make sure pads are powered down when not in use */
+       value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
+       value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
+       tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
 
        drm_dp_aux_unregister(&dpaux->aux);
 
@@ -377,6 +421,7 @@ static int tegra_dpaux_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id tegra_dpaux_of_match[] = {
+       { .compatible = "nvidia,tegra210-dpaux", },
        { .compatible = "nvidia,tegra124-dpaux", },
        { },
 };
@@ -426,8 +471,10 @@ int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output)
                enum drm_connector_status status;
 
                status = tegra_dpaux_detect(dpaux);
-               if (status == connector_status_connected)
+               if (status == connector_status_connected) {
+                       enable_irq(dpaux->irq);
                        return 0;
+               }
 
                usleep_range(1000, 2000);
        }
@@ -440,6 +487,8 @@ int tegra_dpaux_detach(struct tegra_dpaux *dpaux)
        unsigned long timeout;
        int err;
 
+       disable_irq(dpaux->irq);
+
        err = regulator_disable(dpaux->vdd);
        if (err < 0)
                return err;
@@ -463,7 +512,7 @@ int tegra_dpaux_detach(struct tegra_dpaux *dpaux)
 
 enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux)
 {
-       unsigned long value;
+       u32 value;
 
        value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT);
 
@@ -475,7 +524,7 @@ enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux)
 
 int tegra_dpaux_enable(struct tegra_dpaux *dpaux)
 {
-       unsigned long value;
+       u32 value;
 
        value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) |
                DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) |
@@ -493,7 +542,7 @@ int tegra_dpaux_enable(struct tegra_dpaux *dpaux)
 
 int tegra_dpaux_disable(struct tegra_dpaux *dpaux)
 {
-       unsigned long value;
+       u32 value;
 
        value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
        value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;