diff --git a/include/sound/sdca.h b/include/sound/sdca.h index 67ff3c88705d..2bdf4e333e04 100644 --- a/include/sound/sdca.h +++ b/include/sound/sdca.h @@ -26,6 +26,8 @@ struct sdca_dev; * @name: Human-readable string. * @type: Function topology type. * @adr: ACPI address (used for SDCA register access). + * @duplicate: Internal flag to indicate if other functions of the same type + * exist. */ struct sdca_function_desc { struct fwnode_handle *node; @@ -33,6 +35,8 @@ struct sdca_function_desc { const char *name; u32 type; u8 adr; + + bool duplicate; }; /** diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 0e871c786513..b1489178b0ef 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -1452,7 +1452,6 @@ static inline u32 sdca_range_search(struct sdca_control_range *range, } int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, - struct sdca_function_desc *desc, struct sdca_function_data *function); const char *sdca_find_terminal_name(enum sdca_terminal_type type); diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h index 3ec22046d3eb..181541f0f4d8 100644 --- a/include/sound/sdca_jack.h +++ b/include/sound/sdca_jack.h @@ -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); diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 90008d2d06e2..38009168c5a1 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -1310,10 +1310,10 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, return dev_err_probe(dev, -ENOMEM, "failed to parse sdca functions"); + function_data->desc = &peripheral->sdca_data.function[i]; + /* Parse the function */ - ret = sdca_parse_function(dev, peripheral, - &peripheral->sdca_data.function[i], - function_data); + ret = sdca_parse_function(dev, peripheral, function_data); if (!ret) tas_dev->sa_func_data = function_data; else diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c index 6e9b66f71801..a6a3da8de437 100644 --- a/sound/soc/sdca/sdca_class.c +++ b/sound/soc/sdca/sdca_class.c @@ -183,7 +183,6 @@ static void class_boot_work(struct work_struct *work) static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id) { struct device *dev = &sdw->dev; - struct sdca_device_data *data = &sdw->sdca_data; struct regmap_config *dev_config; struct sdca_class_drv *drv; int ret; @@ -199,12 +198,6 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id if (!dev_config) return -ENOMEM; - drv->functions = devm_kcalloc(dev, data->num_functions, - sizeof(*drv->functions), - GFP_KERNEL); - if (!drv->functions) - return -ENOMEM; - drv->dev = dev; drv->sdw = sdw; mutex_init(&drv->regmap_lock); diff --git a/sound/soc/sdca/sdca_class.h b/sound/soc/sdca/sdca_class.h index 6f24ea2bbd38..8b63e62485e6 100644 --- a/sound/soc/sdca/sdca_class.h +++ b/sound/soc/sdca/sdca_class.h @@ -24,7 +24,6 @@ struct sdca_class_drv { struct regmap *dev_regmap; struct sdw_slave *sdw; - struct sdca_function_data *functions; struct sdca_interrupt_info *irq_info; struct mutex regmap_lock; diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 31fc08d51307..1496a15f7d2a 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -27,6 +27,7 @@ #include #include #include "sdca_class.h" +#include "sdca_function_device.h" struct class_function_drv { struct device *dev; @@ -294,8 +295,7 @@ static int class_function_probe(struct auxiliary_device *auxdev, { struct device *dev = &auxdev->dev; struct sdca_class_drv *core = dev_get_drvdata(dev->parent); - struct sdca_device_data *data = &core->sdw->sdca_data; - struct sdca_function_desc *desc; + struct sdca_dev *sdev = auxiliary_dev_to_sdca_dev(auxdev); struct snd_soc_component_driver *cmp_drv; struct snd_soc_dai_driver *dais; struct class_function_drv *drv; @@ -305,7 +305,6 @@ static int class_function_probe(struct auxiliary_device *auxdev, int ndefaults; int num_dais; int ret; - int i; drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); if (!drv) @@ -328,21 +327,9 @@ static int class_function_probe(struct auxiliary_device *auxdev, drv->dev = dev; drv->core = core; + drv->function = &sdev->function; - for (i = 0; i < data->num_functions; i++) { - desc = &data->function[i]; - - if (desc->type == aux_dev_id->driver_data) - break; - } - if (i == core->sdw->sdca_data.num_functions) { - dev_err(dev, "failed to locate function\n"); - return -EINVAL; - } - - drv->function = &core->functions[i]; - - ret = sdca_parse_function(dev, core->sdw, desc, drv->function); + ret = sdca_parse_function(dev, core->sdw, drv->function); if (ret) return ret; @@ -377,7 +364,7 @@ static int class_function_probe(struct auxiliary_device *auxdev, return dev_err_probe(dev, PTR_ERR(drv->regmap), "failed to create regmap"); - switch (desc->type) { + switch (drv->function->desc->type) { case SDCA_FUNCTION_TYPE_UAJ: case SDCA_FUNCTION_TYPE_RJ: cmp_drv->set_jack = class_function_set_jack; diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 196bade11ab5..77940bd6b33c 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -98,7 +98,7 @@ static int find_sdca_function(struct acpi_device *adev, void *data) u32 function_type; int function_index; u64 addr; - int ret; + int i, ret; if (sdca_data->num_functions >= SDCA_MAX_FUNCTION_COUNT) { dev_err(dev, "maximum number of functions exceeded\n"); @@ -159,6 +159,14 @@ static int find_sdca_function(struct acpi_device *adev, void *data) /* store results */ function_index = sdca_data->num_functions; + + for (i = 0; i < function_index; i++) { + if (sdca_data->function[i].type == function_type) { + sdca_data->function[function_index].duplicate = true; + break; + } + } + sdca_data->function[function_index].adr = addr; sdca_data->function[function_index].type = function_type; sdca_data->function[function_index].name = function_name; @@ -1466,6 +1474,7 @@ static int find_sdca_entity_xu(struct device *dev, } static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, + struct sdca_function_data *function, struct fwnode_handle *function_node, struct fwnode_handle *entity_node, struct sdca_entity *entity) @@ -1481,6 +1490,13 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, return ret; } + if (function->desc->duplicate) { + entity->label = devm_kasprintf(dev, GFP_KERNEL, "%d %s", + function->desc->adr, entity->label); + if (!entity->label) + return -ENOMEM; + } + ret = fwnode_property_read_u32(entity_node, "mipi-sdca-entity-type", &tmp); if (ret) { dev_err(dev, "%s: type missing: %d\n", entity->label, ret); @@ -1578,7 +1594,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, return -EINVAL; } - ret = find_sdca_entity(dev, sdw, function_node, + ret = find_sdca_entity(dev, sdw, function, function_node, entity_node, &entities[i]); fwnode_handle_put(entity_node); if (ret) @@ -1605,8 +1621,14 @@ static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data * const char *entity_label) { struct sdca_entity *entity = NULL; + char tmp[64]; int i; + if (function->desc->duplicate) { + snprintf(tmp, sizeof(tmp), "%d %s", function->desc->adr, entity_label); + entity_label = tmp; + } + for (i = 0; i < function->num_entities; i++) { entity = &function->entities[i]; @@ -2158,27 +2180,22 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, * sdca_parse_function - parse ACPI DisCo for a Function * @dev: Pointer to device against which function data will be allocated. * @sdw: SoundWire slave device to be processed. - * @function_desc: Pointer to the Function short descriptor. * @function: Pointer to the Function information, to be populated. * * Return: Returns 0 for success. */ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, - struct sdca_function_desc *function_desc, struct sdca_function_data *function) { + struct fwnode_handle *node = function->desc->node; u32 tmp; int ret; - function->desc = function_desc; - - ret = fwnode_property_read_u32(function_desc->node, - "mipi-sdca-function-busy-max-delay", &tmp); + ret = fwnode_property_read_u32(node, "mipi-sdca-function-busy-max-delay", &tmp); if (!ret) function->busy_max_delay = tmp; - ret = fwnode_property_read_u32(function_desc->node, - "mipi-sdca-function-reset-max-delay", &tmp); + ret = fwnode_property_read_u32(node, "mipi-sdca-function-reset-max-delay", &tmp); if (ret || tmp == 0) { dev_dbg(dev, "reset delay missing, defaulting to 100mS\n"); function->reset_max_delay = 100000; @@ -2187,26 +2204,26 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, } dev_dbg(dev, "%pfwP: name %s busy delay %dus reset delay %dus\n", - function->desc->node, function->desc->name, - function->busy_max_delay, function->reset_max_delay); + node, function->desc->name, function->busy_max_delay, + function->reset_max_delay); - ret = find_sdca_init_table(dev, function_desc->node, function); + ret = find_sdca_init_table(dev, node, function); if (ret) return ret; - ret = find_sdca_entities(dev, sdw, function_desc->node, function); + ret = find_sdca_entities(dev, sdw, node, function); if (ret) return ret; - ret = find_sdca_connections(dev, function_desc->node, function); + ret = find_sdca_connections(dev, node, function); if (ret) return ret; - ret = find_sdca_clusters(dev, function_desc->node, function); + ret = find_sdca_clusters(dev, node, function); if (ret < 0) return ret; - ret = find_sdca_filesets(dev, sdw, function_desc->node, function); + ret = find_sdca_filesets(dev, sdw, node, function); if (ret) return ret; diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 6e10b4e660d9..4539a52a8e32 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -375,8 +375,7 @@ int sdca_irq_data_populate(struct device *dev, struct regmap *regmap, if (!dev) return -ENODEV; - name = kasprintf(GFP_KERNEL, "%s %s %s", function->desc->name, - entity->label, control->label); + name = kasprintf(GFP_KERNEL, "%s %s", entity->label, control->label); if (!name) return -ENOMEM; diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c index 49d317d3b8c8..a2d95b124b78 100644 --- a/sound/soc/sdca/sdca_jack.c +++ b/sound/soc/sdca/sdca_jack.c @@ -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; }