summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-02-27 01:13:41 +0000
committerMark Brown <broonie@kernel.org>2026-02-27 01:13:41 +0000
commitf168e849b7b85ef102fb0b889c0fe90a328042af (patch)
treee115498446996f0e1448279b889c066ec147e542
parent5c74a008ffc62fc57a041602b4517519c8bf9436 (diff)
parent501efdcb3b3ab099fc0ce2f6e668b1c4095dd476 (diff)
downloadlinux-next-f168e849b7b85ef102fb0b889c0fe90a328042af.tar.gz
linux-next-f168e849b7b85ef102fb0b889c0fe90a328042af.zip
SDCA Improvements
Merge series from Charles Keepax <ckeepax@opensource.cirrus.com>: Another fairly mixed bag of small SDCA fixes/improvements. Fix one DisCo property that was treated as mandatory but is actually not present in the first version of the specification. Fix the counting of routes for SU/GE DAPM widgets, this currently makes assumptions that are not guaranteed to be true which can result in too many/few DAPM routes. Then finally a couple improvements to the volume controls, simplify the mapping between ALSA and SDCA volumes and pull the volume stuff back into the SDCA code. It just wasn't sitting right with me that it was being handled in the ASoC core given it is unlikely to ever see any reuse outside of SDCA.
-rw-r--r--include/sound/soc.h1
-rw-r--r--sound/soc/sdca/sdca_asoc.c114
-rw-r--r--sound/soc/sdca/sdca_fdl.c5
-rw-r--r--sound/soc/sdca/sdca_functions.c6
-rw-r--r--sound/soc/soc-ops.c64
5 files changed, 118 insertions, 72 deletions
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 1e0b7cd8d956..7bf7ce085516 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1239,7 +1239,6 @@ struct soc_mixer_control {
unsigned int sign_bit;
unsigned int invert:1;
unsigned int autodisable:1;
- unsigned int sdca_q78:1;
#ifdef CONFIG_SND_SOC_TOPOLOGY
struct snd_soc_dobj dobj;
#endif
diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c
index a0191e5a5a7d..a342a4e56717 100644
--- a/sound/soc/sdca/sdca_asoc.c
+++ b/sound/soc/sdca/sdca_asoc.c
@@ -51,6 +51,25 @@ static bool readonly_control(struct sdca_control *control)
return control->has_fixed || control->mode == SDCA_ACCESS_MODE_RO;
}
+static int ge_count_routes(struct sdca_entity *entity)
+{
+ int count = 0;
+ int i, j;
+
+ for (i = 0; i < entity->ge.num_modes; i++) {
+ struct sdca_ge_mode *mode = &entity->ge.modes[i];
+
+ for (j = 0; j < mode->num_controls; j++) {
+ struct sdca_ge_control *affected = &mode->controls[j];
+
+ if (affected->sel != SDCA_CTL_SU_SELECTOR || affected->val)
+ count++;
+ }
+ }
+
+ return count;
+}
+
/**
* sdca_asoc_count_component - count the various component parts
* @dev: Pointer to the device against which allocations will be done.
@@ -74,6 +93,7 @@ int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *fun
int *num_widgets, int *num_routes, int *num_controls,
int *num_dais)
{
+ struct sdca_control *control;
int i, j;
*num_widgets = function->num_entities - 1;
@@ -83,6 +103,7 @@ int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *fun
for (i = 0; i < function->num_entities - 1; i++) {
struct sdca_entity *entity = &function->entities[i];
+ bool skip_primary_routes = false;
/* Add supply/DAI widget connections */
switch (entity->type) {
@@ -96,6 +117,17 @@ int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *fun
case SDCA_ENTITY_TYPE_PDE:
*num_routes += entity->pde.num_managed;
break;
+ case SDCA_ENTITY_TYPE_GE:
+ *num_routes += ge_count_routes(entity);
+ skip_primary_routes = true;
+ break;
+ case SDCA_ENTITY_TYPE_SU:
+ control = sdca_selector_find_control(dev, entity, SDCA_CTL_SU_SELECTOR);
+ if (!control)
+ return -EINVAL;
+
+ skip_primary_routes = (control->layers == SDCA_ACCESS_LAYER_DEVICE);
+ break;
default:
break;
}
@@ -104,7 +136,8 @@ int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *fun
(*num_routes)++;
/* Add primary entity connections from DisCo */
- *num_routes += entity->num_sources;
+ if (!skip_primary_routes)
+ *num_routes += entity->num_sources;
for (j = 0; j < entity->num_controls; j++) {
if (exported_control(entity, &entity->controls[j]))
@@ -442,7 +475,6 @@ static int entity_parse_su_device(struct device *dev,
struct snd_soc_dapm_route **route)
{
struct sdca_control_range *range;
- int num_routes = 0;
int i, j;
if (!entity->group) {
@@ -478,11 +510,6 @@ static int entity_parse_su_device(struct device *dev,
return -EINVAL;
}
- if (++num_routes > entity->num_sources) {
- dev_err(dev, "%s: too many input routes\n", entity->label);
- return -EINVAL;
- }
-
term = sdca_range_search(range, SDCA_SELECTED_MODE_INDEX,
mode->val, SDCA_SELECTED_MODE_TERM_TYPE);
if (!term) {
@@ -778,6 +805,70 @@ int sdca_asoc_populate_dapm(struct device *dev, struct sdca_function_data *funct
}
EXPORT_SYMBOL_NS(sdca_asoc_populate_dapm, "SND_SOC_SDCA");
+static int q78_write(struct snd_soc_component *component,
+ struct soc_mixer_control *mc,
+ unsigned int reg, const int val)
+{
+ unsigned int mask = GENMASK(mc->sign_bit, 0);
+ unsigned int reg_val;
+
+ if (val < 0 || val > mc->max - mc->min)
+ return -EINVAL;
+
+ reg_val = (val + mc->min) * mc->shift;
+
+ return snd_soc_component_update_bits(component, reg, mask, reg_val);
+}
+
+static int q78_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int ret;
+
+ ret = q78_write(component, mc, mc->reg, ucontrol->value.integer.value[0]);
+ if (ret < 0)
+ return ret;
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ int err; /* Don't drop change flag */
+
+ err = q78_write(component, mc, mc->rreg, ucontrol->value.integer.value[1]);
+ if (err)
+ return err;
+ }
+
+ return ret;
+}
+
+static int q78_read(struct snd_soc_component *component,
+ struct soc_mixer_control *mc, unsigned int reg)
+{
+ unsigned int reg_val;
+ int val;
+
+ reg_val = snd_soc_component_read(component, reg);
+
+ val = (sign_extend32(reg_val, mc->sign_bit) / mc->shift) - mc->min;
+
+ return val & GENMASK(mc->sign_bit, 0);
+}
+
+static int q78_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = q78_read(component, mc, mc->reg);
+
+ if (snd_soc_volsw_is_stereo(mc))
+ ucontrol->value.integer.value[1] = q78_read(component, mc, mc->rreg);
+
+ return 0;
+}
+
static int control_limit_kctl(struct device *dev,
struct sdca_entity *entity,
struct sdca_control *control,
@@ -814,16 +905,15 @@ static int control_limit_kctl(struct device *dev,
tlv[2] = (min * 100) >> 8;
tlv[3] = (max * 100) >> 8;
- step = (step * 100) >> 8;
-
- mc->min = ((int)tlv[2] / step);
- mc->max = ((int)tlv[3] / step);
+ mc->min = min / step;
+ mc->max = max / step;
mc->shift = step;
mc->sign_bit = 15;
- mc->sdca_q78 = 1;
kctl->tlv.p = tlv;
kctl->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ kctl->get = q78_get_volsw;
+ kctl->put = q78_put_volsw;
return 0;
}
diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c
index 07892bc3a44e..994821a6df61 100644
--- a/sound/soc/sdca/sdca_fdl.c
+++ b/sound/soc/sdca/sdca_fdl.c
@@ -46,11 +46,6 @@ int sdca_reset_function(struct device *dev, struct sdca_function_data *function,
if (ret) // Allowed for function reset to not be implemented
return 0;
- if (!function->reset_max_delay) {
- dev_err(dev, "No reset delay specified in DisCo\n");
- return -EINVAL;
- }
-
/*
* Poll up to 16 times but no more than once per ms, these are just
* arbitrarily selected values, so may be fine tuned in future.
diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c
index 95b67bb904c3..a0aed95c7634 100644
--- a/sound/soc/sdca/sdca_functions.c
+++ b/sound/soc/sdca/sdca_functions.c
@@ -2167,8 +2167,12 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw,
ret = fwnode_property_read_u32(function_desc->node,
"mipi-sdca-function-reset-max-delay", &tmp);
- if (!ret)
+ if (ret || tmp == 0) {
+ dev_dbg(dev, "reset delay missing, defaulting to 100mS\n");
+ function->reset_max_delay = 100000;
+ } else {
function->reset_max_delay = tmp;
+ }
dev_dbg(dev, "%pfwP: name %s busy delay %dus reset delay %dus\n",
function->desc->node, function->desc->name,
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index f966d4e13c7f..0d633f38cfdc 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -110,37 +110,6 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
-static int sdca_soc_q78_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_val,
- unsigned int mask, unsigned int shift, int max,
- bool sx)
-{
- int val = reg_val;
-
- if (WARN_ON(!mc->shift))
- return -EINVAL;
-
- val = sign_extend32(val, mc->sign_bit);
- val = (((val * 100) >> 8) / (int)mc->shift);
- val -= mc->min;
-
- return val & mask;
-}
-
-static unsigned int sdca_soc_q78_ctl_to_reg(struct soc_mixer_control *mc, int val,
- unsigned int mask, unsigned int shift, int max)
-{
- unsigned int ret_val;
- int reg_val;
-
- if (WARN_ON(!mc->shift))
- return -EINVAL;
-
- reg_val = val + mc->min;
- ret_val = (int)((reg_val * mc->shift) << 8) / 100;
-
- return ret_val & mask;
-}
-
static int soc_mixer_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_val,
unsigned int mask, unsigned int shift, int max,
bool sx)
@@ -234,27 +203,19 @@ static int soc_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol,
struct soc_mixer_control *mc, int mask, int max)
{
- unsigned int (*ctl_to_reg)(struct soc_mixer_control *, int, unsigned int, unsigned int, int);
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int val1, val_mask;
unsigned int val2 = 0;
bool double_r = false;
int ret;
- if (mc->sdca_q78) {
- ctl_to_reg = sdca_soc_q78_ctl_to_reg;
- val_mask = mask;
- } else {
- ctl_to_reg = soc_mixer_ctl_to_reg;
- val_mask = mask << mc->shift;
- }
-
ret = soc_mixer_valid_ctl(mc, ucontrol->value.integer.value[0], max);
if (ret)
return ret;
- val1 = ctl_to_reg(mc, ucontrol->value.integer.value[0],
+ val1 = soc_mixer_ctl_to_reg(mc, ucontrol->value.integer.value[0],
mask, mc->shift, max);
+ val_mask = mask << mc->shift;
if (snd_soc_volsw_is_stereo(mc)) {
ret = soc_mixer_valid_ctl(mc, ucontrol->value.integer.value[1], max);
@@ -262,10 +223,14 @@ static int soc_put_volsw(struct snd_kcontrol *kcontrol,
return ret;
if (mc->reg == mc->rreg) {
- val1 |= ctl_to_reg(mc, ucontrol->value.integer.value[1], mask, mc->rshift, max);
+ val1 |= soc_mixer_ctl_to_reg(mc,
+ ucontrol->value.integer.value[1],
+ mask, mc->rshift, max);
val_mask |= mask << mc->rshift;
} else {
- val2 = ctl_to_reg(mc, ucontrol->value.integer.value[1], mask, mc->shift, max);
+ val2 = soc_mixer_ctl_to_reg(mc,
+ ucontrol->value.integer.value[1],
+ mask, mc->shift, max);
double_r = true;
}
}
@@ -289,28 +254,21 @@ static int soc_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol,
struct soc_mixer_control *mc, int mask, int max, bool sx)
{
- int (*reg_to_ctl)(struct soc_mixer_control *, unsigned int, unsigned int,
- unsigned int, int, bool);
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg_val;
int val;
- if (mc->sdca_q78)
- reg_to_ctl = sdca_soc_q78_reg_to_ctl;
- else
- reg_to_ctl = soc_mixer_reg_to_ctl;
-
reg_val = snd_soc_component_read(component, mc->reg);
- val = reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx);
+ val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx);
ucontrol->value.integer.value[0] = val;
if (snd_soc_volsw_is_stereo(mc)) {
if (mc->reg == mc->rreg) {
- val = reg_to_ctl(mc, reg_val, mask, mc->rshift, max, sx);
+ val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->rshift, max, sx);
} else {
reg_val = snd_soc_component_read(component, mc->rreg);
- val = reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx);
+ val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx);
}
ucontrol->value.integer.value[1] = val;