فهرست منبع

video: driver: add power domain and opp table support for video

Added changes to support generic power domain and opp table.
This is an alternative for downstream regulator framework.

power domain can be enabled using below dtsi entries.

power-domains =
    <&videocc MVS0C_GDSC>,
	<&videocc MVS0_GDSC>,
	<&rpmhpd SM8450_MXC>,
	<&rpmhpd SM8450_MMCX>
power-domain-names =
    "iris-ctl", "vcodec", "mx", "mmcx";

Power domain handles willbe parsed at driver side using below api's.
   - dev_pm_domain_attach_by_name()
   - devm_pm_opp_attach_genpd()

devm_pm_opp_attach_genpd() provides consumer virtual device handles
and i.e linked to core->dev using device_link_add().

MXC, MMCX rails wilbe powered up by scaling desired rate using
dev_pm_opp_set_rate().

Change-Id: I3d73434cb772078f031aec7cadc2d42ab930edd0
Signed-off-by: Govindaraj Rajagopal <[email protected]>
Govindaraj Rajagopal 2 سال پیش
والد
کامیت
ab9b1a112b

+ 8 - 5
driver/platform/common/inc/msm_vidc_platform.h

@@ -48,6 +48,10 @@ struct bw_table {
 	u32              max_kbps;
 };
 
+struct pd_table {
+	const char      *name;
+};
+
 struct regulator_table {
 	const char      *name;
 	bool             hw_trigger;
@@ -187,6 +191,10 @@ struct msm_vidc_platform_data {
 	unsigned int bw_tbl_size;
 	const struct regulator_table *regulator_tbl;
 	unsigned int regulator_tbl_size;
+	const struct pd_table *pd_tbl;
+	unsigned int pd_tbl_size;
+	const char **opp_tbl;
+	unsigned int opp_tbl_size;
 	const struct clk_table *clk_tbl;
 	unsigned int clk_tbl_size;
 	const struct clk_rst_table *clk_rst_tbl;
@@ -231,11 +239,6 @@ static inline bool is_mmrm_supported(struct msm_vidc_core *core)
 	return !!core->platform->data.supports_mmrm;
 }
 
-static inline bool is_regulator_supported(struct msm_vidc_core *core)
-{
-	return !!core->platform->data.regulator_tbl_size;
-}
-
 int msm_vidc_init_platform(struct platform_device *pdev);
 int msm_vidc_deinit_platform(struct platform_device *pdev);
 

+ 9 - 0
driver/platform/common/src/msm_vidc_platform.c

@@ -214,6 +214,15 @@ static int msm_vidc_init_ops(struct msm_vidc_core *core)
 	core->media_device_ops = &msm_v4l2_media_ops;
 	core->v4l2_m2m_ops = &msm_v4l2_m2m_ops;
 	core->mem_ops = get_mem_ops();
+	if (!core->mem_ops) {
+		d_vpr_e("%s: invalid memory ops\n", __func__);
+		return -EINVAL;
+	}
+	core->res_ops = get_resources_ops();
+	if (!core->res_ops) {
+		d_vpr_e("%s: invalid resource ops\n", __func__);
+		return -EINVAL;
+	}
 
 	return 0;
 }

+ 10 - 0
driver/platform/kalama/src/msm_vidc_kalama.c

@@ -17,6 +17,7 @@
 #include "msm_vidc_internal.h"
 #include "msm_vidc_control_ext.h"
 #include "msm_vidc_memory_ext.h"
+#include "resources_ext.h"
 #include "msm_vidc_iris3.h"
 #include "hfi_property.h"
 #include "hfi_command.h"
@@ -2659,6 +2660,15 @@ static int msm_vidc_init_data(struct msm_vidc_core *core, struct device *dev)
 		core->platform->data = kalama_data;
 
 	core->mem_ops = get_mem_ops_ext();
+	if (!core->mem_ops) {
+		d_vpr_e("%s: invalid memory ext ops\n", __func__);
+		return -EINVAL;
+	}
+	core->res_ops = get_res_ops_ext();
+	if (!core->res_ops) {
+		d_vpr_e("%s: invalid resource ext ops\n", __func__);
+		return -EINVAL;
+	}
 	rc = msm_vidc_kalama_check_ddr_type();
 	if (rc)
 		return rc;

+ 10 - 0
driver/platform/pineapple/src/msm_vidc_pineapple.c

@@ -17,6 +17,7 @@
 #include "msm_vidc_internal.h"
 #include "msm_vidc_control_ext.h"
 #include "msm_vidc_memory_ext.h"
+#include "resources_ext.h"
 #include "msm_vidc_iris33.h"
 #include "hfi_property.h"
 #include "hfi_command.h"
@@ -2646,6 +2647,15 @@ static int msm_vidc_init_data(struct msm_vidc_core *core)
 
 	core->platform->data = pineapple_data;
 	core->mem_ops = get_mem_ops_ext();
+	if (!core->mem_ops) {
+		d_vpr_e("%s: invalid memory ext ops\n", __func__);
+		return -EINVAL;
+	}
+	core->res_ops = get_res_ops_ext();
+	if (!core->res_ops) {
+		d_vpr_e("%s: invalid resource ext ops\n", __func__);
+		return -EINVAL;
+	}
 	rc = msm_vidc_pineapple_check_ddr_type();
 	if (rc)
 		return rc;

+ 1 - 0
driver/platform/pineapple/src/pineapple.c

@@ -1771,6 +1771,7 @@ static int msm_vidc_init_data(struct msm_vidc_core *core)
 	d_vpr_h("%s: initialize pineapple data\n", __func__);
 
 	core->platform->data = pineapple_data;
+
 	rc = msm_vidc_pineapple_check_ddr_type();
 	if (rc)
 		return rc;

+ 10 - 0
driver/platform/waipio/src/msm_vidc_waipio.c

@@ -2140,6 +2140,16 @@ static int msm_vidc_init_data(struct msm_vidc_core *core)
 	d_vpr_h("%s: initialize waipio data\n", __func__);
 
 	core->platform->data = waipio_data;
+	core->mem_ops = get_mem_ops_ext();
+	if (!core->mem_ops) {
+		d_vpr_e("%s: invalid memory ext ops\n", __func__);
+		return -EINVAL;
+	}
+	core->res_ops = get_res_ops_ext();
+	if (!core->res_ops) {
+		d_vpr_e("%s: invalid resource ext ops\n", __func__);
+		return -EINVAL;
+	}
 
 	return rc;
 }

+ 15 - 2
driver/platform/waipio/src/waipio.c

@@ -1668,6 +1668,15 @@ static const struct bw_table waipio_bw_table[] = {
 	{ "venus-ddr",   1000, 15000000 },
 };
 
