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 <quic_grajagop@quicinc.com>
This commit is contained in:
Govindaraj Rajagopal
2023-01-06 11:03:55 +05:30
parent ea34534af0
commit ab9b1a112b
15 changed files with 899 additions and 319 deletions

View File

@@ -48,6 +48,10 @@ struct bw_table {
u32 max_kbps; u32 max_kbps;
}; };
struct pd_table {
const char *name;
};
struct regulator_table { struct regulator_table {
const char *name; const char *name;
bool hw_trigger; bool hw_trigger;
@@ -187,6 +191,10 @@ struct msm_vidc_platform_data {
unsigned int bw_tbl_size; unsigned int bw_tbl_size;
const struct regulator_table *regulator_tbl; const struct regulator_table *regulator_tbl;
unsigned int regulator_tbl_size; 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; const struct clk_table *clk_tbl;
unsigned int clk_tbl_size; unsigned int clk_tbl_size;
const struct clk_rst_table *clk_rst_tbl; 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; 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_init_platform(struct platform_device *pdev);
int msm_vidc_deinit_platform(struct platform_device *pdev); int msm_vidc_deinit_platform(struct platform_device *pdev);

View File

@@ -214,6 +214,15 @@ static int msm_vidc_init_ops(struct msm_vidc_core *core)
core->media_device_ops = &msm_v4l2_media_ops; core->media_device_ops = &msm_v4l2_media_ops;
core->v4l2_m2m_ops = &msm_v4l2_m2m_ops; core->v4l2_m2m_ops = &msm_v4l2_m2m_ops;
core->mem_ops = get_mem_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; return 0;
} }

View File

@@ -17,6 +17,7 @@
#include "msm_vidc_internal.h" #include "msm_vidc_internal.h"
#include "msm_vidc_control_ext.h" #include "msm_vidc_control_ext.h"
#include "msm_vidc_memory_ext.h" #include "msm_vidc_memory_ext.h"
#include "resources_ext.h"
#include "msm_vidc_iris3.h" #include "msm_vidc_iris3.h"
#include "hfi_property.h" #include "hfi_property.h"
#include "hfi_command.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->platform->data = kalama_data;
core->mem_ops = get_mem_ops_ext(); 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(); rc = msm_vidc_kalama_check_ddr_type();
if (rc) if (rc)
return rc; return rc;

View File

@@ -17,6 +17,7 @@
#include "msm_vidc_internal.h" #include "msm_vidc_internal.h"
#include "msm_vidc_control_ext.h" #include "msm_vidc_control_ext.h"
#include "msm_vidc_memory_ext.h" #include "msm_vidc_memory_ext.h"
#include "resources_ext.h"
#include "msm_vidc_iris33.h" #include "msm_vidc_iris33.h"
#include "hfi_property.h" #include "hfi_property.h"
#include "hfi_command.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->platform->data = pineapple_data;
core->mem_ops = get_mem_ops_ext(); 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(); rc = msm_vidc_pineapple_check_ddr_type();
if (rc) if (rc)
return rc; return rc;

View File

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

View File

@@ -2140,6 +2140,16 @@ static int msm_vidc_init_data(struct msm_vidc_core *core)
d_vpr_h("%s: initialize waipio data\n", __func__); d_vpr_h("%s: initialize waipio data\n", __func__);
core->platform->data = waipio_data; 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; return rc;
} }

View File

