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: I3ad900c80a1b12e8397643c078d1269ae9f3dac1
Signed-off-by: Kevin Huang <kevinh@nvidia.com>
Reviewed-on: http://git-master/r/210088
Reviewed-by: Mrutyunjay Sawant <msawant@nvidia.com>
Tested-by: Mrutyunjay Sawant <msawant@nvidia.com>
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);
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,
{
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,
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",
.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)
}
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) {
break;
}
+ tegra_powergate_init_refcount();
+
pr_info("%s: DONE\n", __func__);
return (pg_ops ? 0 : -EINVAL);
int need_disable = 0;
mutex_lock(&dc->lock);
- if (!dc->enabled) {
+ if (!dc->enabled || !tegra_dc_is_powered(dc)) {
mutex_unlock(&dc->lock);
return IRQ_HANDLED;
}
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)
{
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);
#endif
}
-#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)
{
tegra_unpowergate_partition(dc->powergate_id);
- dc->powered = 1;
+}
+
+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);
#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;
struct tegra_dc_shift_clk_div shift_clk_div;
u32 powergate_id;
- bool powered;
bool connected;
bool enabled;
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
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