From: Vaibhav Agarwal Date: Thu, 30 Jun 2016 13:15:37 +0000 (+0530) Subject: greybus: audio: topology: Enable enumerated control support X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=e65579e335da0a65b5a76a343ddff6a6f3c77dd1;p=linux-beck.git greybus: audio: topology: Enable enumerated control support Added .get/.set callback and relevant changes in parser to enable enumerated control support for kcontrols and DAPM widget controls. Currently, it is limited to enumerated strings only. Signed-off-by: Vaibhav Agarwal Reviewed-by: Mark Greer Signed-off-by: Alex Elder --- diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 130548313454..e0779ca64388 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -53,7 +53,7 @@ static struct gbaudio_module_info *find_gb_module( } static const char *gbaudio_map_controlid(struct gbaudio_module_info *module, - __u8 control_id, __u8 index) + __u8 control_id, __u8 index) { struct gbaudio_control *control; @@ -77,8 +77,23 @@ static const char *gbaudio_map_controlid(struct gbaudio_module_info *module, return NULL; } +static int gbaudio_map_controlname(struct gbaudio_module_info *module, + const char *name) +{ + struct gbaudio_control *control; + + list_for_each_entry(control, &module->ctl_list, list) { + if (!strncmp(control->name, name, NAME_SIZE)) + return control->id; + } + + dev_warn(module->dev, "%s: missing in modules controls list\n", name); + + return -EINVAL; +} + static int gbaudio_map_wcontrolname(struct gbaudio_module_info *module, - const char *name) + const char *name) { struct gbaudio_control *control; @@ -92,7 +107,7 @@ static int gbaudio_map_wcontrolname(struct gbaudio_module_info *module, } static int gbaudio_map_widgetname(struct gbaudio_module_info *module, - const char *name) + const char *name) { struct gbaudio_widget *widget; list_for_each_entry(widget, &module->widget_list, list) { @@ -105,7 +120,7 @@ static int gbaudio_map_widgetname(struct gbaudio_module_info *module, } static const char *gbaudio_map_widgetid(struct gbaudio_module_info *module, - __u8 widget_id) + __u8 widget_id) { struct gbaudio_widget *widget; @@ -116,6 +131,27 @@ static const char *gbaudio_map_widgetid(struct gbaudio_module_info *module, return NULL; } +const char **gb_generate_enum_strings(struct gbaudio_module_info *gb, + struct gb_audio_enumerated *gbenum) +{ + const char **strings; + int i; + __u8 *data; + + strings = devm_kzalloc(gb->dev, sizeof(char *) * gbenum->items, + GFP_KERNEL); + data = gbenum->names; + + for (i = 0; i < gbenum->items; i++) { + strings[i] = (const char *)data; + while (*data != '\0') + data++; + data++; + } + + return strings; +} + static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -473,60 +509,288 @@ static int gbaudio_validate_kcontrol_count(struct gb_audio_widget *w) return ret; } +static int gbcodec_enum_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, ctl_id; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct gb_audio_ctl_elem_value gbvalue; + struct gbaudio_module_info *module; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + + module = find_gb_module(gb, kcontrol->id.name); + if (!module) + return -EINVAL; + + ctl_id = gbaudio_map_controlname(module, kcontrol->id.name); + if (ctl_id < 0) + return -EINVAL; + + ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); + return ret; + } + + ucontrol->value.enumerated.item[0] = gbvalue.value.enumerated_item[0]; + if (e->shift_l != e->shift_r) + ucontrol->value.enumerated.item[1] = + gbvalue.value.enumerated_item[1]; + + return 0; +} + +static int gbcodec_enum_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, ctl_id; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct gb_audio_ctl_elem_value gbvalue; + struct gbaudio_module_info *module; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + + module = find_gb_module(gb, kcontrol->id.name); + if (!module) + return -EINVAL; + + ctl_id = gbaudio_map_controlname(module, kcontrol->id.name); + if (ctl_id < 0) + return -EINVAL; + + if (ucontrol->value.enumerated.item[0] > e->max - 1) + return -EINVAL; + gbvalue.value.enumerated_item[0] = ucontrol->value.enumerated.item[0]; + + if (e->shift_l != e->shift_r) { + if (ucontrol->value.enumerated.item[1] > e->max - 1) + return -EINVAL; + gbvalue.value.enumerated_item[1] = + ucontrol->value.enumerated.item[1]; + } + + ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); + } + + return ret; +} + +static int gbaudio_tplg_create_enum_kctl(struct gbaudio_module_info *gb, + struct snd_kcontrol_new *kctl, + struct gb_audio_control *ctl) +{ + struct soc_enum *gbe; + struct gb_audio_enumerated *gb_enum; + int i; + + gbe = devm_kzalloc(gb->dev, sizeof(*gbe), GFP_KERNEL); + if (!gbe) + return -ENOMEM; + + gb_enum = &ctl->info.value.enumerated; + + /* since count=1, and reg is dummy */ + gbe->max = gb_enum->items; + gbe->texts = gb_generate_enum_strings(gb, gb_enum); + + /* debug enum info */ + dev_dbg(gb->dev, "Max:%d, name_length:%d\n", gb_enum->items, + gb_enum->names_length); + for (i = 0; i < gb_enum->items; i++) + dev_dbg(gb->dev, "src[%d]: %s\n", i, gbe->texts[i]); + + *kctl = (struct snd_kcontrol_new) + SOC_ENUM_EXT(ctl->name, *gbe, gbcodec_enum_ctl_get, + gbcodec_enum_ctl_put); + return 0; +} + static int gbaudio_tplg_create_kcontrol(struct gbaudio_module_info *gb, struct snd_kcontrol_new *kctl, struct gb_audio_control *ctl) { + int ret = 0; struct gbaudio_ctl_pvt *ctldata; switch (ctl->iface) { case SNDRV_CTL_ELEM_IFACE_MIXER: - ctldata = devm_kzalloc(gb->dev, sizeof(struct gbaudio_ctl_pvt), - GFP_KERNEL); - if (!ctldata) - return -ENOMEM; - ctldata->ctl_id = ctl->id; - ctldata->data_cport = ctl->data_cport; - ctldata->access = ctl->access; - ctldata->vcount = ctl->count_values; - ctldata->info = &ctl->info; - *kctl = (struct snd_kcontrol_new) - SOC_MIXER_GB(ctl->name, ctl->count, ctldata); - ctldata = NULL; + switch (ctl->info.type) { + case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: + ret = gbaudio_tplg_create_enum_kctl(gb, kctl, ctl); + break; + default: + ctldata = devm_kzalloc(gb->dev, + sizeof(struct gbaudio_ctl_pvt), + GFP_KERNEL); + if (!ctldata) + return -ENOMEM; + ctldata->ctl_id = ctl->id; + ctldata->data_cport = ctl->data_cport; + ctldata->access = ctl->access; + ctldata->vcount = ctl->count_values; + ctldata->info = &ctl->info; + *kctl = (struct snd_kcontrol_new) + SOC_MIXER_GB(ctl->name, ctl->count, ctldata); + ctldata = NULL; + break; + } break; default: return -EINVAL; } dev_dbg(gb->dev, "%s:%d control created\n", ctl->name, ctl->id); + return ret; +} + +static int gbcodec_enum_dapm_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, ctl_id; + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct gbaudio_module_info *module; + struct gb_audio_ctl_elem_value gbvalue; + struct snd_soc_codec *codec = widget->codec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + module = find_gb_module(gb, kcontrol->id.name); + if (!module) + return -EINVAL; + + ctl_id = gbaudio_map_wcontrolname(module, kcontrol->id.name); + if (ctl_id < 0) + return -EINVAL; + + ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); + return ret; + } + + ucontrol->value.enumerated.item[0] = gbvalue.value.enumerated_item[0]; + if (e->shift_l != e->shift_r) + ucontrol->value.enumerated.item[1] = + gbvalue.value.enumerated_item[1]; + return 0; } -static const char * const gbtexts[] = {"Stereo", "Left", "Right"}; +static int gbcodec_enum_dapm_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, wi, ctl_id; + unsigned int val, mux, change; + unsigned int mask; + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct gb_audio_ctl_elem_value gbvalue; + struct gbaudio_module_info *module; + struct snd_soc_codec *codec = widget->codec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + if (ucontrol->value.enumerated.item[0] > e->max - 1) + return -EINVAL; + + module = find_gb_module(gb, kcontrol->id.name); + if (!module) + return -EINVAL; + + ctl_id = gbaudio_map_wcontrolname(module, kcontrol->id.name); + if (ctl_id < 0) + return -EINVAL; + + change = 0; + ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); + return ret; + } -static const SOC_ENUM_SINGLE_DECL( - module_apb1_rx_enum, GBCODEC_APB1_MUX_REG, 0, gbtexts); + mux = ucontrol->value.enumerated.item[0]; + val = mux << e->shift_l; + mask = e->mask << e->shift_l; -static const SOC_ENUM_SINGLE_DECL( - module_mic_enum, GBCODEC_APB1_MUX_REG, 4, gbtexts); + if (gbvalue.value.enumerated_item[0] != + ucontrol->value.enumerated.item[0]) { + change = 1; + gbvalue.value.enumerated_item[0] = + ucontrol->value.enumerated.item[0]; + } + + if (e->shift_l != e->shift_r) { + if (ucontrol->value.enumerated.item[1] > e->max - 1) + return -EINVAL; + val |= ucontrol->value.enumerated.item[1] << e->shift_r; + mask |= e->mask << e->shift_r; + if (gbvalue.value.enumerated_item[1] != + ucontrol->value.enumerated.item[1]) { + change = 1; + gbvalue.value.enumerated_item[1] = + ucontrol->value.enumerated.item[1]; + } + } + + if (change) { + ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err_ratelimited(codec->dev, + "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); + } + for (wi = 0; wi < wlist->num_widgets; wi++) { + widget = wlist->widgets[wi]; + + widget->value = val; + widget->dapm->update = NULL; + snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e); + } + } + + return change; +} static int gbaudio_tplg_create_enum_ctl(struct gbaudio_module_info *gb, struct snd_kcontrol_new *kctl, struct gb_audio_control *ctl) { - switch (ctl->id) { - case 8: - *kctl = (struct snd_kcontrol_new) - SOC_DAPM_ENUM(ctl->name, module_apb1_rx_enum); - break; - case 9: - *kctl = (struct snd_kcontrol_new) - SOC_DAPM_ENUM(ctl->name, module_mic_enum); - break; - default: - return -EINVAL; - } + struct soc_enum *gbe; + struct gb_audio_enumerated *gb_enum; + int i; + + gbe = devm_kzalloc(gb->dev, sizeof(*gbe), GFP_KERNEL); + if (!gbe) + return -ENOMEM; + gb_enum = &ctl->info.value.enumerated; + + /* since count=1, and reg is dummy */ + gbe->max = gb_enum->items; + gbe->texts = gb_generate_enum_strings(gb, gb_enum); + + /* debug enum info */ + dev_dbg(gb->dev, "Max:%d, name_length:%d\n", gb_enum->items, + gb_enum->names_length); + for (i = 0; i < gb_enum->items; i++) + dev_dbg(gb->dev, "src[%d]: %s\n", i, gbe->texts[i]); + + *kctl = (struct snd_kcontrol_new) + SOC_DAPM_ENUM_EXT(ctl->name, *gbe, gbcodec_enum_dapm_ctl_get, + gbcodec_enum_dapm_ctl_put); return 0; } @@ -672,10 +936,18 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, control->name = curr->name; control->wname = w->name; - if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) + if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) { + struct gb_audio_enumerated *gbenum = + &curr->info.value.enumerated; + + csize = offsetof(struct gb_audio_control, info); + csize += offsetof(struct gb_audio_ctl_elem_info, value); + csize += offsetof(struct gb_audio_enumerated, names); + csize += gbenum->names_length; control->texts = (const char * const *) - curr->info.value.enumerated.names; - csize = sizeof(struct gb_audio_control); + gb_generate_enum_strings(module, gbenum); + } else + csize = sizeof(struct gb_audio_control); *w_size += csize; curr = (void *)curr + csize; list_add(&control->list, &module->widget_ctl_list); @@ -810,10 +1082,18 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module, snprintf(curr->name, NAME_SIZE, "GB %d %s", module->dev_id, temp_name); control->name = curr->name; - if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) + if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) { + struct gb_audio_enumerated *gbenum = + &curr->info.value.enumerated; + + csize = offsetof(struct gb_audio_control, info); + csize += offsetof(struct gb_audio_ctl_elem_info, value); + csize += offsetof(struct gb_audio_enumerated, names); + csize += gbenum->names_length; control->texts = (const char * const *) - curr->info.value.enumerated.names; - csize = sizeof(struct gb_audio_control); + gb_generate_enum_strings(module, gbenum); + } else + csize = sizeof(struct gb_audio_control); list_add(&control->list, &module->ctl_list); dev_dbg(module->dev, "%d:%s created of type %d\n", curr->id,