]> git.karo-electronics.de Git - linux-beck.git/commitdiff
greybus: audio: topology: Enable enumerated control support
authorVaibhav Agarwal <vaibhav.agarwal@linaro.org>
Thu, 30 Jun 2016 13:15:37 +0000 (18:45 +0530)
committerAlex Elder <elder@linaro.org>
Wed, 6 Jul 2016 02:17:58 +0000 (21:17 -0500)
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 <vaibhav.agarwal@linaro.org>
Reviewed-by: Mark Greer <mark.greer@animalcreek.com>
Signed-off-by: Alex Elder <elder@linaro.org>
drivers/staging/greybus/audio_topology.c

index 13054831345442860d0d58d7b71cd3e3caf2504b..e0779ca64388df25f9f94bb35f41e410d64e0dcf 100644 (file)
@@ -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,