@@ -1668,6 +1668,15 @@ static const struct bw_table waipio_bw_table[] = {
{ "venus-ddr", 1000, 15000000 }, { "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 */ /* name, clock id, scaling */
static const struct clk_table waipio_clk_table[] = { static const struct clk_table waipio_clk_table[] = {
{ "gcc_video_axi0", GCC_VIDEO_AXI0_CLK, 0 }, { "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 */ /* resources dependent on other module */
.bw_tbl = waipio_bw_table, .bw_tbl = waipio_bw_table,
.bw_tbl_size = ARRAY_SIZE(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 = waipio_clk_table,
.clk_tbl_size = ARRAY_SIZE(waipio_clk_table), .clk_tbl_size = ARRAY_SIZE(waipio_clk_table),
.clk_rst_tbl = waipio_clk_reset_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 = waipio_context_bank_table,
.context_bank_tbl_size = ARRAY_SIZE(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 */ /* platform specific resources */
.freq_tbl = waipio_freq_table, .freq_tbl = waipio_freq_table,
.freq_tbl_size = ARRAY_SIZE(waipio_freq_table), .freq_tbl_size = ARRAY_SIZE(waipio_freq_table),

View File

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

View File

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

View File

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

View File

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

View File

@@ -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_

View File

@@ -6,6 +6,9 @@
#include <linux/sort.h> #include <linux/sort.h>
#include <linux/clk.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/reset.h>
#include <linux/interconnect.h> #include <linux/interconnect.h>
#include <linux/soc/qcom/llcc-qcom.h> #include <linux/soc/qcom/llcc-qcom.h>
@@ -32,6 +35,14 @@ enum reset_state {
DEASSERT, 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) static void __fatal_error(bool fatal)
{ {
WARN_ON(fatal); WARN_ON(fatal);
@@ -89,12 +100,125 @@ static struct mmrm_client *devm_mmrm_get(struct device *dev, struct mmrm_client_
} }
#endif #endif
/* A comparator to compare loads (needed later on) */ static void devm_pd_release(void *res)
static inline int cmp(const void *a, const void *b)
{ {
/* want to sort in reverse so flip the comparison */ struct device *pd = (struct device *)res;
return ((struct freq_table *)b)->freq -
((struct freq_table *)a)->freq; 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) 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; 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 power_domain_info *pdinfo = NULL;
struct regulator_set *regulators; const struct pd_table *pd_tbl;
struct regulator_info *rinfo = NULL; struct power_domain_set *pds;
u32 regulator_count = 0, cnt = 0; struct device **opp_vdevs;
const char **opp_tbl;
u32 pd_count = 0, opp_count = 0, cnt = 0;
int rc = 0; int rc = 0;
if (!core || !core->resource || !core->platform) { if (!core || !core->resource || !core->platform) {
d_vpr_e("%s: invalid params\n", __func__); d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL; return -EINVAL;
} }
regulators = &core->resource->regulator_set; pds = &core->resource->power_domain_set;
/* skip init if regulators not supported */ pd_tbl = core->platform->data.pd_tbl;
if (!is_regulator_supported(core)) { pd_count = core->platform->data.pd_tbl_size;
d_vpr_h("%s: regulators are not available in database\n", __func__);
/* 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; return 0;
} }
regulator_tbl = core->platform->data.regulator_tbl; /* sanitize power domain table */
regulator_count = core->platform->data.regulator_tbl_size; if (!pd_tbl) {
d_vpr_e("%s: invalid power domain tbl\n", __func__);
if (!regulator_tbl || !regulator_count) {
d_vpr_e("%s: invalid regulator tbl %#x or count %d\n",
__func__, regulator_tbl, regulator_count);
return -EINVAL; return -EINVAL;
} }
/* allocate regulator_set */ /* allocate power_domain_set */
regulators->regulator_tbl = devm_kzalloc(&core->pdev->dev, pds->power_domain_tbl = devm_kzalloc(&core->pdev->dev,
sizeof(*regulators->regulator_tbl) * regulator_count, GFP_KERNEL); sizeof(*pds->power_domain_tbl) * pd_count, GFP_KERNEL);
if (!regulators->regulator_tbl) { if (!pds->power_domain_tbl) {
d_vpr_e("%s: failed to alloc memory for regulator table\n", __func__); d_vpr_e("%s: failed to alloc memory for pd table\n", __func__);
return -ENOMEM; return -ENOMEM;
} }
regulators->count = regulator_count; pds->count = pd_count;
/* populate regulator fields */ /* populate power domain fields */
for (cnt = 0; cnt < regulators->count; cnt++) { for (cnt = 0; cnt < pds->count; cnt++)
regulators->regulator_tbl[cnt].name = regulator_tbl[cnt].name; pds->power_domain_tbl[cnt].name = pd_tbl[cnt].name;
regulators->regulator_tbl[cnt].hw_power_collapse = regulator_tbl[cnt].hw_trigger;
}
/* print regulator fields */ /* print power domain fields */
venus_hfi_for_each_regulator(core, rinfo) { venus_hfi_for_each_power_domain(core, pdinfo)
d_vpr_h("%s: name %s hw_power_collapse %d\n", d_vpr_h("%s: pd name %s\n", __func__, pdinfo->name);
__func__, rinfo->name, rinfo->hw_power_collapse);
}
/* get regulator handle */ /* get power domain handle */
venus_hfi_for_each_regulator(core, rinfo) { venus_hfi_for_each_power_domain(core, pdinfo) {
rinfo->regulator = devm_regulator_get(&core->pdev->dev, rinfo->name); pdinfo->genpd_dev = devm_pd_get(&core->pdev->dev, pdinfo->name);
if (IS_ERR_OR_NULL(rinfo->regulator)) { if (IS_ERR_OR_NULL(pdinfo->genpd_dev)) {
rc = PTR_ERR(rinfo->regulator) ? rc = PTR_ERR(pdinfo->genpd_dev) ?
PTR_ERR(rinfo->regulator) : -EBADHANDLE; PTR_ERR(pdinfo->genpd_dev) : -EBADHANDLE;
d_vpr_e("%s: failed to get regulator: %s\n", __func__, rinfo->name); d_vpr_e("%s: failed to get pd: %s\n", __func__, pdinfo->name);
rinfo->regulator = NULL; pdinfo->genpd_dev = NULL;
return rc; 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;
}
/* sanitize opp table */
if (!opp_tbl) {
d_vpr_e("%s: invalid opp table\n", __func__);
return -EINVAL;
}
/* 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; return rc;
} }
@@ -715,230 +904,77 @@ static int __register_mmrm(struct msm_vidc_core *core)
} }
#endif #endif
static int __enable_power_domains(struct msm_vidc_core *core, const char *name)
static int __acquire_regulator(struct msm_vidc_core *core,
struct regulator_info *rinfo)
{ {
struct power_domain_info *pdinfo = NULL;
int rc = 0; int rc = 0;
if (!core || !rinfo) { /* power up rails(mxc & mmcx) to enable RCG(video_cc_mvs0_clk_src) */
d_vpr_e("%s: invalid params\n", __func__); rc = __opp_set_rate(core, ULONG_MAX);
return -EINVAL; if (rc) {
d_vpr_e("%s: opp setrate failed\n", __func__);
return rc;
} }
if (rinfo->hw_power_collapse) { /* power up (gdsc0/gdsc0c) to enable (mvs0/mvs0c) branch clock */
if (!rinfo->regulator) { venus_hfi_for_each_power_domain(core, pdinfo) {
d_vpr_e("%s: invalid regulator\n", __func__); if (strcmp(pdinfo->name, name))
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 __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 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 __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 __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; continue;
found = true;
rc = __acquire_regulator(core, rinfo); rc = pm_runtime_get_sync(pdinfo->genpd_dev);
if (rc) { if (rc) {
d_vpr_e("%s: failed to acquire %s, rc = %d\n", d_vpr_e("%s: failed to get sync: %s\n", __func__, pdinfo->name);
__func__, rinfo->name, rc);
/* Bring attention to this issue */
WARN_ON(true);
return rc; return rc;
} }
/* reset handoff done from core sub_state */ d_vpr_h("%s: enabled power doamin %s\n", __func__, pdinfo->name);
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 rc;
} }
static int __enable_regulator(struct msm_vidc_core *core, const char *reg_name) static int __disable_power_domains(struct msm_vidc_core *core, const char *name)
{ {
struct power_domain_info *pdinfo = NULL;
int rc = 0; int rc = 0;
struct regulator_info *rinfo;
bool found;
if (!core || !reg_name) { /* power down (gdsc0/gdsc0c) to disable (mvs0/mvs0c) branch clock */
d_vpr_e("%s: invalid params\n", __func__); venus_hfi_for_each_power_domain(core, pdinfo) {
return -EINVAL; if (strcmp(pdinfo->name, name))
}
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; continue;
found = true;
rc = regulator_enable(rinfo->regulator); rc = pm_runtime_put_sync(pdinfo->genpd_dev);
if (rc) { if (rc) {
d_vpr_e("%s: failed to enable %s, rc = %d\n", d_vpr_e("%s: failed to put sync: %s\n", __func__, pdinfo->name);
__func__, rinfo->name, rc);
return rc; return rc;
} }
if (!regulator_is_enabled(rinfo->regulator)) { d_vpr_h("%s: disabled power doamin %s\n", __func__, pdinfo->name);
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;
} }
/* 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;
}
msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_GDSC_HANDOFF, 0, __func__);
return rc; return rc;
} }
static int __hand_off_power_domains(struct msm_vidc_core *core)
{
msm_vidc_change_core_sub_state(core, 0, CORE_SUBSTATE_GDSC_HANDOFF, __func__);
return 0;
}
static int __acquire_power_domains(struct msm_vidc_core *core)
{
msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_GDSC_HANDOFF, 0, __func__);
return 0;
}
static int __disable_subcaches(struct msm_vidc_core *core) static int __disable_subcaches(struct msm_vidc_core *core)
{ {
struct subcache_info *sinfo; struct subcache_info *sinfo;
@@ -1238,68 +1274,6 @@ static int update_residency_stats(
return rc; 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, static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
u64 rate) u64 rate)
{ {
@@ -1341,12 +1315,18 @@ static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
return rc; return rc;
} }
#endif
static int __set_clocks(struct msm_vidc_core *core, u64 freq) static int __set_clocks(struct msm_vidc_core *core, u64 freq)
{ {
int rc = 0;
struct clock_info *cl; 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) { venus_hfi_for_each_clock(core, cl) {
if (cl->has_scaling) { if (cl->has_scaling) {
@@ -1477,7 +1457,7 @@ static int __init_resources(struct msm_vidc_core *core)
if (rc) if (rc)
return rc; return rc;
rc = __init_regulators(core); rc = call_res_op(core, gdsc_init, core);
if (rc) if (rc)
return 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_release = __reset_control_release_name,
.reset_control_assert = __reset_control_assert_name, .reset_control_assert = __reset_control_assert_name,
.reset_control_deassert = __reset_control_deassert_name, .reset_control_deassert = __reset_control_deassert_name,
.gdsc_on = __enable_regulator, .gdsc_init = __init_power_domains,
.gdsc_off = __disable_regulator, .gdsc_on = __enable_power_domains,
.gdsc_hw_ctrl = __hand_off_regulators, .gdsc_off = __disable_power_domains,
.gdsc_sw_ctrl = __acquire_regulators, .gdsc_hw_ctrl = __hand_off_power_domains,
.gdsc_sw_ctrl = __acquire_power_domains,
.llcc = llcc_enable, .llcc = llcc_enable,
.set_bw = set_bw, .set_bw = set_bw,
.set_clks = __set_clocks, .set_clks = __set_clocks,

View File

@@ -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;
}

View File

@@ -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_power.o \
$(VIDEO_DRIVER_REL_PATH)/vidc/src/msm_vidc_probe.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.o \
$(VIDEO_DRIVER_REL_PATH)/vidc/src/resources_ext.o \
$(VIDEO_DRIVER_REL_PATH)/vidc/src/firmware.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_debug.o \
$(VIDEO_DRIVER_REL_PATH)/vidc/src/msm_vidc_memory.o \ $(VIDEO_DRIVER_REL_PATH)/vidc/src/msm_vidc_memory.o \