+/* name */
+static const struct pd_table waipio_pd_table[] = {
+	{ "iris-ctl" },
+	{ "vcodec"   },
+};
+
+/* name */
+static const char * const waipio_opp_table[] = { "mx", "mmcx", NULL };
+
 /* name, clock id, scaling */
 static const struct clk_table waipio_clk_table[] = {
 	{ "gcc_video_axi0",         GCC_VIDEO_AXI0_CLK,     0 },
@@ -1701,8 +1710,6 @@ static const struct msm_vidc_platform_data waipio_data = {
 	/* resources dependent on other module */
 	.bw_tbl = waipio_bw_table,
 	.bw_tbl_size = ARRAY_SIZE(waipio_bw_table),
-	.regulator_tbl = NULL,
-	.regulator_tbl_size = 0,
 	.clk_tbl = waipio_clk_table,
 	.clk_tbl_size = ARRAY_SIZE(waipio_clk_table),
 	.clk_rst_tbl = waipio_clk_reset_table,
@@ -1714,6 +1721,12 @@ static const struct msm_vidc_platform_data waipio_data = {
 	.context_bank_tbl = waipio_context_bank_table,
 	.context_bank_tbl_size = ARRAY_SIZE(waipio_context_bank_table),
 
+	/* populate power domain and opp table */
+	.pd_tbl = waipio_pd_table,
+	.pd_tbl_size = ARRAY_SIZE(waipio_pd_table),
+	.opp_tbl = waipio_opp_table,
+	.opp_tbl_size = ARRAY_SIZE(waipio_opp_table),
+
 	/* platform specific resources */
 	.freq_tbl = waipio_freq_table,
 	.freq_tbl_size = ARRAY_SIZE(waipio_freq_table),

+ 0 - 1
driver/variant/iris2/src/msm_vidc_iris2.c

@@ -1038,7 +1038,6 @@ int msm_vidc_init_iris2(struct msm_vidc_core *core)
 	d_vpr_h("%s()\n", __func__);
 	core->venus_ops = &iris2_ops;
 	core->session_ops = &msm_session_ops;
-	core->res_ops = get_resources_ops();
 
 	return 0;
 }

+ 0 - 1
driver/variant/iris3/src/msm_vidc_iris3.c

@@ -1119,7 +1119,6 @@ int msm_vidc_init_iris3(struct msm_vidc_core *core)
 	d_vpr_h("%s()\n", __func__);
 	core->venus_ops = &iris3_ops;
 	core->session_ops = &msm_session_ops;
-	core->res_ops = get_resources_ops();
 
 	return 0;
 }

+ 0 - 1
driver/variant/iris33/src/msm_vidc_iris33.c

@@ -1246,7 +1246,6 @@ int msm_vidc_init_iris33(struct msm_vidc_core *core)
 	d_vpr_h("%s()\n", __func__);
 	core->venus_ops = &iris33_ops;
 	core->session_ops = &msm_session_ops;
-	core->res_ops = get_resources_ops();
 
 	return 0;
 }

+ 16 - 0
driver/vidc/inc/resources.h

@@ -67,6 +67,10 @@ struct msm_vidc_core;
 	venus_hfi_for_each_thing_reverse_continue(__device, __rinfo, \
 			regulator, __from)
 
+/* Power domain set helpers */
+#define venus_hfi_for_each_power_domain(__device, __pdinfo) \
+	venus_hfi_for_each_thing(__device, __pdinfo, power_domain)
+
 /* Clock set helpers */
 #define venus_hfi_for_each_clock(__device, __cinfo) \
 	venus_hfi_for_each_thing(__device, __cinfo, clock)
@@ -118,6 +122,16 @@ struct regulator_set {
 	u32                        count;
 };
 
