Re: [PATCH v6 10/14] media: iris: Add power sequence for Glymur
From: Dmitry Baryshkov
Date: Sun May 17 2026 - 14:09:31 EST
On Fri, May 15, 2026 at 04:51:25PM +0530, Vishnu Reddy wrote:
> The Glymur platform has two video codec cores: vcodec0 and vcodec1.
>
> Both cores share a common clock source (video_cc_mvs0_clk_src) and the
> same power rails. The clock dividers between the source and the branch
> clocks are fixed. So when both cores are running, the source clock always
> runs at the highest frequency requested by either core.
>
> Since both cores share the same power rails, the power corner cannot be
> voted independently. Scaling one core's power corner up or down would
> directly affect the other, leading to under or over-voting.
>
> For these reasons, both cores should voted the clock and power rail must
> be based on the workload of both cores.
>
> Reuse the existing code wherever possible and add power sequence for
> vcodec1.
>
> Reviewed-by: Vikash Garodia <vikash.garodia@xxxxxxxxxxxxxxxx>
> Signed-off-by: Vishnu Reddy <busanna.reddy@xxxxxxxxxxxxxxxx>
> ---
> .../platform/qcom/iris/iris_platform_common.h | 4 +
> drivers/media/platform/qcom/iris/iris_vpu3x.c | 137 ++++++++++++++++++++-
> drivers/media/platform/qcom/iris/iris_vpu_common.h | 1 +
> .../platform/qcom/iris/iris_vpu_register_defines.h | 10 ++
> 4 files changed, 147 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h
> index 1d757cb8e9e1..366e499dec53 100644
> --- a/drivers/media/platform/qcom/iris/iris_platform_common.h
> +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h
> @@ -63,6 +63,9 @@ enum platform_clk_type {
> IRIS_VCODEC_VPP0_CLK,
> IRIS_VCODEC_VPP1_CLK,
> IRIS_APV_HW_CLK,
> + IRIS_AXI_VCODEC1_CLK,
> + IRIS_VCODEC1_CLK,
> + IRIS_VCODEC1_FREERUN_CLK,
> };
>
> struct platform_clk_data {
> @@ -208,6 +211,7 @@ enum platform_pm_domain_type {
> IRIS_VCODEC_VPP0_POWER_DOMAIN,
> IRIS_VCODEC_VPP1_POWER_DOMAIN,
> IRIS_APV_HW_POWER_DOMAIN,
> + IRIS_VCODEC1_POWER_DOMAIN,
> };
>
> struct iris_firmware_data {
> diff --git a/drivers/media/platform/qcom/iris/iris_vpu3x.c b/drivers/media/platform/qcom/iris/iris_vpu3x.c
> index 39e9c78c3a69..68a4997af23f 100644
> --- a/drivers/media/platform/qcom/iris/iris_vpu3x.c
> +++ b/drivers/media/platform/qcom/iris/iris_vpu3x.c
> @@ -17,14 +17,14 @@
> #define NOC_HALT BIT(0)
> #define AON_WRAPPER_SPARE (AON_BASE_OFFS + 0x28)
>
> -static bool iris_vpu3x_hw_power_collapsed(struct iris_core *core)
> +static bool iris_vpu3x_hw_power_collapsed(struct iris_core *core, u32 pwr_status_bit)
> {
> u32 value, pwr_status;
>
> value = readl(core->reg_base + WRAPPER_CORE_POWER_STATUS);
> - pwr_status = value & BIT(1);
> + pwr_status = value & pwr_status_bit;
>
> - return pwr_status ? false : true;
> + return !pwr_status;
> }
>
> static void iris_vpu3_power_off_hardware(struct iris_core *core)
> @@ -32,7 +32,7 @@ static void iris_vpu3_power_off_hardware(struct iris_core *core)
> u32 reg_val = 0, value, i;
> int ret;
>
> - if (iris_vpu3x_hw_power_collapsed(core))
> + if (iris_vpu3x_hw_power_collapsed(core, VCODEC0_POWER_STATUS))
> goto disable_power;
>
> dev_err(core->dev, "video hw is power on\n");
> @@ -78,7 +78,7 @@ static void iris_vpu33_power_off_hardware(struct iris_core *core)
> u32 count = 0;
> int ret;
>
> - if (iris_vpu3x_hw_power_collapsed(core))
> + if (iris_vpu3x_hw_power_collapsed(core, VCODEC0_POWER_STATUS))
> goto disable_power;
>
> dev_err(core->dev, "video hw is power on\n");
> @@ -254,6 +254,124 @@ static void iris_vpu35_power_off_hw(struct iris_core *core)
> iris_disable_unprepare_clock(core, IRIS_AXI_VCODEC_CLK);
> }
>
> +static int iris_vpu36_power_on_hw1(struct iris_core *core)
> +{
> + int ret;
> +
> + ret = iris_enable_power_domains(core, IRIS_VCODEC1_POWER_DOMAIN);
> + if (ret)
> + return ret;
> +
> + ret = iris_prepare_enable_clock(core, IRIS_AXI_VCODEC1_CLK);
> + if (ret)
> + goto err_disable_hw1_power;
> +
> + ret = iris_prepare_enable_clock(core, IRIS_VCODEC1_FREERUN_CLK);
> + if (ret)
> + goto err_disable_axi1_clk;
> +
> + ret = iris_prepare_enable_clock(core, IRIS_VCODEC1_CLK);
> + if (ret)
> + goto err_disable_hw1_free_clk;
And this is what I'm talking about for the indirection. To bring up the
vcodec core, you need to enable several power domains and then power on
several clocks. Here you list them all one by one.
Compare this to:
struct iris_vcodec {
struct device *dev;
struct device* pd;
struct clk_bulk_data *clocks;
unsigned int num_clocks;
};
int iris_vcodec_power_on(struct iris_core *core, struct iris_vcodec *vc)
{
int ret;
ret = iris_opp_set_rate(vc->dev, ULONG_MAX);
if (ret)
return ret;
ret = pm_runtime_get_sync(vc->pd);
if (ret < 0)
return ret;
ret = clk_bulk_prepare_enable(vc->num_clocks, vc->clocks);
if (ret)
pm_runtime_put_sync(vc->pd);
return ret;
}
This is the generic code, handling vcodec enablement on any platform,
including Glymur. Most platforms will have one vcodec instance, Glymur
will have two (or it can be an array of those). The only difference
would be in the platform code, filling the fields of that structure.
> +
> + return 0;
> +
> +err_disable_hw1_free_clk:
> + iris_disable_unprepare_clock(core, IRIS_VCODEC1_FREERUN_CLK);
> +err_disable_axi1_clk:
> + iris_disable_unprepare_clock(core, IRIS_AXI_VCODEC1_CLK);
> +err_disable_hw1_power:
> + iris_disable_power_domains(core, IRIS_VCODEC1_POWER_DOMAIN);
> +
> + return ret;
> +}
> +
> +static int iris_vpu36_power_on_hw(struct iris_core *core)
> +{
> + int ret;
> +
> + ret = iris_vpu35_power_on_hw(core);
> + if (ret)
> + return ret;
> +
> + ret = iris_vpu36_power_on_hw1(core);
> + if (ret)
> + goto err_power_off_hw;
static int iris_vpu_power_on_vcodecs(struct iris_core *core)
{
int i, ret;
for (i = 0; i < core->num_vcodec; i++) {
ret = iris_vcodec_power_on(core, core->vc[i]);
if (ret)
goto err;
}
return 0;
err:
for (; i >= 0; i--)
iris_vcodec_power_off(core, core->vc[i]);
return ret;
}
Similar abstraction for powering on the whole core, calling
iris_vpu_power_on_vcodecs() inside.
> +
> + return 0;
> +
> +err_power_off_hw:
> + iris_vpu35_power_off_hw(core);
> +
> + return ret;
> +}
> +
> +static void iris_vpu36_power_off_hw1(struct iris_core *core)
> +{
> + u32 value, i;
> + int ret;
> +
> + if (iris_vpu3x_hw_power_collapsed(core, VCODEC1_POWER_STATUS))
> + goto disable_power;
> +
> + value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
> + if (value)
> + writel(CORE_CLK_RUN, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
> +
> + for (i = 0; i < core->iris_platform_data->num_vpp_pipe; i++) {
> + ret = readl_poll_timeout(core->reg_base + VCODEC1_SS_IDLE_STATUSN + 4 * i,
> + value, value & DMA_NOC_IDLE, 2000, 20000);
> + if (ret)
> + goto disable_power;
> + }
> +
> + writel(REQ_VCODEC1_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
> + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS,
> + value, value & NOC_LPI_VCODEC1_STATUS_DONE, 2000, 20000);
> + if (ret)
> + goto disable_power;
> +
> + writel(0, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
> +
> + writel(VCODEC1_BRIDGE_SW_RESET | VCODEC1_BRIDGE_HW_RESET_DISABLE, core->reg_base +
> + CPU_CS_AHB_BRIDGE_SYNC_RESET);
> + writel(VCODEC1_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
> + writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
> +
> +disable_power:
> + iris_genpd_set_hwmode(core, IRIS_VCODEC1_POWER_DOMAIN, false);
> + iris_disable_unprepare_clock(core, IRIS_VCODEC1_CLK);
> + iris_disable_unprepare_clock(core, IRIS_VCODEC1_FREERUN_CLK);
> + iris_disable_unprepare_clock(core, IRIS_AXI_VCODEC1_CLK);
> + iris_disable_power_domains(core, IRIS_VCODEC1_POWER_DOMAIN);
> +}
> +
> +static void iris_vpu36_power_off_hw(struct iris_core *core)
> +{
> + iris_vpu35_power_off_hw(core);
> + iris_vpu36_power_off_hw1(core);
> +}
> +
> +static int iris_vpu36_set_hwmode(struct iris_core *core)
> +{
> + int ret;
> +
> + ret = iris_genpd_set_hwmode(core, IRIS_VCODEC_POWER_DOMAIN, true);
> + if (ret)
> + return ret;
> +
> + ret = iris_genpd_set_hwmode(core, IRIS_VCODEC1_POWER_DOMAIN, true);
> + if (ret)
> + goto error_disable_vcodec_hwmode;
> +
> + return 0;
> +
> +error_disable_vcodec_hwmode:
> + iris_genpd_set_hwmode(core, IRIS_VCODEC_POWER_DOMAIN, false);
> +
> + return ret;
> +}
> +
> const struct vpu_ops iris_vpu3_ops = {
> .power_off_hw = iris_vpu3_power_off_hardware,
> .power_on_hw = iris_vpu_power_on_hw,
> @@ -281,3 +399,12 @@ const struct vpu_ops iris_vpu35_ops = {
> .calc_freq = iris_vpu3x_vpu4x_calculate_frequency,
> .set_hwmode = iris_vpu_set_hwmode,
> };
> +
> +const struct vpu_ops iris_vpu36_ops = {
> + .power_off_hw = iris_vpu36_power_off_hw,
> + .power_on_hw = iris_vpu36_power_on_hw,
> + .power_off_controller = iris_vpu35_vpu4x_power_off_controller,
> + .power_on_controller = iris_vpu35_vpu4x_power_on_controller,
> + .calc_freq = iris_vpu3x_vpu4x_calculate_frequency,
> + .set_hwmode = iris_vpu36_set_hwmode,
> +};
> diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h b/drivers/media/platform/qcom/iris/iris_vpu_common.h
> index 09799a375c14..63bf0cec58e2 100644
> --- a/drivers/media/platform/qcom/iris/iris_vpu_common.h
> +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h
> @@ -12,6 +12,7 @@ extern const struct vpu_ops iris_vpu2_ops;
> extern const struct vpu_ops iris_vpu3_ops;
> extern const struct vpu_ops iris_vpu33_ops;
> extern const struct vpu_ops iris_vpu35_ops;
> +extern const struct vpu_ops iris_vpu36_ops;
> extern const struct vpu_ops iris_vpu4x_ops;
>
> struct vpu_ops {
> diff --git a/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
> index 72168b9ffa73..e67d98b8c91e 100644
> --- a/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
> +++ b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
> @@ -7,6 +7,7 @@
> #define __IRIS_VPU_REGISTER_DEFINES_H__
>
> #define VCODEC_BASE_OFFS 0x00000000
> +#define VCODEC1_BASE_OFFS 0x00040000
> #define AON_MVP_NOC_RESET 0x0001F000
> #define CPU_BASE_OFFS 0x000A0000
> #define WRAPPER_BASE_OFFS 0x000B0000
> @@ -14,6 +15,8 @@
> #define AON_BASE_OFFS 0x000E0000
>
> #define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS + 0x70)
> +#define VCODEC1_SS_IDLE_STATUSN (VCODEC1_BASE_OFFS + 0x70)
> +#define DMA_NOC_IDLE BIT(22)
>
> #define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000)
> #define VIDEO_NOC_RESET_REQ (BIT(0) | BIT(1))
> @@ -35,6 +38,8 @@
> #define CPU_CS_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS + 0x160)
> #define CORE_BRIDGE_SW_RESET BIT(0)
> #define CORE_BRIDGE_HW_RESET_DISABLE BIT(1)
> +#define VCODEC1_BRIDGE_SW_RESET BIT(2)
> +#define VCODEC1_BRIDGE_HW_RESET_DISABLE BIT(3)
>
> #define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168)
> #define MSK_SIGNAL_FROM_TENSILICA BIT(0)
> @@ -52,14 +57,19 @@
> #define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58)
> #define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C)
> #define REQ_POWER_DOWN_PREP BIT(0)
> +#define REQ_VCODEC1_POWER_DOWN_PREP BIT(1)
>
> #define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60)
> #define NOC_LPI_STATUS_DONE BIT(0) /* Indicates the NOC handshake is complete */
> #define NOC_LPI_STATUS_DENY BIT(1) /* Indicates the NOC handshake is denied */
> #define NOC_LPI_STATUS_ACTIVE BIT(2) /* Indicates the NOC is active */
> +#define NOC_LPI_VCODEC1_STATUS_DONE BIT(8)
>
> #define WRAPPER_IRIS_VCODEC_VPU_WRAPPER_SPARE_0 (WRAPPER_BASE_OFFS + 0x78)
> #define WRAPPER_CORE_POWER_STATUS (WRAPPER_BASE_OFFS + 0x80)
> +#define VCODEC0_POWER_STATUS BIT(1)
> +#define VCODEC1_POWER_STATUS BIT(4)
> +
> #define WRAPPER_CORE_CLOCK_CONFIG (WRAPPER_BASE_OFFS + 0x88)
> #define CORE_CLK_RUN 0x0
>
>
> --
> 2.34.1
>
--
With best wishes
Dmitry