video: tegra: dc: fix the logic for dis ref-count.
Kevin Huang [Thu, 14 Mar 2013 05:25:37 +0000 (22:25 -0700)]
Use simple logic to meet the requirement of use cases.
All logic is implemented in powergate module. Dependency
of dis partitions are transparent to client.

Change-Id: I6649dc8bf704fcb763dd5b038f947ed0da2c84c4
Signed-off-by: Kevin Huang <kevinh@nvidia.com>
Reviewed-on: http://git-master/r/200652
Reviewed-by: Harshada Kale <hkale@nvidia.com>
Tested-by: Harshada Kale <hkale@nvidia.com>

arch/arm/mach-tegra/powergate-priv.h
arch/arm/mach-tegra/powergate-t11x.c
arch/arm/mach-tegra/powergate-t14x.c
arch/arm/mach-tegra/powergate.c
drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_priv.h
drivers/video/tegra/dc/dc_priv_defs.h
sound/pci/hda/hda_intel.c

index be45b87..dd58ec7 100644 (file)
@@ -100,6 +100,8 @@ struct powergate_ops {
 
        int (*powergate_mc_flush)(int id);
        int (*powergate_mc_flush_done)(int id);
+
+       int (*powergate_init_refcount)(void);
 };
 
 void get_clk_info(struct powergate_partition_info *pg_info);
index 8181513..ab50509 100644 (file)
@@ -553,17 +553,55 @@ err_power:
        return ret;
 }
 
