video: tegra: dc: Powergate dependency between DCs.
Kevin Huang [Tue, 27 Nov 2012 19:09:22 +0000 (11:09 -0800)]
Powergate DISA iff DISB is powergated.

Bug 1178332

Change-Id: I306d142d2caceb94ffba51d75d9a2e2f3a73907f
Signed-off-by: Kevin Huang <kevinh@nvidia.com>
Reviewed-on: http://git-master/r/166639
Reviewed-by: Mrutyunjay Sawant <msawant@nvidia.com>
Tested-by: Mrutyunjay Sawant <msawant@nvidia.com>

drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_priv.h
drivers/video/tegra/dc/dc_priv_defs.h

index 2b47401..37dec60 100644 (file)
@@ -63,6 +63,9 @@
 #define DC_COM_PIN_OUTPUT_POLARITY1_INIT_VAL   0x01000000
 #define DC_COM_PIN_OUTPUT_POLARITY3_INIT_VAL   0x0
 
+/* A mutex used to protect the critical section used by both DC heads. */
+static struct mutex status_lock;
+
 static struct fb_videomode tegra_dc_hdmi_fallback_mode = {
        .refresh = 60,
        .xres = 640,
@@ -1107,13 +1110,6 @@ static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
                break;
        }
 
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
-       if (out->type == TEGRA_DC_OUT_HDMI)
-               dc->powergate_id = TEGRA_POWERGATE_DISB;
-       else
-               dc->powergate_id = TEGRA_POWERGATE_DISA;
-#endif
-
        if (dc->out_ops && dc->out_ops->init)
                dc->out_ops->init(dc);
 }
@@ -1806,7 +1802,9 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
 {
        int failed_init = 0;
 
+       mutex_lock(&status_lock);
        tegra_dc_unpowergate_locked(dc);
+       mutex_unlock(&status_lock);
 
        if (dc->out->enable)
                dc->out->enable(&dc->ndev->dev);
@@ -2074,16 +2072,36 @@ void tegra_dc_blank(struct tegra_dc *dc)
 
 static void _tegra_dc_disable(struct tegra_dc *dc)
 {
+       struct tegra_dc *dc_partner;
        if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) {
                mutex_lock(&dc->one_shot_lock);
                cancel_delayed_work_sync(&dc->one_shot_work);
        }
 
+       mutex_lock(&status_lock);
+
        tegra_dc_io_start(dc);
        _tegra_dc_controller_disable(dc);
        tegra_dc_io_end(dc);
 
-       tegra_dc_powergate_locked(dc);
+       /* Get the handler of the other display controller. */
+       dc_partner = tegra_dc_get_dc(dc->ndev->id ^ 1);
+       if (dc->powergate_id == TEGRA_POWERGATE_DISA) {
+               /* If DISB is powergated, then powergate DISA. */
+               if (!dc_partner->powered)
+                       tegra_dc_powergate_locked(dc);
+       } else if (dc->powergate_id == TEGRA_POWERGATE_DISB) {
+               /* If DISA is enabled, only powergate DISB;
+                * otherwise, powergate DISA and DISB.
+                * */
+               if (dc_partner->enabled) {
+                       tegra_dc_powergate_locked(dc);
+               } else {
+                       tegra_dc_powergate_locked(dc);
+                       tegra_dc_powergate_locked(dc_partner);
+               }
+       }
+       mutex_unlock(&status_lock);
 
        if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
                mutex_unlock(&dc->one_shot_lock);
@@ -2277,11 +2295,13 @@ static int tegra_dc_probe(struct platform_device *ndev)
                dc->win_syncpt[0] = NVSYNCPT_DISP0_A;
                dc->win_syncpt[1] = NVSYNCPT_DISP0_B;
                dc->win_syncpt[2] = NVSYNCPT_DISP0_C;
+               dc->powergate_id = TEGRA_POWERGATE_DISA;
        } else if (TEGRA_DISPLAY2_BASE == res->start) {
                dc->vblank_syncpt = NVSYNCPT_VBLANK1;
                dc->win_syncpt[0] = NVSYNCPT_DISP1_A;
                dc->win_syncpt[1] = NVSYNCPT_DISP1_B;
                dc->win_syncpt[2] = NVSYNCPT_DISP1_C;
+               dc->powergate_id = TEGRA_POWERGATE_DISB;
        } else {
                dev_err(&ndev->dev,
                        "Unknown base address %#08x: unable to assign syncpt\n",
@@ -2326,6 +2346,7 @@ static int tegra_dc_probe(struct platform_device *ndev)
 
        mutex_init(&dc->lock);
        mutex_init(&dc->one_shot_lock);
+       mutex_init(&status_lock);
        init_completion(&dc->frame_end_complete);
        init_waitqueue_head(&dc->wq);
        init_waitqueue_head(&dc->timestamp_wq);
@@ -2439,9 +2460,11 @@ static int tegra_dc_probe(struct platform_device *ndev)
                dc->connected = true;
 
 #ifdef CONFIG_ARCH_TEGRA_11x_SOC
+       mutex_lock(&status_lock);
        /* Powergate display module when it's unconnected. */
        if (!tegra_dc_get_connected(dc))
                tegra_dc_powergate_locked(dc);
+       mutex_unlock(&status_lock);
 #endif
 
        tegra_dc_create_sysfs(&dc->ndev->dev);
index bf600cb..15b39e1 100644 (file)
@@ -213,12 +213,14 @@ static inline void tegra_dc_powergate_locked(struct tegra_dc *dc)
 {
        if (tegra_powergate_is_powered(dc->powergate_id))
                tegra_powergate_partition(dc->powergate_id);
+       dc->powered = 0;
 }
 
 static inline void tegra_dc_unpowergate_locked(struct tegra_dc *dc)
 {
        if (!tegra_powergate_is_powered(dc->powergate_id))
                tegra_unpowergate_partition(dc->powergate_id);
+       dc->powered = 1;
 }
 #else
 static inline void tegra_dc_powergate_locked(struct tegra_dc *dc) { }
index 8803824..0600c2c 100644 (file)
@@ -115,6 +115,7 @@ struct tegra_dc {
        struct tegra_dc_shift_clk_div   shift_clk_div;
 
        u32                             powergate_id;
+       u32                             powered;
 
        bool                            connected;
        bool                            enabled;