* [PATCH 04/78] ASoC: codecs: arizona: Use guard() for mutex locks
From: phucduc.bui @ 2026-06-17 10:31 UTC (permalink / raw)
To: Mark Brown
Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Cheng-Yi Chiang,
Tzung-Bi Shih, Guenter Roeck, Benson Leung, David Rhodes,
Richard Fitzgerald, povik+lin, Charles Keepax, Support Opensource,
Nick Li, Herve Codina, Srinivas Kandagatla, Matthias Brugger,
AngeloGioacchino Del Regno, Shenghao Ding, Kevin Lu, Baojun Xu,
Sen Wang, Oder Chiou, Lars-Peter Clausen, nuno.sa, Steven Eckhoff,
patches, chrome-platform, asahi, linux-arm-msm, linux-sound,
linux-kernel, linux-arm-kernel, linux-mediatek, bui duc phuc
In-Reply-To: <20260617103235.449609-1-phucduc.bui@gmail.com>
From: bui duc phuc <phucduc.bui@gmail.com>
Clean up the code using guard() for mutex locks.
Merely code refactoring, and no behavior change.
Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
sound/soc/codecs/arizona.c | 25 ++++++++++---------------
1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 8c683b0bb74c..77bd02def9c7 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -1158,17 +1158,16 @@ int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags)
struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
int ret = 0;
- mutex_lock(&priv->dvfs_lock);
+ guard(mutex)(&priv->dvfs_lock);
if (!priv->dvfs_cached && !priv->dvfs_reqs) {
ret = arizona_dvfs_enable(component);
if (ret)
- goto err;
+ return ret;
}
priv->dvfs_reqs |= flags;
-err:
- mutex_unlock(&priv->dvfs_lock);
+
return ret;
}
EXPORT_SYMBOL_GPL(arizona_dvfs_up);
@@ -1179,7 +1178,7 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags)
unsigned int old_reqs;
int ret = 0;
- mutex_lock(&priv->dvfs_lock);
+ guard(mutex)(&priv->dvfs_lock);
old_reqs = priv->dvfs_reqs;
priv->dvfs_reqs &= ~flags;
@@ -1187,7 +1186,6 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags)
if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs)
ret = arizona_dvfs_disable(component);
- mutex_unlock(&priv->dvfs_lock);
return ret;
}
EXPORT_SYMBOL_GPL(arizona_dvfs_down);
@@ -1199,7 +1197,7 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
int ret = 0;
- mutex_lock(&priv->dvfs_lock);
+ guard(mutex)(&priv->dvfs_lock);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -1222,7 +1220,6 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
break;
}
- mutex_unlock(&priv->dvfs_lock);
return ret;
}
EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev);
@@ -1657,13 +1654,11 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_component *component,
{ 0x80, 0x0 },
};
- mutex_lock(&arizona->dac_comp_lock);
-
- dac_comp[1].def = arizona->dac_comp_coeff;
- if (rate >= 176400)
- dac_comp[2].def = arizona->dac_comp_enabled;
-
- mutex_unlock(&arizona->dac_comp_lock);
+ scoped_guard(mutex, &arizona->dac_comp_lock) {
+ dac_comp[1].def = arizona->dac_comp_coeff;
+ if (rate >= 176400)
+ dac_comp[2].def = arizona->dac_comp_enabled;
+ }
regmap_multi_reg_write(arizona->regmap,
dac_comp,
--
2.43.0
^ permalink raw reply related
* [PATCH 03/78] ASoC: codecs: arizona-jack: Use guard() for mutex locks
From: phucduc.bui @ 2026-06-17 10:31 UTC (permalink / raw)
To: Mark Brown
Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Cheng-Yi Chiang,
Tzung-Bi Shih, Guenter Roeck, Benson Leung, David Rhodes,
Richard Fitzgerald, povik+lin, Charles Keepax, Support Opensource,
Nick Li, Herve Codina, Srinivas Kandagatla, Matthias Brugger,
AngeloGioacchino Del Regno, Shenghao Ding, Kevin Lu, Baojun Xu,
Sen Wang, Oder Chiou, Lars-Peter Clausen, nuno.sa, Steven Eckhoff,
patches, chrome-platform, asahi, linux-arm-msm, linux-sound,
linux-kernel, linux-arm-kernel, linux-mediatek, bui duc phuc
In-Reply-To: <20260617103235.449609-1-phucduc.bui@gmail.com>
From: bui duc phuc <phucduc.bui@gmail.com>
Clean up the code using guard() for mutex locks.
Merely code refactoring, and no behavior change.
Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
sound/soc/codecs/arizona-jack.c | 194 +++++++++++++++-----------------
1 file changed, 92 insertions(+), 102 deletions(-)
diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c
index a9063bac2752..fc09b31943a6 100644
--- a/sound/soc/codecs/arizona-jack.c
+++ b/sound/soc/codecs/arizona-jack.c
@@ -528,12 +528,11 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
int ret, reading, state, report;
bool mic = false;
- mutex_lock(&info->lock);
+ guard(mutex)(&info->lock);
/* If we got a spurious IRQ for some reason then ignore it */
if (!info->hpdet_active) {
dev_warn(arizona->dev, "Spurious HPDET IRQ\n");
- mutex_unlock(&info->lock);
return IRQ_NONE;
}
@@ -546,7 +545,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
ret = arizona_hpdet_read(info);
if (ret == -EAGAIN)
- goto out;
+ return IRQ_HANDLED;
else if (ret < 0)
goto done;
reading = ret;
@@ -559,7 +558,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
ret = arizona_hpdet_do_id(info, &reading, &mic);
if (ret == -EAGAIN)
- goto out;
+ return IRQ_HANDLED;
else if (ret < 0)
goto done;
@@ -596,9 +595,6 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
if (state)
info->hpdet_done = true;
-out:
- mutex_unlock(&info->lock);
-
return IRQ_HANDLED;
}
@@ -707,15 +703,13 @@ static void arizona_micd_timeout_work(struct work_struct *work)
struct arizona_priv,
micd_timeout_work.work);
- mutex_lock(&info->lock);
+ guard(mutex)(&info->lock);
dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n");
info->detecting = false;
arizona_identify_headphone(info);
-
- mutex_unlock(&info->lock);
}
static int arizona_micd_adc_read(struct arizona_priv *info)
@@ -921,12 +915,11 @@ static void arizona_micd_detect(struct work_struct *work)
cancel_delayed_work_sync(&info->micd_timeout_work);
- mutex_lock(&info->lock);
+ guard(mutex)(&info->lock);
/* If the cable was removed while measuring ignore the result */
if (!(info->jack->status & SND_JACK_MECHANICAL)) {
dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n");
- mutex_unlock(&info->lock);
return;
}
@@ -936,7 +929,6 @@ static void arizona_micd_detect(struct work_struct *work)
arizona_button_reading(info);
pm_runtime_mark_last_busy(arizona->dev);
- mutex_unlock(&info->lock);
}
static irqreturn_t arizona_micdet(int irq, void *data)
@@ -948,10 +940,10 @@ static irqreturn_t arizona_micdet(int irq, void *data)
cancel_delayed_work_sync(&info->micd_detect_work);
cancel_delayed_work_sync(&info->micd_timeout_work);
- mutex_lock(&info->lock);
- if (!info->detecting)
- debounce = 0;
- mutex_unlock(&info->lock);
+ scoped_guard(mutex, &info->lock) {
+ if (!info->detecting)
+ debounce = 0;
+ }
if (debounce)
queue_delayed_work(system_power_efficient_wq,
@@ -969,9 +961,8 @@ static void arizona_hpdet_work(struct work_struct *work)
struct arizona_priv,
hpdet_work.work);
- mutex_lock(&info->lock);
+ guard(mutex)(&info->lock);
arizona_start_hpdet_acc_id(info);
- mutex_unlock(&info->lock);
}
static int arizona_hpdet_wait(struct arizona_priv *info)
@@ -1013,6 +1004,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
struct arizona *arizona = info->arizona;
unsigned int val, present, mask;
bool cancelled_hp, cancelled_mic;
+ irqreturn_t ret_irq = IRQ_HANDLED;
int ret, i;
cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work);
@@ -1020,110 +1012,108 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
pm_runtime_get_sync(arizona->dev);
- mutex_lock(&info->lock);
-
- if (info->micd_clamp) {
- mask = ARIZONA_MICD_CLAMP_STS;
- present = 0;
- } else {
- mask = ARIZONA_JD1_STS;
- if (arizona->pdata.jd_invert)
+ scoped_guard(mutex, &info->lock) {
+ if (info->micd_clamp) {
+ mask = ARIZONA_MICD_CLAMP_STS;
present = 0;
- else
- present = ARIZONA_JD1_STS;
- }
+ } else {
+ mask = ARIZONA_JD1_STS;
+ if (arizona->pdata.jd_invert)
+ present = 0;
+ else
+ present = ARIZONA_JD1_STS;
+ }
- ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val);
- if (ret) {
- dev_err(arizona->dev, "Failed to read jackdet status: %d\n", ret);
- mutex_unlock(&info->lock);
- pm_runtime_put_autosuspend(arizona->dev);
- return IRQ_NONE;
- }
+ ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to read jackdet status: %d\n", ret);
+ ret_irq = IRQ_NONE;
+ break;
+ }
- val &= mask;
- if (val == info->last_jackdet) {
- dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n");
- if (cancelled_hp)
- queue_delayed_work(system_power_efficient_wq,
- &info->hpdet_work,
- msecs_to_jiffies(HPDET_DEBOUNCE));
+ val &= mask;
+ if (val == info->last_jackdet) {
+ dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n");
+ if (cancelled_hp)
+ queue_delayed_work(system_power_efficient_wq,
+ &info->hpdet_work,
+ msecs_to_jiffies(HPDET_DEBOUNCE));
- if (cancelled_mic) {
- int micd_timeout = arizona->pdata.micd_timeout;
+ if (cancelled_mic) {
+ int micd_timeout = arizona->pdata.micd_timeout;
- queue_delayed_work(system_power_efficient_wq,
- &info->micd_timeout_work,
- msecs_to_jiffies(micd_timeout));
+ queue_delayed_work(system_power_efficient_wq,
+ &info->micd_timeout_work,
+ msecs_to_jiffies(micd_timeout));
+ }
+
+ goto out;
}
+ info->last_jackdet = val;
- goto out;
- }
- info->last_jackdet = val;
+ if (info->last_jackdet == present) {
+ dev_dbg(arizona->dev, "Detected jack\n");
+ snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL);
- if (info->last_jackdet == present) {
- dev_dbg(arizona->dev, "Detected jack\n");
- snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL);
+ info->detecting = true;
+ info->mic = false;
+ info->jack_flips = 0;
- info->detecting = true;
- info->mic = false;
- info->jack_flips = 0;
+ if (!arizona->pdata.hpdet_acc_id) {
+ arizona_start_mic(info);
+ } else {
+ queue_delayed_work(system_power_efficient_wq,
+ &info->hpdet_work,
+ msecs_to_jiffies(HPDET_DEBOUNCE));
+ }
- if (!arizona->pdata.hpdet_acc_id) {
- arizona_start_mic(info);
+ if (info->micd_clamp || !arizona->pdata.jd_invert)
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_JACK_DETECT_DEBOUNCE,
+ ARIZONA_MICD_CLAMP_DB |
+ ARIZONA_JD1_DB, 0);
} else {
- queue_delayed_work(system_power_efficient_wq,
- &info->hpdet_work,
- msecs_to_jiffies(HPDET_DEBOUNCE));
- }
+ dev_dbg(arizona->dev, "Detected jack removal\n");
- if (info->micd_clamp || !arizona->pdata.jd_invert)
- regmap_update_bits(arizona->regmap,
- ARIZONA_JACK_DETECT_DEBOUNCE,
- ARIZONA_MICD_CLAMP_DB |
- ARIZONA_JD1_DB, 0);
- } else {
- dev_dbg(arizona->dev, "Detected jack removal\n");
+ arizona_stop_mic(info);
- arizona_stop_mic(info);
+ info->num_hpdet_res = 0;
+ for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++)
+ info->hpdet_res[i] = 0;
+ info->mic = false;
+ info->hpdet_done = false;
+ info->hpdet_retried = false;
- info->num_hpdet_res = 0;
- for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++)
- info->hpdet_res[i] = 0;
- info->mic = false;
- info->hpdet_done = false;
- info->hpdet_retried = false;
-
- snd_soc_jack_report(info->jack, 0, ARIZONA_JACK_MASK | info->micd_button_mask);
+ snd_soc_jack_report(info->jack, 0,
+ ARIZONA_JACK_MASK | info->micd_button_mask);
- /*
- * If the jack was removed during a headphone detection we
- * need to wait for the headphone detection to finish, as
- * it can not be aborted. We don't want to be able to start
- * a new headphone detection from a fresh insert until this
- * one is finished.
- */
- arizona_hpdet_wait(info);
+ /*
+ * If the jack was removed during a headphone detection we
+ * need to wait for the headphone detection to finish, as
+ * it can not be aborted. We don't want to be able to start
+ * a new headphone detection from a fresh insert until this
+ * one is finished.
+ */
+ arizona_hpdet_wait(info);
- regmap_update_bits(arizona->regmap,
- ARIZONA_JACK_DETECT_DEBOUNCE,
- ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB,
- ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB);
- }
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_JACK_DETECT_DEBOUNCE,
+ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB,
+ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB);
+ }
out:
- /* Clear trig_sts to make sure DCVDD is not forced up */
- regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG,
- ARIZONA_MICD_CLAMP_FALL_TRIG_STS |
- ARIZONA_MICD_CLAMP_RISE_TRIG_STS |
- ARIZONA_JD1_FALL_TRIG_STS |
- ARIZONA_JD1_RISE_TRIG_STS);
-
- mutex_unlock(&info->lock);
+ /* Clear trig_sts to make sure DCVDD is not forced up */
+ regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG,
+ ARIZONA_MICD_CLAMP_FALL_TRIG_STS |
+ ARIZONA_MICD_CLAMP_RISE_TRIG_STS |
+ ARIZONA_JD1_FALL_TRIG_STS |
+ ARIZONA_JD1_RISE_TRIG_STS);
+ }
pm_runtime_put_autosuspend(arizona->dev);
- return IRQ_HANDLED;
+ return ret_irq;
}
/* Map a level onto a slot in the register bank */
--
2.43.0
^ permalink raw reply related
* [PATCH 02/78] ASoC: codecs: ak4613: Use guard() for mutex locks
From: phucduc.bui @ 2026-06-17 10:31 UTC (permalink / raw)
To: Mark Brown
Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Cheng-Yi Chiang,
Tzung-Bi Shih, Guenter Roeck, Benson Leung, David Rhodes,
Richard Fitzgerald, povik+lin, Charles Keepax, Support Opensource,
Nick Li, Herve Codina, Srinivas Kandagatla, Matthias Brugger,
AngeloGioacchino Del Regno, Shenghao Ding, Kevin Lu, Baojun Xu,
Sen Wang, Oder Chiou, Lars-Peter Clausen, nuno.sa, Steven Eckhoff,
patches, chrome-platform, asahi, linux-arm-msm, linux-sound,
linux-kernel, linux-arm-kernel, linux-mediatek, bui duc phuc
In-Reply-To: <20260617103235.449609-1-phucduc.bui@gmail.com>
From: bui duc phuc <phucduc.bui@gmail.com>
Clean up the code using guard() for mutex locks.
Merely code refactoring, and no behavior change.
Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
sound/soc/codecs/ak4613.c | 72 +++++++++++++++++++--------------------
1 file changed, 35 insertions(+), 37 deletions(-)
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index 3e0696b5abf5..413ef55b918a 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -384,7 +384,7 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
struct device *dev = component->dev;
- mutex_lock(&priv->lock);
+ guard(mutex)(&priv->lock);
priv->cnt--;
if (priv->cnt < 0) {
dev_err(dev, "unexpected counter error\n");
@@ -392,7 +392,6 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
}
if (!priv->cnt)
priv->ctrl1 = 0;
- mutex_unlock(&priv->lock);
}
static void ak4613_hw_constraints(struct ak4613_priv *priv,
@@ -507,10 +506,9 @@ static int ak4613_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_component *component = dai->component;
struct ak4613_priv *priv = snd_soc_component_get_drvdata(component);
- mutex_lock(&priv->lock);
+ guard(mutex)(&priv->lock);
ak4613_hw_constraints(priv, substream);
priv->cnt++;
- mutex_unlock(&priv->lock);
return 0;
}
@@ -599,42 +597,42 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
*/
ret = -EINVAL;
- mutex_lock(&priv->lock);
- if (priv->cnt > 1) {
- /*
- * If it was already working, use current priv->ctrl1
- */
- ret = 0;
- } else {
- /*
- * It is not yet working,
- */
- unsigned int channel = params_channels(params);
- u8 tdm;
-
- /* STEREO or TDM */
- if (channel == 2)
- tdm = AK4613_CONFIG_MODE_STEREO;
- else
- tdm = AK4613_CONFIG_GET(priv, MODE);
-
- for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) {
- const struct ak4613_interface *iface = ak4613_iface + i;
-
- if ((iface->fmt == fmt) && (iface->width == width)) {
- /*
- * Ctrl1
- * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
- * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE|
- * < tdm > < iface->dif >
- */
- priv->ctrl1 = (tdm << 6) | (iface->dif << 3);
- ret = 0;
- break;
+ scoped_guard(mutex, &priv->lock) {
+ if (priv->cnt > 1) {
+ /*
+ * If it was already working, use current priv->ctrl1
+ */
+ ret = 0;
+ } else {
+ /*
+ * It is not yet working,
+ */
+ unsigned int channel = params_channels(params);
+ u8 tdm;
+
+ /* STEREO or TDM */
+ if (channel == 2)
+ tdm = AK4613_CONFIG_MODE_STEREO;
+ else
+ tdm = AK4613_CONFIG_GET(priv, MODE);
+
+ for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) {
+ const struct ak4613_interface *iface = ak4613_iface + i;
+
+ if ((iface->fmt == fmt) && (iface->width == width)) {
+ /*
+ * Ctrl1
+ * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
+ * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE|
+ * < tdm > < iface->dif >
+ */
+ priv->ctrl1 = (tdm << 6) | (iface->dif << 3);
+ ret = 0;
+ break;
+ }
}
}
}
- mutex_unlock(&priv->lock);
if (ret < 0)
goto hw_params_end;
--
2.43.0
^ permalink raw reply related
* [PATCH 01/78] ASoC: codecs: ab8500: Use guard() for mutex locks
From: phucduc.bui @ 2026-06-17 10:31 UTC (permalink / raw)
To: Mark Brown
Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Cheng-Yi Chiang,
Tzung-Bi Shih, Guenter Roeck, Benson Leung, David Rhodes,
Richard Fitzgerald, povik+lin, Charles Keepax, Support Opensource,
Nick Li, Herve Codina, Srinivas Kandagatla, Matthias Brugger,
AngeloGioacchino Del Regno, Shenghao Ding, Kevin Lu, Baojun Xu,
Sen Wang, Oder Chiou, Lars-Peter Clausen, nuno.sa, Steven Eckhoff,
patches, chrome-platform, asahi, linux-arm-msm, linux-sound,
linux-kernel, linux-arm-kernel, linux-mediatek, bui duc phuc
In-Reply-To: <20260617103235.449609-1-phucduc.bui@gmail.com>
From: bui duc phuc <phucduc.bui@gmail.com>
Clean up the code using guard() for mutex locks.
Merely code refactoring, and no behavior change.
Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
sound/soc/codecs/ab8500-codec.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index 6e8ef9cd1b31..20beb830fd62 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -989,9 +989,8 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
- mutex_lock(&drvdata->ctrl_lock);
+ guard(mutex)(&drvdata->ctrl_lock);
ucontrol->value.enumerated.item[0] = drvdata->sid_status;
- mutex_unlock(&drvdata->ctrl_lock);
return 0;
}
@@ -1014,7 +1013,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
return -EIO;
}
- mutex_lock(&drvdata->ctrl_lock);
+ guard(mutex)(&drvdata->ctrl_lock);
sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF);
if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) {
@@ -1025,7 +1024,8 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
} else {
status = -EBUSY;
}
- goto out;
+ dev_dbg(component->dev, "%s: Exit\n", __func__);
+ return status;
}
snd_soc_component_write(component, AB8500_SIDFIRADR, 0);
@@ -1043,9 +1043,6 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
drvdata->sid_status = SID_FIR_CONFIGURED;
-out:
- mutex_unlock(&drvdata->ctrl_lock);
-
dev_dbg(component->dev, "%s: Exit\n", __func__);
return status;
--
2.43.0
^ permalink raw reply related
* [PATCH 00/78] ASoC: codecs: Use guard() for mutex & spin locks
From: phucduc.bui @ 2026-06-17 10:31 UTC (permalink / raw)
To: Mark Brown
Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Cheng-Yi Chiang,
Tzung-Bi Shih, Guenter Roeck, Benson Leung, David Rhodes,
Richard Fitzgerald, povik+lin, Charles Keepax, Support Opensource,
Nick Li, Herve Codina, Srinivas Kandagatla, Matthias Brugger,
AngeloGioacchino Del Regno, Shenghao Ding, Kevin Lu, Baojun Xu,
Sen Wang, Oder Chiou, Lars-Peter Clausen, nuno.sa, Steven Eckhoff,
patches, chrome-platform, asahi, linux-arm-msm, linux-sound,
linux-kernel, linux-arm-kernel, linux-mediatek, bui duc phuc
From: bui duc phuc <phucduc.bui@gmail.com>
Hi all,
This series converts mutex and spinlock handling in the ASoC codec
drivers to use guard() and scoped_guard() helpers.
The changes are purely refactoring and should have no functional
impact.
Compile tested only.
Best regards,
Phuc
bui duc phuc (78):
ASoC: codecs: ab8500: Use guard() for mutex locks
ASoC: codecs: ak4613: Use guard() for mutex locks
ASoC: codecs: arizona-jack: Use guard() for mutex locks
ASoC: codecs: arizona: Use guard() for mutex locks
ASoC: codecs: aw87390: Use guard() for mutex locks
ASoC: codecs: aw88081: Use guard() for mutex locks
ASoC: codecs: aw88166: Use guard() for mutex locks
ASoC: codecs: aw88261: Use guard() for mutex locks
ASoC: codecs: aw88395: Use guard() for mutex locks
ASoC: codecs: aw88399: Use guard() for mutex locks
ASoC: codecs: cros_ec_codec: Use guard() for mutex locks
ASoC: codecs: cs-amp-lib: Use guard() for mutex locks
ASoC: codecs: cs35l56: Use guard() for mutex locks
ASoC: codecs: cs42l42: Use guard() for mutex locks
ASoC: codecs: cs42l43: Use guard() for mutex locks
ASoC: codecs: cs42l84: Use guard() for mutex locks
ASoC: codecs: cs43130: Use guard() for mutex locks
ASoC: codecs: cs47l15: Use guard() for mutex locks
ASoC: codecs: cs47l35: Use guard() for mutex locks
ASoC: codecs: cs47l85: Use guard() for mutex locks
ASoC: codecs: cs47l90: Use guard() for mutex locks
ASoC: codecs: cs47l92: Use guard() for mutex locks
ASoC: codecs: cs48l32: Use guard() for mutex locks
ASoC: codecs: cs2072x: Use guard() for mutex locks
ASoC: codecs: da7213: Use guard() for mutex locks
ASoC: codecs: da7219: Use guard() for mutex locks
ASoC: codecs: es8316: Use guard() for mutex locks
ASoC: codecs: es8326: Use guard() for mutex locks
ASoC: codecs: es9356: Use guard() for mutex locks
ASoC: codecs: fs210x: Use guard() for mutex locks
ASoC: codecs: hdac_hdmi: Use guard() for mutex locks
ASoC: codecs: hdmi-codec: Use guard() for mutex locks
ASoC: codecs: idt821034: Use guard() for mutex locks
ASoC: codecs: lpass-macro: Use guard() for mutex locks
ASoC: codecs: madera: Use guard() for mutex locks
ASoC: codecs: max98095: Use guard() for mutex locks
ASoC: codecs: mt6359-accdet: Use guard() for mutex locks
ASoC: codecs: pcm512x: Use guard() for mutex locks
ASoC: codecs: pcm6240: Use guard() for mutex locks
ASoC: codecs: peb2466: Use guard() for mutex locks
ASoC: codecs: rt5514-spi: Use guard() for mutex locks
ASoC: codecs: rt5645: Use guard() for mutex locks
ASoC: codecs: rt5665: Use guard() for mutex locks
ASoC: codecs: rt5668: Use guard() for mutex locks
ASoC: codecs: rt5677: Use guard() for mutex locks
ASoC: codecs: rt5682: Use guard() for mutex locks
ASoC: codecs: rt700: Use guard() for mutex locks
ASoC: codecs: rt711: Use guard() for mutex locks
ASoC: codecs: rt712: Use guard() for mutex locks
ASoC: codecs: rt721: Use guard() for mutex locks
ASoC: codecs: rt722: Use guard() for mutex locks
ASoC: codecs: sigmadsp: Use guard() for mutex locks
ASoC: codecs: sta350: Use guard() for mutex locks
ASoC: codecs: sta32x: Use guard() for mutex locks
ASoC: codecs: tas2781: Use guard() for mutex locks
ASoC: codecs: tas2783: Use guard() for mutex locks
ASoC: codecs: tas5805m: Use guard() for mutex locks
ASoC: codecs: tas675x: Use guard() for mutex locks
ASoC: codecs: tlv320dac33: Use guard() for mutex & spin locks
ASoC: codecs: tscs42xx: Use guard() for mutex locks
ASoC: codecs: tscs454: Use guard() for mutex locks
ASoC: codecs: twl6040: Use guard() for mutex locks
ASoC: codecs: wcd-mbhc: Use guard() for mutex locks
ASoC: codecs: wcd934x: Use guard() for mutex locks
ASoC: codecs: wcd937x: Use guard() for mutex locks
ASoC: codecs: wcd938x: Use guard() for mutex locks
ASoC: codecs: wcd939x: Use guard() for mutex locks
ASoC: codecs: wm0010: Use guard() for mutex & spin locks
ASoC: codecs: wm2000: Use guard() for mutex locks
ASoC: codecs: wm5102: Use guard() for mutex locks
ASoC: codecs: wm8731: Use guard() for mutex locks
ASoC: codecs: wm8903: Use guard() for mutex locks
ASoC: codecs: wm8958: Use guard() for mutex locks
ASoC: codecs: wm8962: Use guard() for mutex locks
ASoC: codecs: wm8994: Use guard() for mutex locks
ASoC: codecs: wm971x: Use guard() for mutex locks
ASoC: codecs: wm_adsp: Use guard() for mutex locks
ASoC: codecs: wsa88xx: Use guard() for mutex locks
sound/soc/codecs/ab8500-codec.c | 11 +-
sound/soc/codecs/ak4613.c | 72 ++++---
sound/soc/codecs/arizona-jack.c | 194 +++++++++----------
sound/soc/codecs/arizona.c | 25 +--
sound/soc/codecs/aw87390.c | 9 +-
sound/soc/codecs/aw88081.c | 17 +-
sound/soc/codecs/aw88166.c | 23 +--
sound/soc/codecs/aw88261.c | 20 +-
sound/soc/codecs/aw88395/aw88395.c | 23 +--
sound/soc/codecs/aw88395/aw88395_device.c | 39 ++--
sound/soc/codecs/aw88399.c | 14 +-
sound/soc/codecs/cros_ec_codec.c | 17 +-
sound/soc/codecs/cs-amp-lib.c | 10 +-
sound/soc/codecs/cs35l56-shared.c | 6 +-
sound/soc/codecs/cs35l56.c | 56 +++---
sound/soc/codecs/cs42l42.c | 222 +++++++++++-----------
sound/soc/codecs/cs42l43-jack.c | 57 +++---
sound/soc/codecs/cs42l43.c | 16 +-
sound/soc/codecs/cs42l84.c | 9 +-
sound/soc/codecs/cs43130.c | 96 +++++-----
sound/soc/codecs/cs47l15.c | 10 +-
sound/soc/codecs/cs47l35.c | 10 +-
sound/soc/codecs/cs47l85.c | 10 +-
sound/soc/codecs/cs47l90.c | 10 +-
sound/soc/codecs/cs47l92.c | 10 +-
sound/soc/codecs/cs48l32.c | 28 ++-
sound/soc/codecs/cx2072x.c | 4 +-
sound/soc/codecs/da7213.c | 36 ++--
sound/soc/codecs/da7219.c | 59 ++----
sound/soc/codecs/es8316.c | 30 ++-
sound/soc/codecs/es8326.c | 29 ++-
sound/soc/codecs/es9356.c | 29 ++-
sound/soc/codecs/fs210x.c | 86 +++------
sound/soc/codecs/hdac_hdmi.c | 116 ++++++-----
sound/soc/codecs/hdmi-codec.c | 16 +-
sound/soc/codecs/idt821034.c | 120 +++++-------
sound/soc/codecs/lpass-macro-common.c | 11 +-
sound/soc/codecs/madera.c | 31 +--
sound/soc/codecs/max98095.c | 34 ++--
sound/soc/codecs/mt6359-accdet.c | 12 +-
sound/soc/codecs/pcm512x.c | 17 +-
sound/soc/codecs/pcm6240.c | 27 ++-
sound/soc/codecs/peb2466.c | 15 +-
sound/soc/codecs/rt5514-spi.c | 20 +-
sound/soc/codecs/rt5645.c | 161 ++++++++--------
sound/soc/codecs/rt5665.c | 7 +-
sound/soc/codecs/rt5668.c | 9 +-
sound/soc/codecs/rt5677-spi.c | 35 ++--
sound/soc/codecs/rt5677.c | 77 ++++----
sound/soc/codecs/rt5682-sdw.c | 23 ++-
sound/soc/codecs/rt5682.c | 4 +-
sound/soc/codecs/rt5682s.c | 16 +-
sound/soc/codecs/rt700-sdw.c | 13 +-
sound/soc/codecs/rt711-sdca-sdw.c | 33 ++--
sound/soc/codecs/rt711-sdca.c | 7 +-
sound/soc/codecs/rt711-sdw.c | 23 ++-
sound/soc/codecs/rt711.c | 19 +-
sound/soc/codecs/rt712-sdca-sdw.c | 34 ++--
sound/soc/codecs/rt712-sdca.c | 7 +-
sound/soc/codecs/rt721-sdca-sdw.c | 33 ++--
sound/soc/codecs/rt721-sdca.c | 3 +-
sound/soc/codecs/rt722-sdca-sdw.c | 33 ++--
sound/soc/codecs/rt722-sdca.c | 3 +-
sound/soc/codecs/sigmadsp.c | 16 +-
sound/soc/codecs/sta32x.c | 12 +-
sound/soc/codecs/sta350.c | 12 +-
sound/soc/codecs/tas2781-comlib-i2c.c | 4 +-
sound/soc/codecs/tas2781-i2c.c | 20 +-
sound/soc/codecs/tas2783-sdw.c | 89 +++++----
sound/soc/codecs/tas5805m.c | 15 +-
sound/soc/codecs/tas675x.c | 6 +-
sound/soc/codecs/tlv320dac33.c | 77 +++-----
sound/soc/codecs/tscs42xx.c | 44 ++---
sound/soc/codecs/tscs454.c | 106 ++++-------
sound/soc/codecs/twl6040.c | 4 +-
sound/soc/codecs/wcd-mbhc-v2.c | 142 +++++++-------
sound/soc/codecs/wcd934x.c | 46 ++---
sound/soc/codecs/wcd937x.c | 24 +--
sound/soc/codecs/wcd938x.c | 21 +-
sound/soc/codecs/wcd939x.c | 19 +-
sound/soc/codecs/wm0010.c | 63 +++---
sound/soc/codecs/wm2000.c | 27 +--
sound/soc/codecs/wm5102.c | 12 +-
sound/soc/codecs/wm8731.c | 3 +-
sound/soc/codecs/wm8903.c | 3 +-
sound/soc/codecs/wm8958-dsp2.c | 9 +-
sound/soc/codecs/wm8962.c | 7 +-
sound/soc/codecs/wm8994.c | 51 ++---
sound/soc/codecs/wm9712.c | 4 +-
sound/soc/codecs/wm9713.c | 4 +-
sound/soc/codecs/wm_adsp.c | 87 +++------
sound/soc/codecs/wsa883x.c | 10 +-
sound/soc/codecs/wsa884x.c | 10 +-
93 files changed, 1330 insertions(+), 1863 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH v4 3/6] drm/verisilicon: introduce per-variant hardware ops table
From: Joey Lu @ 2026-06-17 10:30 UTC (permalink / raw)
To: Icenowy Zheng, maarten.lankhorst, mripard, tzimmermann, airlied,
simona, robh, krzk+dt, conor+dt
Cc: ychuang3, schung, yclu4, dri-devel, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <38d07e3b3c841d380c987800d8ef85065be94e79.camel@iscas.ac.cn>
On 6/15/2026 4:37 PM, Icenowy Zheng wrote:
> 在 2026-06-15一的 14:50 +0800,Joey Lu写道:
>
>> +
>> drm_crtc_vblank_on(crtc);
>> }
>>
>> @@ -119,7 +148,8 @@ static bool vs_crtc_mode_fixup(struct drm_crtc
>> *crtc,
>> }
>>
>> static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
>> - .atomic_flush = drm_crtc_vblank_atomic_flush,
>> + .atomic_begin = vs_crtc_atomic_begin,
>> + .atomic_flush = vs_crtc_atomic_flush,
>> .atomic_enable = vs_crtc_atomic_enable,
>> .atomic_disable = vs_crtc_atomic_disable,
>> .mode_set_nofb = vs_crtc_mode_set_nofb,
>> @@ -132,7 +162,7 @@ static int vs_crtc_enable_vblank(struct drm_crtc
>> *crtc)
>> struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
>> struct vs_dc *dc = vcrtc->dc;
>>
>> - regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
>> VSDC_TOP_IRQ_VSYNC(vcrtc->id));
>> + dc->funcs->enable_vblank(dc, vcrtc->id);
>>
>> return 0;
>> }
>> @@ -142,7 +172,7 @@ static void vs_crtc_disable_vblank(struct
>> drm_crtc *crtc)
>> struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
>> struct vs_dc *dc = vcrtc->dc;
>>
>> - regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
>> VSDC_TOP_IRQ_VSYNC(vcrtc->id));
>> + dc->funcs->disable_vblank(dc, vcrtc->id);
>> }
>>
>> static const struct drm_crtc_funcs vs_crtc_funcs = {
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c
>> b/drivers/gpu/drm/verisilicon/vs_dc.c
>> index dad9967bc10b..9729b693d360 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_dc.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
>> @@ -8,9 +8,7 @@
>> #include <linux/of.h>
>> #include <linux/of_graph.h>
>>
>> -#include "vs_crtc.h"
>> #include "vs_dc.h"
>> -#include "vs_dc_top_regs.h"
>> #include "vs_drm.h"
>> #include "vs_hwdb.h"
>>
>> @@ -33,7 +31,7 @@ static irqreturn_t vs_dc_irq_handler(int irq, void
>> *private)
>> struct vs_dc *dc = private;
>> u32 irqs;
>>
>> - regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &irqs);
>> + irqs = dc->funcs->irq_ack(dc);
> The definition of bits in 0x0010 seems to be different to ones in
> 0x147C, although the first 2 bits are the same. (e.g. `BIT(2)` is
> "cursor interrupt" in DC8000 0x147C but "secure reset done" in DC8200
> 0x0010).
>
> Should some kind of translation be done in `irq_ack` ? (and add a
> unified interrupt definition that aims to be the translation target).
>
> Thanks,
> Icenowy
I will add a set of unified IRQ bit definitions (e.g.
`VSDC_IRQ_VSYNC(n)`) in a shared header. Each `irq_ack` implementation
will translate its hardware-specific register bits into those unified
bits before returning, so `vs_drm_handle_irq` can use the unified
definitions without knowing which register layout was used. Currently
the VSYNC bits (BIT(0)/BIT(1)) coincide between DC8200 0x0010 and DC8000
0x147C, so the translation is trivial; this change will prevent future
confusion from diverging bit meanings (such as BIT(2)).
>>
>> vs_drm_handle_irq(dc, irqs);
>>
>> @@ -136,6 +134,8 @@ static int vs_dc_probe(struct platform_device
>> *pdev)
>> dev_info(dev, "Found DC%x rev %x customer %x\n", dc-
>>> identity.model,
>> dc->identity.revision, dc->identity.customer_id);
>>
>> + dc->funcs = &vs_dc8200_funcs;
>> +
>> if (port_count > dc->identity.display_count) {
>> dev_err(dev, "too many downstream ports than HW
>> capability\n");
>> ret = -EINVAL;
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h
>> b/drivers/gpu/drm/verisilicon/vs_dc.h
>> index ed1016f18758..544e1a37065b 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_dc.h
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
>> @@ -14,6 +14,7 @@
>> #include <linux/reset.h>
>>
>> #include <drm/drm_device.h>
>> +#include <drm/drm_plane.h>
>>
>> #include "vs_hwdb.h"
>>
>> @@ -22,6 +23,34 @@
>>
>> struct vs_drm_dev;
>> struct vs_crtc;
>> +struct vs_dc;
>> +
>> +struct vs_dc_funcs {
>> + /* Bridge: atomic_enable, atomic_disable */
>> + void (*panel_enable_ex)(struct vs_dc *dc, unsigned int
>> output);
>> + void (*panel_disable_ex)(struct vs_dc *dc, unsigned int
>> output);
>> +
>> + /* CRTC: atomic_begin, atomic_flush */
>> + void (*crtc_begin)(struct vs_dc *dc, unsigned int output);
>> + void (*crtc_flush)(struct vs_dc *dc, unsigned int output);
>> +
>> + /* CRTC: atomic_enable, atomic_disable */
>> + void (*crtc_enable)(struct vs_dc *dc, unsigned int output);
>> + void (*crtc_disable)(struct vs_dc *dc, unsigned int output);
>> +
>> + /* CRTC: enable_vblank, disable_vblank */
>> + void (*enable_vblank)(struct vs_dc *dc, unsigned int
>> output);
>> + void (*disable_vblank)(struct vs_dc *dc, unsigned int
>> output);
>> +
>> + /* Primary plane: atomic_enable, atomic_disable,
>> atomic_update */
>> + void (*primary_plane_enable_ex)(struct vs_dc *dc, unsigned
>> int output);
>> + void (*primary_plane_disable_ex)(struct vs_dc *dc, unsigned
>> int output);
>> + void (*primary_plane_update_ex)(struct vs_dc *dc, unsigned
>> int output,
>> + struct drm_plane_state
>> *state);
>> +
>> + /* IRQ acknowledge */
>> + u32 (*irq_ack)(struct vs_dc *dc);
>> +};
>>
>> struct vs_dc {
>> struct regmap *regs;
>> @@ -33,6 +62,9 @@ struct vs_dc {
>>
>> struct vs_drm_dev *drm_dev;
>> struct vs_chip_identity identity;
>> + const struct vs_dc_funcs *funcs;
>> };
>>
>> +extern const struct vs_dc_funcs vs_dc8200_funcs;
>> +
>> #endif /* _VS_DC_H_ */
>>
>>
^ permalink raw reply
* Re: [PATCH] KVM: arm64: Add missing hyp_enter when trapping sysreg
From: Fuad Tabba @ 2026-06-17 10:28 UTC (permalink / raw)
To: Vincent Donnefort
Cc: maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
catalin.marinas, will, linux-arm-kernel, kvmarm, kernel-team
In-Reply-To: <20260617095238.1530121-1-vdonnefort@google.com>
On Wed, 17 Jun 2026 at 10:55, Vincent Donnefort <vdonnefort@google.com> wrote:
>
> Add a missing hypervisor event call for hyp_enter on sysreg trapping,
> causing an unbalanced hyp_enter/hyp_exit.
>
> The enum hyp_enter_exit_reason is not ABI, so we can keep the ERET
> reasons at the end for clarity.
>
> Fixes: 696dfec22b8e ("KVM: arm64: Add hyp_enter/hyp_exit events to nVHE/pKVM hyp")
> Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
One thing that caught my eye (as Sashiko would say: pre-existing, not
introduced here), __hyp_enter_exit_reason_str() relies on positional
correspondence between strs[] and enum hyp_enter_exit_reason, with no
compile-time check. Inserting a new value in the middle of the enum
(as you do here) silently shifts all the strings after it if the table
isn't updated in lockstep.
A BUILD_BUG_ON(ARRAY_SIZE(strs)...) would at least catch size
mismatches; catching ordering bugs is harder without per-entry
initialisers like [HYP_REASON_SYS] = "sys".
Something for a future patch, for now:
Reviewed-by: Fuad Tabba <tabba@google.com>
Tested-by: Fuad Tabba <tabba@google.com>
Cheers,
/fuad
>
> diff --git a/arch/arm64/include/asm/kvm_hypevents.h b/arch/arm64/include/asm/kvm_hypevents.h
> index 743c49bd878f..5f6e6789d121 100644
> --- a/arch/arm64/include/asm/kvm_hypevents.h
> +++ b/arch/arm64/include/asm/kvm_hypevents.h
> @@ -12,6 +12,7 @@
> enum hyp_enter_exit_reason {
> HYP_REASON_SMC,
> HYP_REASON_HVC,
> + HYP_REASON_SYS,
> HYP_REASON_PSCI,
> HYP_REASON_HOST_ABORT,
> HYP_REASON_GUEST_EXIT,
> diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> index 06db299c37a8..45a4abb9619d 100644
> --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> @@ -913,6 +913,7 @@ void handle_trap(struct kvm_cpu_context *host_ctxt)
> handle_host_mem_abort(host_ctxt);
> break;
> case ESR_ELx_EC_SYS64:
> + trace_hyp_enter(host_ctxt, HYP_REASON_SYS);
> if (handle_host_mte(esr))
> break;
> fallthrough;
> diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c
> index c4b3ee552131..c84434e2349a 100644
> --- a/arch/arm64/kvm/hyp_trace.c
> +++ b/arch/arm64/kvm/hyp_trace.c
> @@ -398,6 +398,7 @@ static const char *__hyp_enter_exit_reason_str(u8 reason)
> static const char strs[][12] = {
> "smc",
> "hvc",
> + "sys",
> "psci",
> "host_abort",
> "guest_exit",
>
> base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
> --
> 2.54.0.1136.gdb2ca164c4-goog
>
^ permalink raw reply
* [PATCH v1] virt: arm-cca-guest: Drop unused assignment of platform_device_id driver data
From: Uwe Kleine-König (The Capable Hub) @ 2026-06-17 10:28 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon; +Cc: linux-arm-kernel, linux-kernel
The driver explicitly sets the .driver_data member of struct
platform_device_id to zero without relying on that value. Drop this
unused assignment.
While touching this array use a named initializer for .name.
Signed-off-by: Uwe Kleine-König (The Capable Hub) <u.kleine-koenig@baylibre.com>
---
drivers/virt/coco/arm-cca-guest/arm-cca-guest.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/virt/coco/arm-cca-guest/arm-cca-guest.c b/drivers/virt/coco/arm-cca-guest/arm-cca-guest.c
index 66d00b6ceb78..6155576a9711 100644
--- a/drivers/virt/coco/arm-cca-guest/arm-cca-guest.c
+++ b/drivers/virt/coco/arm-cca-guest/arm-cca-guest.c
@@ -223,7 +223,7 @@ module_exit(arm_cca_guest_exit);
/* modalias, so userspace can autoload this module when RSI is available */
static const struct platform_device_id arm_cca_match[] __maybe_unused = {
- { RSI_PDEV_NAME, 0},
+ { .name = RSI_PDEV_NAME },
{ }
};
base-commit: 4fa3f5fabb30bf00d7475d5a33459ea83d639bf9
--
2.47.3
^ permalink raw reply related
* Re: [PATCH v4 3/6] drm/verisilicon: introduce per-variant hardware ops table
From: Joey Lu @ 2026-06-17 10:26 UTC (permalink / raw)
To: Icenowy Zheng, maarten.lankhorst, mripard, tzimmermann, airlied,
simona, robh, krzk+dt, conor+dt
Cc: ychuang3, schung, yclu4, dri-devel, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <38d07e3b3c841d380c987800d8ef85065be94e79.camel@iscas.ac.cn>
On 6/15/2026 4:37 PM, Icenowy Zheng wrote:
> 在 2026-06-15一的 14:50 +0800,Joey Lu写道:
>> The DC8200 and DCUltraLite share a broadly similar register layout
>> but
>> differ in how the bridge, CRTC, primary plane and IRQ paths are
>> driven.
>> Introduce a vs_dc_funcs vtable so each variant can supply its own
>> implementation without scattering conditionals across multiple files.
>>
>> Add enum vs_dc_generation (VSDC_GEN_DC8000 / VSDC_GEN_DC8200) to
>> vs_hwdb.h and a generation field to struct vs_chip_identity.
>> Annotate
>> all four existing DC8200 HWDB entries with VSDC_GEN_DC8200.
>>
>> Extract the DC8200-specific hardware ops into a new vs_dc8200.c:
>> panel_enable_ex / panel_disable_ex - PANEL_CONFIG/START + CONFIG_EX
>> commit
>> enable_vblank / disable_vblank - TOP_IRQ_EN VSYNC bit
>> primary_plane_enable_ex / disable_ex / update_ex - FB_CONFIG_EX
>> path
>> irq_ack - reads TOP_IRQ_ACK
>>
>> Update vs_bridge.c, vs_crtc.c, vs_primary_plane.c and vs_dc.c to
>> dispatch through dc->funcs instead of directly touching registers.
>> vs_crtc.c gains atomic_begin and atomic_flush hooks to allow variants
>> to gate per-frame commit cycles.
>>
>> No behaviour change for existing DC8200 platforms.
>>
>> Signed-off-by: Joey Lu <a0987203069@gmail.com>
>> ---
>> drivers/gpu/drm/verisilicon/Makefile | 2 +-
>> drivers/gpu/drm/verisilicon/vs_bridge.c | 20 +---
>> drivers/gpu/drm/verisilicon/vs_crtc.c | 38 ++++++-
>> drivers/gpu/drm/verisilicon/vs_dc.c | 6 +-
>> drivers/gpu/drm/verisilicon/vs_dc.h | 32 ++++++
>> drivers/gpu/drm/verisilicon/vs_dc8200.c | 107
>> ++++++++++++++++++
>> drivers/gpu/drm/verisilicon/vs_hwdb.c | 4 +
>> drivers/gpu/drm/verisilicon/vs_hwdb.h | 6 +
>> .../gpu/drm/verisilicon/vs_primary_plane.c | 32 +-----
>> 9 files changed, 196 insertions(+), 51 deletions(-)
>> create mode 100644 drivers/gpu/drm/verisilicon/vs_dc8200.c
>>
>> diff --git a/drivers/gpu/drm/verisilicon/Makefile
>> b/drivers/gpu/drm/verisilicon/Makefile
>> index 426f4bcaa834..9d4cd16452fa 100644
>> --- a/drivers/gpu/drm/verisilicon/Makefile
>> +++ b/drivers/gpu/drm/verisilicon/Makefile
>> @@ -1,6 +1,6 @@
>> # SPDX-License-Identifier: GPL-2.0-only
>>
>> -verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_drm.o
>> vs_hwdb.o \
>> +verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_dc8200.o
>> vs_drm.o vs_hwdb.o \
>> vs_plane.o vs_primary_plane.o vs_cursor_plane.o
>>
>> obj-$(CONFIG_DRM_VERISILICON_DC) += verisilicon-dc.o
>> diff --git a/drivers/gpu/drm/verisilicon/vs_bridge.c
>> b/drivers/gpu/drm/verisilicon/vs_bridge.c
>> index 7a93049368db..6ff2ac745b15 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_bridge.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_bridge.c
>> @@ -162,15 +162,8 @@ static void vs_bridge_enable_common(struct
>> vs_crtc *crtc,
>> VSDC_DISP_PANEL_CONFIG_DE_EN |
>> VSDC_DISP_PANEL_CONFIG_DAT_EN |
>> VSDC_DISP_PANEL_CONFIG_CLK_EN);
>> - regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
>> - VSDC_DISP_PANEL_CONFIG_RUNNING);
>> - regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
>> - VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
>> - regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
>> - VSDC_DISP_PANEL_START_RUNNING(output));
>> -
>> - regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc-
>>> id),
>> - VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
>> +
>> + dc->funcs->panel_enable_ex(dc, output);
>> }
>>
>> static void vs_bridge_atomic_enable_dpi(struct drm_bridge *bridge,
>> @@ -228,14 +221,7 @@ static void vs_bridge_atomic_disable(struct
>> drm_bridge *bridge,
>> struct vs_dc *dc = crtc->dc;
>> unsigned int output = crtc->id;
>>
>> - regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
>> - VSDC_DISP_PANEL_START_MULTI_DISP_SYNC |
>> - VSDC_DISP_PANEL_START_RUNNING(output));
>> - regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
>> - VSDC_DISP_PANEL_CONFIG_RUNNING);
>> -
>> - regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(crtc-
>>> id),
>> - VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
>> + dc->funcs->panel_disable_ex(dc, output);
>> }
>>
>> static const struct drm_bridge_funcs vs_dpi_bridge_funcs = {
>> diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c
>> b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> index 0b8a35d09cd2..679d6541ba1b 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_crtc.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
>> @@ -16,10 +16,33 @@
>> #include "vs_crtc_regs.h"
>> #include "vs_crtc.h"
>> #include "vs_dc.h"
>> -#include "vs_dc_top_regs.h"
>> #include "vs_drm.h"
>> #include "vs_plane.h"
>>
>> +static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
>> + struct drm_atomic_commit *state)
>> +{
>> + struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
>> + struct vs_dc *dc = vcrtc->dc;
>> + unsigned int output = vcrtc->id;
>> +
>> + if (dc->funcs->crtc_begin)
>> + dc->funcs->crtc_begin(dc, output);
>> +}
>> +
>> +static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
>> + struct drm_atomic_commit *state)
>> +{
>> + struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
>> + struct vs_dc *dc = vcrtc->dc;
>> + unsigned int output = vcrtc->id;
>> +
>> + if (dc->funcs->crtc_flush)
>> + dc->funcs->crtc_flush(dc, output);
>> +
>> + drm_crtc_vblank_atomic_flush(crtc, state);
>> +}
>> +
>> static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
>> struct drm_atomic_commit *state)
>> {
>> @@ -30,6 +53,9 @@ static void vs_crtc_atomic_disable(struct drm_crtc
>> *crtc,
>> drm_crtc_vblank_off(crtc);
>>
>> clk_disable_unprepare(dc->pix_clk[output]);
>> +
>> + if (dc->funcs->crtc_disable)
>> + dc->funcs->crtc_disable(dc, output);
> Should this be `crtc_disable_ex` ? Because the clock-related operation
> is shared.
I will rename `crtc_disable` to `crtc_disable_ex` and `crtc_enable` to
`crtc_enable_ex` in v5 to make clear these hooks extend the shared clock
operations rather than replace them.
>> }
>>
>> static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
>> @@ -42,6 +68,9 @@ static void vs_crtc_atomic_enable(struct drm_crtc
>> *crtc,
>> drm_WARN_ON(&dc->drm_dev->base,
>> clk_prepare_enable(dc->pix_clk[output]));
>>
>> + if (dc->funcs->crtc_enable)
>> + dc->funcs->crtc_enable(dc, output);
> Ditto for appending `_ex` .
Addressed above.
>> +
>> drm_crtc_vblank_on(crtc);
>> }
>>
>> @@ -119,7 +148,8 @@ static bool vs_crtc_mode_fixup(struct drm_crtc
>> *crtc,
>> }
>>
>> static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
>> - .atomic_flush = drm_crtc_vblank_atomic_flush,
>> + .atomic_begin = vs_crtc_atomic_begin,
>> + .atomic_flush = vs_crtc_atomic_flush,
>> .atomic_enable = vs_crtc_atomic_enable,
>> .atomic_disable = vs_crtc_atomic_disable,
>> .mode_set_nofb = vs_crtc_mode_set_nofb,
>> @@ -132,7 +162,7 @@ static int vs_crtc_enable_vblank(struct drm_crtc
>> *crtc)
>> struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
>> struct vs_dc *dc = vcrtc->dc;
>>
>> - regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
>> VSDC_TOP_IRQ_VSYNC(vcrtc->id));
>> + dc->funcs->enable_vblank(dc, vcrtc->id);
>>
>> return 0;
>> }
>> @@ -142,7 +172,7 @@ static void vs_crtc_disable_vblank(struct
>> drm_crtc *crtc)
>> struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
>> struct vs_dc *dc = vcrtc->dc;
>>
>> - regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
>> VSDC_TOP_IRQ_VSYNC(vcrtc->id));
>> + dc->funcs->disable_vblank(dc, vcrtc->id);
>> }
>>
>> static const struct drm_crtc_funcs vs_crtc_funcs = {
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c
>> b/drivers/gpu/drm/verisilicon/vs_dc.c
>> index dad9967bc10b..9729b693d360 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_dc.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc.c
>> @@ -8,9 +8,7 @@
>> #include <linux/of.h>
>> #include <linux/of_graph.h>
>>
>> -#include "vs_crtc.h"
>> #include "vs_dc.h"
>> -#include "vs_dc_top_regs.h"
>> #include "vs_drm.h"
>> #include "vs_hwdb.h"
>>
>> @@ -33,7 +31,7 @@ static irqreturn_t vs_dc_irq_handler(int irq, void
>> *private)
>> struct vs_dc *dc = private;
>> u32 irqs;
>>
>> - regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &irqs);
>> + irqs = dc->funcs->irq_ack(dc);
> The definition of bits in 0x0010 seems to be different to ones in
> 0x147C, although the first 2 bits are the same. (e.g. `BIT(2)` is
> "cursor interrupt" in DC8000 0x147C but "secure reset done" in DC8200
> 0x0010).
>
> Should some kind of translation be done in `irq_ack` ? (and add a
> unified interrupt definition that aims to be the translation target).
>
> Thanks,
> Icenowy
>
>>
>> vs_drm_handle_irq(dc, irqs);
>>
>> @@ -136,6 +134,8 @@ static int vs_dc_probe(struct platform_device
>> *pdev)
>> dev_info(dev, "Found DC%x rev %x customer %x\n", dc-
>>> identity.model,
>> dc->identity.revision, dc->identity.customer_id);
>>
>> + dc->funcs = &vs_dc8200_funcs;
>> +
>> if (port_count > dc->identity.display_count) {
>> dev_err(dev, "too many downstream ports than HW
>> capability\n");
>> ret = -EINVAL;
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h
>> b/drivers/gpu/drm/verisilicon/vs_dc.h
>> index ed1016f18758..544e1a37065b 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_dc.h
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc.h
>> @@ -14,6 +14,7 @@
>> #include <linux/reset.h>
>>
>> #include <drm/drm_device.h>
>> +#include <drm/drm_plane.h>
>>
>> #include "vs_hwdb.h"
>>
>> @@ -22,6 +23,34 @@
>>
>> struct vs_drm_dev;
>> struct vs_crtc;
>> +struct vs_dc;
>> +
>> +struct vs_dc_funcs {
>> + /* Bridge: atomic_enable, atomic_disable */
>> + void (*panel_enable_ex)(struct vs_dc *dc, unsigned int
>> output);
>> + void (*panel_disable_ex)(struct vs_dc *dc, unsigned int
>> output);
>> +
>> + /* CRTC: atomic_begin, atomic_flush */
>> + void (*crtc_begin)(struct vs_dc *dc, unsigned int output);
>> + void (*crtc_flush)(struct vs_dc *dc, unsigned int output);
>> +
>> + /* CRTC: atomic_enable, atomic_disable */
>> + void (*crtc_enable)(struct vs_dc *dc, unsigned int output);
>> + void (*crtc_disable)(struct vs_dc *dc, unsigned int output);
>> +
>> + /* CRTC: enable_vblank, disable_vblank */
>> + void (*enable_vblank)(struct vs_dc *dc, unsigned int
>> output);
>> + void (*disable_vblank)(struct vs_dc *dc, unsigned int
>> output);
>> +
>> + /* Primary plane: atomic_enable, atomic_disable,
>> atomic_update */
>> + void (*primary_plane_enable_ex)(struct vs_dc *dc, unsigned
>> int output);
>> + void (*primary_plane_disable_ex)(struct vs_dc *dc, unsigned
>> int output);
>> + void (*primary_plane_update_ex)(struct vs_dc *dc, unsigned
>> int output,
>> + struct drm_plane_state
>> *state);
>> +
>> + /* IRQ acknowledge */
>> + u32 (*irq_ack)(struct vs_dc *dc);
>> +};
>>
>> struct vs_dc {
>> struct regmap *regs;
>> @@ -33,6 +62,9 @@ struct vs_dc {
>>
>> struct vs_drm_dev *drm_dev;
>> struct vs_chip_identity identity;
>> + const struct vs_dc_funcs *funcs;
>> };
>>
>> +extern const struct vs_dc_funcs vs_dc8200_funcs;
>> +
>> #endif /* _VS_DC_H_ */
>> diff --git a/drivers/gpu/drm/verisilicon/vs_dc8200.c
>> b/drivers/gpu/drm/verisilicon/vs_dc8200.c
>> new file mode 100644
>> index 000000000000..800df9279e9b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/verisilicon/vs_dc8200.c
>> @@ -0,0 +1,107 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2025 Icenowy Zheng <uwu@icenowy.me>
>> + */
>> +
>> +#include <linux/regmap.h>
>> +
>> +#include "vs_bridge_regs.h"
>> +#include "vs_dc.h"
>> +#include "vs_dc_top_regs.h"
>> +#include "vs_plane.h"
>> +#include "vs_primary_plane_regs.h"
>> +
>> +static void vs_dc8200_panel_enable_ex(struct vs_dc *dc, unsigned int
>> output)
>> +{
>> + regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
>> + VSDC_DISP_PANEL_CONFIG_RUNNING);
>> + regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
>> + VSDC_DISP_PANEL_START_MULTI_DISP_SYNC);
>> + regmap_set_bits(dc->regs, VSDC_DISP_PANEL_START,
>> + VSDC_DISP_PANEL_START_RUNNING(output));
>> +
>> + regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(output),
>> + VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
>> +}
>> +
>> +static void vs_dc8200_panel_disable_ex(struct vs_dc *dc, unsigned
>> int output)
>> +{
>> + regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_CONFIG(output),
>> + VSDC_DISP_PANEL_CONFIG_RUNNING);
>> + regmap_clear_bits(dc->regs, VSDC_DISP_PANEL_START,
>> + VSDC_DISP_PANEL_START_MULTI_DISP_SYNC |
>> + VSDC_DISP_PANEL_START_RUNNING(output));
>> +
>> + regmap_set_bits(dc->regs, VSDC_DISP_PANEL_CONFIG_EX(output),
>> + VSDC_DISP_PANEL_CONFIG_EX_COMMIT);
>> +}
>> +
>> +static void vs_dc8200_enable_vblank(struct vs_dc *dc, unsigned int
>> output)
>> +{
>> + regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN,
>> + VSDC_TOP_IRQ_VSYNC(output));
>> +}
>> +
>> +static void vs_dc8200_disable_vblank(struct vs_dc *dc, unsigned int
>> output)
>> +{
>> + regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN,
>> + VSDC_TOP_IRQ_VSYNC(output));
>> +}
>> +
>> +static void vs_dc8200_plane_commit(struct vs_dc *dc, unsigned int
>> output)
>> +{
>> + regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> + VSDC_FB_CONFIG_EX_COMMIT);
>> +}
>> +
>> +static void vs_dc8200_primary_plane_enable_ex(struct vs_dc *dc,
>> unsigned int output)
>> +{
>> + regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> + VSDC_FB_CONFIG_EX_FB_EN);
>> + regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> + VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
>> + VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
>> +
>> + vs_dc8200_plane_commit(dc, output);
>> +}
>> +
>> +static void vs_dc8200_primary_plane_disable_ex(struct vs_dc *dc,
>> unsigned int output)
>> +{
>> + regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> + VSDC_FB_CONFIG_EX_FB_EN);
>> +
>> + vs_dc8200_plane_commit(dc, output);
>> +}
>> +
>> +static void vs_dc8200_primary_plane_update_ex(struct vs_dc *dc,
>> unsigned int output,
>> + struct drm_plane_state
>> *state)
>> +{
>> + regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
>> + VSDC_MAKE_PLANE_POS(state->crtc_x, state-
>>> crtc_y));
>> + regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
>> + VSDC_MAKE_PLANE_POS(state->crtc_x + state-
>>> crtc_w,
>> + state->crtc_y + state-
>>> crtc_h));
>> + regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
>> + VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
>> +
>> + vs_dc8200_plane_commit(dc, output);
>> +}
>> +
>> +static u32 vs_dc8200_irq_ack(struct vs_dc *dc)
>> +{
>> + u32 irqs;
>> +
>> + regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &irqs);
>> + return irqs;
>> +}
>> +
>> +const struct vs_dc_funcs vs_dc8200_funcs = {
>> + .panel_enable_ex = vs_dc8200_panel_enable_ex,
>> + .panel_disable_ex =
>> vs_dc8200_panel_disable_ex,
>> + .enable_vblank = vs_dc8200_enable_vblank,
>> + .disable_vblank =
>> vs_dc8200_disable_vblank,
>> + .primary_plane_enable_ex =
>> vs_dc8200_primary_plane_enable_ex,
>> + .primary_plane_disable_ex =
>> vs_dc8200_primary_plane_disable_ex,
>> + .primary_plane_update_ex =
>> vs_dc8200_primary_plane_update_ex,
>> + .irq_ack = vs_dc8200_irq_ack,
>> +};
>> diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.c
>> b/drivers/gpu/drm/verisilicon/vs_hwdb.c
>> index 2a0f7c59afa3..91524d16f778 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_hwdb.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_hwdb.c
>> @@ -94,6 +94,7 @@ static struct vs_chip_identity vs_chip_identities[]
>> = {
>> .revision = 0x5720,
>> .customer_id = ~0U,
>>
>> + .generation = VSDC_GEN_DC8200,
>> .display_count = 2,
>> .max_cursor_size = 64,
>> .formats = &vs_formats_no_yuv444,
>> @@ -103,6 +104,7 @@ static struct vs_chip_identity
>> vs_chip_identities[] = {
>> .revision = 0x5721,
>> .customer_id = 0x30B,
>>
>> + .generation = VSDC_GEN_DC8200,
>> .display_count = 2,
>> .max_cursor_size = 64,
>> .formats = &vs_formats_no_yuv444,
>> @@ -112,6 +114,7 @@ static struct vs_chip_identity
>> vs_chip_identities[] = {
>> .revision = 0x5720,
>> .customer_id = 0x310,
>>
>> + .generation = VSDC_GEN_DC8200,
>> .display_count = 2,
>> .max_cursor_size = 64,
>> .formats = &vs_formats_with_yuv444,
>> @@ -121,6 +124,7 @@ static struct vs_chip_identity
>> vs_chip_identities[] = {
>> .revision = 0x5720,
>> .customer_id = 0x311,
>>
>> + .generation = VSDC_GEN_DC8200,
>> .display_count = 2,
>> .max_cursor_size = 64,
>> .formats = &vs_formats_no_yuv444,
>> diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.h
>> b/drivers/gpu/drm/verisilicon/vs_hwdb.h
>> index 2065ecb73043..a15c8b565604 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_hwdb.h
>> +++ b/drivers/gpu/drm/verisilicon/vs_hwdb.h
>> @@ -9,6 +9,11 @@
>> #include <linux/regmap.h>
>> #include <linux/types.h>
>>
>> +enum vs_dc_generation {
>> + VSDC_GEN_DC8000,
>> + VSDC_GEN_DC8200,
>> +};
>> +
>> struct vs_formats {
>> const u32 *array;
>> unsigned int num;
>> @@ -19,6 +24,7 @@ struct vs_chip_identity {
>> u32 revision;
>> u32 customer_id;
>>
>> + enum vs_dc_generation generation;
>> u32 display_count;
>> /*
>> * The hardware only supports square cursor planes, so this
>> field
>> diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
>> b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
>> index 1f2be41ae496..f992cb277f61 100644
>> --- a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
>> +++ b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
>> @@ -53,12 +53,6 @@ static int vs_primary_plane_atomic_check(struct
>> drm_plane *plane,
>> return 0;
>> }
>>
>> -static void vs_primary_plane_commit(struct vs_dc *dc, unsigned int
>> output)
>> -{
>> - regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> - VSDC_FB_CONFIG_EX_COMMIT);
>> -}
>> -
>> static void vs_primary_plane_atomic_enable(struct drm_plane *plane,
>> struct drm_atomic_commit
>> *atomic_state)
>> {
>> @@ -69,13 +63,8 @@ static void vs_primary_plane_atomic_enable(struct
>> drm_plane *plane,
>> unsigned int output = vcrtc->id;
>> struct vs_dc *dc = vcrtc->dc;
>>
>> - regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> - VSDC_FB_CONFIG_EX_FB_EN);
>> - regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> - VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK,
>> - VSDC_FB_CONFIG_EX_DISPLAY_ID(output));
>> -
>> - vs_primary_plane_commit(dc, output);
>> + if (dc->funcs->primary_plane_enable_ex)
>> + dc->funcs->primary_plane_enable_ex(dc, output);
>> }
>>
>> static void vs_primary_plane_atomic_disable(struct drm_plane *plane,
>> @@ -88,10 +77,8 @@ static void vs_primary_plane_atomic_disable(struct
>> drm_plane *plane,
>> unsigned int output = vcrtc->id;
>> struct vs_dc *dc = vcrtc->dc;
>>
>> - regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output),
>> - VSDC_FB_CONFIG_EX_FB_EN);
>> -
>> - vs_primary_plane_commit(dc, output);
>> + if (dc->funcs->primary_plane_disable_ex)
>> + dc->funcs->primary_plane_disable_ex(dc, output);
>> }
>>
>> static void vs_primary_plane_atomic_update(struct drm_plane *plane,
>> @@ -133,18 +120,11 @@ static void
>> vs_primary_plane_atomic_update(struct drm_plane *plane,
>> regmap_write(dc->regs, VSDC_FB_STRIDE(output),
>> fb->pitches[0]);
>>
>> - regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output),
>> - VSDC_MAKE_PLANE_POS(state->crtc_x, state-
>>> crtc_y));
>> - regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output),
>> - VSDC_MAKE_PLANE_POS(state->crtc_x + state-
>>> crtc_w,
>> - state->crtc_y + state-
>>> crtc_h));
>> regmap_write(dc->regs, VSDC_FB_SIZE(output),
>> VSDC_MAKE_PLANE_SIZE(state->crtc_w, state-
>>> crtc_h));
>>
>> - regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output),
>> - VSDC_FB_BLEND_CONFIG_BLEND_DISABLE);
>> -
>> - vs_primary_plane_commit(dc, output);
>> + if (dc->funcs->primary_plane_update_ex)
>> + dc->funcs->primary_plane_update_ex(dc, output,
>> state);
>> }
>>
>> static const struct drm_plane_helper_funcs
>> vs_primary_plane_helper_funcs = {
^ permalink raw reply
* Re: [PATCH v4 1/6] dt-bindings: display: verisilicon, dc: generalize for single-output variants
From: Joey Lu @ 2026-06-17 10:25 UTC (permalink / raw)
To: Icenowy Zheng, maarten.lankhorst, mripard, tzimmermann, airlied,
simona, robh, krzk+dt, conor+dt
Cc: ychuang3, schung, yclu4, dri-devel, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <3683c5c617324f5835529617325745ef48fa1943.camel@iscas.ac.cn>
On 6/15/2026 4:19 PM, Icenowy Zheng wrote:
> 在 2026-06-15一的 14:49 +0800,Joey Lu写道:
>> The existing schema hard-codes the five-clock/three-reset/dual-port
>> topology of the DC8200 IP block, preventing reuse for single-output
>> variants such as the Verisilicon DCUltraLite used in the Nuvoton
>> MA35D1
>> SoC.
>>
>> Rework the schema so that variant-specific constraints are expressed
>> via
>> allOf/if blocks:
>>
>> - Add nuvoton,ma35d1-dcu to the SoC-specific compatible enum. The
>> generic verisilicon,dc fallback remains the driver-binding string.
>> - Move clock and reset items descriptions into the per-variant
>> allOf/if
>> blocks; keep only minItems/maxItems at the top level so the base
>> schema
>> accepts all variants.
>> - Restore full items lists for clock-names and reset-names at the top
>> level with minItems so the names are validated against the
>> descriptions.
>> - Keep ports in the global required list and keep
>> additionalProperties: false.
>> - Add an allOf/if block for thead,th1520-dc8200: five-clock (core,
>> axi,
>> ahb, pix0, pix1), three-reset (core, axi, ahb), required resets.
>> - Add an allOf/if block for nuvoton,ma35d1-dcu: two-clock (core,
>> pix0),
>> one-reset (core), required resets.
>>
>> Signed-off-by: Joey Lu <a0987203069@gmail.com>
>> ---
>> .../bindings/display/verisilicon,dc.yaml | 80
>> +++++++++++++++++--
>> 1 file changed, 73 insertions(+), 7 deletions(-)
>>
>> diff --git
>> a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
>> b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
>> index 9dc35ab973f2..0c41286b8223 100644
>> --- a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
>> +++ b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
>> @@ -17,6 +17,7 @@ properties:
>> items:
>> - enum:
>> - thead,th1520-dc8200
>> + - nuvoton,ma35d1-dcu
>> - const: verisilicon,dc # DC IPs have discoverable ID/revision
>> registers
>>
>> reg:
>> @@ -26,14 +27,12 @@ properties:
>> maxItems: 1
>>
>> clocks:
>> - items:
>> - - description: DC Core clock
>> - - description: DMA AXI bus clock
>> - - description: Configuration AHB bus clock
>> - - description: Pixel clock of output 0
>> - - description: Pixel clock of output 1
> Clock descriptions should still be in the global part instead of the
> per-compatible part.
>
> In the per-compatible part, clock-names should be constraint for SoCs.
I will move the `items:` clock descriptions back into the global
`clocks:` property, covering all five possible clocks. In the
per-compatible sections I will remove the description items and only
constrain `clocks: minItems/maxItems` and `clock-names:
minItems/maxItems`; for nuvoton,ma35d1-dcu I will additionally override
`clock-names: items:` to the two names actually used (core, pix0).
>> + minItems: 2
>> + maxItems: 5
>>
>> clock-names:
>> + minItems: 2
>> + maxItems: 5
>> items:
>> - const: core
>> - const: axi
>> @@ -42,12 +41,16 @@ properties:
>> - const: pix1
>>
>> resets:
>> + minItems: 1
>> + maxItems: 3
>> items:
>> - description: DC Core reset
>> - description: DMA AXI bus reset
>> - description: Configuration AHB bus reset
>>
>> reset-names:
>> + minItems: 1
>> + maxItems: 3
>> items:
>> - const: core
>> - const: axi
>> @@ -59,7 +62,7 @@ properties:
>> properties:
>> port@0:
>> $ref: /schemas/graph.yaml#/properties/port
>> - description: The first output channel , endpoint 0 should be
>> + description: The first output channel, endpoint 0 should be
> If you really want to fix this, please make it a separated patch
> instead of doing it here, for commit atomicity.
>
> Thanks,
> Icenowy
I’ll drop this change and keep it as is
>> used for DPI format output and endpoint 1 should be used
>> for DP format output.
>>
>> @@ -77,6 +80,69 @@ required:
>> - clock-names
>> - ports
>>
>> +allOf:
>> + - if:
>> + properties:
>> + compatible:
>> + contains:
>> + const: thead,th1520-dc8200
>> + then:
>> + properties:
>> + clocks:
>> + minItems: 5
>> + maxItems: 5
>> + items:
>> + - description: DC Core clock
>> + - description: DMA AXI bus clock
>> + - description: Configuration AHB bus clock
>> + - description: Pixel clock of output 0
>> + - description: Pixel clock of output 1
>> +
>> + clock-names:
>> + minItems: 5
>> + maxItems: 5
>> +
>> + resets:
>> + minItems: 3
>> + maxItems: 3
>> +
>> + reset-names:
>> + minItems: 3
>> + maxItems: 3
>> +
>> + required:
>> + - resets
>> + - reset-names
>> +
>> + - if:
>> + properties:
>> + compatible:
>> + contains:
>> + const: nuvoton,ma35d1-dcu
>> + then:
>> + properties:
>> + clocks:
>> + minItems: 2
>> + maxItems: 2
>> + items:
>> + - description: DC Core clock
>> + - description: Pixel clock of output 0
>> +
>> + clock-names:
>> + minItems: 2
>> + maxItems: 2
>> +
>> + resets:
>> + minItems: 1
>> + maxItems: 1
>> +
>> + reset-names:
>> + maxItems: 1
>> +
>> + required:
>> + - resets
>> + - reset-names
>> +
>> additionalProperties: false
>>
>> examples:
^ permalink raw reply
* [PATCH 28/37] drm/mediatek: dpi: Switch to atomic bridge callbacks
From: Maxime Ripard @ 2026-06-17 10:14 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
dri-devel, Maxime Ripard, Chun-Kuang Hu, Philipp Zabel,
Matthias Brugger, AngeloGioacchino Del Regno, linux-mediatek,
linux-arm-kernel
In-Reply-To: <20260617-drm-all-atomic-bridges-v1-0-b63e6316166b@kernel.org>
The mediatek mtk_dpi bridge still uses the deprecated non-atomic bridge
callbacks.
Switch to their atomic counterparts.
Generated by the following Coccinelle script:
@ is_bridge @
identifier funcs;
@@
struct drm_bridge_funcs funcs = {
...,
};
@ has_create_state depends on is_bridge @
identifier funcs, f;
@@
struct drm_bridge_funcs funcs = {
...,
.atomic_create_state = f,
...,
};
@ update_struct depends on (is_bridge && !has_create_state) @
identifier is_bridge.funcs;
identifier f;
@@
struct drm_bridge_funcs funcs = {
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
...,
};
@ update_pre_enable_struct depends on is_bridge @
identifier is_bridge.funcs;
identifier f;
@@
struct drm_bridge_funcs funcs = {
...,
- .pre_enable = f,
+ .atomic_pre_enable = f,
...,
};
@ update_pre_enable_impl depends on update_pre_enable_struct @
identifier update_pre_enable_struct.f;
identifier b;
@@
-void f(struct drm_bridge *b)
+void f(struct drm_bridge *b, struct drm_atomic_commit *commit)
{
...
}
@ update_enable_struct depends on is_bridge @
identifier is_bridge.funcs;
identifier f;
@@
struct drm_bridge_funcs funcs = {
...,
- .enable = f,
+ .atomic_enable = f,
...,
};
@ update_enable_impl depends on update_enable_struct @
identifier update_enable_struct.f;
identifier b;
@@
-void f(struct drm_bridge *b)
+void f(struct drm_bridge *b, struct drm_atomic_commit *commit)
{
...
}
@ update_disable_struct depends on is_bridge @
identifier is_bridge.funcs;
identifier f;
@@
struct drm_bridge_funcs funcs = {
...,
- .disable = f,
+ .atomic_disable = f,
...,
};
@ update_disable_impl depends on update_disable_struct @
identifier update_disable_struct.f;
identifier b;
@@
-void f(struct drm_bridge *b)
+void f(struct drm_bridge *b, struct drm_atomic_commit *commit)
{
...
}
@ update_post_disable_struct depends on is_bridge @
identifier is_bridge.funcs;
identifier f;
@@
struct drm_bridge_funcs funcs = {
...,
- .post_disable = f,
+ .atomic_post_disable = f,
...,
};
@ update_post_disable_impl depends on update_post_disable_struct @
identifier update_post_disable_struct.f;
identifier b;
@@
-void f(struct drm_bridge *b)
+void f(struct drm_bridge *b, struct drm_atomic_commit *commit)
{
...
}
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
To: Chun-Kuang Hu <chunkuang.hu@kernel.org>
To: Philipp Zabel <p.zabel@pengutronix.de>
To: Matthias Brugger <matthias.bgg@gmail.com>
To: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Cc: linux-mediatek@lists.infradead.org
Cc: linux-arm-kernel@lists.infradead.org
---
drivers/gpu/drm/mediatek/mtk_dpi.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index 959c994eef24..0e4f430de983 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -848,21 +848,23 @@ static void mtk_dpi_bridge_mode_set(struct drm_bridge *bridge,
struct mtk_dpi *dpi = bridge_to_dpi(bridge);
drm_mode_copy(&dpi->mode, adjusted_mode);
}
-static void mtk_dpi_bridge_disable(struct drm_bridge *bridge)
+static void mtk_dpi_bridge_disable(struct drm_bridge *bridge,
+ struct drm_atomic_commit *commit)
{
struct mtk_dpi *dpi = bridge_to_dpi(bridge);
mtk_dpi_power_off(dpi);
if (dpi->pinctrl && dpi->pins_gpio)
pinctrl_select_state(dpi->pinctrl, dpi->pins_gpio);
}
-static void mtk_dpi_bridge_enable(struct drm_bridge *bridge)
+static void mtk_dpi_bridge_enable(struct drm_bridge *bridge,
+ struct drm_atomic_commit *commit)
{
struct mtk_dpi *dpi = bridge_to_dpi(bridge);
if (dpi->pinctrl && dpi->pins_dpi)
pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
@@ -980,12 +982,12 @@ static void mtk_dpi_debugfs_init(struct drm_bridge *bridge, struct dentry *root)
static const struct drm_bridge_funcs mtk_dpi_bridge_funcs = {
.attach = mtk_dpi_bridge_attach,
.mode_set = mtk_dpi_bridge_mode_set,
.mode_valid = mtk_dpi_bridge_mode_valid,
- .disable = mtk_dpi_bridge_disable,
- .enable = mtk_dpi_bridge_enable,
+ .atomic_disable = mtk_dpi_bridge_disable,
+ .atomic_enable = mtk_dpi_bridge_enable,
.atomic_check = mtk_dpi_bridge_atomic_check,
.atomic_get_output_bus_fmts = mtk_dpi_bridge_atomic_get_output_bus_fmts,
.atomic_get_input_bus_fmts = mtk_dpi_bridge_atomic_get_input_bus_fmts,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
--
2.54.0
^ permalink raw reply related
* [PATCH 26/37] drm/exynos: mic: Switch to atomic bridge callbacks
From: Maxime Ripard @ 2026-06-17 10:14 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
dri-devel, Maxime Ripard, Inki Dae, Seung-Woo Kim, Kyungmin Park,
Krzysztof Kozlowski, Alim Akhtar, linux-arm-kernel,
linux-samsung-soc
In-Reply-To: <20260617-drm-all-atomic-bridges-v1-0-b63e6316166b@kernel.org>
The mic bridge still uses the deprecated non-atomic bridge
callbacks.
Switch to their atomic counterparts, adding the bridge state
handlers if not already present.
Generated by the following Coccinelle script:
@ is_bridge @
identifier funcs;
@@
struct drm_bridge_funcs funcs = {
...,
};
@ has_create_state depends on is_bridge @
identifier funcs, f;
@@
struct drm_bridge_funcs funcs = {
...,
.atomic_create_state = f,
...,
};
@ update_struct depends on (is_bridge && !has_create_state) @
identifier is_bridge.funcs;
identifier f;
@@
struct drm_bridge_funcs funcs = {
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
...,
};
@ update_pre_enable_struct depends on (is_bridge && !has_create_state) @
identifier is_bridge.funcs;
identifier f;
@@
struct drm_bridge_funcs funcs = {
...,
- .pre_enable = f,
+ .atomic_pre_enable = f,
...,
};
@ update_pre_enable_impl depends on update_pre_enable_struct @
identifier update_pre_enable_struct.f;
identifier b;
@@
-void f(struct drm_bridge *b)
+void f(struct drm_bridge *b, struct drm_atomic_commit *commit)
{
...
}
@ update_enable_struct depends on (is_bridge && !has_create_state) @
identifier is_bridge.funcs;
identifier f;
@@
struct drm_bridge_funcs funcs = {
...,
- .enable = f,
+ .atomic_enable = f,
...,
};
@ update_enable_impl depends on update_enable_struct @
identifier update_enable_struct.f;
identifier b;
@@
-void f(struct drm_bridge *b)
+void f(struct drm_bridge *b, struct drm_atomic_commit *commit)
{
...
}
@ update_disable_struct depends on (is_bridge && !has_create_state) @
identifier is_bridge.funcs;
identifier f;
@@
struct drm_bridge_funcs funcs = {
...,
- .disable = f,
+ .atomic_disable = f,
...,
};
@ update_disable_impl depends on update_disable_struct @
identifier update_disable_struct.f;
identifier b;
@@
-void f(struct drm_bridge *b)
+void f(struct drm_bridge *b, struct drm_atomic_commit *commit)
{
...
}
@ update_post_disable_struct depends on (is_bridge && !has_create_state) @
identifier is_bridge.funcs;
identifier f;
@@
struct drm_bridge_funcs funcs = {
...,
- .post_disable = f,
+ .atomic_post_disable = f,
...,
};
@ update_post_disable_impl depends on update_post_disable_struct @
identifier update_post_disable_struct.f;
identifier b;
@@
-void f(struct drm_bridge *b)
+void f(struct drm_bridge *b, struct drm_atomic_commit *commit)
{
...
}
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
To: Inki Dae <inki.dae@samsung.com>
To: Seung-Woo Kim <sw0312.kim@samsung.com>
To: Kyungmin Park <kyungmin.park@samsung.com>
To: Krzysztof Kozlowski <krzk@kernel.org>
Cc: Alim Akhtar <alim.akhtar@samsung.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-samsung-soc@vger.kernel.org
---
drivers/gpu/drm/exynos/exynos_drm_mic.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c
index e68c954ec3e6..3069f958137f 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_mic.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c
@@ -19,10 +19,11 @@
#include <linux/regmap.h>
#include <video/of_videomode.h>
#include <video/videomode.h>
+#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_encoder.h>
#include <drm/drm_print.h>
#include "exynos_drm_drv.h"
@@ -226,11 +227,12 @@ static void mic_set_reg_on(struct exynos_mic *mic, bool enable)
reg |= MIC_UPD_REG;
writel(reg, mic->reg + MIC_OP);
}
-static void mic_post_disable(struct drm_bridge *bridge)
+static void mic_post_disable(struct drm_bridge *bridge,
+ struct drm_atomic_commit *commit)
{
struct exynos_mic *mic = bridge->driver_private;
mutex_lock(&mic_mutex);
if (!mic->enabled)
@@ -255,11 +257,12 @@ static void mic_mode_set(struct drm_bridge *bridge,
drm_display_mode_to_videomode(mode, &mic->vm);
mic->i80_mode = to_exynos_crtc(bridge->encoder->crtc)->i80_mode;
mutex_unlock(&mic_mutex);
}
-static void mic_pre_enable(struct drm_bridge *bridge)
+static void mic_pre_enable(struct drm_bridge *bridge,
+ struct drm_atomic_commit *commit)
{
struct exynos_mic *mic = bridge->driver_private;
int ret;
mutex_lock(&mic_mutex);
@@ -293,13 +296,16 @@ static void mic_pre_enable(struct drm_bridge *bridge)
unlock:
mutex_unlock(&mic_mutex);
}
static const struct drm_bridge_funcs mic_bridge_funcs = {
- .post_disable = mic_post_disable,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_post_disable = mic_post_disable,
.mode_set = mic_mode_set,
- .pre_enable = mic_pre_enable,
+ .atomic_pre_enable = mic_pre_enable,
};
static int exynos_mic_bind(struct device *dev, struct device *master,
void *data)
{
--
2.54.0
^ permalink raw reply related
* [PATCH 00/37] drm/bridge: Convert all bridges to atomic
From: Maxime Ripard @ 2026-06-17 10:14 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
dri-devel, Maxime Ripard, Sasha Finkelstein, Janne Grunau, asahi,
Benson Leung, Guenter Roeck, chrome-platform, Francesco Dolcini,
Peter Senna Tschudin, Ian Ray, Martyn Welch,
Manikandan Muralidharan, Dharma Balasubiramani, Russell King,
Inki Dae, Seung-Woo Kim, Kyungmin Park, Krzysztof Kozlowski,
Alim Akhtar, linux-arm-kernel, linux-samsung-soc, Linus Walleij,
Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
AngeloGioacchino Del Regno, linux-mediatek, Rob Clark,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul,
Marijn Suijten, freedreno, Tomi Valkeinen, Alain Volmat,
Raphael Gallais-Pou
Hi,
Over the years, most of the bridges have been converted to atomic
modesetting and hooks, but not all of them. This forces us to maintain
two different code path in quite a few places, which is pretty
bothersome. The switch to atomic modesetting for legacy bridges though
is pretty trivial, and we don't have a lot of drivers still using the
legacy path.
This series converts all bridges to atomic modesetting and drops the
legacy codepaths where relevant.
Let me know what you think,
Maxime
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
Maxime Ripard (37):
drm/adp: mipi: Switch to atomic bridge callbacks
drm/bridge: analogix-anx6345: Switch to atomic bridge callbacks
drm/bridge: analogix-anx78xx: Switch to atomic bridge callbacks
drm/bridge: aux-bridge: Switch to atomic bridge callbacks
drm/bridge: aux-hpd-bridge: Switch to atomic bridge callbacks
drm/bridge: chrontel-ch7033: Switch to atomic bridge callbacks
drm/bridge: cros-ec-anx7688: Switch to atomic bridge callbacks
drm/bridge: lontium-lt8713sx: Switch to atomic bridge callbacks
drm/bridge: lontium-lt8912b: Switch to atomic bridge callbacks
drm/bridge: lontium-lt9611uxc: Switch to atomic bridge callbacks
drm/bridge: lvds-codec: Switch to atomic bridge callbacks
drm/bridge: megachips-stdpxxxx-ge-b850v3-fw: Switch to atomic bridge callbacks
drm/bridge: microchip-lvds: Switch to atomic bridge callbacks
drm/bridge: nxp-ptn3460: Switch to atomic bridge callbacks
drm/bridge: of-display-mode-bridge: Switch to atomic bridge callbacks
drm/bridge: parade-ps8622: Switch to atomic bridge callbacks
drm/bridge: sii9234: Switch to atomic bridge callbacks
drm/bridge: sil-sii8620: Switch to atomic bridge callbacks
drm/bridge: simple-bridge: Switch to atomic bridge callbacks
drm/bridge: tc358764: Switch to atomic bridge callbacks
drm/bridge: tda998x: Switch to atomic bridge callbacks
drm/bridge: ti-tfp410: Switch to atomic bridge callbacks
drm/bridge: ti-tpd12s015: Switch to atomic bridge callbacks
drm/bridge: thc63lvd1024: Switch to atomic bridge callbacks
drm/bridge: waveshare-dsi: Switch to atomic bridge callbacks
drm/exynos: mic: Switch to atomic bridge callbacks
drm/mcde: dsi: Switch to atomic bridge callbacks
drm/mediatek: dpi: Switch to atomic bridge callbacks
drm/msm: dsi: Switch to atomic bridge callbacks
drm/omap: dpi: Switch to atomic bridge callbacks
drm/omap: dsi: Switch to atomic bridge callbacks
drm/omap: sdi: Switch to atomic bridge callbacks
drm/omap: venc: Switch to atomic bridge callbacks
drm/sti: dvo: Switch to atomic bridge callbacks
drm/sti: hda: Switch to atomic bridge callbacks
drm/sti: hdmi: Switch to atomic bridge callbacks
drm/bridge: Remove legacy bridge callback support
drivers/gpu/drm/adp/adp-mipi.c | 4 +
drivers/gpu/drm/bridge/analogix/analogix-anx6345.c | 13 ++-
drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c | 13 ++-
drivers/gpu/drm/bridge/aux-bridge.c | 4 +
drivers/gpu/drm/bridge/aux-hpd-bridge.c | 4 +
drivers/gpu/drm/bridge/chrontel-ch7033.c | 13 ++-
drivers/gpu/drm/bridge/cros-ec-anx7688.c | 4 +
drivers/gpu/drm/bridge/lontium-lt8713sx.c | 4 +
drivers/gpu/drm/bridge/lontium-lt8912b.c | 8 +-
drivers/gpu/drm/bridge/lontium-lt9611uxc.c | 3 +
drivers/gpu/drm/bridge/lvds-codec.c | 10 +-
.../drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 3 +
drivers/gpu/drm/bridge/microchip-lvds.c | 3 +
drivers/gpu/drm/bridge/nxp-ptn3460.c | 17 ++--
drivers/gpu/drm/bridge/of-display-mode-bridge.c | 4 +
drivers/gpu/drm/bridge/parade-ps8622.c | 18 ++--
drivers/gpu/drm/bridge/sii9234.c | 4 +
drivers/gpu/drm/bridge/sil-sii8620.c | 4 +
drivers/gpu/drm/bridge/simple-bridge.c | 13 ++-
drivers/gpu/drm/bridge/tc358764.c | 13 ++-
drivers/gpu/drm/bridge/tda998x_drv.c | 13 ++-
drivers/gpu/drm/bridge/thc63lvd1024.c | 14 ++-
drivers/gpu/drm/bridge/ti-tfp410.c | 10 +-
drivers/gpu/drm/bridge/ti-tpd12s015.c | 4 +
drivers/gpu/drm/bridge/waveshare-dsi.c | 14 ++-
drivers/gpu/drm/drm_bridge.c | 27 +-----
drivers/gpu/drm/exynos/exynos_drm_mic.c | 14 ++-
drivers/gpu/drm/mcde/mcde_dsi.c | 3 +
drivers/gpu/drm/mediatek/mtk_dpi.c | 10 +-
drivers/gpu/drm/msm/dsi/dsi_manager.c | 13 ++-
drivers/gpu/drm/omapdrm/dss/dpi.c | 14 ++-
drivers/gpu/drm/omapdrm/dss/dsi.c | 14 ++-
drivers/gpu/drm/omapdrm/dss/sdi.c | 14 ++-
drivers/gpu/drm/omapdrm/dss/venc.c | 14 ++-
drivers/gpu/drm/sti/sti_dvo.c | 20 ++--
drivers/gpu/drm/sti/sti_hda.c | 20 ++--
drivers/gpu/drm/sti/sti_hdmi.c | 20 ++--
drivers/gpu/drm/tests/drm_bridge_test.c | 104 ---------------------
include/drm/drm_bridge.h | 103 --------------------
39 files changed, 272 insertions(+), 332 deletions(-)
---
base-commit: 60dc0946bbad3eef8bc66a5a8b09b98dbc6e09c0
change-id: 20260615-drm-all-atomic-bridges-4da4fe7df58f
prerequisite-change-id: 20260530-drm-no-more-bridge-reset-ca20d5e22740:v2
prerequisite-patch-id: 8a8fbf1313a047a4a519f531a71c6c6f87b9bc83
prerequisite-patch-id: a4e58ec70eaf69e00dba8b06a9ea276476d99743
prerequisite-patch-id: d352592969ab04b77b981b3f214f2564e03adbb5
prerequisite-patch-id: 12209462a46aa438a0e5c415de008e2328128fe5
prerequisite-patch-id: 6193aa414873bcc7fa5d1062ed34cc124838ea6a
prerequisite-patch-id: 076dae12bdbc2c270f30588a8d6e95563359b309
prerequisite-patch-id: ffc56b19b1cd7ca4e909224903d8d43b39ae7fc6
prerequisite-patch-id: 5dfc850e535eea05740da327f1ec8ab1a57a3218
prerequisite-patch-id: 128438e2c3f56d29d05cf98486786c307441678b
prerequisite-patch-id: 9f030232058e7b6c6eb9978c0e2ea8baebadbfd8
prerequisite-patch-id: 9aa995e299738e71811fce508043e049216dbee8
prerequisite-patch-id: 3c8c77a634ee316df7d077926b84f97f75ce3b9d
prerequisite-patch-id: d60457e4989ffd8315fc5dfa0fe938f20ba5769e
prerequisite-patch-id: d8aca430669a3514e9c7c8071392fd975c729943
prerequisite-patch-id: f13b78cddc8e1d0a24ad2f07822cddcc2f349d56
prerequisite-patch-id: 274645e4a50795892bba19c49b7228223837e4ee
prerequisite-patch-id: b6e9e252c6b97ff453c31e7ce9f0c7e30af226ad
prerequisite-patch-id: eaf8dc37e5fa9671a749287932de5cedeaa19367
prerequisite-patch-id: ed140d0015fe32b1a312902d6a49dfa505d0c062
prerequisite-patch-id: 1798291a06b97eb5e44521dcb1d2db22266b7322
prerequisite-patch-id: 643d01d3dbe83950d3b3f1cec7adddc297163f24
prerequisite-patch-id: 3e63f40fbd7b98e342da46b1aa377e39fb4e20fe
prerequisite-patch-id: 83a906dc9533cfd63233328737c13092b0117164
prerequisite-patch-id: 24f53e9c4f55a4e879fa5a32b30910ed6c1cd269
prerequisite-patch-id: bd7e4106e2f0ef8608121b0408eac85d0f17b4df
prerequisite-patch-id: 2b76a8943c04e7be9da62a78caa5fc86437d87e3
prerequisite-patch-id: 59b0884a01a819a1d67e9135ea8e313a97bbb4fc
prerequisite-patch-id: bb66b9cc8daa47a2ebedbf0b8bfe67b3f5afc193
prerequisite-patch-id: 476834604d587da752945e53b2e4c6e0aba74b38
prerequisite-patch-id: 0d0c0d400e2dda1b2a2143c2bd34d6a361b97f13
prerequisite-patch-id: 27768e65101e85d23f008b22c9e34e8556334e1c
prerequisite-patch-id: 8ee093b3d6884c6c7dcd5072893c58dad092fda6
prerequisite-patch-id: 9ed3a66dc12bd099b09a495bd8148ecc363d3779
prerequisite-patch-id: 288c493e2466e08e8a7b3137fbb443e17a99e510
prerequisite-patch-id: 6f00079c63b96a7de1603f1524f06c0ad27c2cd3
prerequisite-patch-id: f5c58ba609d0b609afd627e0452d6de34bb12f4e
prerequisite-patch-id: 7f22cb2b55bd93af027ca50709fbbda072c15a06
prerequisite-patch-id: 922a3d5ceb0607718a290ffd1f02e3e573a49cf4
prerequisite-patch-id: 1d79eb945c8ca7ecae7b9d94b3ea07883c0337a9
prerequisite-patch-id: 2f97212d2d87fabca5724d003cc6c70ae1e026b0
prerequisite-patch-id: f667fac52310410fc067bebcf2c12e5757edbc51
prerequisite-patch-id: ddc5ac03c75f6c6bd080e379b1c86c0024f09010
prerequisite-patch-id: 1811f9a95469f9ab89faacc8fa23af3a8def0e10
prerequisite-patch-id: 29684ab02ba8bf4ee366a56627c02ff4ef0d5af3
prerequisite-patch-id: 00ebcbd12168a25f625d41dc4e0fc11659e00773
prerequisite-patch-id: 1c570484a5d1a5209db2290b991dd7914e219e1c
prerequisite-patch-id: 740c4469f788188f0e08fcbe9772ab2654892638
prerequisite-patch-id: 2fb23f6f9a41c7ddf94480c904ad7ec22c161f95
prerequisite-patch-id: d7f382a50b8db0525f2341a86705d01cdfbd9b56
prerequisite-patch-id: d7243d13750efb81a0e93d223f1afd7e30bcae24
prerequisite-patch-id: 6fd0d1296c2b01204a703d740cded71e448f7514
prerequisite-patch-id: e093cf415c55b339b04562e3dc970361a971b226
prerequisite-patch-id: 5f2c35278a4eb15ccf7f388b996a2ae2ba9b7396
prerequisite-patch-id: 9c732ea87d43b26c1e16377fe3a4de42f603f153
prerequisite-patch-id: 0d06852811ed3d3b52b701be99df135772b22d86
prerequisite-patch-id: 972a1fe51fc62083eee9af1ba4cccc2350f87820
prerequisite-patch-id: f78004b914048a14f48f9efbcf95586d84b33f23
prerequisite-patch-id: cd38c76c499c1e9722889d97938b0406eff31940
prerequisite-patch-id: 0777475583042460d0ef343533d8e2c37d3d2c4b
prerequisite-patch-id: 2e535f65d1b8e0e768e8f897f779f06c31d128a6
prerequisite-patch-id: ee47825cdbe1d4b937a6775fca324171015282d1
prerequisite-patch-id: 1e4c68816dc7d1672dfd7e7175a1680a40ab18ae
prerequisite-patch-id: 7509918098c4aabff7639a8f3849f4b2c56fca53
prerequisite-patch-id: fd07b55c2a713e73540f3049818d996fcd5ef966
prerequisite-patch-id: 5b269160fdaa636392cb2f1bf362c0cc393f08b8
prerequisite-patch-id: 021c28b68be7ccbab88067648f9730be2c70cdf6
prerequisite-patch-id: 8b208c636fb9439764239b991fd123ffc1addf1f
prerequisite-patch-id: dd6127d8825c9225abe959d6720b58bbac72a978
prerequisite-patch-id: 0001484c6655bfd60677b5af414aac14cbcf7431
prerequisite-patch-id: 592c6729eac409319be242a8358b469236f159b3
prerequisite-patch-id: dfcbcf6d807e482d41bf04ac4e65227e8ab5f39d
prerequisite-patch-id: 9817811602db4b50fed170d50d43c1989dc06202
prerequisite-patch-id: 6f932774c07dbe23e95a0fa35cdb1d3d9122e84b
prerequisite-patch-id: 70d2333469ba724002e019f785ca50b6fe636648
prerequisite-patch-id: f29fad83d6c2e4bf4e9c4606b7bf1e150f2be3c6
prerequisite-patch-id: d4c58215c22b28a9b7488f666004e399b47dac8b
prerequisite-patch-id: 14734115bea19a7d037f8f68899ac8a4016b3b43
prerequisite-patch-id: 8e74affd6c728fd5096ce92e57bbff3126685151
Best regards,
--
Maxime Ripard <mripard@kernel.org>
^ permalink raw reply
* Re: [PATCH v9 1/9] perf cs-etm: Fix thread leaks on trace queue init failure
From: James Clark @ 2026-06-17 10:07 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260616-b4-arm_cs_callchain_support_v1-v9-1-f8fad931c413@arm.com>
On 16/06/2026 3:51 pm, Leo Yan wrote:
> cs_etm__init_traceid_queue() allocates the frontend and decode threads,
> if a later allocation fails, the error path does not drop thread
> reference that was already acquired.
>
> Release both thread pointers with thread__zput() on the error path, so
> does not leak thread references or leave stale pointers behind.
>
> Fixes: 951ccccdc715 ("perf cs-etm: Only track threads instead of PID and TIDs")
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index 0927b0b9c06b15046afeafe23fe170b8248cfcc6..d484a6155c2c22fa916d0365987302f6bb9978e9 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -627,6 +627,8 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
> queue->tid);
> tidq->decode_thread = machine__findnew_thread(&etm->session->machines.host, -1,
> queue->tid);
> + if (!tidq->frontend_thread || !tidq->decode_thread)
> + goto out;
>
> tidq->packet = zalloc(sizeof(struct cs_etm_packet));
> if (!tidq->packet)
> @@ -661,6 +663,8 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
> zfree(&tidq->prev_packet);
> zfree(&tidq->packet);
> out:
> + thread__zput(tidq->frontend_thread);
> + thread__zput(tidq->decode_thread);
> return rc;
> }
>
>
Reviewed-by: James Clark <james.clark@linaro.org>
^ permalink raw reply
* Re: [PATCH v9 0/9] perf cs-etm: Support thread stack and callchain
From: James Clark @ 2026-06-17 10:06 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users, Leo Yan,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260616-b4-arm_cs_callchain_support_v1-v9-0-f8fad931c413@arm.com>
On 16/06/2026 3:51 pm, Leo Yan wrote:
> This series adds thread-stack and synthesized callchain support for Arm
> CoreSight, which comes from older series [1] but heavily rewritten.
>
> CS ETM previously kept last-branch state in a per-trace-queue buffer.
> That effectively makes the state per CPU, while the call/return history
> belongs to a thread. This series moves branch tracking to the common
> thread-stack code.
>
> The series records CoreSight branches with thread_stack__event(), uses
> thread_stack__br_sample() for last branch entries, flushes thread stacks
> after decoder resets.
>
> A decoder reset between AUX trace buffers is treated as a global trace
> discontinuity, so all thread stacks are flushed, so avoids carrying
> stale call/return history across a trace discontinuity.
>
> One limitation remains for instructions emulated by the kernel. In that
> case the exception return address may not match the return address
> stored in the thread stack, because after exception return can be one
> instruction ahead. The stack can still recover when a later return
> matches an upper caller. Given emulated instructions are not the common
> target for performance callchain analysis. Supporting this would require
> extending the common thread-stack path to accept both the real target
> address and an adjusted address for stack matching, so this series
> leaves that extra complexity out.
>
> The series has been tested on Orion6 board:
>
> perf test 136 -vvv
> 136: CoreSight synthesized callchain:
> --- start ---
> test child forked, pid 3539
> ---- end(0) ----
> 136: CoreSight synthesized callchain : Ok
>
> perf script --itrace=g16i10il64
>
> callchain_test 17468 [005] 1031003.229943: 10 instructions:
> aaaac32507c4 main+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> ffff90bd225c __libc_start_call_main+0x7c (/usr/lib/aarch64-linux-gnu/libc.so.6)
> ffff90bd233c call_init+0x9c (inlined)
> ffff90bd233c __libc_start_main_impl+0x9c (inlined)
> aaaac3250670 _start+0x30 (/home/kernel/leoy/test_cs_callchain/callchain_test)
>
> callchain_test 17468 [005] 1031003.229943: 10 instructions:
> aaaac3250774 do_svc+0xc (/home/kernel/leoy/test_cs_callchain/callchain_test)
> aaaac3250798 print+0xc (/home/kernel/leoy/test_cs_callchain/callchain_test)
> aaaac32507b0 foo+0xc (/home/kernel/leoy/test_cs_callchain/callchain_test)
> aaaac32507c8 main+0xc (/home/kernel/leoy/test_cs_callchain/callchain_test)
> ffff90bd225c __libc_start_call_main+0x7c (/usr/lib/aarch64-linux-gnu/libc.so.6)
> ffff90bd233c call_init+0x9c (inlined)
> ffff90bd233c __libc_start_main_impl+0x9c (inlined)
> aaaac3250670 _start+0x30 (/home/kernel/leoy/test_cs_callchain/callchain_test)
>
> callchain_test 17468 [005] 1031003.229944: 10 instructions:
> ffff800080010c20 vectors+0x420 ([kernel.kallsyms])
> aaaac3250784 do_svc+0x1c (/home/kernel/leoy/test_cs_callchain/callchain_test)
> aaaac3250798 print+0xc (/home/kernel/leoy/test_cs_callchain/callchain_test)
> aaaac32507b0 foo+0xc (/home/kernel/leoy/test_cs_callchain/callchain_test)
> aaaac32507c8 main+0xc (/home/kernel/leoy/test_cs_callchain/callchain_test)
> ffff90bd225c __libc_start_call_main+0x7c (/usr/lib/aarch64-linux-gnu/libc.so.6)
> ffff90bd233c call_init+0x9c (inlined)
> ffff90bd233c __libc_start_main_impl+0x9c (inlined)
> aaaac3250670 _start+0x30 (/home/kernel/leoy/test_cs_callchain/callchain_test)
>
> Note, the test fails on Juno board which is caused by many discontinuity
> packets (mainly caused by NO_SYNC elem). This is likely caused by the
> FIFO overflow on the path.
It passes 20/20 times on my r0 Juno board. But I wonder if reducing the
timestamp interval fixes it by reducing the rate of trace generation?
I'm just about to send patches that will change it to "timestamp=14" for
--per-thread mode (only context packet timestamps). And "timestamp=7"
for per-cpu mode (64 cycle interval).
>
> [1] https://lore.kernel.org/linux-arm-kernel/20200220052701.7754-1-leo.yan@linaro.org/
>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> Changes in v9:
> - Added patch 01 to fixed thread leak during trace queue init (sashiko).
> - Added check in instruction and branch samples in
> cs_etm__add_stack_event() (sashiko).
> - Released frontend_thread properly in cs_etm__context() (sashiko).
> - Refined cs_etm__flush_all_stack() to use switch (sashiko).
> - Gathered James' review tags.
> - Rebased on the latest perf-tools-next.
> - Link to v8: https://lore.kernel.org/r/20260611-b4-arm_cs_callchain_support_v1-v8-0-737948584fea@arm.com
>
> Changes in v8:
> - Updated test_arm_coresight_disasm.sh to pass "--itrace=b" and updated
> examples in arm-cs-trace-disasm.py (James).
> - Removed static annotation in callchain workload and renamed functions
> with prefix "callchain_" to reduce naming conflict (James).
> - For callchain test pre-condition check, removed the aarch64 check and
> added the root permission check (James).
> - Resolved the shellcheck errors (James).
> - Link to v7: https://lore.kernel.org/r/20260611-b4-arm_cs_callchain_support_v1-v7-0-1ba770c862ae@arm.com
>
> Changes in v7:
> - Rebased on the latest perf-tools-next.
> - Used struct_size() for allocation callchain struct (James).
> - Added a helper cs_etm__packet_has_taken_branch() (James).
> - Minor improvements for the callchain test (used record-ctl FIFO and
> reworked the validation callstack push / pop).
> - Link to v6: https://lore.kernel.org/r/20260526-b4-arm_cs_callchain_support_v1-v6-0-f9f49f53c9dd@arm.com
>
> Changes in v6:
> - Heavily rewrote the patches since restarted the work after 6 years.
> - Changed to use the common thread-stack for branch stack and callchain
> management.
> - Added a callchain test.
> - Link to v5: https://lore.kernel.org/linux-arm-kernel/20200220052701.7754-1-leo.yan@linaro.org/
>
> Changes in v5:
> - Addressed Mike's suggestion for performance improvement for function
> cs_etm__instr_addr() for quick calculation for non T32;
> - Removed the patch 'perf cs-etm: Synchronize instruction sample with
> the thread stack' (Mike);
> - Fixed the issue for exception is taken for branch target address
> accessing, for the branch sample and stack thread handling, the
> related patches are 01, 02, 07;
> - Fixed the stack thread handling for instruction emulation and single
> step with patches 08, 09.
> - Link to v4: https://lore.kernel.org/linux-arm-kernel/20200203020716.31832-1-leo.yan@linaro.org/
>
> ---
> Leo Yan (9):
> perf cs-etm: Fix thread leaks on trace queue init failure
> perf cs-etm: Filter synthesized branch samples
> perf cs-etm: Decode ETE exception packets
> perf cs-etm: Refactor instruction size handling
> perf cs-etm: Use thread-stack for last branch entries
> perf cs-etm: Flush thread stacks after decoder reset
> perf cs-etm: Support call indentation
> perf cs-etm: Synthesize callchains for instruction samples
> perf test: Add Arm CoreSight callchain test
>
> tools/perf/Documentation/perf-test.txt | 6 +-
> tools/perf/scripts/python/arm-cs-trace-disasm.py | 9 +-
> tools/perf/tests/builtin-test.c | 1 +
> tools/perf/tests/shell/coresight/callchain.sh | 172 ++++++++++
> .../shell/coresight/test_arm_coresight_disasm.sh | 4 +-
> tools/perf/tests/tests.h | 1 +
> tools/perf/tests/workloads/Build | 2 +
> tools/perf/tests/workloads/callchain.c | 33 ++
> tools/perf/util/cs-etm.c | 367 +++++++++++++--------
> 9 files changed, 446 insertions(+), 149 deletions(-)
> ---
> base-commit: 44543bb53ebfecb5ce4e890053a666affab9e482
> change-id: 20260521-b4-arm_cs_callchain_support_v1-2c2a70719bcc
>
> Best regards,
^ permalink raw reply
* RE: [PATCH v3 2/7] gpio: regmap: add gpio_regmap_get_gpiochip() accessor
From: Yu-Chun Lin [林祐君] @ 2026-06-17 9:54 UTC (permalink / raw)
To: Michael Walle, Bartosz Golaszewski, Andy Shevchenko
Cc: linusw@kernel.org, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org, afaerber@suse.com, wbg@kernel.org,
mathieu.dubois-briand@bootlin.com, lars@metafoo.de,
Michael.Hennerich@analog.com, jic23@kernel.org,
nuno.sa@analog.com, andy@kernel.org, dlechner@baylibre.com,
TY_Chang[張子逸], linux-gpio@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-realtek-soc@lists.infradead.org, linux-iio@vger.kernel.org,
CY_Huang[黃鉦晏],
Stanley Chang[昌育德],
James Tai [戴志峰]
In-Reply-To: <DJB6XO07EC8Q.1X9P752MLFB4N@kernel.org>
Hi Michael,
> Hi,
>
> On Wed Jun 17, 2026 at 10:36 AM CEST, Yu-Chun Lin [林祐君] wrote:
>>>>>> Without an accessor like gpio_regmap_get_gpiochip(), we cannot
>>>>>> retrieve the gpio_chip instantiated inside gpio-regmap.c to
>>>>>> fulfill these requirements in our
>>>>>> map() function.
>>>
>>> Why is gpiochip_irq_reqres() called in the first place? Isn't that
>>> only called if the irq handling is set up via gc->irq.chip and not
>>> via
>>> gpiochip_irqchip_add_domain() like in gpio-regmap?
>>>
>>
>> The panic was caused by my driver including
>> 'GPIOCHIP_IRQ_RESOURCE_HELPERS', which forced the call to 'gpiochip_irq_reqres()' and crashed.
>
> But why did you use it if your irq domain isn't managed by the gpiolib, but rather your own >irq domain? Before going with option #3 I'd double check if that is correct in your driver.
>
> -michael
Do you mean that a custom IRQ domain shouldn't be mixed with gpiolib features like
'GPIOCHIP_IRQ_RESOURCE_HELPERS'?
Additional information: our GPIO controller receives 3 separate interrupt lines.
Because the standard 'regmap_irq_chip' mechanism in 'gpio-regmap' does not support
this multi-line hardware design, we are forced to create our own IRQ domain and pass
it via 'config->irq_domain'.
Given this constraint (that we must use our own IRQ domain), are you suggesting
that we should implement our own 'irq_request_resources' and
'irq_release_resources' callbacks instead of relying on
'GPIOCHIP_IRQ_RESOURCE_HELPERS'?
But if that is the case, we would much prefer to let the core gpiolib handle
these resource and state management tasks for us *as proposed in option 3), rather
than duplicating the effort in our driver.
Best Regards,
Yu-Chun
^ permalink raw reply
* Re: [PATCH v9 9/9] perf test: Add Arm CoreSight callchain test
From: James Clark @ 2026-06-17 10:03 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260616-b4-arm_cs_callchain_support_v1-v9-9-f8fad931c413@arm.com>
On 16/06/2026 3:51 pm, Leo Yan wrote:
> Add a CoreSight shell test for synthesized callchains.
>
> The test uses the new callchain workload to generate trace and decodes
> it with synthesis callchain. It then verifies that the instruction
> samples show the expected callchain push and pop.
>
> Use control FIFOs so tracing starts only around the workload, which
> keeps the trace data small. The test is limited to with the cs_etm
> event available and root permission.
>
> After:
>
> perf test 138 -vvv
> 138: CoreSight synthesized callchain:
> ---- start ----
> test child forked, pid 35581
> Callchain flow matched:
> l1=4642868 l2=4642880 l3=4642895 l4=4642919 l5=4670494 l6=4670500 l7=4670520
> ---- end(0) ----
> 138: CoreSight synthesized callchain : Ok
>
> Assisted-by: Codex:GPT-5.5
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/Documentation/perf-test.txt | 6 +-
> tools/perf/tests/builtin-test.c | 1 +
> tools/perf/tests/shell/coresight/callchain.sh | 172 ++++++++++++++++++++++++++
> tools/perf/tests/tests.h | 1 +
> tools/perf/tests/workloads/Build | 2 +
> tools/perf/tests/workloads/callchain.c | 33 +++++
> 6 files changed, 213 insertions(+), 2 deletions(-)
>
> diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt
> index 81c8525f594680d814f80e6f88bcce8d867bb350..859df74e62efc4b1e80da13ae8e053356f68ae54 100644
> --- a/tools/perf/Documentation/perf-test.txt
> +++ b/tools/perf/Documentation/perf-test.txt
> @@ -57,7 +57,8 @@ OPTIONS
> --workload=::
> Run a built-in workload, to list them use '--list-workloads', current
> ones include: noploop, thloop, leafloop, sqrtloop, brstack, datasym,
> - context_switch_loop, deterministic, named_threads and landlock.
> + context_switch_loop, deterministic, named_threads, landlock and
> + callchain.
>
> Used with the shell script regression tests.
>
> @@ -69,7 +70,8 @@ OPTIONS
> 'named_threads' accepts the number of threads and the number of loops to
> do in each thread.
>
> - The datasym, landlock and deterministic workloads don't accept any.
> + The datasym, landlock, deterministic and callchain workloads don't accept
> + any.
>
> --list-workloads::
> List the available workloads to use with -w/--workload.
> diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
> index 7e75f590f225e3284980829707ca8d916c98cada..1d1f38127e05429a27f31beda814f2b5f5a75089 100644
> --- a/tools/perf/tests/builtin-test.c
> +++ b/tools/perf/tests/builtin-test.c
> @@ -168,6 +168,7 @@ static struct test_workload *workloads[] = {
> &workload__jitdump,
> &workload__context_switch_loop,
> &workload__deterministic,
> + &workload__callchain,
>
> #ifdef HAVE_RUST_SUPPORT
> &workload__code_with_type,
> diff --git a/tools/perf/tests/shell/coresight/callchain.sh b/tools/perf/tests/shell/coresight/callchain.sh
> new file mode 100755
> index 0000000000000000000000000000000000000000..13cca7dc11184002e3ddc058c0d0ffa1c458c483
> --- /dev/null
> +++ b/tools/perf/tests/shell/coresight/callchain.sh
> @@ -0,0 +1,172 @@
> +#!/bin/bash
> +# CoreSight synthesized callchain (exclusive)
> +# SPDX-License-Identifier: GPL-2.0
> +
> +glb_err=1
> +
> +if ! tmpdir=$(mktemp -d /tmp/perf-cs-callchain-test.XXXXXX); then
> + echo "mktemp failed"
> + exit 1
> +fi
> +
> +cleanup_files()
> +{
> + rm -rf "$tmpdir"
> +}
> +
> +trap cleanup_files EXIT
> +trap 'cleanup_files; exit $glb_err' TERM INT
> +
> +skip_if_system_is_not_ready()
> +{
> + perf list | grep -Pzq 'cs_etm//' || {
> + echo "[Skip] cs_etm event is not available" >&2
> + return 2
> + }
> +
> + # Requires root for trace in kernel
> + [ "$(id -u)" = 0 ] || {
> + echo "[Skip] No root permission" >&2
> + return 2
> + }
> +
> + return 0
> +}
> +
> +record_trace()
> +{
> + local data=$1
> + local script=$2
> +
> + local cf="$tmpdir/ctl"
> + local af="$tmpdir/ack"
> +
> + mkfifo "$cf" "$af"
> +
> + perf record -o "$data" -e cs_etm// --per-thread -D -1 --control fifo:"$cf","$af" -- \
> + perf test --record-ctl fifo:"$cf","$af" -w callchain >/dev/null 2>&1 &&
> +
> + # It is safe to use 'i3i' with a three-instruction interval, since the
> + # workload is compiled with -O0.
> + perf script --itrace=g16i3il64 -i "$data" > "$script"
Is there a reason we don't generate callstacks on branch samples and use
--itrace=g16bl64? That removes the magic number 3 and reduces the output
file size and test runtime a bit.
All I had to do was copy the same "if (etm->synth_opts.callchain) { ..."
block to cs_etm__synth_branch_sample(). It seems like the grepping
doesn't exactly match the branch sample format so the test fails, but
I'm sure that could be fixed.
I suppose there is value in testing instruction output, but maybe we can
add the option for users to add callstacks to branch samples, even if
it's not tested.
> +}
> +
> +callchain_regex_1()
> +{
> + printf '%s' \
> +'perf[[:space:]]+[0-9]+[[:space:]]+\[[0-9]+\][[:space:]]+([0-9.]+:[[:space:]]+)?[0-9]+ instructions:[[:space:]]*\n'\
> +'[[:space:]]+[[:xdigit:]]+ callchain_foo\+0x[[:xdigit:]]+ \(.*/perf\)\n'\
> +'[[:space:]]+[[:xdigit:]]+ callchain\+0x[[:xdigit:]]+ \(.*/perf\)\n'\
> +'([[:space:]]+[[:xdigit:]]+ .*\n)*'
> +}
> +
> +callchain_regex_2()
> +{
> + printf '%s' \
> +'perf[[:space:]]+[0-9]+[[:space:]]+\[[0-9]+\][[:space:]]+([0-9.]+:[[:space:]]+)?[0-9]+ instructions:[[:space:]]*\n'\
> +'[[:space:]]+[[:xdigit:]]+ callchain_do_syscall\+0x[[:xdigit:]]+ \(.*/perf\)\n'\
> +'[[:space:]]+[[:xdigit:]]+ callchain_foo\+0x[[:xdigit:]]+ \(.*/perf\)\n'\
> +'[[:space:]]+[[:xdigit:]]+ callchain\+0x[[:xdigit:]]+ \(.*/perf\)\n'\
> +'([[:space:]]+[[:xdigit:]]+ .*\n)*'
> +}
> +
> +callchain_regex_3()
> +{
> + printf '%s' \
> +'perf[[:space:]]+[0-9]+[[:space:]]+\[[0-9]+\][[:space:]]+([0-9.]+:[[:space:]]+)?[0-9]+ instructions:[[:space:]]*\n'\
> +'[[:space:]]+[[:xdigit:]]+ syscall(@plt)?\+0x[[:xdigit:]]+ \(.*\)\n'\
> +'[[:space:]]+[[:xdigit:]]+ callchain_do_syscall\+0x[[:xdigit:]]+ \(.*/perf\)\n'\
> +'[[:space:]]+[[:xdigit:]]+ callchain_foo\+0x[[:xdigit:]]+ \(.*/perf\)\n'\
> +'[[:space:]]+[[:xdigit:]]+ callchain\+0x[[:xdigit:]]+ \(.*/perf\)\n'\
> +'([[:space:]]+[[:xdigit:]]+ .*\n)*'
> +}
> +
> +callchain_regex_4()
> +{
> + printf '%s' \
> +'perf[[:space:]]+[0-9]+[[:space:]]+\[[0-9]+\][[:space:]]+([0-9.]+:[[:space:]]+)?[0-9]+ instructions:[[:space:]]*\n'\
> +'[[:space:]]+[[:xdigit:]]+ .*\+0x[[:xdigit:]]+ \(\[kernel\.kallsyms\]\)\n'\
> +'[[:space:]]+[[:xdigit:]]+ syscall(@plt)?\+0x[[:xdigit:]]+ \(.*\)\n'\
> +'[[:space:]]+[[:xdigit:]]+ callchain_do_syscall\+0x[[:xdigit:]]+ \(.*/perf\)\n'\
> +'[[:space:]]+[[:xdigit:]]+ callchain_foo\+0x[[:xdigit:]]+ \(.*/perf\)\n'\
> +'[[:space:]]+[[:xdigit:]]+ callchain\+0x[[:xdigit:]]+ \(.*/perf\)\n'\
> +'([[:space:]]+[[:xdigit:]]+ .*\n)*'
> +}
> +
> +find_after_line()
> +{
> + local regex="$1"
> + local file="$2"
> + local start="$3"
> + local offset
> + local line
> +
> + # Search in byte offset
> + offset=$(
> + tail -n +"$start" "$file" |
> + grep -Pzob -m1 "$regex" |
> + tr '\0' '\n' |
> + sed -n 's/^\([0-9][0-9]*\):.*/\1/p;q'
> + )
> +
> + if [ -z "$offset" ]; then
> + echo "Failed to match regex after line $start" >&2
> + echo "Regex:" >&2
> + printf '%s\n' "$regex" >&2
> + echo "Context from line $start:" >&2
> + sed -n "${start},$((start + 100))p" "$file" >&2
> + return 1
> + fi
> +
> + # Convert from offset to line
> + line=$(
> + tail -n +"$start" "$file" |
> + head -c "$offset" |
> + wc -l
> + )
> +
> + echo "$((start + line))"
> +}
> +
> +check_callchain_flow()
> +{
> + local file="$1"
> + local l1 l2 l3 l4 l5 l6 l7
> +
> + # Callchain push
> + l1=$(find_after_line "$(callchain_regex_1)" "$file" 1) || return 1
> + l2=$(find_after_line "$(callchain_regex_2)" "$file" "$((l1 + 1))") || return 1
> + l3=$(find_after_line "$(callchain_regex_3)" "$file" "$((l2 + 1))") || return 1
> + l4=$(find_after_line "$(callchain_regex_4)" "$file" "$((l3 + 1))") || return 1
> +
> + # Callchain pop
> + l5=$(find_after_line "$(callchain_regex_3)" "$file" "$((l4 + 1))") || return 1
> + l6=$(find_after_line "$(callchain_regex_2)" "$file" "$((l5 + 1))") || return 1
> + l7=$(find_after_line "$(callchain_regex_1)" "$file" "$((l6 + 1))") || return 1
> +
> + echo "Callchain flow matched:"
> + echo " l1=$l1 l2=$l2 l3=$l3 l4=$l4 l5=$l5 l6=$l6 l7=$l7"
> +
> + return 0
> +}
> +
> +run_test()
> +{
> + local data=$tmpdir/perf.data
> + local script=$tmpdir/perf.script
> +
> + if ! record_trace "$data" "$script"; then
> + echo "perf record/script failed"
> + return
> + fi
> +
> + check_callchain_flow "$script" || return
> +
> + glb_err=0
> +}
> +
> +skip_if_system_is_not_ready || exit 2
> +
> +run_test
> +
> +exit $glb_err
> diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
> index 7cedf05be544ad79a99e86d30dfa4f7b01ca0837..cee9e6b62dcc838c864bbe76efe3b638ed75b134 100644
> --- a/tools/perf/tests/tests.h
> +++ b/tools/perf/tests/tests.h
> @@ -248,6 +248,7 @@ DECLARE_WORKLOAD(inlineloop);
> DECLARE_WORKLOAD(jitdump);
> DECLARE_WORKLOAD(context_switch_loop);
> DECLARE_WORKLOAD(deterministic);
> +DECLARE_WORKLOAD(callchain);
>
> #ifdef HAVE_RUST_SUPPORT
> DECLARE_WORKLOAD(code_with_type);
> diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build
> index 7bb4b9829ba245740c8967e6bf3235614cdd55a3..048e371eb63e316453b6b46ebd0a02794c3d25d7 100644
> --- a/tools/perf/tests/workloads/Build
> +++ b/tools/perf/tests/workloads/Build
> @@ -13,6 +13,7 @@ perf-test-y += inlineloop.o
> perf-test-y += jitdump.o
> perf-test-y += context_switch_loop.o
> perf-test-y += deterministic.o
> +perf-test-y += callchain.o
>
> ifeq ($(CONFIG_RUST_SUPPORT),y)
> perf-test-y += code_with_type.o
> @@ -27,3 +28,4 @@ CFLAGS_traploop.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
> CFLAGS_inlineloop.o = -g -O2
> CFLAGS_deterministic.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
> CFLAGS_named_threads.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
> +CFLAGS_callchain.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
> diff --git a/tools/perf/tests/workloads/callchain.c b/tools/perf/tests/workloads/callchain.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..3951423d8115e9efb49af8ba2586001fc6f02761
> --- /dev/null
> +++ b/tools/perf/tests/workloads/callchain.c
> @@ -0,0 +1,33 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/compiler.h>
> +#include <sys/syscall.h>
> +#include <unistd.h>
> +#include "../tests.h"
> +
> +/*
> + * Mark as noinline to establish the call chain, and avoid the static
> + * annotation to prevent LTO from renaming the functions.
> + */
> +noinline void callchain_do_syscall(void);
> +noinline void callchain_foo(void);
> +noinline int callchain(int argc, const char **argv);
> +
> +noinline void callchain_do_syscall(void)
> +{
> + syscall(SYS_getpid);
> +}
> +
> +noinline void callchain_foo(void)
> +{
> + callchain_do_syscall();
> +}
> +
> +noinline int callchain(int argc __maybe_unused,
> + const char **argv __maybe_unused)
> +{
> + callchain_foo();
> +
> + return 0;
> +}
> +
> +DEFINE_WORKLOAD(callchain);
>
^ permalink raw reply
* [PATCH v7 10/13] firmware: arm_scmi: add Powercap MAI get/set support
From: Philip Radford @ 2026-06-17 9:59 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Philip Radford
In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com>
Add support for Power Measurement Averaging Interval (MAI) get and set
operations to the SCMI powercap protocol driver. Extends scmi_powercap_info
to store MAI configuration and implement MAI get/set via xfer and optional
fast-channel support.
Signed-off-by: Philip Radford <philip.radford@arm.com>
---
V5-V6
- Fixed commit length
- Changed warning message wording
- Fixed line lengths and alignment
- Updated docs for new fields
---
drivers/firmware/arm_scmi/powercap.c | 127 +++++++++++++++++++++++++++
include/linux/scmi_protocol.h | 18 ++++
2 files changed, 145 insertions(+)
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index 1800ee295b07..03ce02b1b83b 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -407,6 +407,34 @@ scmi_powercap_domain_attrs_process(const struct scmi_protocol_handle *ph,
dom_info->notify_powercap_measurement_change =
SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) {
+ struct scmi_msg_resp_powercap_domain_attributes_v3 *resp_v3 = r;
+
+ flags = le32_to_cpu(resp_v3->attributes);
+ if (pinfo->notify_measurements_cmd)
+ dom_info->notify_powercap_measurement_change =
+ SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
+
+ dom_info->mai_config = SUPPORTS_POWERCAP_MAI_CONFIGURATION(flags);
+ dom_info->min_mai = le32_to_cpu(resp_v3->min_mai);
+ dom_info->max_mai = le32_to_cpu(resp_v3->max_mai);
+ dom_info->mai_step = le32_to_cpu(resp_v3->mai_step);
+
+ if (dom_info->mai_config) {
+ ret = scmi_powercap_validate(dom_info->min_mai,
+ dom_info->max_mai,
+ dom_info->mai_step,
+ dom_info->mai_config);
+
+ if (ret) {
+ dev_warn(ph->dev, "Platform reported invalid MAI config for domain %d - %s\n",
+ dom_info->id, dom_info->name);
+
+ return ret;
+ }
+ }
+ }
+
dom_info->extended_names = SUPPORTS_EXTENDED_NAMES(flags);
dom_info->async_powercap_cap_set =
@@ -1088,6 +1116,103 @@ static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
return 0;
}
+static int scmi_powercap_xfer_mai_get(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 *mai)
+{
+ int ret;
+ struct scmi_xfer *t;
+
+ ret = ph->xops->xfer_get_init(ph, POWERCAP_MAI_GET, sizeof(u32),
+ sizeof(u32), &t);
+
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(domain_id, t->tx.buf);
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ *mai = get_unaligned_le32(t->rx.buf);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int scmi_powercap_xfer_mai_set(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 mai)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_powercap_cap_or_pai_set *msg;
+
+ ret = ph->xops->xfer_get_init(ph, POWERCAP_MAI_SET, sizeof(*msg),
+ 0, &t);
+ if (ret)
+ return ret;
+
+ msg = t->tx.buf;
+ msg->domain_id = cpu_to_le32(domain_id);
+ msg->flags = cpu_to_le32(0);
+ msg->value = cpu_to_le32(mai);
+
+ ret = ph->xops->do_xfer(ph, t);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
+static int
+scmi_powercap_measurements_interval_get(const struct scmi_protocol_handle *ph,
+ u32 domain_id,
+ u32 *val)
+{
+ const struct scmi_powercap_info *pc;
+ struct scmi_fc_info *fci;
+
+ if (!val)
+ return -EINVAL;
+
+ pc = scmi_powercap_dom_info_get(ph, domain_id);
+ if (!pc)
+ return -EINVAL;
+
+ fci = pc->cpli[CPL0].fc_info;
+ if (fci && fci[POWERCAP_FC_MAI].get_addr) {
+ *val = ioread32(fci[POWERCAP_FC_MAI].get_addr);
+ trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_MAI_GET,
+ domain_id, 0, *val, 0);
+ return 0;
+ }
+
+ return scmi_powercap_xfer_mai_get(ph, domain_id, val);
+}
+
+static int
+scmi_powercap_measurements_interval_set(const struct scmi_protocol_handle *ph,
+ u32 domain_id,
+ u32 val)
+{
+ const struct scmi_powercap_info *pc;
+ struct scmi_fc_info *fci;
+
+ pc = scmi_powercap_dom_info_get(ph, domain_id);
+ if (!pc)
+ return -EINVAL;
+
+ if (!pc->mai_config || !val || val < pc->min_mai || val > pc->max_mai)
+ return -EINVAL;
+
+ fci = pc->cpli[CPL0].fc_info;
+ if (fci && fci[POWERCAP_FC_MAI].set_addr) {
+ iowrite32(val, fci[POWERCAP_FC_MAI].set_addr);
+ ph->hops->fastchannel_db_ring(fci[POWERCAP_FC_MAI].set_db);
+ trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_MAI_SET, domain_id, 0, val, 0);
+ return 0;
+ }
+
+ return scmi_powercap_xfer_mai_set(ph, domain_id, val);
+}
+
static const struct scmi_powercap_proto_ops powercap_proto_ops = {
.num_domains_get = scmi_powercap_num_domains_get,
.info_get = scmi_powercap_dom_info_get,
@@ -1100,6 +1225,8 @@ static const struct scmi_powercap_proto_ops powercap_proto_ops = {
.measurements_get = scmi_powercap_measurements_get,
.measurements_threshold_set = scmi_powercap_measurements_threshold_set,
.measurements_threshold_get = scmi_powercap_measurements_threshold_get,
+ .measurements_interval_get = scmi_powercap_measurements_interval_get,
+ .measurements_interval_set = scmi_powercap_measurements_interval_set,
};
static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index d0f6c0102559..90615611be4a 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -654,6 +654,12 @@ struct scmi_powercap_cpl_info {
* reports power data on an abstract linear scale.
* @extended_names: Support for long names.
* @fastchannels: Support for at least one fastchannel,
+ * @mai_config: MAI configuration support.
+ * @min_mai: Minimum supported Power Measurement Averaging Interval in
+ * microseconds.
+ * @max_mai: Maximum supporte Power Measurement Averaging Interval in
+ microseconds.
+ * @mai_step: Step size between supported MAI values in microseconds.
* @name: name assigned to the Powercap Domain by platform.
* @sustainable_power: Maximum sustainable power consumption for this domain
* under normal conditions.
@@ -675,6 +681,10 @@ struct scmi_powercap_info {
bool powercap_scale_uw;
bool extended_names;
bool fastchannels;
+ bool mai_config;
+ u32 min_mai;
+ u32 max_mai;
+ u32 mai_step;
char name[SCMI_MAX_STR_SIZE];
unsigned int sustainable_power;
unsigned int accuracy;
@@ -733,6 +743,10 @@ struct scmi_powercap_info {
* @measurements_threshold_get: get the currently configured low and high power
* thresholds used when registering callbacks for
* notification POWERCAP_MEASUREMENTS_NOTIFY.
+ * @measurements_interval_get: get the current Power Measurement Averaging
+ * Interval (MAI) value for the specified domain.
+ * @measurements_interval_set: set the Power Measurement Averaging Interval
+ * (MAI) value for the specified domain.
*/
struct scmi_powercap_proto_ops {
int (*num_domains_get)(const struct scmi_protocol_handle *ph);
@@ -758,6 +772,10 @@ struct scmi_powercap_proto_ops {
int (*measurements_threshold_get)(const struct scmi_protocol_handle *ph,
u32 domain_id, u32 *power_thresh_low,
u32 *power_thresh_high);
+ int (*measurements_interval_get)(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 *val);
+ int (*measurements_interval_set)(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 val);
};
enum scmi_pinctrl_selector_type {
--
2.47.3
^ permalink raw reply related
* [PATCH v7 13/13] powercap: arm_scmi: Synthetic zone enable/disable
From: Philip Radford @ 2026-06-17 9:59 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Philip Radford
In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com>
The synthetic instance root contols the same set of top-level SCMI domains
already handled by the control-type enable/disable helpers previously
introduced.
Add synthetic zone enabled attribute to the existing per-instance helpers
instead of duplicating the enable-state tracking and rollback logic.
Signed-off-by: Philip Radford <philip.radford@arm.com>
---
V6->V7
- Re-factored due to new control-type enable disable patch at the beginning
of the series
- Prevent instance_root_get_enable() reporting a stale state
V5->V6
- Added use of to_scmi_powercap_root macro
- Changed instance_root_set_)enable_state to bail out on any error
- Changed logic in instance_root_get_enable to not check child states
---
drivers/powercap/arm_scmi_powercap.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_scmi_powercap.c
index e1bad4b19990..23708f9934d4 100644
--- a/drivers/powercap/arm_scmi_powercap.c
+++ b/drivers/powercap/arm_scmi_powercap.c
@@ -458,10 +458,34 @@ static int instance_root_get_constraint(struct powercap_zone *pz, int cid, u64 *
return -EOPNOTSUPP;
}
+static int instance_root_set_enable(struct powercap_zone *pz, bool mode)
+{
+ struct scmi_powercap_root *pr = to_scmi_powercap_root(pz);
+
+ return scmi_powercap_set_root_children_enable_state(pr, mode);
+}
+
+static int instance_root_get_enable(struct powercap_zone *pz, bool *mode)
+{
+ struct scmi_powercap_root *pr = to_scmi_powercap_root(pz);
+ int ret;
+
+ if (!mode)
+ return -EINVAL;
+
+ ret = scmi_powercap_read_root_children_enable_state(pr, mode);
+ if (!ret)
+ pr->enabled = *mode;
+
+ return ret;
+}
+
static const struct powercap_zone_ops instance_root_ops = {
.get_max_power_range_uw = scmi_powercap_get_max_power_range_uw,
.get_power_uw = instance_root_get_power_uw,
.release = instance_root_release,
+ .set_enable = instance_root_set_enable,
+ .get_enable = instance_root_get_enable,
};
static const struct powercap_zone_constraint_ops instance_root_const_ops = {
--
2.47.3
^ permalink raw reply related
* [PATCH v7 12/13] powercap: arm_scmi: Add get_power_uw to synthetic node
From: Philip Radford @ 2026-06-17 9:59 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Philip Radford
In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com>
Exposes the current power usage from the immediate children of the
synthetic (root) powercap node. Iterates over pr->spzones and sums per-zone
power.
Signed-off-by: Philip Radford <philip.radford@arm.com>
---
V6->V7
- Fixed commit message length
V5->V6
- Moved multiple u64 values to the same line
- Reworked logic to walk SCMI zones and filter by parent
- Added to_scmi_powercap_root macro
---
drivers/powercap/arm_scmi_powercap.c | 35 ++++++++++++++++++++++++++--
1 file changed, 33 insertions(+), 2 deletions(-)
diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_scmi_powercap.c
index d45e4af0cdc7..e1bad4b19990 100644
--- a/drivers/powercap/arm_scmi_powercap.c
+++ b/drivers/powercap/arm_scmi_powercap.c
@@ -17,6 +17,9 @@
#define to_scmi_powercap_zone(z) \
container_of(z, struct scmi_powercap_zone, zone)
+#define to_scmi_powercap_root(z) \
+ container_of(z, struct scmi_powercap_root, instance_root.zone)
+
static const struct scmi_powercap_proto_ops *powercap_ops;
struct scmi_powercap_zone {
@@ -411,9 +414,37 @@ static int instance_root_release(struct powercap_zone *pz)
return 0;
}
-static int instance_root_get_power_uw(struct powercap_zone *pz, u64 *v)
+static int instance_root_get_power_uw(struct powercap_zone *pz, u64 *power_uw)
{
- *v = 0;
+ struct scmi_powercap_root *pr = to_scmi_powercap_root(pz);
+ struct scmi_powercap_zone *child;
+
+ u64 p, acc = 0;
+ int i, ret;
+
+ if (!pz || !power_uw)
+ return -EINVAL;
+
+ if (!pr)
+ return -ENODEV;
+
+ for (i = 0; i < pr->num_zones; i++) {
+ child = &pr->spzones[i];
+
+ if (!child->registered || child->invalid)
+ continue;
+
+ if (child->info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID)
+ continue;
+
+ ret = scmi_powercap_get_power_uw(&child->zone, &p);
+ if (!ret)
+ acc += p;
+ else
+ dev_dbg(child->dev, "Failed to read child power: %u\n", ret);
+ }
+
+ *power_uw = acc;
return 0;
}
--
2.47.3
^ permalink raw reply related
* [PATCH v7 11/13] powercap: arm_scmi: Create synthetic parent node for multi-instance
From: Philip Radford @ 2026-06-17 9:59 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Philip Radford
In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com>
An SCMI powercap instance may expose a hierarchy of domains, or even a
forest of multiple domain trees rooted at SCMI_POWERCAP_ROOT_ZONE_ID.
Those hierarchies are valid within the namespace of a single SCMI instance.
Currently, the powercap framework has no notion of SCMI instances. If root
domains from multiple SCMI instances are registered directly under the same
Linux powercap control type, the per-instance boundaries are lost and the
resulting Linux hierarchy becomes a merge of otherwise independent SCMI
topologies.
Add a synthetic top-level powercap zone per SCMI instance and register that
instance's SCMI root domains beneath it. This keeps each instance's SCMI
hierarchy grouped together.
Signed-off-by: Philip Radford <philip.radford@arm.com>
---
V6->V7
- Added cleanup for zones during scmi_powercap_probe
V5->V6
- Amended omission of spz initialization
- Tested unloading and loading powercap module
- Re-wrote commit message
---
drivers/powercap/arm_scmi_powercap.c | 93 +++++++++++++++++++++++++---
1 file changed, 86 insertions(+), 7 deletions(-)
diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_scmi_powercap.c
index 2f8a1e0ecccf..d45e4af0cdc7 100644
--- a/drivers/powercap/arm_scmi_powercap.c
+++ b/drivers/powercap/arm_scmi_powercap.c
@@ -38,6 +38,7 @@ struct scmi_powercap_root {
struct scmi_powercap_zone *spzones;
struct list_head *registered_zones;
struct list_head scmi_zones;
+ struct scmi_powercap_zone instance_root;
};
static LIST_HEAD(scmi_powercap_roots);
@@ -401,18 +402,61 @@ static const struct powercap_zone_constraint_ops constraint_ops = {
.get_name = scmi_powercap_get_name,
};
+/*
+ * Multi-instance constraints to meet driver requrements due to the fact
+ * that full zone semantics aren't available for the synthetic zone.
+ */
+static int instance_root_release(struct powercap_zone *pz)
+{
+ return 0;
+}
+
+static int instance_root_get_power_uw(struct powercap_zone *pz, u64 *v)
+{
+ *v = 0;
+ return 0;
+}
+
+static int instance_root_set_constraint(struct powercap_zone *pz, int cid, u64 v)
+{
+ return -EOPNOTSUPP;
+}
+
+static int instance_root_get_constraint(struct powercap_zone *pz, int cid, u64 *v)
+{
+ return -EOPNOTSUPP;
+}
+
+static const struct powercap_zone_ops instance_root_ops = {
+ .get_max_power_range_uw = scmi_powercap_get_max_power_range_uw,
+ .get_power_uw = instance_root_get_power_uw,
+ .release = instance_root_release,
+};
+
+static const struct powercap_zone_constraint_ops instance_root_const_ops = {
+ .set_power_limit_uw = instance_root_set_constraint,
+ .get_power_limit_uw = instance_root_get_constraint,
+ .set_time_window_us = instance_root_set_constraint,
+ .get_time_window_us = instance_root_get_constraint,
+};
+
static void scmi_powercap_unregister_all_zones(struct scmi_powercap_root *pr)
{
int i;
/* Un-register children zones first starting from the leaves */
- for (i = pr->num_zones - 1; i >= 0; i--) {
+ for (i = pr->num_zones; i >= 0; i--) {
if (!list_empty(&pr->registered_zones[i])) {
struct scmi_powercap_zone *spz;
- list_for_each_entry(spz, &pr->registered_zones[i], node)
+ list_for_each_entry(spz, &pr->registered_zones[i], node) {
+ if (!spz->registered)
+ continue;
+
+ spz->registered = false;
powercap_unregister_zone(scmi_top_pcntrl,
&spz->zone);
+ }
}
}
}
@@ -451,7 +495,10 @@ static int scmi_powercap_register_zone(struct scmi_powercap_root *pr,
parent ? &parent->zone : NULL,
&zone_ops, spz->info->num_cpli, &constraint_ops);
if (!IS_ERR(z)) {
- spz->height = scmi_powercap_get_zone_height(spz);
+ if (parent)
+ spz->height = parent->height + 1;
+ else
+ spz->height = 0;
spz->registered = true;
list_move(&spz->node, &pr->registered_zones[spz->height]);
dev_dbg(spz->dev, "Registered node %s - parent %s - height:%d\n",
@@ -522,6 +569,8 @@ static int scmi_zones_register(struct device *dev,
struct scmi_powercap_zone *parent;
parent = scmi_powercap_get_parent_zone(spz);
+ if (!parent)
+ parent = &pr->instance_root;
if (parent && !parent->registered) {
zones_stack[sp++] = spz;
spz = parent;
@@ -562,8 +611,11 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
int ret, i;
struct scmi_powercap_root *pr;
struct scmi_powercap_zone *spz;
+ struct scmi_powercap_zone *ir;
struct scmi_protocol_handle *ph;
struct device *dev = &sdev->dev;
+ char *instance_name;
+ struct powercap_zone *z;
if (!sdev->handle)
return -ENODEV;
@@ -591,7 +643,7 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
return -ENOMEM;
/* Allocate for worst possible scenario of maximum tree height. */
- pr->registered_zones = devm_kcalloc(dev, pr->num_zones,
+ pr->registered_zones = devm_kcalloc(dev, pr->num_zones + 1,
sizeof(*pr->registered_zones),
GFP_KERNEL);
if (!pr->registered_zones)
@@ -599,6 +651,9 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
INIT_LIST_HEAD(&pr->scmi_zones);
+ for (i = 0; i <= pr->num_zones; i++)
+ INIT_LIST_HEAD(&pr->registered_zones[i]);
+
for (i = 0, spz = pr->spzones; i < pr->num_zones; i++, spz++) {
/*
* Powercap domains are validate by the protocol layer, i.e.
@@ -611,7 +666,6 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
spz->ph = ph;
spz->spzones = pr->spzones;
INIT_LIST_HEAD(&spz->node);
- INIT_LIST_HEAD(&pr->registered_zones[i]);
list_add_tail(&spz->node, &pr->scmi_zones);
/*
@@ -629,19 +683,44 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
}
}
+ ir = &pr->instance_root;
+ ir->dev = dev;
+ INIT_LIST_HEAD(&ir->node);
+ instance_name = devm_kasprintf(dev, GFP_KERNEL, "instance_%s", dev_name(dev));
+ if (!instance_name)
+ return -ENOMEM;
+
+ z = powercap_register_zone(&ir->zone, scmi_top_pcntrl,
+ instance_name, NULL, &instance_root_ops, 0,
+ &instance_root_const_ops);
+
+ if (IS_ERR(z)) {
+ ret = PTR_ERR(z);
+ dev_err(dev, "Failed to register sysnthetic instance root: %d\n", ret);
+ return ret;
+ }
+
+ ir->registered = true;
+ ir->height = 0;
+ list_add_tail(&ir->node, &pr->registered_zones[0]);
+
/*
* Scan array of retrieved SCMI powercap domains and register them
* recursively starting from the root domains.
*/
ret = scmi_zones_register(dev, pr);
- if (ret)
+ if (ret) {
+ scmi_powercap_unregister_all_zones(pr);
return ret;
+ }
INIT_LIST_HEAD(&pr->node);
ret = scmi_powercap_read_root_children_enable_state(pr, &pr->enabled);
- if (ret)
+ if (ret) {
+ scmi_powercap_unregister_all_zones(pr);
return ret;
+ }
mutex_lock(&scmi_powercap_roots_lock);
list_add_tail(&pr->node, &scmi_powercap_roots);
--
2.47.3
^ permalink raw reply related
* [PATCH v7 09/13] powercap: arm_scmi: Enable multiple constraints support
From: Philip Radford @ 2026-06-17 9:59 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi,
Rafael J. Wysocki, Philip Radford
In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com>
From: Cristian Marussi <cristian.marussi@arm.com>
Initialize the domains with all the discovered available constraints,
making available multiple per-domain constraints when the platform has
advertised support for multiple concurrent power limits.
CC: "Rafael J. Wysocki" <rafael@kernel.org>
CC: linux-pm@vger.kernel.org
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
[Philip: Amended Copyright]
Signed-off-by: Philip Radford <philip.radford@arm.com>
---
drivers/powercap/arm_scmi_powercap.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_scmi_powercap.c
index 90d1fa70b1d4..2f8a1e0ecccf 100644
--- a/drivers/powercap/arm_scmi_powercap.c
+++ b/drivers/powercap/arm_scmi_powercap.c
@@ -2,7 +2,7 @@
/*
* SCMI Powercap support.
*
- * Copyright (C) 2022 ARM Ltd.
+ * Copyright (C) 2022-2026 ARM Ltd.
*/
#include <linux/device.h>
@@ -449,7 +449,7 @@ static int scmi_powercap_register_zone(struct scmi_powercap_root *pr,
z = powercap_register_zone(&spz->zone, scmi_top_pcntrl, spz->info->name,
parent ? &parent->zone : NULL,
- &zone_ops, 1, &constraint_ops);
+ &zone_ops, spz->info->num_cpli, &constraint_ops);
if (!IS_ERR(z)) {
spz->height = scmi_powercap_get_zone_height(spz);
spz->registered = true;
--
2.47.3
^ permalink raw reply related
* [PATCH v7 07/13] firmware: arm_scmi: Extend powercap report to include MAI
From: Philip Radford @ 2026-06-17 9:59 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Philip Radford
In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com>
Extend scmi_powercap_meas_changed_report to include MAI change
notifications.
Signed-off-by: Philip Radford <philip.radford@arm.com>
---
V6->V7
- Corrected naming of define sizes
V5->V6
- Re-worded existing comment for POWERCAP_MEASUREMENTS_NOTIFY
- Added define for V2/V3 sizes
- Used new definitions
---
drivers/firmware/arm_scmi/powercap.c | 28 +++++++++++++++-------------
include/linux/scmi_protocol.h | 1 +
2 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index 7ed6b6467813..f6f9161a0138 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -11,6 +11,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/scmi_protocol.h>
+#include <linux/stddef.h>
#include <trace/events/scmi.h>
@@ -21,6 +22,8 @@
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
#define CPL0 0
+#define SZ_V3 (sizeof(struct scmi_powercap_meas_changed_notify_payld))
+#define SZ_V2 (SZ_V3 - sizeof(__le32))
enum scmi_powercap_protocol_cmd {
POWERCAP_DOMAIN_ATTRIBUTES = 0x3,
@@ -164,6 +167,7 @@ struct scmi_powercap_meas_changed_notify_payld {
__le32 agent_id;
__le32 domain_id;
__le32 power;
+ __le32 mai;
};
struct scmi_msg_powercap_cpc {
@@ -1205,24 +1209,18 @@ static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
struct scmi_msg_powercap_notify_thresh *notify;
/*
- * Note that we have to pick the most recently configured
- * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY
- * enable request and we fail, complaining, if no thresholds
- * were ever set, since this is an indication the API has been
- * used wrongly.
+ * Build the POWERCAP_MEASUREMENTS_NOTIFY enable request using the
+ * most recently configured thresholds.
+ *
+ * The absence of thresholds is not considered an error:
+ * notifications can still be generated to report MAI changes, even
+ * when low and high are set to zero.
*/
ret = scmi_powercap_measurements_threshold_get(ph, domain,
&low, &high);
if (ret)
return ret;
- if (enable && !low && !high) {
- dev_err(ph->dev,
- "Invalid Measurements Notify thresholds: %u/%u\n",
- low, high);
- return -EINVAL;
- }
-
ret = ph->xops->xfer_get_init(ph, message_id,
sizeof(*notify), 0, &t);
if (ret)
@@ -1338,13 +1336,17 @@ scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph,
const struct scmi_powercap_meas_changed_notify_payld *p = payld;
struct scmi_powercap_meas_changed_report *r = report;
- if (sizeof(*p) != payld_sz)
+ if (payld_sz != SZ_V2 && payld_sz != SZ_V3)
break;
r->timestamp = timestamp;
r->agent_id = le32_to_cpu(p->agent_id);
r->domain_id = le32_to_cpu(p->domain_id);
r->power = le32_to_cpu(p->power);
+ r->mai = 0;
+ if (payld_sz == SZ_V3 && PROTOCOL_REV_MAJOR(ph->version) >= 0x3)
+ r->mai = le32_to_cpu(p->mai);
+
*src_id = r->domain_id;
rep = r;
break;
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 299fa8499b3f..d0f6c0102559 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -1134,5 +1134,6 @@ struct scmi_powercap_meas_changed_report {
unsigned int agent_id;
unsigned int domain_id;
unsigned int power;
+ unsigned int mai;
};
#endif /* _LINUX_SCMI_PROTOCOL_H */
--
2.47.3
^ permalink raw reply related
* [PATCH v7 08/13] include: trace: Add new parameter to trace_scmi_fc_call
From: Philip Radford @ 2026-06-17 9:59 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi,
Philip Radford
In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com>
From: Cristian Marussi <cristian.marussi@arm.com>
Since SCMIv4.0 some of the supported Fastchannels can be configured using
an additional parameter like CPL_ID or Capability_ID.
Add equivalent support in the SCMI fastchannel traces to printout also such
parameter and fix all the existent call sites.
When such parameter is not used, it will simply show up as zero.
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Signed-off-by: Philip Radford <philip.radford@arm.com>
---
drivers/firmware/arm_scmi/perf.c | 8 ++++----
drivers/firmware/arm_scmi/powercap.c | 12 +++++++-----
include/trace/events/scmi.h | 12 +++++++-----
3 files changed, 18 insertions(+), 14 deletions(-)
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index 7f283f457e02..88d614e3184b 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -552,7 +552,7 @@ static int __scmi_perf_limits_set(const struct scmi_protocol_handle *ph,
struct scmi_fc_info *fci = &dom->fc_info[PERF_FC_LIMIT];
trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LIMITS_SET,
- dom->id, min_perf, max_perf);
+ dom->id, 0, min_perf, max_perf);
iowrite32(max_perf, fci->set_addr);
iowrite32(min_perf, fci->set_addr + 4);
ph->hops->fastchannel_db_ring(fci->set_db);
@@ -636,7 +636,7 @@ static int __scmi_perf_limits_get(const struct scmi_protocol_handle *ph,
*max_perf = ioread32(fci->get_addr);
*min_perf = ioread32(fci->get_addr + 4);
trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LIMITS_GET,
- dom->id, *min_perf, *max_perf);
+ dom->id, 0, *min_perf, *max_perf);
return 0;
}
@@ -706,7 +706,7 @@ static int __scmi_perf_level_set(const struct scmi_protocol_handle *ph,
struct scmi_fc_info *fci = &dom->fc_info[PERF_FC_LEVEL];
trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LEVEL_SET,
- dom->id, level, 0);
+ dom->id, 0, level, 0);
iowrite32(level, fci->set_addr);
ph->hops->fastchannel_db_ring(fci->set_db);
return 0;
@@ -769,7 +769,7 @@ static int __scmi_perf_level_get(const struct scmi_protocol_handle *ph,
if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].get_addr) {
*level = ioread32(dom->fc_info[PERF_FC_LEVEL].get_addr);
trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LEVEL_GET,
- dom->id, *level, 0);
+ dom->id, 0, *level, 0);
return 0;
}
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index f6f9161a0138..1800ee295b07 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -603,7 +603,7 @@ static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr) {
*power_cap = ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr);
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET,
- dom->id, *power_cap, 0);
+ dom->id, cpl_id, *power_cap, 0);
return 0;
}
@@ -736,7 +736,7 @@ static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
iowrite32(power_cap, fci->set_addr);
ph->hops->fastchannel_db_ring(fci->set_db);
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET,
- domain_id, power_cap, 0);
+ domain_id, cpl_id, power_cap, 0);
ret = 0;
} else {
ret = pi->xfer_cap_set(ph, pc, cpl_id, power_cap, ignore_dresp);
@@ -841,7 +841,8 @@ static int scmi_powercap_avg_interval_get(const struct scmi_protocol_handle *ph,
POWERCAP_PAI_GET : POWERCAP_CAI_GET;
*val = ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_XAI].get_addr);
- trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, trace_cmd, domain_id, *val, 0);
+ trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, trace_cmd, domain_id,
+ cpl_id, *val, 0);
return 0;
}
@@ -914,7 +915,8 @@ static int scmi_powercap_avg_interval_set(const struct scmi_protocol_handle *ph,
POWERCAP_PAI_SET : POWERCAP_CAI_SET;
struct scmi_fc_info *fci = &pc->cpli[cpl_id].fc_info[POWERCAP_FC_XAI];
- trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, trace_cmd, domain_id, ivl, 0);
+ trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, trace_cmd, domain_id,
+ cpl_id, ivl, 0);
iowrite32(ivl, fci->set_addr);
ph->hops->fastchannel_db_ring(fci->set_db);
return 0;
@@ -967,7 +969,7 @@ static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
/* See SCMIv4.0 3.10.2 - Payload is 32bit ONLY avg_power */
*avg_ivl = 0;
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_MEASUREMENTS_GET,
- pc->id, *avg_power, *avg_ivl);
+ pc->id, 0, *avg_power, *avg_ivl);
return 0;
}
diff --git a/include/trace/events/scmi.h b/include/trace/events/scmi.h
index 703b7bb68e44..b03da7323d04 100644
--- a/include/trace/events/scmi.h
+++ b/include/trace/events/scmi.h
@@ -10,13 +10,14 @@
#define TRACE_SCMI_MAX_TAG_LEN 6
TRACE_EVENT(scmi_fc_call,
- TP_PROTO(u8 protocol_id, u8 msg_id, u32 res_id, u32 val1, u32 val2),
- TP_ARGS(protocol_id, msg_id, res_id, val1, val2),
+ TP_PROTO(u8 protocol_id, u8 msg_id, u32 res_id, u32 sub_id, u32 val1, u32 val2),
+ TP_ARGS(protocol_id, msg_id, res_id, sub_id, val1, val2),
TP_STRUCT__entry(
__field(u8, protocol_id)
__field(u8, msg_id)
__field(u32, res_id)
+ __field(u32, sub_id)
__field(u32, val1)
__field(u32, val2)
),
@@ -25,13 +26,14 @@ TRACE_EVENT(scmi_fc_call,
__entry->protocol_id = protocol_id;
__entry->msg_id = msg_id;
__entry->res_id = res_id;
+ __entry->sub_id = sub_id;
__entry->val1 = val1;
__entry->val2 = val2;
),
- TP_printk("pt=%02X msg_id=%02X res_id:%u vals=%u:%u",
- __entry->protocol_id, __entry->msg_id,
- __entry->res_id, __entry->val1, __entry->val2)
+ TP_printk("pt=%02X msg_id=%02X res_id:%u sub_id:%u vals=%u:%u",
+ __entry->protocol_id, __entry->msg_id,
+ __entry->res_id, __entry->sub_id, __entry->val1, __entry->val2)
);
TRACE_EVENT(scmi_xfer_begin,
--
2.47.3
^ permalink raw reply related
* [PATCH v7 04/13] firmware: arm_scmi: Add SCMIv4.0 Powercap basic support
From: Philip Radford @ 2026-06-17 9:59 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi,
Philip Radford
In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com>
From: Cristian Marussi <cristian.marussi@arm.com>
Add SCMIv4.0 Powercap support for enumerating multiple CPLs of a domain
when available.
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
[Philip: Fixed sparse issues where int was expected]
Signed-off-by: Philip Radford <philip.radford@arm.com>
---
V6->V7
- prevent possible cpli[0] out-of-bounds access
---
drivers/firmware/arm_scmi/powercap.c | 474 +++++++++++++++++++++------
include/linux/scmi_protocol.h | 1 +
2 files changed, 379 insertions(+), 96 deletions(-)
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index 47aa6dde4a52..51771c833183 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -33,6 +33,7 @@ enum scmi_powercap_protocol_cmd {
POWERCAP_CAP_NOTIFY = 0xa,
POWERCAP_MEASUREMENTS_NOTIFY = 0xb,
POWERCAP_DESCRIBE_FASTCHANNEL = 0xc,
+ POWERCAP_CPC_ATTRIBUTES = 0xd,
};
enum {
@@ -69,19 +70,58 @@ struct scmi_msg_resp_powercap_domain_attributes {
__le32 parent_id;
};
+struct scmi_msg_resp_powercap_domain_attributes_v3 {
+ __le32 attributes;
+#define SUPPORTS_POWERCAP_MAI_CONFIGURATION(x) ((x) & BIT(25))
+#define SUPPORTS_POWERCAP_FASTCHANNELS(x) ((x) & BIT(22))
+#define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY_V3(x) ((x) & BIT(21))
+#define SUPPORTS_POWERCAP_CAI_CONFIGURATION(x) ((x) & BIT(20))
+ u8 name[SCMI_SHORT_NAME_MAX_SIZE];
+ __le32 min_mai;
+ __le32 max_mai;
+ __le32 mai_step;
+ __le32 min_power_cap;
+ __le32 max_power_cap;
+ __le32 power_cap_step;
+ __le32 sustainable_power;
+ __le32 accuracy;
+ __le32 parent_id;
+ __le32 min_cai;
+ __le32 max_cai;
+ __le32 cai_step;
+};
+
+struct scmi_msg_powercap_get_v3 {
+ __le32 domain_id;
+ __le32 cpli;
+};
+
struct scmi_msg_powercap_set_cap_or_pai {
- __le32 domain;
+ __le32 domain_id;
__le32 flags;
#define CAP_SET_ASYNC BIT(1)
#define CAP_SET_IGNORE_DRESP BIT(0)
__le32 value;
};
+struct scmi_msg_powercap_set_cap_v3 {
+ __le32 domain_id;
+ __le32 cpli;
+ __le32 flags;
+ __le32 power_cap;
+};
+
struct scmi_msg_resp_powercap_cap_set_complete {
- __le32 domain;
+ __le32 domain_id;
__le32 power_cap;
};
+struct scmi_msg_resp_powercap_cap_set_complete_v3 {
+ __le32 domain_id;
+ __le32 power_cap;
+ __le32 cpli;
+};
+
struct scmi_msg_resp_powercap_meas_get {
__le32 power;
__le32 pai;
@@ -112,6 +152,33 @@ struct scmi_powercap_meas_changed_notify_payld {
__le32 power;
};
+struct scmi_msg_powercap_cpc {
+ __le32 domain_id;
+ __le32 desc_index;
+};
+
+struct scmi_msg_resp_powercap_cpc {
+ __le32 num_cpl;
+#define NUM_RETURNED(n) (le32_get_bits((n), GENMASK(15, 0)))
+#define NUM_REMAINING(n) (le32_get_bits((n), GENMASK(31, 16)))
+ struct {
+ __le32 cpli;
+ __le32 flags;
+ __le32 min_power_cap;
+ __le32 max_power_cap;
+ __le32 power_cap_step;
+ __le32 min_cai;
+ __le32 max_cai;
+ __le32 cai_step;
+ u8 name[SCMI_SHORT_NAME_MAX_SIZE];
+ } desc[];
+};
+
+struct scmi_cpls_priv {
+ u32 domain_id;
+ struct scmi_powercap_cpl_info *cpli;
+};
+
struct scmi_powercap_state {
bool enabled;
u32 last_pcap;
@@ -129,6 +196,11 @@ struct powercap_info {
bool notify_measurements_cmd;
struct scmi_powercap_state *states;
struct scmi_powercap_info *powercaps;
+ int (*xfer_cap_get)(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 *power_cap);
+ int (*xfer_cap_set)(const struct scmi_protocol_handle *ph,
+ const struct scmi_powercap_info *pc,
+ u32 cpl_id, u32 power_cap, bool ignore_dresp);
};
static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {
@@ -192,111 +264,244 @@ scmi_powercap_validate(unsigned int min_val, unsigned int max_val,
return 0;
}
+static void iter_powercap_cpls_prepare_message(void *message,
+ unsigned int desc_index,
+ const void *priv)
+{
+ struct scmi_msg_powercap_cpc *msg = message;
+ const struct scmi_cpls_priv *p = priv;
+
+ msg->domain_id = cpu_to_le32(p->domain_id);
+ msg->desc_index = cpu_to_le32(desc_index);
+}
+
+static int iter_powercap_cpls_update_state(struct scmi_iterator_state *st,
+ const void *response, void *priv)
+{
+ const struct scmi_msg_resp_powercap_cpc *r = response;
+
+ st->num_returned = NUM_RETURNED(r->num_cpl);
+ st->num_remaining = NUM_REMAINING(r->num_cpl);
+
+ return 0;
+}
+
static int
-scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
- struct powercap_info *pinfo,
- struct scmi_powercap_info *dom_info)
+iter_powercap_cpls_process_response(const struct scmi_protocol_handle *ph,
+ const void *response,
+ struct scmi_iterator_state *st, void *priv)
{
+ const struct scmi_msg_resp_powercap_cpc *r = response;
+ struct scmi_cpls_priv *p = priv;
+ struct scmi_powercap_cpl_info *cpl;
+
+ cpl = &p->cpli[st->desc_index + st->loop_idx];
+
+ cpl->id = le32_to_cpu(r->desc[st->loop_idx].cpli);
+ cpl->cap_config = le32_to_cpu(r->desc[st->loop_idx].flags) & BIT(0);
+
+ cpl->min_power_cap = le32_to_cpu(r->desc[st->loop_idx].min_power_cap);
+ cpl->max_power_cap = le32_to_cpu(r->desc[st->loop_idx].max_power_cap);
+ cpl->power_cap_step = le32_to_cpu(r->desc[st->loop_idx].power_cap_step);
+ if (!cpl->power_cap_step && cpl->min_power_cap != cpl->max_power_cap)
+ return -EINVAL;
+
+ cpl->min_avg_ivl = le32_to_cpu(r->desc[st->loop_idx].min_cai);
+ cpl->max_avg_ivl = le32_to_cpu(r->desc[st->loop_idx].max_cai);
+ cpl->avg_ivl_step = le32_to_cpu(r->desc[st->loop_idx].cai_step);
+ if (!cpl->avg_ivl_step && cpl->min_avg_ivl != cpl->max_avg_ivl)
+ return -EINVAL;
+
+ cpl->avg_ivl_config = cpl->min_avg_ivl != cpl->max_avg_ivl;
+
+ strscpy(cpl->name, r->desc[st->loop_idx].name, SCMI_SHORT_NAME_MAX_SIZE);
+
+ return 0;
+}
+
+static int scmi_powercap_cpls_enumerate(const struct scmi_protocol_handle *ph,
+ struct scmi_powercap_info *dom_info)
+{
+ void *iter;
+ struct scmi_iterator_ops ops = {
+ .prepare_message = iter_powercap_cpls_prepare_message,
+ .update_state = iter_powercap_cpls_update_state,
+ .process_response = iter_powercap_cpls_process_response,
+ };
+ struct scmi_cpls_priv cpriv = {
+ .domain_id = dom_info->id,
+ .cpli = dom_info->cpli,
+ };
+
+ iter = ph->hops->iter_response_init(ph, &ops, dom_info->num_cpli,
+ POWERCAP_CPC_ATTRIBUTES,
+ sizeof(struct scmi_msg_powercap_cpc),
+ &cpriv);
+ if (IS_ERR(iter))
+ return PTR_ERR(iter);
+
+ return ph->hops->iter_response_run(iter);
+}
+
+static int
+scmi_powercap_domain_attrs_process(const struct scmi_protocol_handle *ph,
+ struct powercap_info *pinfo,
+ struct scmi_powercap_info *dom_info, void *r)
+{
+ struct scmi_msg_resp_powercap_domain_attributes *resp = r;
+ u32 flags = le32_to_cpu(resp->attributes);
+ bool cap_config;
int ret;
- u32 flags;
- struct scmi_xfer *t;
- struct scmi_msg_resp_powercap_domain_attributes *resp;
- ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,
- sizeof(dom_info->id), sizeof(*resp), &t);
- if (ret)
- return ret;
+ cap_config = SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
+ if (PROTOCOL_REV_MAJOR(ph->version) < 0x3) {
+ dom_info->num_cpli = 1;
+ } else {
+ dom_info->num_cpli = le32_get_bits(resp->attributes,
+ GENMASK(18, 15));
- put_unaligned_le32(dom_info->id, t->tx.buf);
- resp = t->rx.buf;
+ if (!dom_info->num_cpli)
+ dom_info->num_cpli = 1;
- ret = ph->xops->do_xfer(ph, t);
- if (!ret) {
- flags = le32_to_cpu(resp->attributes);
+ if (cap_config && !dom_info->num_cpli)
+ return -EINVAL;
+ }
+
+ dom_info->cpli = devm_kcalloc(ph->dev, dom_info->num_cpli,
+ sizeof(*dom_info->cpli), GFP_KERNEL);
+ if (!dom_info->cpli)
+ return -ENOMEM;
- if (pinfo->notify_cap_cmd)
+ if (pinfo->notify_cap_cmd) {
+ if (PROTOCOL_REV_MAJOR(ph->version) < 0x3)
dom_info->notify_powercap_cap_change =
SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
- if (pinfo->notify_measurements_cmd)
- dom_info->notify_powercap_measurement_change =
- SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
- dom_info->async_powercap_cap_set =
- SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
-
- dom_info->powercap_monitoring =
- SUPPORTS_POWERCAP_MONITORING(flags);
- dom_info->powercap_scale_mw =
- SUPPORTS_POWER_UNITS_MW(flags);
- dom_info->powercap_scale_uw =
- SUPPORTS_POWER_UNITS_UW(flags);
- dom_info->fastchannels =
- SUPPORTS_POWERCAP_FASTCHANNELS(flags);
-
- strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
-
- dom_info->sustainable_power =
- le32_to_cpu(resp->sustainable_power);
- dom_info->accuracy = le32_to_cpu(resp->accuracy);
-
- dom_info->parent_id = le32_to_cpu(resp->parent_id);
- if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
- (dom_info->parent_id >= pinfo->num_domains ||
- dom_info->parent_id == dom_info->id)) {
- dev_err(ph->dev,
- "Platform reported inconsistent parent ID for domain %d - %s\n",
- dom_info->id, dom_info->name);
- ret = -ENODEV;
- }
+ else
+ dom_info->notify_powercap_cap_change =
+ SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY_V3(flags);
+ }
+
+ if (pinfo->notify_measurements_cmd)
+ dom_info->notify_powercap_measurement_change =
+ SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
+
+ dom_info->extended_names = SUPPORTS_EXTENDED_NAMES(flags);
+
+ dom_info->async_powercap_cap_set =
+ SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
+
+ dom_info->powercap_monitoring =
+ SUPPORTS_POWERCAP_MONITORING(flags);
+ dom_info->powercap_scale_mw =
+ SUPPORTS_POWER_UNITS_MW(flags);
+ dom_info->powercap_scale_uw =
+ SUPPORTS_POWER_UNITS_UW(flags);
+ dom_info->fastchannels =
+ SUPPORTS_POWERCAP_FASTCHANNELS(flags);
+
+ strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
+
+ dom_info->sustainable_power =
+ le32_to_cpu(resp->sustainable_power);
+ dom_info->accuracy = le32_to_cpu(resp->accuracy);
+
+ dom_info->parent_id = le32_to_cpu(resp->parent_id);
+ if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
+ (dom_info->parent_id >= pinfo->num_domains ||
+ dom_info->parent_id == dom_info->id)) {
+ dev_err(ph->dev,
+ "Platform reported inconsistent parent ID for domain %d - %s\n",
+ dom_info->id, dom_info->name);
+ return -ENODEV;
+ }
+ dom_info->cpli[0].id = CPL0;
+ if (PROTOCOL_REV_MAJOR(ph->version) < 0x3)
dom_info->cpli[0].avg_ivl_config =
SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
+ else
+ dom_info->cpli[0].avg_ivl_config =
+ SUPPORTS_POWERCAP_CAI_CONFIGURATION(flags);
+
+ if (PROTOCOL_REV_MAJOR(ph->version) < 0x3) {
dom_info->cpli[0].min_avg_ivl = le32_to_cpu(resp->min_pai);
dom_info->cpli[0].max_avg_ivl = le32_to_cpu(resp->max_pai);
dom_info->cpli[0].avg_ivl_step = le32_to_cpu(resp->pai_step);
- ret = scmi_powercap_validate(dom_info->cpli[0].min_avg_ivl,
- dom_info->cpli[0].max_avg_ivl,
- dom_info->cpli[0].avg_ivl_step,
- dom_info->cpli[0].avg_ivl_config);
- if (ret) {
- dev_err(ph->dev,
- "Platform reported inconsistent PAI config for domain %d - %s\n",
- dom_info->id, dom_info->name);
- goto clean;
- }
+ } else {
+ struct scmi_msg_resp_powercap_domain_attributes_v3 *resp = r;
- dom_info->cpli[0].cap_config =
- SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
- dom_info->cpli[0].min_power_cap = le32_to_cpu(resp->min_power_cap);
- dom_info->cpli[0].max_power_cap = le32_to_cpu(resp->max_power_cap);
- dom_info->cpli[0].power_cap_step = le32_to_cpu(resp->power_cap_step);
- ret = scmi_powercap_validate(dom_info->cpli[0].min_power_cap,
- dom_info->cpli[0].max_power_cap,
- dom_info->cpli[0].power_cap_step,
- dom_info->cpli[0].cap_config);
- if (ret) {
- dev_err(ph->dev,
- "Platform reported inconsistent CAP config for domain %d - %s\n",
- dom_info->id, dom_info->name);
- goto clean;
- }
+ dom_info->cpli[0].min_avg_ivl = le32_to_cpu(resp->min_cai);
+ dom_info->cpli[0].max_avg_ivl = le32_to_cpu(resp->max_cai);
+ dom_info->cpli[0].avg_ivl_step = le32_to_cpu(resp->cai_step);
+ }
+
+ ret = scmi_powercap_validate(dom_info->cpli[0].min_avg_ivl,
+ dom_info->cpli[0].max_avg_ivl,
+ dom_info->cpli[0].avg_ivl_step,
+ dom_info->cpli[0].avg_ivl_config);
+ if (ret) {
+ dev_err(ph->dev,
+ "Platform reported inconsistent PAI config for domain %d - %s\n",
+ dom_info->id, dom_info->name);
+ return ret;
+ }
- /* Just using same short name */
- strscpy(dom_info->cpli[0].name, dom_info->name,
- SCMI_SHORT_NAME_MAX_SIZE);
+ dom_info->cpli[0].cap_config = cap_config;
+ dom_info->cpli[0].min_power_cap = le32_to_cpu(resp->min_power_cap);
+ dom_info->cpli[0].max_power_cap = le32_to_cpu(resp->max_power_cap);
+ dom_info->cpli[0].power_cap_step = le32_to_cpu(resp->power_cap_step);
+ ret = scmi_powercap_validate(dom_info->cpli[0].min_power_cap,
+ dom_info->cpli[0].max_power_cap,
+ dom_info->cpli[0].power_cap_step,
+ dom_info->cpli[0].cap_config);
+ if (ret) {
+ dev_err(ph->dev,
+ "Platform reported inconsistent CAP config for domain %d - %s\n",
+ dom_info->id, dom_info->name);
+ return ret;
}
+ /* Just using same short name */
+ strscpy(dom_info->cpli[0].name, dom_info->name, SCMI_SHORT_NAME_MAX_SIZE);
+
+ return 0;
+}
+
+static int
+scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
+ struct powercap_info *pinfo,
+ struct scmi_powercap_info *dom_info)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_powercap_domain_attributes *resp;
+
+ ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,
+ sizeof(dom_info->id), 0, &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(dom_info->id, t->tx.buf);
+ resp = t->rx.buf;
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ ret = scmi_powercap_domain_attrs_process(ph, pinfo, dom_info, resp);
-clean:
ph->xops->xfer_put(ph, t);
/*
* If supported overwrite short name with the extended one;
* on error just carry on and use already provided short name.
*/
- if (!ret && SUPPORTS_EXTENDED_NAMES(flags))
+ if (!ret && dom_info->extended_names)
ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET,
dom_info->id, NULL, dom_info->name,
SCMI_MAX_STR_SIZE);
+ /* When protocol version > 0x3 there can possibly be more than 1 CPLs */
+ if (!ret && dom_info->num_cpli > 1)
+ ret = scmi_powercap_cpls_enumerate(ph, dom_info);
+
return ret;
}
@@ -306,14 +511,7 @@ scmi_powercap_domain_initialize(const struct scmi_protocol_handle *ph,
{
struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
- dom_info->num_cpli = 1;
- dom_info->cpli = devm_kcalloc(ph->dev, dom_info->num_cpli,
- sizeof(*dom_info->cpli), GFP_KERNEL);
- if (!dom_info->cpli)
- return -ENOMEM;
-
dom_info->id = domain;
- dom_info->cpli[0].id = CPL0;
return scmi_powercap_domain_attributes_get(ph, pinfo, dom_info);
}
@@ -337,7 +535,7 @@ scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)
}
static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 *power_cap)
+ u32 domain_id, u32 cpl_id, u32 *power_cap)
{
int ret;
struct scmi_xfer *t;
@@ -348,6 +546,33 @@ static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
return ret;
put_unaligned_le32(domain_id, t->tx.buf);
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ *power_cap = get_unaligned_le32(t->rx.buf);
+
+ ph->xops->xfer_put(ph, t);
+
+ return ret;
+}
+
+static int scmi_powercap_xfer_cap_get_v3(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id,
+ u32 *power_cap)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_powercap_get_v3 *msg;
+
+ ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(*msg),
+ sizeof(u32), &t);
+ if (ret)
+ return ret;
+
+ msg = t->tx.buf;
+ msg->domain_id = cpu_to_le32(domain_id);
+ msg->cpli = cpu_to_le32(cpl_id);
+
ret = ph->xops->do_xfer(ph, t);
if (!ret)
*power_cap = get_unaligned_le32(t->rx.buf);
@@ -361,6 +586,8 @@ static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
const struct scmi_powercap_info *dom,
u32 cpl_id, u32 *power_cap)
{
+ struct powercap_info *pi = ph->get_priv(ph);
+
if (dom->cpli[cpl_id].fc_info &&
dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr) {
*power_cap = ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr);
@@ -369,7 +596,7 @@ static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
return 0;
}
- return scmi_powercap_xfer_cap_get(ph, dom->id, power_cap);
+ return pi->xfer_cap_get(ph, dom->id, cpl_id, power_cap);
}
static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
@@ -402,7 +629,7 @@ static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
return ret;
msg = t->tx.buf;
- msg->domain = cpu_to_le32(pc->id);
+ msg->domain_id = cpu_to_le32(pc->id);
msg->flags =
cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, pc->async_powercap_cap_set) |
FIELD_PREP(CAP_SET_IGNORE_DRESP, ignore_dresp));
@@ -416,7 +643,7 @@ static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
struct scmi_msg_resp_powercap_cap_set_complete *resp;
resp = t->rx.buf;
- if (le32_to_cpu(resp->domain) == pc->id)
+ if (le32_to_cpu(resp->domain_id) == pc->id)
dev_dbg(ph->dev,
"Powercap ID %d CAP set async to %u\n",
pc->id,
@@ -430,6 +657,51 @@ static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
return ret;
}
+static int scmi_powercap_xfer_cap_set_v3(const struct scmi_protocol_handle *ph,
+ const struct scmi_powercap_info *pc,
+ u32 cpl_id, u32 power_cap,
+ bool ignore_dresp)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_powercap_set_cap_v3 *msg;
+
+ ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
+ sizeof(*msg), 0, &t);
+ if (ret)
+ return ret;
+
+ msg = t->tx.buf;
+ msg->domain_id = cpu_to_le32(pc->id);
+ msg->cpli = cpu_to_le32(cpl_id);
+ msg->flags =
+ cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, pc->async_powercap_cap_set) |
+ FIELD_PREP(CAP_SET_IGNORE_DRESP, ignore_dresp));
+ msg->power_cap = cpu_to_le32(power_cap);
+
+ if (!pc->async_powercap_cap_set || ignore_dresp) {
+ ret = ph->xops->do_xfer(ph, t);
+ } else {
+ ret = ph->xops->do_xfer_with_response(ph, t);
+ if (!ret) {
+ struct scmi_msg_resp_powercap_cap_set_complete_v3 *resp;
+
+ resp = t->rx.buf;
+ if (le32_to_cpu(resp->domain_id) == pc->id &&
+ le32_to_cpu(resp->cpli) == pc->cpli[cpl_id].id)
+ dev_dbg(ph->dev,
+ "Powercap ID:%d/CPLI:%d CAP set async to %u\n",
+ pc->id, cpl_id,
+ get_unaligned_le32(&resp->power_cap));
+ else
+ ret = -EPROTO;
+ }
+ }
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
+}
+
static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
struct powercap_info *pi, u32 domain_id,
u32 cpl_id, u32 power_cap, bool ignore_dresp)
@@ -456,12 +728,12 @@ static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
domain_id, power_cap, 0);
ret = 0;
} else {
- ret = scmi_powercap_xfer_cap_set(ph, pc, cpl_id, power_cap,
- ignore_dresp);
+ ret = pi->xfer_cap_set(ph, pc, cpl_id, power_cap, ignore_dresp);
}
- /* Save the last explicitly set non-zero powercap value */
- if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 && !ret && power_cap)
+ /* Save the last explicitly set non-zero powercap value for CPL0 */
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 && !ret &&
+ cpl_id == CPL0 && power_cap)
pi->states[domain_id].last_pcap = power_cap;
return ret;
@@ -480,8 +752,8 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
if (!power_cap)
return -EINVAL;
- /* Just log the last set request if acting on a disabled domain */
- if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 &&
+ /* Just log the last set request on CPL0 on a disabled domain */
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 && cpl_id == CPL0 &&
!pi->states[domain_id].enabled) {
pi->states[domain_id].last_pcap = power_cap;
return 0;
@@ -554,7 +826,7 @@ static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
return ret;
msg = t->tx.buf;
- msg->domain = cpu_to_le32(domain_id);
+ msg->domain_id = cpu_to_le32(domain_id);
msg->flags = cpu_to_le32(0);
msg->value = cpu_to_le32(pai);
@@ -1013,6 +1285,16 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
if (!pinfo)
return -ENOMEM;
+ ph->set_priv(ph, pinfo);
+
+ if (PROTOCOL_REV_MAJOR(ph->version) < 0x3) {
+ pinfo->xfer_cap_get = scmi_powercap_xfer_cap_get;
+ pinfo->xfer_cap_set = scmi_powercap_xfer_cap_set;
+ } else {
+ pinfo->xfer_cap_get = scmi_powercap_xfer_cap_get_v3;
+ pinfo->xfer_cap_set = scmi_powercap_xfer_cap_set_v3;
+ }
+
ret = scmi_powercap_attributes_get(ph, pinfo);
if (ret)
return ret;
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 9918fb30100c..547ab4763a63 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -673,6 +673,7 @@ struct scmi_powercap_info {
bool powercap_monitoring;
bool powercap_scale_mw;
bool powercap_scale_uw;
+ bool extended_names;
bool fastchannels;
char name[SCMI_MAX_STR_SIZE];
unsigned int sustainable_power;
--
2.47.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox