drm/tegra: sor - Protect CRC debugfs against enable state
authorThierry Reding <treding@nvidia.com>
Wed, 26 Mar 2014 10:13:16 +0000 (11:13 +0100)
committerThierry Reding <treding@nvidia.com>
Thu, 5 Jun 2014 21:09:35 +0000 (23:09 +0200)
Accessing the CRC debugfs file will hang the system if the SOR is not
enabled, so make sure that it is stays enabled until the CRC has been
read.

Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/gpu/drm/tegra/sor.c

index 078bd37..7d66f6e 100644 (file)
@@ -34,6 +34,7 @@ struct tegra_sor {
 
        struct tegra_dpaux *dpaux;
 
+       struct mutex lock;
        bool enabled;
 
        struct dentry *debugfs;
@@ -299,14 +300,16 @@ static int tegra_output_sor_enable(struct tegra_output *output)
        unsigned int vbe, vse, hbe, hse, vbs, hbs, i;
        struct tegra_sor *sor = to_sor(output);
        unsigned long value;
-       int err;
+       int err = 0;
+
+       mutex_lock(&sor->lock);
 
        if (sor->enabled)
-               return 0;
+               goto unlock;
 
        err = clk_prepare_enable(sor->clk);
        if (err < 0)
-               return err;
+               goto unlock;
 
        reset_control_deassert(sor->rst);
 
@@ -388,7 +391,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
        err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS);
        if (err < 0) {
                dev_err(sor->dev, "failed to power on I/O rail: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        usleep_range(5, 100);
@@ -512,21 +515,21 @@ static int tegra_output_sor_enable(struct tegra_output *output)
                if (err < 0) {
                        dev_err(sor->dev, "failed to probe eDP link: %d\n",
                                err);
-                       return err;
+                       goto unlock;
                }
 
                err = drm_dp_link_power_up(aux, &link);
                if (err < 0) {
                        dev_err(sor->dev, "failed to power up eDP link: %d\n",
                                err);
-                       return err;
+                       goto unlock;
                }
 
                err = drm_dp_link_configure(aux, &link);
                if (err < 0) {
                        dev_err(sor->dev, "failed to configure eDP link: %d\n",
                                err);
-                       return err;
+                       goto unlock;
                }
 
                rate = drm_dp_link_rate_to_bw_code(link.rate);
@@ -561,7 +564,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
                if (err < 0) {
                        dev_err(sor->dev, "DP fast link training failed: %d\n",
                                err);
-                       return err;
+                       goto unlock;
                }
 
                dev_dbg(sor->dev, "fast link training succeeded\n");
@@ -570,7 +573,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
        err = tegra_sor_power_up(sor, 250);
        if (err < 0) {
                dev_err(sor->dev, "failed to power up SOR: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        /* start display controller in continuous mode */
@@ -635,7 +638,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
        err = tegra_sor_setup_pwm(sor, 250);
        if (err < 0) {
                dev_err(sor->dev, "failed to setup PWM: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
@@ -647,18 +650,20 @@ static int tegra_output_sor_enable(struct tegra_output *output)
        err = tegra_sor_attach(sor);
        if (err < 0) {
                dev_err(sor->dev, "failed to attach SOR: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        err = tegra_sor_wakeup(sor);
        if (err < 0) {
                dev_err(sor->dev, "failed to enable DC: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        sor->enabled = true;
 
-       return 0;
+unlock:
+       mutex_unlock(&sor->lock);
+       return err;
 }
 
 static int tegra_sor_detach(struct tegra_sor *sor)
@@ -786,15 +791,17 @@ static int tegra_output_sor_disable(struct tegra_output *output)
        struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
        struct tegra_sor *sor = to_sor(output);
        unsigned long value;
-       int err;
+       int err = 0;
+
+       mutex_lock(&sor->lock);
 
        if (!sor->enabled)
-               return 0;
+               goto unlock;
 
        err = tegra_sor_detach(sor);
        if (err < 0) {
                dev_err(sor->dev, "failed to detach SOR: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        tegra_sor_writel(sor, 0, SOR_STATE_1);
@@ -835,21 +842,21 @@ static int tegra_output_sor_disable(struct tegra_output *output)
        err = tegra_sor_power_down(sor);
        if (err < 0) {
                dev_err(sor->dev, "failed to power down SOR: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        if (sor->dpaux) {
                err = tegra_dpaux_disable(sor->dpaux);
                if (err < 0) {
                        dev_err(sor->dev, "failed to disable DP: %d\n", err);
-                       return err;
+                       goto unlock;
                }
        }
 
        err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS);
        if (err < 0) {
                dev_err(sor->dev, "failed to power off I/O rail: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        reset_control_assert(sor->rst);
@@ -857,7 +864,9 @@ static int tegra_output_sor_disable(struct tegra_output *output)
 
        sor->enabled = false;
 
-       return 0;
+unlock:
+       mutex_unlock(&sor->lock);
+       return err;
 }
 
 static int tegra_output_sor_setup_clock(struct tegra_output *output,
@@ -952,10 +961,16 @@ static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
                                  size_t size, loff_t *ppos)
 {
        struct tegra_sor *sor = file->private_data;
+       ssize_t num, err;
        char buf[10];
-       ssize_t num;
        u32 value;
-       int err;
+
+       mutex_lock(&sor->lock);
+
+       if (!sor->enabled) {
+               err = -EAGAIN;
+               goto unlock;
+       }
 
        value = tegra_sor_readl(sor, SOR_STATE_1);
        value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
@@ -971,14 +986,18 @@ static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
 
        err = tegra_sor_crc_wait(sor, 100);
        if (err < 0)
-               return err;
+               goto unlock;
 
        tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A);
        value = tegra_sor_readl(sor, SOR_CRC_B);
 
        num = scnprintf(buf, sizeof(buf), "%08x\n", value);
 
-       return simple_read_from_buffer(buffer, size, ppos, buf, num);
+       err = simple_read_from_buffer(buffer, size, ppos, buf, num);
+
+unlock:
+       mutex_unlock(&sor->lock);
+       return err;
 }
 
 static const struct file_operations tegra_sor_crc_fops = {
@@ -1168,6 +1187,8 @@ static int tegra_sor_probe(struct platform_device *pdev)
        sor->client.ops = &sor_client_ops;
        sor->client.dev = &pdev->dev;
 
+       mutex_init(&sor->lock);
+
        err = host1x_client_register(&sor->client);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to register host1x client: %d\n",