ASoC: SDCA: Add correct masks whilst reporting SDCA jack status

Currently, all SDCA jacks simply report against a mask of 0xFFFF. This
works fine for system with a single SDCA jack control as the status
reflects that single control at all times. However, if two SDCA
jack controls exist in the system, such as a separate representation for
input and output, then the second control can cancel reports from the
other since it will only report its relevant bits and zero in all other
slots. This is exactly what the mask is for.

Build up a mask using all the possible states for an SCDA jack control
at registration time and use that mask when reporting a particular jack.
It is worth noting this still doesn't handle cases such as two headphone
jacks as that would require separate ALSA jacks to report to.

Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Link: https://patch.msgid.link/20260430150931.2025953-2-ckeepax@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Charles Keepax 2026-04-30 16:09:29 +01:00 committed by Mark Brown
parent 7fd2df204f
commit cb3c257e08
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
2 changed files with 47 additions and 31 deletions

View File

@ -18,10 +18,13 @@ struct snd_soc_jack;
* struct jack_state - Jack state structure to keep data between interrupts
* @kctl: Pointer to the ALSA control attached to this jack
* @jack: Pointer to the ASoC jack struct for this jack
* @mask: Possible reported jack status bits for this jack
*/
struct jack_state {
struct snd_kcontrol *kctl;
struct snd_soc_jack *jack;
unsigned int mask;
};
int sdca_jack_alloc_state(struct sdca_interrupt *interrupt);

View File

@ -145,6 +145,32 @@ int sdca_jack_alloc_state(struct sdca_interrupt *interrupt)
}
EXPORT_SYMBOL_NS_GPL(sdca_jack_alloc_state, "SND_SOC_SDCA");
static int type_get_mask(enum sdca_terminal_type type)
{
switch (type) {
case SDCA_TERM_TYPE_LINEIN_STEREO:
case SDCA_TERM_TYPE_LINEIN_FRONT_LR:
case SDCA_TERM_TYPE_LINEIN_CENTER_LFE:
case SDCA_TERM_TYPE_LINEIN_SURROUND_LR:
case SDCA_TERM_TYPE_LINEIN_REAR_LR:
return SND_JACK_LINEIN;
case SDCA_TERM_TYPE_LINEOUT_STEREO:
case SDCA_TERM_TYPE_LINEOUT_FRONT_LR:
case SDCA_TERM_TYPE_LINEOUT_CENTER_LFE:
case SDCA_TERM_TYPE_LINEOUT_SURROUND_LR:
case SDCA_TERM_TYPE_LINEOUT_REAR_LR:
return SND_JACK_LINEOUT;
case SDCA_TERM_TYPE_MIC_JACK:
return SND_JACK_MICROPHONE;
case SDCA_TERM_TYPE_HEADPHONE_JACK:
return SND_JACK_HEADPHONE;
case SDCA_TERM_TYPE_HEADSET_JACK:
return SND_JACK_HEADSET;
default:
return 0;
}
}
/**
* sdca_jack_set_jack - attach an ASoC jack to SDCA
* @info: SDCA interrupt information.
@ -154,7 +180,8 @@ EXPORT_SYMBOL_NS_GPL(sdca_jack_alloc_state, "SND_SOC_SDCA");
*/
int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack)
{
int i, ret;
int i, j;
int ret;
guard(mutex)(&info->irq_lock);
@ -162,6 +189,7 @@ int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *ja
struct sdca_interrupt *interrupt = &info->irqs[i];
struct sdca_control *control = interrupt->control;
struct sdca_entity *entity = interrupt->entity;
struct sdca_control_range *range;
struct jack_state *jack_state;
if (!interrupt->irq)
@ -169,6 +197,20 @@ int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *ja
switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
range = sdca_selector_find_range(interrupt->dev, entity,
SDCA_CTL_GE_SELECTED_MODE,
SDCA_SELECTED_MODE_NCOLS, 0);
if (!range)
return -EINVAL;
for (j = 0; j < range->rows; j++) {
enum sdca_terminal_type type;
type = sdca_range(range, SDCA_SELECTED_MODE_TERM_TYPE, j);
jack_state->mask |= type_get_mask(type);
}
jack_state = interrupt->priv;
jack_state->jack = jack;
@ -191,7 +233,6 @@ int sdca_jack_report(struct sdca_interrupt *interrupt)
struct jack_state *jack_state = interrupt->priv;
struct sdca_control_range *range;
enum sdca_terminal_type type;
unsigned int report = 0;
unsigned int reg, val;
int ret;
@ -213,35 +254,7 @@ int sdca_jack_report(struct sdca_interrupt *interrupt)
type = sdca_range_search(range, SDCA_SELECTED_MODE_INDEX,
val, SDCA_SELECTED_MODE_TERM_TYPE);
switch (type) {
case SDCA_TERM_TYPE_LINEIN_STEREO:
case SDCA_TERM_TYPE_LINEIN_FRONT_LR:
case SDCA_TERM_TYPE_LINEIN_CENTER_LFE:
case SDCA_TERM_TYPE_LINEIN_SURROUND_LR:
case SDCA_TERM_TYPE_LINEIN_REAR_LR:
report = SND_JACK_LINEIN;
break;
case SDCA_TERM_TYPE_LINEOUT_STEREO:
case SDCA_TERM_TYPE_LINEOUT_FRONT_LR:
case SDCA_TERM_TYPE_LINEOUT_CENTER_LFE:
case SDCA_TERM_TYPE_LINEOUT_SURROUND_LR:
case SDCA_TERM_TYPE_LINEOUT_REAR_LR:
report = SND_JACK_LINEOUT;
break;
case SDCA_TERM_TYPE_MIC_JACK:
report = SND_JACK_MICROPHONE;
break;
case SDCA_TERM_TYPE_HEADPHONE_JACK:
report = SND_JACK_HEADPHONE;
break;
case SDCA_TERM_TYPE_HEADSET_JACK:
report = SND_JACK_HEADSET;
break;
default:
break;
}
snd_soc_jack_report(jack_state->jack, report, 0xFFFF);
snd_soc_jack_report(jack_state->jack, type_get_mask(type), jack_state->mask);
return 0;
}