+struct power_domain_info {
+	struct device             *genpd_dev;
+	const char                *name;
+};
+
+struct power_domain_set {
+	struct power_domain_info  *power_domain_tbl;
+	u32                        count;
+};
+
 struct clock_residency {
 	struct list_head           list;
 	u64                        rate;
@@ -202,6 +216,7 @@ struct msm_vidc_resource {
 	u32                        irq;
 	struct bus_set             bus_set;
 	struct regulator_set       regulator_set;
+	struct power_domain_set    power_domain_set;
 	struct clock_set           clock_set;
 	struct reset_set           reset_set;
 	struct subcache_set        subcache_set;
@@ -227,6 +242,7 @@ struct msm_vidc_resources_ops {
 	int (*reset_control_deassert)(struct msm_vidc_core *core,
 			const char *name);
 
+	int (*gdsc_init)(struct msm_vidc_core *core);
 	int (*gdsc_on)(struct msm_vidc_core *core, const char *name);
 	int (*gdsc_off)(struct msm_vidc_core *core, const char *name);
 	int (*gdsc_hw_ctrl)(struct msm_vidc_core *core);

+ 13 - 0
driver/vidc/inc/resources_ext.h

@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _RESOURCES_EXT_H_
+#define _RESOURCES_EXT_H_
+
+struct msm_vidc_resources_ops;
+
+const struct msm_vidc_resources_ops *get_res_ops_ext(void);
+
+#endif // _RESOURCES_EXT_H_

+ 281 - 300
driver/vidc/src/resources.c

@@ -6,6 +6,9 @@
 
 #include <linux/sort.h>
 #include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
 #include <linux/reset.h>
 #include <linux/interconnect.h>
 #include <linux/soc/qcom/llcc-qcom.h>
@@ -32,6 +35,14 @@ enum reset_state {
 	DEASSERT,
 };
 
+/* A comparator to compare loads (needed later on) */
+static inline int cmp(const void *a, const void *b)
+{
+	/* want to sort in reverse so flip the comparison */
+	return ((struct freq_table *)b)->freq -
+		((struct freq_table *)a)->freq;
+}
+
 static void __fatal_error(bool fatal)
 {
 	WARN_ON(fatal);
@@ -89,12 +100,125 @@ static struct mmrm_client *devm_mmrm_get(struct device *dev, struct mmrm_client_
 }
 #endif
 
-/* A comparator to compare loads (needed later on) */
-static inline int cmp(const void *a, const void *b)
+static void devm_pd_release(void *res)
 {
-	/* want to sort in reverse so flip the comparison */
-	return ((struct freq_table *)b)->freq -
-		((struct freq_table *)a)->freq;
+	struct device *pd = (struct device *)res;
+
+	d_vpr_h("%s(): %s\n", __func__, dev_name(pd));
+	dev_pm_domain_detach(pd, true);
+}
+
+static struct device *devm_pd_get(struct device *dev, const char *name)
+{
+	struct device *pd = NULL;
+	int rc = 0;
+
+	pd = dev_pm_domain_attach_by_name(dev, name);
+	if (!pd) {
+		d_vpr_e("%s: pm domain attach failed %s\n", __func__, name);
+		return NULL;
+	}
+
+	rc = devm_add_action_or_reset(dev, devm_pd_release, (void *)pd);
+	if (rc) {
+		d_vpr_e("%s: add action or reset failed %s\n", __func__, name);
+		return NULL;
+	}
+
+	return pd;
+}
+
+static void devm_opp_dl_release(void *res)
+{
+	struct device_link *link = (struct device_link *)res;
+
+	d_vpr_h("%s(): %s\n", __func__, dev_name(&link->link_dev));
+	device_link_del(link);
+}
+
+static int devm_opp_dl_get(struct device *dev, struct device *supplier)
+{
+	u32 flag = DL_FLAG_RPM_ACTIVE | DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS;
+	struct device_link *link = NULL;
+	int rc = 0;
+
+	link = device_link_add(dev, supplier, flag);
+	if (!link) {
+		d_vpr_e("%s: device link add failed\n", __func__);
+		return -EINVAL;
+	}
+
+	rc = devm_add_action_or_reset(dev, devm_opp_dl_release, (void *)link);
+	if (rc) {
+		d_vpr_e("%s: add action or reset failed\n", __func__);
+		return rc;
+	}
+
+	return rc;
+}
+
+static void devm_pm_runtime_put_sync(void *res)
+{
+	struct device *dev = (struct device *)res;
+
+	d_vpr_h("%s(): %s\n", __func__, dev_name(dev));
+	pm_runtime_put_sync(dev);
+}
+
+static int devm_pm_runtime_get_sync(struct device *dev)
+{
+	int rc = 0;
+
+	rc = pm_runtime_get_sync(dev);
+	if (rc) {
+		d_vpr_e("%s: pm domain get sync failed\n", __func__);
+		return rc;
+	}
+
+	rc = devm_add_action_or_reset(dev, devm_pm_runtime_put_sync, (void *)dev);
+	if (rc) {
+		d_vpr_e("%s: add action or reset failed\n", __func__);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int __opp_set_rate(struct msm_vidc_core *core, u64 freq)
+{
+	unsigned long opp_freq = 0;
+	struct dev_pm_opp *opp;
+	int rc = 0;
+
+	if (!core) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+	opp_freq = freq;
+
+	/* find max(ceil) freq from opp table */
+	opp = dev_pm_opp_find_freq_ceil(&core->pdev->dev, &opp_freq);
+	if (IS_ERR(opp)) {
+		opp = dev_pm_opp_find_freq_floor(&core->pdev->dev, &opp_freq);
+		if (IS_ERR(opp)) {
+			d_vpr_e("%s: unable to find freq %lld in opp table\n", __func__, freq);
+			return -EINVAL;
+		}
+	}
+	dev_pm_opp_put(opp);
+
+	/* print freq value */
+	d_vpr_h("%s: set rate %lld (requested %lld)\n",
+		__func__, opp_freq, freq);
+
+	/* scale freq to power up mxc & mmcx */
+	rc = dev_pm_opp_set_rate(&core->pdev->dev, opp_freq);
+	if (rc) {
+		d_vpr_e("%s: failed to set rate\n", __func__);
+		return rc;
+	}
+
+	return rc;
 }
 
 static int __init_register_base(struct msm_vidc_core *core)
@@ -221,68 +345,133 @@ static int __init_bus(struct msm_vidc_core *core)
 	return rc;
 }
 
-static int __init_regulators(struct msm_vidc_core *core)
+static int __init_power_domains(struct msm_vidc_core *core)
 {
-	const struct regulator_table *regulator_tbl;
-	struct regulator_set *regulators;
-	struct regulator_info *rinfo = NULL;
-	u32 regulator_count = 0, cnt = 0;
+	struct power_domain_info *pdinfo = NULL;
+	const struct pd_table *pd_tbl;
+	struct power_domain_set *pds;
+	struct device **opp_vdevs;
+	const char **opp_tbl;
+	u32 pd_count = 0, opp_count = 0, cnt = 0;
 	int rc = 0;
 
 	if (!core || !core->resource || !core->platform) {
 		d_vpr_e("%s: invalid params\n", __func__);
 		return -EINVAL;
 	}
-	regulators = &core->resource->regulator_set;
+	pds = &core->resource->power_domain_set;
 
-	/* skip init if regulators not supported */
-	if (!is_regulator_supported(core)) {
-		d_vpr_h("%s: regulators are not available in database\n", __func__);
+	pd_tbl = core->platform->data.pd_tbl;
+	pd_count = core->platform->data.pd_tbl_size;
+
+	/* skip init if power domain not supported */
+	if (!pd_count) {
+		d_vpr_h("%s: power domain entries not available in db\n", __func__);
 		return 0;
 	}
 
-	regulator_tbl = core->platform->data.regulator_tbl;
-	regulator_count = core->platform->data.regulator_tbl_size;
-
-	if (!regulator_tbl || !regulator_count) {
-		d_vpr_e("%s: invalid regulator tbl %#x or count %d\n",
-			__func__, regulator_tbl, regulator_count);
+	/* sanitize power domain table */
+	if (!pd_tbl) {
+		d_vpr_e("%s: invalid power domain tbl\n", __func__);
 		return -EINVAL;
 	}
 
-	/* allocate regulator_set */
-	regulators->regulator_tbl = devm_kzalloc(&core->pdev->dev,
-			sizeof(*regulators->regulator_tbl) * regulator_count, GFP_KERNEL);
-	if (!regulators->regulator_tbl) {
-		d_vpr_e("%s: failed to alloc memory for regulator table\n", __func__);
+	/* allocate power_domain_set */
+	pds->power_domain_tbl = devm_kzalloc(&core->pdev->dev,
+			sizeof(*pds->power_domain_tbl) * pd_count, GFP_KERNEL);
+	if (!pds->power_domain_tbl) {
+		d_vpr_e("%s: failed to alloc memory for pd table\n", __func__);
 		return -ENOMEM;
 	}
-	regulators->count = regulator_count;
+	pds->count = pd_count;
 
-	/* populate regulator fields */
-	for (cnt = 0; cnt < regulators->count; cnt++) {
-		regulators->regulator_tbl[cnt].name = regulator_tbl[cnt].name;
-		regulators->regulator_tbl[cnt].hw_power_collapse = regulator_tbl[cnt].hw_trigger;
+	/* populate power domain fields */
+	for (cnt = 0; cnt < pds->count; cnt++)
+		pds->power_domain_tbl[cnt].name = pd_tbl[cnt].name;
+
+	/* print power domain fields */
+	venus_hfi_for_each_power_domain(core, pdinfo)
+		d_vpr_h("%s: pd name %s\n", __func__, pdinfo->name);
+
+	/* get power domain handle */
+	venus_hfi_for_each_power_domain(core, pdinfo) {
+		pdinfo->genpd_dev = devm_pd_get(&core->pdev->dev, pdinfo->name);
+		if (IS_ERR_OR_NULL(pdinfo->genpd_dev)) {
+			rc = PTR_ERR(pdinfo->genpd_dev) ?
+				PTR_ERR(pdinfo->genpd_dev) : -EBADHANDLE;
+			d_vpr_e("%s: failed to get pd: %s\n", __func__, pdinfo->name);
+			pdinfo->genpd_dev = NULL;
+			return rc;
+		}
+	}
+
+	opp_tbl = core->platform->data.opp_tbl;
+	opp_count = core->platform->data.opp_tbl_size;
+
+	/* skip init if opp not supported */
+	if (opp_count < 2) {
+		d_vpr_h("%s: opp entries not available\n", __func__);
+		return 0;
 	}
 
-	/* print regulator fields */
-	venus_hfi_for_each_regulator(core, rinfo) {
-		d_vpr_h("%s: name %s hw_power_collapse %d\n",
-			__func__, rinfo->name, rinfo->hw_power_collapse);
+	/* sanitize opp table */
+	if (!opp_tbl) {
+		d_vpr_e("%s: invalid opp table\n", __func__);
+		return -EINVAL;
 	}
 
-	/* get regulator handle */
-	venus_hfi_for_each_regulator(core, rinfo) {
-		rinfo->regulator = devm_regulator_get(&core->pdev->dev, rinfo->name);
-		if (IS_ERR_OR_NULL(rinfo->regulator)) {
-			rc = PTR_ERR(rinfo->regulator) ?
-				PTR_ERR(rinfo->regulator) : -EBADHANDLE;
-			d_vpr_e("%s: failed to get regulator: %s\n", __func__, rinfo->name);
-			rinfo->regulator = NULL;
+	/* ignore NULL entry at the end of table */
+	opp_count -= 1;
+
+	/* print opp table entries */
+	for (cnt = 0; cnt < opp_count; cnt++)
+		d_vpr_h("%s: opp name %s\n", __func__, opp_tbl[cnt]);
+
+	/* populate opp power domains(for rails) */
+	rc = devm_pm_opp_attach_genpd(&core->pdev->dev, opp_tbl, &opp_vdevs);
+	if (rc)
+		return rc;
+
+	/* create device_links b/w consumer(dev) and multiple suppliers(mx, mmcx) */
+	for (cnt = 0; cnt < opp_count; cnt++) {
+		rc = devm_opp_dl_get(&core->pdev->dev, opp_vdevs[cnt]);
+		if (rc) {
+			d_vpr_e("%s: failed to create dl: %s\n",
+				__func__, dev_name(opp_vdevs[cnt]));
 			return rc;
 		}
 	}
 
+	/* initialize opp table from device tree */
+	rc = devm_pm_opp_of_add_table(&core->pdev->dev);
+	if (rc) {
+		d_vpr_e("%s: failed to add opp table\n", __func__);
+		return rc;
+	}
+
+	/**
+	 * 1. power up mx & mmcx supply for RCG(mvs0_clk_src)
+	 * 2. power up gdsc0c for mvs0c branch clk
+	 * 3. power up gdsc0 for mvs0 branch clk
+	 */
+
+	/**
+	 * power up mxc, mmcx rails to enable supply for
+	 * RCG(video_cc_mvs0_clk_src)
+	 */
+	/* enable runtime pm */
+	rc = devm_pm_runtime_enable(&core->pdev->dev);
+	if (rc) {
+		d_vpr_e("%s: failed to enable runtime pm\n", __func__);
+		return rc;
+	}
+	/* power up rails(mxc & mmcx) */
+	rc = devm_pm_runtime_get_sync(&core->pdev->dev);
+	if (rc) {
+		d_vpr_e("%s: failed to get sync runtime pm\n", __func__);
+		return rc;
+	}
+
 	return rc;
 }
 
@@ -715,228 +904,75 @@ static int __register_mmrm(struct msm_vidc_core *core)
 }
 #endif
 
-
-
-static int __acquire_regulator(struct msm_vidc_core *core,
-			       struct regulator_info *rinfo)
+static int __enable_power_domains(struct msm_vidc_core *core, const char *name)
 {
+	struct power_domain_info *pdinfo = NULL;
 	int rc = 0;
 
-	if (!core || !rinfo) {
-		d_vpr_e("%s: invalid params\n", __func__);
-		return -EINVAL;
+	/* power up rails(mxc & mmcx) to enable RCG(video_cc_mvs0_clk_src) */
+	rc = __opp_set_rate(core, ULONG_MAX);
+	if (rc) {
+		d_vpr_e("%s: opp setrate failed\n", __func__);
+		return rc;
 	}
 
-	if (rinfo->hw_power_collapse) {
-		if (!rinfo->regulator) {
-			d_vpr_e("%s: invalid regulator\n", __func__);
-			rc = -EINVAL;
-			goto exit;
-		}
-
-		if (regulator_get_mode(rinfo->regulator) ==
-				REGULATOR_MODE_NORMAL) {
-			/* clear handoff from core sub_state */
-			msm_vidc_change_core_sub_state(core,
-				CORE_SUBSTATE_GDSC_HANDOFF, 0, __func__);
-			d_vpr_h("Skip acquire regulator %s\n", rinfo->name);
-			goto exit;
-		}
+	/* power up (gdsc0/gdsc0c) to enable (mvs0/mvs0c) branch clock */
+	venus_hfi_for_each_power_domain(core, pdinfo) {
+		if (strcmp(pdinfo->name, name))
+			continue;
 
-		rc = regulator_set_mode(rinfo->regulator,
-				REGULATOR_MODE_NORMAL);
+		rc = pm_runtime_get_sync(pdinfo->genpd_dev);
 		if (rc) {
-			/*
-			 * This is somewhat fatal, but nothing we can do
-			 * about it. We can't disable the regulator w/o
-			 * getting it back under s/w control
-			 */
-			d_vpr_e("Failed to acquire regulator control: %s\n",
-				rinfo->name);
-			goto exit;
-		} else {
-			/* reset handoff from core sub_state */
-			msm_vidc_change_core_sub_state(core,
-				CORE_SUBSTATE_GDSC_HANDOFF, 0, __func__);
-			d_vpr_h("Acquired regulator control from HW: %s\n",
-					rinfo->name);
-
-		}
-
-		if (!regulator_is_enabled(rinfo->regulator)) {
-			d_vpr_e("%s: Regulator is not enabled %s\n",
-				__func__, rinfo->name);
-			__fatal_error(true);
+			d_vpr_e("%s: failed to get sync: %s\n", __func__, pdinfo->name);
+			return rc;
 		}
+		d_vpr_h("%s: enabled power doamin %s\n", __func__, pdinfo->name);
 	}
 
-exit:
 	return rc;
 }
 
-static int __acquire_regulators(struct msm_vidc_core *core)
+static int __disable_power_domains(struct msm_vidc_core *core, const char *name)
 {
+	struct power_domain_info *pdinfo = NULL;
 	int rc = 0;
-	struct regulator_info *rinfo;
 
-	venus_hfi_for_each_regulator(core, rinfo)
-		__acquire_regulator(core, rinfo);
-
-	return rc;
-}
-
-static int __hand_off_regulator(struct msm_vidc_core *core,
-	struct regulator_info *rinfo)
-{
-	int rc = 0;
-
-	if (rinfo->hw_power_collapse) {
-		if (!rinfo->regulator) {
-			d_vpr_e("%s: invalid regulator\n", __func__);
-			return -EINVAL;
-		}
+	/* power down (gdsc0/gdsc0c) to disable (mvs0/mvs0c) branch clock */
+	venus_hfi_for_each_power_domain(core, pdinfo) {
+		if (strcmp(pdinfo->name, name))
+			continue;
 
-		rc = regulator_set_mode(rinfo->regulator,
-				REGULATOR_MODE_FAST);
+		rc = pm_runtime_put_sync(pdinfo->genpd_dev);
 		if (rc) {
-			d_vpr_e("Failed to hand off regulator control: %s\n",
-				rinfo->name);
+			d_vpr_e("%s: failed to put sync: %s\n", __func__, pdinfo->name);
 			return rc;
-		} else {
-			/* set handoff done in core sub_state */
-			msm_vidc_change_core_sub_state(core,
-				0, CORE_SUBSTATE_GDSC_HANDOFF, __func__);
-			d_vpr_h("Hand off regulator control to HW: %s\n",
-					rinfo->name);
-		}
-
-		if (!regulator_is_enabled(rinfo->regulator)) {
-			d_vpr_e("%s: Regulator is not enabled %s\n",
-				__func__, rinfo->name);
-			__fatal_error(true);
 		}
+		d_vpr_h("%s: disabled power doamin %s\n", __func__, pdinfo->name);
 	}
 
-	return rc;
-}
-
-static int __hand_off_regulators(struct msm_vidc_core *core)
-{
-	struct regulator_info *rinfo;
-	int rc = 0, c = 0;
-
-	venus_hfi_for_each_regulator(core, rinfo) {
-		rc = __hand_off_regulator(core, rinfo);
-		/*
-		 * If one regulator hand off failed, driver should take
-		 * the control for other regulators back.
-		 */
-		if (rc)
-			goto err_reg_handoff_failed;
-		c++;
+	/* power down rails(mxc & mmcx) to disable RCG(video_cc_mvs0_clk_src) */
+	rc = __opp_set_rate(core, 0);
+	if (rc) {
+		d_vpr_e("%s: opp setrate failed\n", __func__);
+		return rc;
 	}
-
-	return rc;
-err_reg_handoff_failed:
-	venus_hfi_for_each_regulator_reverse_continue(core, rinfo, c)
-		__acquire_regulator(core, rinfo);
+	msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_GDSC_HANDOFF, 0, __func__);
 
 	return rc;
 }
 
-static int __disable_regulator(struct msm_vidc_core *core, const char *reg_name)
+static int __hand_off_power_domains(struct msm_vidc_core *core)
 {
-	int rc = 0;
-	struct regulator_info *rinfo;
-	bool found;
-
-	if (!core || !reg_name) {
-		d_vpr_e("%s: invalid params\n", __func__);
-		return -EINVAL;
-	}
+	msm_vidc_change_core_sub_state(core, 0, CORE_SUBSTATE_GDSC_HANDOFF, __func__);
 
-	found = false;
-	venus_hfi_for_each_regulator(core, rinfo) {
-		if (!rinfo->regulator) {
-			d_vpr_e("%s: invalid regulator %s\n",
-				__func__, rinfo->name);
-			return -EINVAL;
-		}
-		if (strcmp(rinfo->name, reg_name))
-			continue;
-		found = true;
-
-		rc = __acquire_regulator(core, rinfo);
-		if (rc) {
-			d_vpr_e("%s: failed to acquire %s, rc = %d\n",
-				__func__, rinfo->name, rc);
-			/* Bring attention to this issue */
-			WARN_ON(true);
-			return rc;
-		}
-		/* reset handoff done from core sub_state */
-		msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_GDSC_HANDOFF, 0, __func__);
-
-		rc = regulator_disable(rinfo->regulator);
-		if (rc) {
-			d_vpr_e("%s: failed to disable %s, rc = %d\n",
-				__func__, rinfo->name, rc);
-			return rc;
-		}
-		d_vpr_h("%s: disabled regulator %s\n", __func__, rinfo->name);
-		break;
-	}
-	if (!found) {
-		d_vpr_e("%s: regulator %s not found\n", __func__, reg_name);
-		return -EINVAL;
-	}
-
-	return rc;
+	return 0;
 }
 
-static int __enable_regulator(struct msm_vidc_core *core, const char *reg_name)
+static int __acquire_power_domains(struct msm_vidc_core *core)
 {
-	int rc = 0;
-	struct regulator_info *rinfo;
-	bool found;
-
-	if (!core || !reg_name) {
-		d_vpr_e("%s: invalid params\n", __func__);
-		return -EINVAL;
-	}
-
-	found = false;
-	venus_hfi_for_each_regulator(core, rinfo) {
-		if (!rinfo->regulator) {
-			d_vpr_e("%s: invalid regulator %s\n",
-				__func__, rinfo->name);
-			return -EINVAL;
-		}
-		if (strcmp(rinfo->name, reg_name))
-			continue;
-		found = true;
+	msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_GDSC_HANDOFF, 0, __func__);
 
-		rc = regulator_enable(rinfo->regulator);
-		if (rc) {
-			d_vpr_e("%s: failed to enable %s, rc = %d\n",
-				__func__, rinfo->name, rc);
-			return rc;
-		}
-		if (!regulator_is_enabled(rinfo->regulator)) {
-			d_vpr_e("%s: regulator %s not enabled\n",
-				__func__, rinfo->name);
-			regulator_disable(rinfo->regulator);
-			return -EINVAL;
-		}
-		d_vpr_h("%s: enabled regulator %s\n", __func__, rinfo->name);
-		break;
-	}
-	if (!found) {
-		d_vpr_e("%s: regulator %s not found\n", __func__, reg_name);
-		return -EINVAL;
-	}
-
-	return rc;
+	return 0;
 }
 
 static int __disable_subcaches(struct msm_vidc_core *core)
@@ -1238,68 +1274,6 @@ static int update_residency_stats(
 	return rc;
 }
 
-#ifdef CONFIG_MSM_MMRM
-static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
-			  u64 rate)
-{
-	int rc = 0;
-	struct mmrm_client_data client_data;
-	struct mmrm_client *client;
-	u64 srate;
-
-	/* not registered */
-	if (!core || !cl || !core->platform) {
-		d_vpr_e("%s: invalid params\n", __func__);
-		return -EINVAL;
-	}
-
-	if (is_mmrm_supported(core) && !cl->mmrm_client) {
-		d_vpr_e("%s: invalid mmrm client\n", __func__);
-		return -EINVAL;
-	}
-
-	/* update clock residency stats */
-	update_residency_stats(core, cl, rate);
-
-	/*
-	 * This conversion is necessary since we are scaling clock values based on
-	 * the branch clock. However, mmrm driver expects source clock to be registered
-	 * and used for scaling.
-	 * TODO: Remove this scaling if using source clock instead of branch clock.
-	 */
-	srate = rate * MSM_VIDC_CLOCK_SOURCE_SCALING_RATIO;
-
-	/* bail early if requested clk rate is not changed */
-	if (rate == cl->prev)
-		return 0;
-
-	d_vpr_p("Scaling clock %s to %llu, prev %llu\n",
-		cl->name, srate, cl->prev * MSM_VIDC_CLOCK_SOURCE_SCALING_RATIO);
-
-	if (is_mmrm_supported(core)) {
-		/* set clock rate to mmrm driver */
-		client = cl->mmrm_client;
-		memset(&client_data, 0, sizeof(client_data));
-		client_data.num_hw_blocks = 1;
-		rc = mmrm_client_set_value(client, &client_data, srate);
-		if (rc) {
-			d_vpr_e("%s: Failed to set mmrm clock rate %llu %s: %d\n",
-				__func__, srate, cl->name, rc);
-			return rc;
-		}
-	} else {
-		/* set clock rate to clock driver */
-		rc = clk_set_rate(cl->clk, srate);
-		if (rc) {
-			d_vpr_e("%s: Failed to set clock rate %llu %s: %d\n",
-				__func__, srate, cl->name, rc);
-			return rc;
-		}
-	}
-	cl->prev = rate;
-	return rc;
-}
-#else
 static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
 			  u64 rate)
 {
@@ -1341,12 +1315,18 @@ static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
 
 	return rc;
 }
-#endif
 
 static int __set_clocks(struct msm_vidc_core *core, u64 freq)
 {
-	int rc = 0;
 	struct clock_info *cl;
+	int rc = 0;
+
+	/* scale mxc & mmcx rails */
+	rc = __opp_set_rate(core, freq);
+	if (rc) {
+		d_vpr_e("%s: opp setrate failed %lld\n", __func__, freq);
+		return rc;
+	}
 
 	venus_hfi_for_each_clock(core, cl) {
 		if (cl->has_scaling) {
@@ -1477,7 +1457,7 @@ static int __init_resources(struct msm_vidc_core *core)
 	if (rc)
 		return rc;
 
-	rc = __init_regulators(core);
+	rc = call_res_op(core, gdsc_init, core);
 	if (rc)
 		return rc;
 
@@ -1748,10 +1728,11 @@ static const struct msm_vidc_resources_ops res_ops = {
 	.reset_control_release = __reset_control_release_name,
 	.reset_control_assert = __reset_control_assert_name,
 	.reset_control_deassert = __reset_control_deassert_name,
-	.gdsc_on = __enable_regulator,
-	.gdsc_off = __disable_regulator,
-	.gdsc_hw_ctrl = __hand_off_regulators,
-	.gdsc_sw_ctrl = __acquire_regulators,
+	.gdsc_init = __init_power_domains,
+	.gdsc_on = __enable_power_domains,
+	.gdsc_off = __disable_power_domains,
+	.gdsc_hw_ctrl = __hand_off_power_domains,
+	.gdsc_sw_ctrl = __acquire_power_domains,
 	.llcc = llcc_enable,
 	.set_bw = set_bw,
 	.set_clks = __set_clocks,

+ 516 - 0
driver/vidc/src/resources_ext.c

@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#ifdef CONFIG_MSM_MMRM
+#include <linux/soc/qcom/msm_mmrm.h>
+#endif
+
+#include "resources.h"
+#include "msm_vidc_core.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_power.h"
+#include "msm_vidc_driver.h"
+#include "msm_vidc_platform.h"
+
+static void __fatal_error(bool fatal)
+{
+	WARN_ON(fatal);
+}
+
+static int __init_regulators(struct msm_vidc_core *core)
+{
+	const struct regulator_table *regulator_tbl;
+	struct regulator_set *regulators;
+	struct regulator_info *rinfo = NULL;
+	u32 regulator_count = 0, cnt = 0;
+	int rc = 0;
+
+	if (!core || !core->resource || !core->platform) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+	regulators = &core->resource->regulator_set;
+
+	regulator_tbl = core->platform->data.regulator_tbl;
+	regulator_count = core->platform->data.regulator_tbl_size;
+
+	/* skip init if regulators not supported */
+	if (!regulator_count) {
+		d_vpr_h("%s: regulators are not available in database\n", __func__);
+		return 0;
+	}
+
+	/* sanitize regulator table */
+	if (!regulator_tbl) {
+		d_vpr_e("%s: invalid regulator tbl\n", __func__);
+		return -EINVAL;
+	}
+
+	/* allocate regulator_set */
+	regulators->regulator_tbl = devm_kzalloc(&core->pdev->dev,
+			sizeof(*regulators->regulator_tbl) * regulator_count, GFP_KERNEL);
+	if (!regulators->regulator_tbl) {
+		d_vpr_e("%s: failed to alloc memory for regulator table\n", __func__);
+		return -ENOMEM;
+	}
+	regulators->count = regulator_count;
+
+	/* populate regulator fields */
+	for (cnt = 0; cnt < regulators->count; cnt++) {
+		regulators->regulator_tbl[cnt].name = regulator_tbl[cnt].name;
+		regulators->regulator_tbl[cnt].hw_power_collapse = regulator_tbl[cnt].hw_trigger;
+	}
+
+	/* print regulator fields */
+	venus_hfi_for_each_regulator(core, rinfo) {
+		d_vpr_h("%s: name %s hw_power_collapse %d\n",
+			__func__, rinfo->name, rinfo->hw_power_collapse);
+	}
+
+	/* get regulator handle */
+	venus_hfi_for_each_regulator(core, rinfo) {
+		rinfo->regulator = devm_regulator_get(&core->pdev->dev, rinfo->name);
+		if (IS_ERR_OR_NULL(rinfo->regulator)) {
+			rc = PTR_ERR(rinfo->regulator) ?
+				PTR_ERR(rinfo->regulator) : -EBADHANDLE;
+			d_vpr_e("%s: failed to get regulator: %s\n", __func__, rinfo->name);
+			rinfo->regulator = NULL;
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+static int __acquire_regulator(struct msm_vidc_core *core,
+			       struct regulator_info *rinfo)
+{
+	int rc = 0;
+
+	if (!core || !rinfo) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	if (rinfo->hw_power_collapse) {
+		if (!rinfo->regulator) {
+			d_vpr_e("%s: invalid regulator\n", __func__);
+			rc = -EINVAL;
+			goto exit;
+		}
+
+		if (regulator_get_mode(rinfo->regulator) ==
+				REGULATOR_MODE_NORMAL) {
+			/* clear handoff from core sub_state */
+			msm_vidc_change_core_sub_state(core,
+				CORE_SUBSTATE_GDSC_HANDOFF, 0, __func__);
+			d_vpr_h("Skip acquire regulator %s\n", rinfo->name);
+			goto exit;
+		}
+
+		rc = regulator_set_mode(rinfo->regulator,
+				REGULATOR_MODE_NORMAL);
+		if (rc) {
+			/*
+			 * This is somewhat fatal, but nothing we can do
+			 * about it. We can't disable the regulator w/o
+			 * getting it back under s/w control
+			 */
+			d_vpr_e("Failed to acquire regulator control: %s\n",
+				rinfo->name);
+			goto exit;
+		} else {
+			/* reset handoff from core sub_state */
+			msm_vidc_change_core_sub_state(core,
+				CORE_SUBSTATE_GDSC_HANDOFF, 0, __func__);
+			d_vpr_h("Acquired regulator control from HW: %s\n",
+					rinfo->name);
+
+		}
+
+		if (!regulator_is_enabled(rinfo->regulator)) {
+			d_vpr_e("%s: Regulator is not enabled %s\n",
+				__func__, rinfo->name);
+			__fatal_error(true);
+		}
+	}
+
+exit:
+	return rc;
+}
+
+static int __hand_off_regulator(struct msm_vidc_core *core,
+	struct regulator_info *rinfo)
+{
+	int rc = 0;
+
+	if (rinfo->hw_power_collapse) {
+		if (!rinfo->regulator) {
+			d_vpr_e("%s: invalid regulator\n", __func__);
+			return -EINVAL;
+		}
+
+		rc = regulator_set_mode(rinfo->regulator,
+				REGULATOR_MODE_FAST);
+		if (rc) {
+			d_vpr_e("Failed to hand off regulator control: %s\n",
+				rinfo->name);
+			return rc;
+		} else {
+			/* set handoff done in core sub_state */
+			msm_vidc_change_core_sub_state(core,
+				0, CORE_SUBSTATE_GDSC_HANDOFF, __func__);
+			d_vpr_h("Hand off regulator control to HW: %s\n",
+					rinfo->name);
+		}
+
+		if (!regulator_is_enabled(rinfo->regulator)) {
+			d_vpr_e("%s: Regulator is not enabled %s\n",
+				__func__, rinfo->name);
+			__fatal_error(true);
+		}
+	}
+
+	return rc;
+}
+
+static int __enable_regulator(struct msm_vidc_core *core, const char *reg_name)
+{
+	int rc = 0;
+	struct regulator_info *rinfo;
+	bool found;
+
+	if (!core || !reg_name) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	found = false;
+	venus_hfi_for_each_regulator(core, rinfo) {
+		if (!rinfo->regulator) {
+			d_vpr_e("%s: invalid regulator %s\n",
+				__func__, rinfo->name);
+			return -EINVAL;
+		}
+		if (strcmp(rinfo->name, reg_name))
+			continue;
+		found = true;
+
+		rc = regulator_enable(rinfo->regulator);
+		if (rc) {
+			d_vpr_e("%s: failed to enable %s, rc = %d\n",
+				__func__, rinfo->name, rc);
+			return rc;
+		}
+		if (!regulator_is_enabled(rinfo->regulator)) {
+			d_vpr_e("%s: regulator %s not enabled\n",
+				__func__, rinfo->name);
+			regulator_disable(rinfo->regulator);
+			return -EINVAL;
+		}
+		d_vpr_h("%s: enabled regulator %s\n", __func__, rinfo->name);
+		break;
+	}
+	if (!found) {
+		d_vpr_e("%s: regulator %s not found\n", __func__, reg_name);
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+static int __disable_regulator(struct msm_vidc_core *core, const char *reg_name)
+{
+	int rc = 0;
+	struct regulator_info *rinfo;
+	bool found;
+
+	if (!core || !reg_name) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	found = false;
+	venus_hfi_for_each_regulator(core, rinfo) {
+		if (!rinfo->regulator) {
+			d_vpr_e("%s: invalid regulator %s\n",
+				__func__, rinfo->name);
+			return -EINVAL;
+		}
+		if (strcmp(rinfo->name, reg_name))
+			continue;
+		found = true;
+
+		rc = __acquire_regulator(core, rinfo);
+		if (rc) {
+			d_vpr_e("%s: failed to acquire %s, rc = %d\n",
+				__func__, rinfo->name, rc);
+			/* Bring attention to this issue */
+			WARN_ON(true);
+			return rc;
+		}
+		/* reset handoff done from core sub_state */
+		msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_GDSC_HANDOFF, 0, __func__);
+
+		rc = regulator_disable(rinfo->regulator);
+		if (rc) {
+			d_vpr_e("%s: failed to disable %s, rc = %d\n",
+				__func__, rinfo->name, rc);
+			return rc;
+		}
+		d_vpr_h("%s: disabled regulator %s\n", __func__, rinfo->name);
+		break;
+	}
+	if (!found) {
+		d_vpr_e("%s: regulator %s not found\n", __func__, reg_name);
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+static int __hand_off_regulators(struct msm_vidc_core *core)
+{
+	struct regulator_info *rinfo;
+	int rc = 0, c = 0;
+
+	venus_hfi_for_each_regulator(core, rinfo) {
+		rc = __hand_off_regulator(core, rinfo);
+		/*
+		 * If one regulator hand off failed, driver should take
+		 * the control for other regulators back.
+		 */
+		if (rc)
+			goto err_reg_handoff_failed;
+		c++;
+	}
+
+	return rc;
+err_reg_handoff_failed:
+	venus_hfi_for_each_regulator_reverse_continue(core, rinfo, c)
+		__acquire_regulator(core, rinfo);
+
+	return rc;
+}
+
+static int __acquire_regulators(struct msm_vidc_core *core)
+{
+	int rc = 0;
+	struct regulator_info *rinfo;
+
+	venus_hfi_for_each_regulator(core, rinfo)
+		__acquire_regulator(core, rinfo);
+
+	return rc;
+}
+
+static struct clock_residency *get_residency_stats(struct clock_info *cl, u64 rate)
+{
+	struct clock_residency *residency = NULL;
+	bool found = false;
+
+	if (!cl) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return NULL;
+	}
+
+	list_for_each_entry(residency, &cl->residency_list, list) {
+		if (residency->rate == rate) {
+			found = true;
+			break;
+		}
+	}
+
+	return found ? residency : NULL;
+}
+
+static int update_residency_stats(
+	struct msm_vidc_core *core, struct clock_info *cl, u64 rate)
+{
+	struct clock_residency *cur_residency = NULL, *prev_residency = NULL;
+	u64 cur_time_us = 0;
+	int rc = 0;
+
+	if (!core || !cl) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	/* skip update if scaling not supported */
+	if (!cl->has_scaling)
+		return 0;
+
+	/* skip update if rate not changed */
+	if (rate == cl->prev)
+		return 0;
+
+	/* get current time in ns */
+	cur_time_us = ktime_get_ns() / 1000;
+
+	/* update previous rate residency end or total time */
+	prev_residency = get_residency_stats(cl, cl->prev);
+	if (prev_residency) {
+		if (prev_residency->start_time_us)
+			prev_residency->total_time_us = cur_time_us - prev_residency->start_time_us;
+
+		/* reset start time us */
+		prev_residency->start_time_us = 0;
+	}
+
+	/* clk disable case - no need to update new entry */
+	if (rate == 0)
+		return 0;
+
+	/* check if rate entry is present */
+	cur_residency = get_residency_stats(cl, rate);
+	if (!cur_residency) {
+		d_vpr_e("%s: entry not found. rate %llu\n", __func__, rate);
+		return -EINVAL;
+	}
+
+	/* update residency start time for current rate/freq */
+	cur_residency->start_time_us = cur_time_us;
+
+	return rc;
+}
+
+#ifdef CONFIG_MSM_MMRM
+static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
+			  u64 rate)
+{
+	int rc = 0;
+	struct mmrm_client_data client_data;
+	struct mmrm_client *client;
+	u64 srate;
+
+	/* not registered */
+	if (!core || !cl || !core->platform) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	if (is_mmrm_supported(core) && !cl->mmrm_client) {
+		d_vpr_e("%s: invalid mmrm client\n", __func__);
+		return -EINVAL;
+	}
+
+	/* update clock residency stats */
+	update_residency_stats(core, cl, rate);
+
+	/*
+	 * This conversion is necessary since we are scaling clock values based on
+	 * the branch clock. However, mmrm driver expects source clock to be registered
+	 * and used for scaling.
+	 * TODO: Remove this scaling if using source clock instead of branch clock.
+	 */
+	srate = rate * MSM_VIDC_CLOCK_SOURCE_SCALING_RATIO;
+
+	/* bail early if requested clk rate is not changed */
+	if (rate == cl->prev)
+		return 0;
+
+	d_vpr_p("Scaling clock %s to %llu, prev %llu\n",
+		cl->name, srate, cl->prev * MSM_VIDC_CLOCK_SOURCE_SCALING_RATIO);
+
+	if (is_mmrm_supported(core)) {
+		/* set clock rate to mmrm driver */
+		client = cl->mmrm_client;
+		memset(&client_data, 0, sizeof(client_data));
+		client_data.num_hw_blocks = 1;
+		rc = mmrm_client_set_value(client, &client_data, srate);
+		if (rc) {
+			d_vpr_e("%s: Failed to set mmrm clock rate %llu %s: %d\n",
+				__func__, srate, cl->name, rc);
+			return rc;
+		}
+	} else {
+		/* set clock rate to clock driver */
+		rc = clk_set_rate(cl->clk, srate);
+		if (rc) {
+			d_vpr_e("%s: Failed to set clock rate %llu %s: %d\n",
+				__func__, srate, cl->name, rc);
+			return rc;
+		}
+	}
+	cl->prev = rate;
+	return rc;
+}
+#else
+static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
+			  u64 rate)
+{
+	u64 srate;
+	int rc = 0;
+
+	/* not registered */
+	if (!core || !cl || !core->capabilities) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	/* update clock residency stats */
+	update_residency_stats(core, cl, rate);
+
+	/*
+	 * This conversion is necessary since we are scaling clock values based on
+	 * the branch clock. However, mmrm driver expects source clock to be registered
+	 * and used for scaling.
+	 * TODO: Remove this scaling if using source clock instead of branch clock.
+	 */
+	srate = rate * MSM_VIDC_CLOCK_SOURCE_SCALING_RATIO;
+
+	/* bail early if requested clk rate is not changed */
+	if (rate == cl->prev)
+		return 0;
+
+	d_vpr_p("Scaling clock %s to %llu, prev %llu\n",
+		cl->name, srate, cl->prev * MSM_VIDC_CLOCK_SOURCE_SCALING_RATIO);
+
+	rc = clk_set_rate(cl->clk, srate);
+	if (rc) {
+		d_vpr_e("%s: Failed to set clock rate %llu %s: %d\n",
+			__func__, srate, cl->name, rc);
+		return rc;
+	}
+
+	cl->prev = rate;
+
+	return rc;
+}
+#endif
+
+static int __set_clocks_ext(struct msm_vidc_core *core, u64 freq)
+{
+	int rc = 0;
+	struct clock_info *cl;
+
+	venus_hfi_for_each_clock(core, cl) {
+		if (cl->has_scaling) {
+			rc = __set_clk_rate(core, cl, freq);
+			if (rc)
+				return rc;
+		}
+	}
+
+	return 0;
+}
+
+const struct msm_vidc_resources_ops *get_res_ops_ext(void)
+{
+	const struct msm_vidc_resources_ops *res_ops = get_resources_ops();
+	static struct msm_vidc_resources_ops res_ops_ext;
+
+	memcpy(&res_ops_ext, res_ops, sizeof(struct msm_vidc_resources_ops));
+	res_ops_ext.gdsc_init        = __init_regulators;
+	res_ops_ext.gdsc_on          = __enable_regulator;
+	res_ops_ext.gdsc_off         = __disable_regulator;
+	res_ops_ext.gdsc_hw_ctrl     = __hand_off_regulators;
+	res_ops_ext.gdsc_sw_ctrl     = __acquire_regulators;
+	res_ops_ext.set_clks         = __set_clocks_ext;
+
+	return &res_ops_ext;
+}

+ 1 - 0
msm_video/Kbuild

@@ -88,6 +88,7 @@ msm_video-objs += $(VIDEO_DRIVER_REL_PATH)/vidc/src/msm_vidc_v4l2.o \
                   $(VIDEO_DRIVER_REL_PATH)/vidc/src/msm_vidc_power.o \
                   $(VIDEO_DRIVER_REL_PATH)/vidc/src/msm_vidc_probe.o \
                   $(VIDEO_DRIVER_REL_PATH)/vidc/src/resources.o \
+                  $(VIDEO_DRIVER_REL_PATH)/vidc/src/resources_ext.o \
                   $(VIDEO_DRIVER_REL_PATH)/vidc/src/firmware.o \
                   $(VIDEO_DRIVER_REL_PATH)/vidc/src/msm_vidc_debug.o \
                   $(VIDEO_DRIVER_REL_PATH)/vidc/src/msm_vidc_memory.o \