From: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
To: Atanas Filipov <atanas.filipov@oss.qualcomm.com>,
Mauro Carvalho Chehab <mchehab@kernel.org>
Cc: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>,
Loic Poulain <loic.poulain@oss.qualcomm.com>,
Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>,
linux-media@vger.kernel.org, linux-arm-msm@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH 3/4] media: qcom: camss: share VFE clock sources by the highest rate
Date: Fri, 23 Oct 2026 22:41:00 +0100 [thread overview]
Message-ID: <4a88a2ab-0020-41d1-8924-bba8ac42f429@linaro.org> (raw)
In-Reply-To: <20261023152857.2859767-4-atanas.filipov@oss.qualcomm.com>
On 23/10/2026 16:28, Atanas Filipov wrote:
> Group the VFE clocks with rate tables by their clock source at probe
> time, so that a VFE no longer lowers a shared source under a running
> one. A source shared by several VFEs follows the highest rate requested
> by its running users and drops to the highest remaining one when a VFE
> is powered off; with no user left it keeps its rate, as it may feed
> consumers outside CAMSS. A source with a single VFE user is dropped to
> its lowest rate on power off. Clocks whose rate the VFE does not set are
> left untouched.
>
> Grouping is enabled through the vfe_clock_domains resource flag, so far
> only on SM8250, where the VFE AREG clocks share the fast AHB source and
> the VFE Lite clocks share one core clock source. The current parent of
> an RCG depends on its last rate, so other SoCs keep setting their VFE
> clocks directly.
>
> Also let the VFE set the SM8250 AREG clock rate instead of the CSID.
You're simultaneously adding a new clock to an existing list and
introducing a behaviour change at the same time.
Those two things should be split on principle.
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
> drivers/media/platform/qcom/camss/camss-vfe.c | 14 +-
> drivers/media/platform/qcom/camss/camss.c | 175 +++++++++++++++++-
> drivers/media/platform/qcom/camss/camss.h | 13 ++
> 3 files changed, 196 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
> index 87096095b1eb..a3133720c765 100644
> --- a/drivers/media/platform/qcom/camss/camss-vfe.c
> +++ b/drivers/media/platform/qcom/camss/camss-vfe.c
> @@ -907,12 +907,15 @@ static int vfe_match_clock_names(struct vfe_device *vfe,
> {
> char vfe_name[7]; /* vfeXXX\0 */
> char vfe_lite_name[12]; /* vfe_liteXXX\0 */
> + char vfe_areg_name[12]; /* vfeXXX_areg\0 */
>
> snprintf(vfe_name, sizeof(vfe_name), "vfe%d", vfe->id);
> snprintf(vfe_lite_name, sizeof(vfe_lite_name), "vfe_lite%d", vfe->id);
> + snprintf(vfe_areg_name, sizeof(vfe_areg_name), "vfe%d_areg", vfe->id);
>
> return (!strcmp(clock->name, vfe_name) ||
> !strcmp(clock->name, vfe_lite_name) ||
> + !strcmp(clock->name, vfe_areg_name) ||
> !strcmp(clock->name, "vfe_lite") ||
> !strcmp(clock->name, "camnoc_axi") ||
> !strcmp(clock->name, "camnoc_rt_axi"));
> @@ -1002,7 +1005,7 @@ static int vfe_set_clock_rates(struct vfe_device *vfe)
> return -EINVAL;
> }
>
> - ret = clk_set_rate(clock->clk, rate);
> + ret = camss_set_clock_rate(vfe->camss, clock, rate);
> if (ret < 0) {
> dev_err(dev, "clk set rate failed: %d\n", ret);
> return ret;
> @@ -1101,12 +1104,12 @@ int vfe_get(struct vfe_device *vfe)
>
> ret = vfe_set_clock_rates(vfe);
> if (ret < 0)
> - goto error_pm_runtime_get;
> + goto error_release_rates;
>
> ret = camss_enable_clocks(vfe->nclocks, vfe->clock,
> vfe->camss->dev);
> if (ret < 0)
> - goto error_pm_runtime_get;
> + goto error_release_rates;
>
> ret = vfe_reset(vfe);
> if (ret < 0)
> @@ -1131,7 +1134,9 @@ int vfe_get(struct vfe_device *vfe)
> error_reset:
> camss_disable_clocks(vfe->nclocks, vfe->clock);
>
> -error_pm_runtime_get:
> +error_release_rates:
> + camss_release_clock_rates(vfe->camss, vfe->nclocks, vfe->clock);
> +
> pm_runtime_put_sync(vfe->camss->dev);
> error_domain_off:
> vfe->res->hw_ops->pm_domain_off(vfe);
> @@ -1159,6 +1164,7 @@ void vfe_put(struct vfe_device *vfe)
> vfe->res->hw_ops->vfe_halt(vfe);
> }
> camss_disable_clocks(vfe->nclocks, vfe->clock);
> + camss_release_clock_rates(vfe->camss, vfe->nclocks, vfe->clock);
> pm_runtime_put_sync(vfe->camss->dev);
> vfe->res->hw_ops->pm_domain_off(vfe);
> }
> diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
> index 7fe12049b78a..db5212b14d5a 100644
> --- a/drivers/media/platform/qcom/camss/camss.c
> +++ b/drivers/media/platform/qcom/camss/camss.c
> @@ -2053,7 +2053,7 @@ static const struct camss_subdev_resources csid_res_8250[] = {
> .clock_rate = { { 400000000 },
> { 400000000 },
> { 0 },
> - { 100000000, 200000000, 300000000, 400000000 },
> + { 0 },
> { 0 } },
> .reg = { "csid0" },
> .interrupt = { "csid0" },
> @@ -2070,7 +2070,7 @@ static const struct camss_subdev_resources csid_res_8250[] = {
> .clock_rate = { { 400000000 },
> { 400000000 },
> { 0 },
> - { 100000000, 200000000, 300000000, 400000000 },
> + { 0 },
> { 0 } },
> .reg = { "csid1" },
> .interrupt = { "csid1" },
> @@ -4509,6 +4509,167 @@ void camss_disable_clocks(int nclocks, struct camss_clock *clock)
> clk_disable_unprepare(clock[i].clk);
> }
>
> +/*
> + * A clock domain is a clock source shared by clocks of different engines.
> + * Its rate follows the highest rate requested by its users, so that an
> + * engine started at a lower rate never lowers the rate under an engine that
> + * is still running.
> + */
> +struct camss_clock_domain {
> + struct list_head node;
> + struct clk *parent;
> + struct list_head users;
> + unsigned int nusers;
> +};
It seems to me as a read this that the above text is the abstract
description of at least one patch - which should have examples in the
commit log, to make it less abstract.
> +
> +struct camss_clock_user {
> + struct list_head node;
> + struct camss_clock_domain *domain;
> + struct camss_clock *clock;
> + unsigned long rate;
> +};
> +
> +static int camss_add_clock_user(struct camss *camss, struct camss_clock *clock)
> +{
> + struct camss_clock_domain *domain;
> + struct camss_clock_user *user;
> + struct clk *parent;
> +
> + if (!clock->nfreqs)
> + return 0;
> +
> + parent = clk_get_parent(clock->clk);
> + if (!parent)
> + return 0;
> +
> + list_for_each_entry(domain, &camss->clock_domains, node)
> + if (clk_is_match(domain->parent, parent))
> + goto add_user;
> +
> + domain = devm_kzalloc(camss->dev, sizeof(*domain), GFP_KERNEL);
> + if (!domain)
> + return -ENOMEM;
> +
> + domain->parent = parent;
> + INIT_LIST_HEAD(&domain->users);
> + list_add_tail(&domain->node, &camss->clock_domains);
> +
> +add_user:
> + user = devm_kzalloc(camss->dev, sizeof(*user), GFP_KERNEL);
> + if (!user)
> + return -ENOMEM;
> +
> + user->domain = domain;
> + user->clock = clock;
> + list_add_tail(&user->node, &domain->users);
> + domain->nusers++;
> + clock->user = user;
> +
> + return 0;
> +}
> +
> +/*
> + * camss_init_clock_domains - Group the VFE clocks by clock source
> + * @camss: CAMSS device
> + *
> + * A clock whose source feeds no other VFE clock forms a dedicated domain
> + * with a single user. Only SoCs whose VFE sets the rates of clocks with a
> + * fixed source group their clocks; the VFE clocks of other SoCs are set
> + * directly.
What do you mean by user here, its not clear to me. Another engine ? I
think so - you mean the JPEG has a dependency on the clocks listed in
CAMSS ?
I thought we decided for full separation of JPEG and CAMSS/IFE ?
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int camss_init_clock_domains(struct camss *camss)
> +{
> + int ret, i, j;
> +
> + if (!camss->res->vfe_clock_domains)
> + return 0;
> +
> + for (i = 0; i < camss->res->vfe_num; i++)
> + for (j = 0; j < camss->vfe[i].nclocks; j++) {
> + ret = camss_add_clock_user(camss, &camss->vfe[i].clock[j]);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int camss_apply_clock_domain(struct camss_clock_domain *domain)
> +{
> + struct camss_clock_user *user, *top = NULL;
> +
> + list_for_each_entry(user, &domain->users, node)
> + if (user->rate && (!top || user->rate > top->rate))
> + top = user;
> +
> + if (!top)
> + return 0;
> +
> + return clk_set_rate(top->clock->clk, top->rate);
> +}
> +
> +/*
> + * camss_set_clock_rate - Set the rate of a clock
> + * @camss: CAMSS device
> + * @clock: Clock
> + * @rate: Requested rate
> + *
> + * A shared clock is set to the highest rate requested by the running users
> + * of its source.
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +int camss_set_clock_rate(struct camss *camss, struct camss_clock *clock,
> + unsigned long rate)
> +{
> + int ret;
> +
> + if (!clock->user)
> + return clk_set_rate(clock->clk, rate);
> +
> + mutex_lock(&camss->clock_lock);
> + clock->user->rate = rate;
> + ret = camss_apply_clock_domain(clock->user->domain);
> + mutex_unlock(&camss->clock_lock);
> +
> + return ret;
> +}
> +
> +/*
> + * camss_release_clock_rates - Release the rates requested by an engine
> + * @camss: CAMSS device
> + * @nclocks: Number of clocks of the engine
> + * @clock: Clocks of the engine
> + *
> + * A dedicated clock drops to its lowest rate. A shared clock drops to the
> + * highest rate of the users still running, and keeps its rate when none is
> + * left, as its source may feed consumers outside CAMSS.
> + */
> +void camss_release_clock_rates(struct camss *camss, int nclocks,
> + struct camss_clock *clock)
> +{
> + int i;
> +
> + mutex_lock(&camss->clock_lock);
> +
> + for (i = 0; i < nclocks; i++) {
> + struct camss_clock_user *user = clock[i].user;
> +
> + if (!user || !user->rate)
> + continue;
> +
> + user->rate = 0;
> + if (user->domain->nusers == 1)
> + clk_set_rate(user->clock->clk, 0);
> + else
> + camss_apply_clock_domain(user->domain);
> + }
> +
> + mutex_unlock(&camss->clock_lock);
> +}
> +
> /*
> * camss_find_sensor_pad - Find the media pad via which the sensor is linked
> * @entity: Media entity to start searching from
> @@ -5427,6 +5588,11 @@ static int camss_probe(struct platform_device *pdev)
>
> atomic_set(&camss->ref_count, 0);
> camss->dev = dev;
> + INIT_LIST_HEAD(&camss->clock_domains);
> +
> + ret = devm_mutex_init(dev, &camss->clock_lock);
> + if (ret)
> + return ret;
> platform_set_drvdata(pdev, camss);
>
> camss->csiphy = devm_kcalloc(dev, camss->res->csiphy_num,
> @@ -5474,6 +5640,10 @@ static int camss_probe(struct platform_device *pdev)
> if (ret < 0)
> goto err_genpd_cleanup;
>
> + ret = camss_init_clock_domains(camss);
> + if (ret < 0)
> + goto err_genpd_cleanup;
> +
> ret = dma_set_mask_and_coherent(dev, 0xffffffff);
> if (ret)
> goto err_genpd_cleanup;
> @@ -5736,6 +5906,7 @@ static const struct camss_resources sm8250_resources = {
> .csiphy_num = ARRAY_SIZE(csiphy_res_8250),
> .csid_num = ARRAY_SIZE(csid_res_8250),
> .vfe_num = ARRAY_SIZE(vfe_res_8250),
> + .vfe_clock_domains = true,
> };
>
> static const struct camss_resources sc8280xp_resources = {
> diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
> index 7dc2fd9f03e0..ad1d49275e0f 100644
> --- a/drivers/media/platform/qcom/camss/camss.h
> +++ b/drivers/media/platform/qcom/camss/camss.h
> @@ -11,6 +11,8 @@
> #define QC_MSM_CAMSS_H
>
> #include <linux/device.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> #include <linux/types.h>
> #include <media/v4l2-async.h>
> #include <media/v4l2-device.h>
> @@ -120,6 +122,7 @@ struct camss_resources {
> const unsigned int tpg_num;
> const unsigned int csid_num;
> const unsigned int vfe_num;
> + const bool vfe_clock_domains;
> };
>
> struct camss {
> @@ -141,6 +144,9 @@ struct camss {
> struct icc_path *icc_path[ICC_SM8250_COUNT];
> const struct camss_resources *res;
> bool legacy_phy;
> + /* Protects clock_domains and the rates requested by their users */
> + struct mutex clock_lock;
> + struct list_head clock_domains;
> };
>
> struct camss_camera_interface {
> @@ -153,11 +159,14 @@ struct camss_async_subdev {
> struct camss_camera_interface interface;
> };
>
> +struct camss_clock_user;
> +
> struct camss_clock {
> struct clk *clk;
> const char *name;
> u32 *freq;
> u32 nfreqs;
> + struct camss_clock_user *user;
> };
>
> struct parent_dev_ops {
> @@ -170,6 +179,10 @@ void camss_add_clock_margin(u64 *rate);
> int camss_enable_clocks(int nclocks, struct camss_clock *clock,
> struct device *dev);
> void camss_disable_clocks(int nclocks, struct camss_clock *clock);
> +int camss_set_clock_rate(struct camss *camss, struct camss_clock *clock,
> + unsigned long rate);
> +void camss_release_clock_rates(struct camss *camss, int nclocks,
> + struct camss_clock *clock);
> struct media_pad *camss_find_sensor_pad(struct media_entity *entity);
> s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
> unsigned int lanes);
next prev parent reply other threads:[~2026-10-23 21:41 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-23 15:28 [PATCH 0/4] media: qcom: camss: share VFE clock sources on SM8250 Atanas Filipov
2026-10-23 15:28 ` [PATCH 1/4] media: qcom: camss: clamp VFE clock rates to the highest level Atanas Filipov
2026-10-23 15:45 ` Loic Poulain
2026-10-23 15:50 ` Atanas Filipov
2026-10-23 19:07 ` Gjorgji Rosikopulos (Consultant)
2026-10-23 16:07 ` Atanas Filipov
2026-10-23 15:28 ` [PATCH 2/4] media: qcom: camss: sm8250: do not set VFE core clock rates from CSID Atanas Filipov
2026-10-23 15:37 ` Loic Poulain
2026-10-23 21:32 ` Bryan O'Donoghue
2026-10-24 8:19 ` Atanas Filipov
2026-10-23 15:28 ` [PATCH 3/4] media: qcom: camss: share VFE clock sources by the highest rate Atanas Filipov
2026-10-23 21:41 ` Bryan O'Donoghue [this message]
2026-10-24 8:19 ` Atanas Filipov
2026-10-23 15:28 ` [PATCH 4/4] media: qcom: camss: sm8250: set the camera NoC AXI rate from the VFE Atanas Filipov
2026-10-23 21:44 ` [PATCH 0/4] media: qcom: camss: share VFE clock sources on SM8250 Bryan O'Donoghue
2026-10-24 8:19 ` Atanas Filipov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4a88a2ab-0020-41d1-8924-bba8ac42f429@linaro.org \
--to=bryan.odonoghue@linaro.org \
--cc=atanas.filipov@oss.qualcomm.com \
--cc=gjorgji.rosikopulos@oss.qualcomm.com \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=loic.poulain@oss.qualcomm.com \
--cc=mchehab@kernel.org \
--cc=vladimir.zapolskiy@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®