+void tegra11x_powergate_dis_partition(void)
+{
+       tegra1xx_powergate(TEGRA_POWERGATE_DISB,
+               &tegra11x_powergate_partition_info[TEGRA_POWERGATE_DISB]);
+
+       tegra11x_powergate_partition_internal(TEGRA_POWERGATE_VENC,
+               &tegra11x_powergate_partition_info[TEGRA_POWERGATE_DISA]);
+
+       tegra1xx_powergate(TEGRA_POWERGATE_DISA,
+               &tegra11x_powergate_partition_info[TEGRA_POWERGATE_DISA]);
+}
+
+/* The logic manages the ref-count for dis partitions. The dependency between
+ * disa and disb is hided from client. */
+bool tegra11x_powergate_check_dis_refcount(int id, int op)
+{
+       WARN_ONCE(atomic_read(&ref_count_a), "dis ref a count underflow");
+       WARN_ONCE(atomic_read(&ref_count_b), "dis ref b count underflow");
+
+       if (op && id == TEGRA_POWERGATE_DISA) {
+               if (atomic_inc_return(&ref_count_a) != 1)
+                       return 0;
+       } else if (op && id == TEGRA_POWERGATE_DISB) {
+               if (tegra_powergate_is_powered(TEGRA_POWERGATE_DISA))
+                       atomic_inc(&ref_count_a);
+               if (atomic_inc_return(&ref_count_b) != 1)
+                       return 0;
+       } else if (!op && id == TEGRA_POWERGATE_DISA) {
+               if (atomic_dec_return(&ref_count_a) != 0)
+                       return 0;
+       } else if (!op && id == TEGRA_POWERGATE_DISB) {
+               atomic_dec(&ref_count_a);
+               if (atomic_dec_return(&ref_count_b) != 0) {
+                       return 0;
+               } else if (atomic_read(&ref_count_a) == 0) {
+                       tegra11x_powergate_dis_partition();
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
 int tegra11x_powergate_partition(int id)
 {
        int ret;
 
-       WARN_ONCE(atomic_read(&ref_count_a) < 0, "ref count A underflow");
-       WARN_ONCE(atomic_read(&ref_count_b) < 0, "ref count B underflow");
-
-       if (id == TEGRA_POWERGATE_DISA && atomic_dec_return(&ref_count_a) != 0)
-               return 0;
-       else if (id == TEGRA_POWERGATE_DISB &&
-               atomic_dec_return(&ref_count_b) != 0)
+       if ((id == TEGRA_POWERGATE_DISA || id == TEGRA_POWERGATE_DISB) &&
+                       !tegra11x_powergate_check_dis_refcount(id, 0))
                return 0;
 
        ret = tegra11x_check_partition_pg_seq(id,
@@ -582,13 +620,8 @@ int tegra11x_unpowergate_partition(int id)
 {
        int ret;
 
-       WARN_ONCE(atomic_read(&ref_count_a) < 0, "ref count A underflow");
-       WARN_ONCE(atomic_read(&ref_count_b) < 0, "ref count B underflow");
-
-       if (id == TEGRA_POWERGATE_DISA && atomic_inc_return(&ref_count_a) != 1)
-               return 0;
-       else if (id == TEGRA_POWERGATE_DISB &&
-               atomic_inc_return(&ref_count_b) != 1)
+       if ((id == TEGRA_POWERGATE_DISA || id == TEGRA_POWERGATE_DISB) &&
+                       !tegra11x_powergate_check_dis_refcount(id, 1))
                return 0;
 
        ret = tegra11x_check_partition_pug_seq(id,
@@ -626,6 +659,20 @@ spinlock_t *tegra11x_get_powergate_lock(void)
        return &tegra11x_powergate_lock;
 }
 
+int tegra11x_powergate_init_refcount(void)
+{
+       if (tegra_powergate_is_powered(TEGRA_POWERGATE_DISA))
+                       atomic_set(&ref_count_a, 1);
+       else
+                       atomic_set(&ref_count_a, 0);
+
+       if (tegra_powergate_is_powered(TEGRA_POWERGATE_DISB))
+                       atomic_set(&ref_count_b, 1);
+       else
+                       atomic_set(&ref_count_b, 0);
+       return 0;
+}
+
 static struct powergate_ops tegra11x_powergate_ops = {
        .soc_name = "tegra11x",
 
@@ -645,6 +692,8 @@ static struct powergate_ops tegra11x_powergate_ops = {
 
        .powergate_mc_flush = tegra11x_powergate_mc_flush,
        .powergate_mc_flush_done = tegra11x_powergate_mc_flush_done,
+
+       .powergate_init_refcount = tegra11x_powergate_init_refcount,
 };
 
 struct powergate_ops *tegra11x_powergate_init_chip_support(void)
index bcb35fa..000a637 100644 (file)
@@ -571,17 +571,55 @@ err_power:
        return ret;
 }
 
+void tegra14x_powergate_dis_partition(void)
+{
+       tegra1xx_powergate(TEGRA_POWERGATE_DISB,
+               &tegra14x_powergate_partition_info[TEGRA_POWERGATE_DISB]);
+
+       tegra14x_powergate_partition_internal(TEGRA_POWERGATE_VENC,
+               &tegra14x_powergate_partition_info[TEGRA_POWERGATE_DISA]);
+
+       tegra1xx_powergate(TEGRA_POWERGATE_DISA,
+               &tegra14x_powergate_partition_info[TEGRA_POWERGATE_DISA]);
+}
+
+/* The logic manages the ref-count for dis partitions. The dependency between
+ * disa and disb is hided from client. */
+bool tegra14x_powergate_check_dis_refcount(int id, int op)
+{
+       WARN_ONCE(atomic_read(&ref_count_a), "dis ref a count underflow");
+       WARN_ONCE(atomic_read(&ref_count_b), "dis ref b count underflow");
+
+       if (op && id == TEGRA_POWERGATE_DISA) {
+               if (atomic_inc_return(&ref_count_a) != 1)
+                       return 0;
+       } else if (op && id == TEGRA_POWERGATE_DISB) {
+               if (tegra_powergate_is_powered(TEGRA_POWERGATE_DISA))
+                       atomic_inc(&ref_count_a);
+               if (atomic_inc_return(&ref_count_b) != 1)
+                       return 0;
+       } else if (!op && id == TEGRA_POWERGATE_DISA) {
+               if (atomic_dec_return(&ref_count_a) != 0)
+                       return 0;
+       } else if (!op && id == TEGRA_POWERGATE_DISB) {
+               atomic_dec(&ref_count_a);
+               if (atomic_dec_return(&ref_count_b) != 0) {
+                       return 0;
+               } else if (atomic_read(&ref_count_a) == 0) {
+                       tegra14x_powergate_dis_partition();
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
 int tegra14x_powergate_partition(int id)
 {
        int ret;
 
-       WARN_ONCE(atomic_read(&ref_count_a) < 0, "ref count A underflow");
-       WARN_ONCE(atomic_read(&ref_count_b) < 0, "ref count B underflow");
-
-       if (id == TEGRA_POWERGATE_DISA && atomic_dec_return(&ref_count_a) != 0)
-               return 0;
-       else if (id == TEGRA_POWERGATE_DISB &&
-               atomic_dec_return(&ref_count_b) != 0)
+       if ((id == TEGRA_POWERGATE_DISA || id == TEGRA_POWERGATE_DISB) &&
+                       !tegra14x_powergate_check_dis_refcount(id, 0))
                return 0;
 
        if (skip_pg_check(id, false))
@@ -603,13 +641,8 @@ int tegra14x_unpowergate_partition(int id)
 {
        int ret;
 
-       WARN_ONCE(atomic_read(&ref_count_a) < 0, "ref count A underflow");
-       WARN_ONCE(atomic_read(&ref_count_b) < 0, "ref count B underflow");
-
-       if (id == TEGRA_POWERGATE_DISA && atomic_inc_return(&ref_count_a) != 1)
-               return 0;
-       else if (id == TEGRA_POWERGATE_DISB &&
-               atomic_inc_return(&ref_count_b) != 1)
+       if ((id == TEGRA_POWERGATE_DISA || id == TEGRA_POWERGATE_DISB) &&
+                       !tegra14x_powergate_check_dis_refcount(id, 1))
                return 0;
 
        if (skip_pg_check(id, true))
@@ -656,6 +689,20 @@ spinlock_t *tegra14x_get_powergate_lock(void)
        return &tegra14x_powergate_lock;
 }
 
+int tegra14x_powergate_init_refcount(void)
+{
+       if (tegra_powergate_is_powered(TEGRA_POWERGATE_DISA))
+                       atomic_set(&ref_count_a, 1);
+       else
+                       atomic_set(&ref_count_a, 0);
+
+       if (tegra_powergate_is_powered(TEGRA_POWERGATE_DISB))
+                       atomic_set(&ref_count_b, 1);
+       else
+                       atomic_set(&ref_count_b, 0);
+       return 0;
+}
+
 static struct powergate_ops tegra14x_powergate_ops = {
        .soc_name = "tegra14x",
 
@@ -677,6 +724,8 @@ static struct powergate_ops tegra14x_powergate_ops = {
 
        .powergate_mc_flush = tegra14x_powergate_mc_flush,
        .powergate_mc_flush_done = tegra14x_powergate_mc_flush_done,
+
+       .powergate_init_refcount = tegra14x_powergate_init_refcount,
 };
 
 struct powergate_ops *tegra14x_powergate_init_chip_support(void)
index 7639a4c..78437e0 100644 (file)
@@ -553,6 +553,14 @@ const char *tegra_powergate_get_name(int id)
 }
 EXPORT_SYMBOL(tegra_powergate_get_name);
 
+int tegra_powergate_init_refcount(void)
+{
+       if (!pg_ops->powergate_init_refcount)
+               return 0;
+
+       return pg_ops->powergate_init_refcount();
+}
+
 int __init tegra_powergate_init(void)
 {
        switch (tegra_chip_id) {
@@ -578,6 +586,8 @@ int __init tegra_powergate_init(void)
                        break;
        }
 
+       tegra_powergate_init_refcount();
+
        pr_info("%s: DONE\n", __func__);
 
        return (pg_ops ? 0 : -EINVAL);
index ad505f1..6a9579e 100644 (file)
@@ -1602,7 +1602,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
                return IRQ_NONE;
 
        mutex_lock(&dc->lock);
-       if (!dc->enabled) {
+       if (!dc->enabled || !tegra_dc_is_powered(dc)) {
                mutex_unlock(&dc->lock);
                return IRQ_HANDLED;
        }
@@ -2265,56 +2265,6 @@ static void tegra_dc_underflow_worker(struct work_struct *work)
        mutex_unlock(&dc->lock);
 }
 
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
-/* A mutex used to protect the critical section used by both DC heads. */
-static struct mutex tegra_dc_powergate_status_lock;
-
-/* defer turning off DISA until DISB is turned off */
-void tegra_dc_powergate_locked(struct tegra_dc *dc)
-{
-       struct tegra_dc *dc_partner;
-
-       mutex_lock(&tegra_dc_powergate_status_lock);
-       /* Get the handler of the other display controller. */
-       dc_partner = tegra_dc_get_dc(dc->ndev->id ^ 1);
-       if (!dc_partner)
-               _tegra_dc_powergate_locked(dc);
-       else 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(&tegra_dc_powergate_status_lock);
-}
-
-
-/* to turn on DISB we must first power on DISA */
-void tegra_dc_unpowergate_locked(struct tegra_dc *dc)
-{
-       mutex_lock(&tegra_dc_powergate_status_lock);
-       if (dc->powergate_id == TEGRA_POWERGATE_DISB) {
-               struct tegra_dc *dc_partner;
-
-               /* Get the handler of the other display controller. */
-               dc_partner = tegra_dc_get_dc(dc->ndev->id ^ 1);
-               if (dc_partner)
-                       _tegra_dc_unpowergate_locked(dc_partner);
-       }
-       _tegra_dc_unpowergate_locked(dc);
-       mutex_unlock(&tegra_dc_powergate_status_lock);
-}
-#endif
-
 #ifdef CONFIG_SWITCH
 static ssize_t switch_modeset_print_mode(struct switch_dev *sdev, char *buf)
 {
@@ -2462,9 +2412,6 @@ static int tegra_dc_probe(struct platform_device *ndev)
 
        mutex_init(&dc->lock);
        mutex_init(&dc->one_shot_lock);
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
-       mutex_init(&tegra_dc_powergate_status_lock);
-#endif
        init_completion(&dc->frame_end_complete);
        init_waitqueue_head(&dc->wq);
        init_waitqueue_head(&dc->timestamp_wq);
index 96f4736..229f755 100644 (file)
@@ -229,22 +229,20 @@ static inline unsigned long tegra_dc_clk_get_rate(struct tegra_dc *dc)
        return clk_get_rate(dc->clk);
 }
 
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
-static inline void _tegra_dc_powergate_locked(struct tegra_dc *dc)
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+static inline void tegra_dc_powergate_locked(struct tegra_dc *dc)
 {
        tegra_powergate_partition(dc->powergate_id);
-       dc->powered = 0;
 }
 
-static inline void _tegra_dc_unpowergate_locked(struct tegra_dc *dc)
+static inline void tegra_dc_unpowergate_locked(struct tegra_dc *dc)
 {
-       int ret;
-       ret = tegra_unpowergate_partition(dc->powergate_id);
-       if (ret) {
-               dev_err(&dc->ndev->dev, "%s: unpowergate failed\n", __func__);
-               return;
-       }
-       dc->powered = 1;
+       tegra_unpowergate_partition(dc->powergate_id);
+}
+
+static inline bool tegra_dc_is_powered(struct tegra_dc *dc)
+{
+       return tegra_powergate_is_powered(dc->powergate_id);
 }
 
 void tegra_dc_powergate_locked(struct tegra_dc *dc);
@@ -252,6 +250,10 @@ void tegra_dc_unpowergate_locked(struct tegra_dc *dc);
 #else
 static inline void tegra_dc_powergate_locked(struct tegra_dc *dc) { }
 static inline void tegra_dc_unpowergate_locked(struct tegra_dc *dc) { }
+static inline bool tegra_dc_is_powered(struct tegra_dc *dc)
+{
+       return true;
+}
 #endif
 
 extern struct tegra_dc_out_ops tegra_dc_rgb_ops;
index 94ba3b0..197e073 100644 (file)
@@ -120,7 +120,6 @@ struct tegra_dc {
        struct tegra_dc_shift_clk_div   shift_clk_div;
 
        u32                             powergate_id;
-       bool                            powered;
 
        bool                            connected;
        bool                            enabled;
index e65988d..4e85d74 100644 (file)
@@ -1516,7 +1516,7 @@ static void azx_platform_enable_clocks(struct azx *chip)
        int i;
 
 #ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
-#if defined(CONFIG_ARCH_TEGRA_11x_SOC)
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
        tegra_unpowergate_partition(TEGRA_POWERGATE_DISB);
 #endif
 #endif
@@ -1539,7 +1539,7 @@ static void azx_platform_disable_clocks(struct azx *chip)
                clk_disable(chip->platform_clks[i]);
 
 #ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
-#if defined(CONFIG_ARCH_TEGRA_11x_SOC)
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
        tegra_powergate_partition(TEGRA_POWERGATE_DISB);
 #endif
 #endif