root/drivers/staging/greybus/audio_topology.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Greybus audio driver
 * Copyright 2015-2016 Google Inc.
 * Copyright 2015-2016 Linaro Ltd.
 */

#include <linux/greybus.h>
#include "audio_codec.h"

#define GBAUDIO_INVALID_ID      0xFF

struct gbaudio_ctl_pvt {
        unsigned int ctl_id;
        unsigned int data_cport;
        unsigned int access;
        unsigned int vcount;
        struct gb_audio_ctl_elem_info *info;
};

static struct gbaudio_module_info *find_gb_module(struct gbaudio_codec_info *codec,
                                                  char const *name)
{
        int dev_id;
        char begin[NAME_SIZE];
        struct gbaudio_module_info *module;

        if (!name)
                return NULL;

        if (sscanf(name, "%s %d", begin, &dev_id) != 2)
                return NULL;

        dev_dbg(codec->dev, "%s:Find module#%d\n", __func__, dev_id);

        mutex_lock(&codec->lock);
        list_for_each_entry(module, &codec->module_list, list) {
                if (module->dev_id == dev_id) {
                        mutex_unlock(&codec->lock);
                        return module;
                }
        }
        mutex_unlock(&codec->lock);
        dev_warn(codec->dev, "%s: module#%d missing in codec list\n", name,
                 dev_id);
        return NULL;
}

static const char *gbaudio_map_controlid(struct gbaudio_module_info *module,
                                         __u8 control_id, __u8 index)
{
        struct gbaudio_control *control;

        if (control_id == GBAUDIO_INVALID_ID)
                return NULL;

        list_for_each_entry(control, &module->ctl_list, list) {
                if (control->id == control_id) {
                        if (index == GBAUDIO_INVALID_ID)
                                return control->name;
                        if (index >= control->items)
                                return NULL;
                        return control->texts[index];
                }
        }
        list_for_each_entry(control, &module->widget_ctl_list, list) {
                if (control->id == control_id) {
                        if (index == GBAUDIO_INVALID_ID)
                                return control->name;
                        if (index >= control->items)
                                return NULL;
                        return control->texts[index];
                }
        }
        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)
{
        struct gbaudio_control *control;

        list_for_each_entry(control, &module->widget_ctl_list, list) {
                if (!strncmp(control->wname, name, NAME_SIZE))
                        return control->id;
        }
        dev_warn(module->dev, "%s: missing in modules controls list\n", name);

        return -EINVAL;
}

static int gbaudio_map_widgetname(struct gbaudio_module_info *module,
                                  const char *name)
{
        struct gbaudio_widget *widget;

        list_for_each_entry(widget, &module->widget_list, list) {
                if (!strncmp(widget->name, name, NAME_SIZE))
                        return widget->id;
        }
        dev_warn(module->dev, "%s: missing in modules widgets list\n", name);

        return -EINVAL;
}

static const char *gbaudio_map_widgetid(struct gbaudio_module_info *module,
                                        __u8 widget_id)
{
        struct gbaudio_widget *widget;

        list_for_each_entry(widget, &module->widget_list, list) {
                if (widget->id == widget_id)
                        return widget->name;
        }
        return NULL;
}

static const char **gb_generate_enum_strings(struct gbaudio_module_info *gb,
                                             struct gb_audio_enumerated *gbenum)
{
        const char **strings;
        int i;
        unsigned int items;
        __u8 *data;

        items = le32_to_cpu(gbenum->items);
        strings = devm_kcalloc(gb->dev, items, sizeof(char *), GFP_KERNEL);
        if (!strings)
                return NULL;

        data = gbenum->names;

        for (i = 0; i < 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)
{
        unsigned int max;
        const char *name;
        struct gbaudio_ctl_pvt *data;
        struct gb_audio_ctl_elem_info *info;
        struct gbaudio_module_info *module;
        struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
        struct gbaudio_codec_info *gbcodec = snd_soc_component_get_drvdata(comp);

        dev_dbg(comp->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
        data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
        info = (struct gb_audio_ctl_elem_info *)data->info;

        if (!info) {
                dev_err(comp->dev, "NULL info for %s\n", uinfo->id.name);
                return -EINVAL;
        }

        /* update uinfo */
        uinfo->access = data->access;
        uinfo->count = data->vcount;
        uinfo->type = (__force snd_ctl_elem_type_t)info->type;

        switch (info->type) {
        case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN:
        case GB_AUDIO_CTL_ELEM_TYPE_INTEGER:
                uinfo->value.integer.min = le32_to_cpu(info->value.integer.min);
                uinfo->value.integer.max = le32_to_cpu(info->value.integer.max);
                break;
        case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED:
                max = le32_to_cpu(info->value.enumerated.items);
                uinfo->value.enumerated.items = max;
                if (uinfo->value.enumerated.item > max - 1)
                        uinfo->value.enumerated.item = max - 1;
                module = find_gb_module(gbcodec, kcontrol->id.name);
                if (!module)
                        return -EINVAL;
                name = gbaudio_map_controlid(module, data->ctl_id,
                                             uinfo->value.enumerated.item);
                strscpy(uinfo->value.enumerated.name, name, sizeof(uinfo->value.enumerated.name));
                break;
        default:
                dev_err(comp->dev, "Invalid type: %d for %s:kcontrol\n",
                        info->type, kcontrol->id.name);
                break;
        }
        return 0;
}

static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
{
        int ret;
        struct gb_audio_ctl_elem_info *info;
        struct gbaudio_ctl_pvt *data;
        struct gb_audio_ctl_elem_value gbvalue;
        struct gbaudio_module_info *module;
        struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
        struct gbaudio_codec_info *gb = snd_soc_component_get_drvdata(comp);
        struct gb_bundle *bundle;

        dev_dbg(comp->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
        module = find_gb_module(gb, kcontrol->id.name);
        if (!module)
                return -EINVAL;

        data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
        info = (struct gb_audio_ctl_elem_info *)data->info;
        bundle = to_gb_bundle(module->dev);

        ret = gb_pm_runtime_get_sync(bundle);
        if (ret)
                return ret;

        ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id,
                                      GB_AUDIO_INVALID_INDEX, &gbvalue);

        gb_pm_runtime_put_autosuspend(bundle);

        if (ret) {
                dev_err_ratelimited(comp->dev, "%d:Error in %s for %s\n", ret,
                                    __func__, kcontrol->id.name);
                return ret;
        }

        /* update ucontrol */
        switch (info->type) {
        case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN:
        case GB_AUDIO_CTL_ELEM_TYPE_INTEGER:
                ucontrol->value.integer.value[0] =
                        le32_to_cpu(gbvalue.value.integer_value[0]);
                if (data->vcount == 2)
                        ucontrol->value.integer.value[1] =
                                le32_to_cpu(gbvalue.value.integer_value[1]);
                break;
        case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED:
                ucontrol->value.enumerated.item[0] =
                        le32_to_cpu(gbvalue.value.enumerated_item[0]);
                if (data->vcount == 2)
                        ucontrol->value.enumerated.item[1] =
                                le32_to_cpu(gbvalue.value.enumerated_item[1]);
                break;
        default:
                dev_err(comp->dev, "Invalid type: %d for %s:kcontrol\n",
                        info->type, kcontrol->id.name);
                ret = -EINVAL;
                break;
        }
        return ret;
}

static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
{
        int ret = 0;
        struct gb_audio_ctl_elem_info *info;
        struct gbaudio_ctl_pvt *data;
        struct gb_audio_ctl_elem_value gbvalue;
        struct gbaudio_module_info *module;
        struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
        struct gbaudio_codec_info *gb = snd_soc_component_get_drvdata(comp);
        struct gb_bundle *bundle;

        dev_dbg(comp->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
        module = find_gb_module(gb, kcontrol->id.name);
        if (!module)
                return -EINVAL;

        data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
        info = (struct gb_audio_ctl_elem_info *)data->info;
        bundle = to_gb_bundle(module->dev);

        /* update ucontrol */
        switch (info->type) {
        case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN:
        case GB_AUDIO_CTL_ELEM_TYPE_INTEGER:
                gbvalue.value.integer_value[0] =
                        cpu_to_le32(ucontrol->value.integer.value[0]);
                if (data->vcount == 2)
                        gbvalue.value.integer_value[1] =
                                cpu_to_le32(ucontrol->value.integer.value[1]);
                break;
        case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED:
                gbvalue.value.enumerated_item[0] =
                        cpu_to_le32(ucontrol->value.enumerated.item[0]);
                if (data->vcount == 2)
                        gbvalue.value.enumerated_item[1] =
                                cpu_to_le32(ucontrol->value.enumerated.item[1]);
                break;
        default:
                dev_err(comp->dev, "Invalid type: %d for %s:kcontrol\n",
                        info->type, kcontrol->id.name);
                ret = -EINVAL;
                break;
        }

        if (ret)
                return ret;

        ret = gb_pm_runtime_get_sync(bundle);
        if (ret)
                return ret;

        ret = gb_audio_gb_set_control(module->mgmt_connection, data->ctl_id,
                                      GB_AUDIO_INVALID_INDEX, &gbvalue);

        gb_pm_runtime_put_autosuspend(bundle);

        if (ret) {
                dev_err_ratelimited(comp->dev, "%d:Error in %s for %s\n", ret,
                                    __func__, kcontrol->id.name);
        }

        return ret;
}

#define SOC_MIXER_GB(xname, kcount, data) \
{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .count = kcount, .info = gbcodec_mixer_ctl_info, \
        .get = gbcodec_mixer_ctl_get, .put = gbcodec_mixer_ctl_put, \
        .private_value = (unsigned long)data }

/*
 * although below callback functions seems redundant to above functions.
 * same are kept to allow provision for different handling in case
 * of DAPM related sequencing, etc.
 */
static int gbcodec_mixer_dapm_ctl_info(struct snd_kcontrol *kcontrol,
                                       struct snd_ctl_elem_info *uinfo)
{
        int platform_max, platform_min;
        struct gbaudio_ctl_pvt *data;
        struct gb_audio_ctl_elem_info *info;

        data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
        info = (struct gb_audio_ctl_elem_info *)data->info;

        /* update uinfo */
        platform_max = le32_to_cpu(info->value.integer.max);
        platform_min = le32_to_cpu(info->value.integer.min);

        if (platform_max == 1 &&
            !strnstr(kcontrol->id.name, " Volume", sizeof(kcontrol->id.name)))
                uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
        else
                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;

        uinfo->count = data->vcount;
        uinfo->value.integer.min = platform_min;
        uinfo->value.integer.max = platform_max;

        return 0;
}

static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol,
                                      struct snd_ctl_elem_value *ucontrol)
{
        int ret;
        struct gbaudio_ctl_pvt *data;
        struct gb_audio_ctl_elem_value gbvalue;
        struct gbaudio_module_info *module;
        struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
        struct snd_soc_dapm_widget *widget = wlist->widgets[0];
        struct device *codec_dev = snd_soc_dapm_to_dev(widget->dapm);
        struct gbaudio_codec_info *gb = dev_get_drvdata(codec_dev);
        struct gb_bundle *bundle;

        dev_dbg(codec_dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
        module = find_gb_module(gb, kcontrol->id.name);
        if (!module)
                return -EINVAL;

        data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
        bundle = to_gb_bundle(module->dev);

        if (data->vcount == 2)
                dev_warn(codec_dev,
                         "GB: Control '%s' is stereo, which is not supported\n",
                         kcontrol->id.name);

        ret = gb_pm_runtime_get_sync(bundle);
        if (ret)
                return ret;

        ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id,
                                      GB_AUDIO_INVALID_INDEX, &gbvalue);

        gb_pm_runtime_put_autosuspend(bundle);

        if (ret) {
                dev_err_ratelimited(codec_dev, "%d:Error in %s for %s\n", ret,
                                    __func__, kcontrol->id.name);
                return ret;
        }
        /* update ucontrol */
        ucontrol->value.integer.value[0] =
                le32_to_cpu(gbvalue.value.integer_value[0]);

        return ret;
}

static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol,
                                      struct snd_ctl_elem_value *ucontrol)
{
        int ret, wi, max, connect;
        unsigned int mask, val;
        struct gb_audio_ctl_elem_info *info;
        struct gbaudio_ctl_pvt *data;
        struct gb_audio_ctl_elem_value gbvalue;
        struct gbaudio_module_info *module;
        struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
        struct snd_soc_dapm_widget *widget = wlist->widgets[0];
        struct device *codec_dev = snd_soc_dapm_to_dev(widget->dapm);
        struct gbaudio_codec_info *gb = dev_get_drvdata(codec_dev);
        struct gb_bundle *bundle;

        dev_dbg(codec_dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
        module = find_gb_module(gb, kcontrol->id.name);
        if (!module)
                return -EINVAL;

        data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
        info = (struct gb_audio_ctl_elem_info *)data->info;
        bundle = to_gb_bundle(module->dev);

        if (data->vcount == 2)
                dev_warn(codec_dev,
                         "GB: Control '%s' is stereo, which is not supported\n",
                         kcontrol->id.name);

        max = le32_to_cpu(info->value.integer.max);
        mask = (1 << fls(max)) - 1;
        val = ucontrol->value.integer.value[0] & mask;
        connect = !!val;

        ret = gb_pm_runtime_get_sync(bundle);
        if (ret)
                return ret;

        ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id,
                                      GB_AUDIO_INVALID_INDEX, &gbvalue);
        if (ret)
                goto exit;

        /* update ucontrol */
        if (le32_to_cpu(gbvalue.value.integer_value[0]) != val) {
                for (wi = 0; wi < wlist->num_widgets; wi++) {
                        widget = wlist->widgets[wi];
                        snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol,
                                                        connect, NULL);
                }
                gbvalue.value.integer_value[0] =
                        cpu_to_le32(ucontrol->value.integer.value[0]);

                ret = gb_audio_gb_set_control(module->mgmt_connection,
                                              data->ctl_id,
                                              GB_AUDIO_INVALID_INDEX, &gbvalue);
        }

exit:
        gb_pm_runtime_put_autosuspend(bundle);
        if (ret)
                dev_err_ratelimited(codec_dev, "%d:Error in %s for %s\n", ret,
                                    __func__, kcontrol->id.name);
        return ret;
}

#define SOC_DAPM_MIXER_GB(xname, kcount, data) \
{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .count = kcount, .info = gbcodec_mixer_dapm_ctl_info, \
        .get = gbcodec_mixer_dapm_ctl_get, .put = gbcodec_mixer_dapm_ctl_put, \
        .private_value = (unsigned long)data}

static int gbcodec_event_spk(struct snd_soc_dapm_widget *w,
                             struct snd_kcontrol *k, int event)
{
        /* Ensure GB speaker is connected */

        return 0;
}

static int gbcodec_event_hp(struct snd_soc_dapm_widget *w,
                            struct snd_kcontrol *k, int event)
{
        /* Ensure GB module supports jack slot */

        return 0;
}

static int gbcodec_event_int_mic(struct snd_soc_dapm_widget *w,
                                 struct snd_kcontrol *k, int event)
{
        /* Ensure GB module supports jack slot */

        return 0;
}

static int gbaudio_validate_kcontrol_count(struct gb_audio_widget *w)
{
        int ret = 0;

        switch (w->type) {
        case snd_soc_dapm_spk:
        case snd_soc_dapm_hp:
        case snd_soc_dapm_mic:
        case snd_soc_dapm_output:
        case snd_soc_dapm_input:
                if (w->ncontrols)
                        ret = -EINVAL;
                break;
        case snd_soc_dapm_switch:
        case snd_soc_dapm_mux:
                if (w->ncontrols != 1)
                        ret = -EINVAL;
                break;
        default:
                break;
        }

        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_component *comp = snd_kcontrol_chip(kcontrol);
        struct gbaudio_codec_info *gb = snd_soc_component_get_drvdata(comp);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        struct gb_audio_ctl_elem_value gbvalue;
        struct gbaudio_module_info *module;
        struct gb_bundle *bundle;

        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;

        bundle = to_gb_bundle(module->dev);

        ret = gb_pm_runtime_get_sync(bundle);
        if (ret)
                return ret;

        ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id,
                                      GB_AUDIO_INVALID_INDEX, &gbvalue);

        gb_pm_runtime_put_autosuspend(bundle);

        if (ret) {
                dev_err_ratelimited(comp->dev, "%d:Error in %s for %s\n", ret,
                                    __func__, kcontrol->id.name);
                return ret;
        }

        ucontrol->value.enumerated.item[0] =
                le32_to_cpu(gbvalue.value.enumerated_item[0]);
        if (e->shift_l != e->shift_r)
                ucontrol->value.enumerated.item[1] =
                        le32_to_cpu(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_component *comp = snd_kcontrol_chip(kcontrol);
        struct gbaudio_codec_info *gb = snd_soc_component_get_drvdata(comp);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        struct gb_audio_ctl_elem_value gbvalue;
        struct gbaudio_module_info *module;
        struct gb_bundle *bundle;

        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->items - 1)
                return -EINVAL;
        gbvalue.value.enumerated_item[0] =
                cpu_to_le32(ucontrol->value.enumerated.item[0]);

        if (e->shift_l != e->shift_r) {
                if (ucontrol->value.enumerated.item[1] > e->items - 1)
                        return -EINVAL;
                gbvalue.value.enumerated_item[1] =
                        cpu_to_le32(ucontrol->value.enumerated.item[1]);
        }

        bundle = to_gb_bundle(module->dev);

        ret = gb_pm_runtime_get_sync(bundle);
        if (ret)
                return ret;

        ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id,
                                      GB_AUDIO_INVALID_INDEX, &gbvalue);

        gb_pm_runtime_put_autosuspend(bundle);

        if (ret) {
                dev_err_ratelimited(comp->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->items = le32_to_cpu(gb_enum->items);
        gbe->texts = gb_generate_enum_strings(gb, gb_enum);
        if (!gbe->texts)
                return -ENOMEM;

        /* debug enum info */
        dev_dbg(gb->dev, "Max:%d, name_length:%d\n", gbe->items,
                le16_to_cpu(gb_enum->names_length));
        for (i = 0; i < gbe->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 (__force int)SNDRV_CTL_ELEM_IFACE_MIXER:
                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 = le16_to_cpu(ctl->data_cport);
                        ctldata->access = le32_to_cpu(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 device *codec_dev = snd_soc_dapm_to_dev(widget->dapm);
        struct gbaudio_codec_info *gb = dev_get_drvdata(codec_dev);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        struct gb_bundle *bundle;

        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;

        bundle = to_gb_bundle(module->dev);

        ret = gb_pm_runtime_get_sync(bundle);
        if (ret)
                return ret;

        ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id,
                                      GB_AUDIO_INVALID_INDEX, &gbvalue);

        gb_pm_runtime_put_autosuspend(bundle);

        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] = le32_to_cpu(gbvalue.value.enumerated_item[0]);
        if (e->shift_l != e->shift_r)
                ucontrol->value.enumerated.item[1] =
                        le32_to_cpu(gbvalue.value.enumerated_item[1]);

        return 0;
}

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;
        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 device *codec_dev = snd_soc_dapm_to_dev(widget->dapm);
        struct gbaudio_codec_info *gb = dev_get_drvdata(codec_dev);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        struct gb_bundle *bundle;

        if (ucontrol->value.enumerated.item[0] > e->items - 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;
        bundle = to_gb_bundle(module->dev);

        ret = gb_pm_runtime_get_sync(bundle);
        if (ret)
                return ret;

        ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id,
                                      GB_AUDIO_INVALID_INDEX, &gbvalue);

        gb_pm_runtime_put_autosuspend(bundle);

        if (ret) {
                dev_err_ratelimited(codec_dev, "%d:Error in %s for %s\n", ret,
                                    __func__, kcontrol->id.name);
                return ret;
        }

        mux = ucontrol->value.enumerated.item[0];
        val = mux << e->shift_l;

        if (le32_to_cpu(gbvalue.value.enumerated_item[0]) !=
            ucontrol->value.enumerated.item[0]) {
                change = 1;
                gbvalue.value.enumerated_item[0] =
                        cpu_to_le32(ucontrol->value.enumerated.item[0]);
        }

        if (e->shift_l != e->shift_r) {
                if (ucontrol->value.enumerated.item[1] > e->items - 1)
                        return -EINVAL;
                val |= ucontrol->value.enumerated.item[1] << e->shift_r;
                if (le32_to_cpu(gbvalue.value.enumerated_item[1]) !=
                    ucontrol->value.enumerated.item[1]) {
                        change = 1;
                        gbvalue.value.enumerated_item[1] =
                                cpu_to_le32(ucontrol->value.enumerated.item[1]);
                }
        }

        if (change) {
                ret = gb_pm_runtime_get_sync(bundle);
                if (ret)
                        return ret;

                ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id,
                                              GB_AUDIO_INVALID_INDEX, &gbvalue);

                gb_pm_runtime_put_autosuspend(bundle);

                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];
                        snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
                                                      val, e, NULL);
                }
        }

        return change;
}

static int gbaudio_tplg_create_enum_ctl(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->items = le32_to_cpu(gb_enum->items);
        gbe->texts = gb_generate_enum_strings(gb, gb_enum);
        if (!gbe->texts)
                return -ENOMEM;

        /* debug enum info */
        dev_dbg(gb->dev, "Max:%d, name_length:%d\n", gbe->items,
                le16_to_cpu(gb_enum->names_length));
        for (i = 0; i < gbe->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;
}

static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_module_info *gb,
                                         struct snd_kcontrol_new *kctl,
                                         struct gb_audio_control *ctl)
{
        struct gbaudio_ctl_pvt *ctldata;

        ctldata = devm_kzalloc(gb->dev, sizeof(struct gbaudio_ctl_pvt),
                               GFP_KERNEL);
        if (!ctldata)
                return -ENOMEM;
        ctldata->ctl_id = ctl->id;
        ctldata->data_cport = le16_to_cpu(ctl->data_cport);
        ctldata->access = le32_to_cpu(ctl->access);
        ctldata->vcount = ctl->count_values;
        ctldata->info = &ctl->info;
        *kctl = (struct snd_kcontrol_new)
                SOC_DAPM_MIXER_GB(ctl->name, ctl->count, ctldata);

        return 0;
}

static int gbaudio_tplg_create_wcontrol(struct gbaudio_module_info *gb,
                                        struct snd_kcontrol_new *kctl,
                                        struct gb_audio_control *ctl)
{
        int ret;

        switch (ctl->iface) {
        case (__force int)SNDRV_CTL_ELEM_IFACE_MIXER:
                switch (ctl->info.type) {
                case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED:
                        ret = gbaudio_tplg_create_enum_ctl(gb, kctl, ctl);
                        break;
                default:
                        ret = gbaudio_tplg_create_mixer_ctl(gb, kctl, ctl);
                        break;
                }
                break;
        default:
                return -EINVAL;
        }

        dev_dbg(gb->dev, "%s:%d DAPM control created, ret:%d\n", ctl->name,
                ctl->id, ret);
        return ret;
}

static int gbaudio_widget_event(struct snd_soc_dapm_widget *w,
                                struct snd_kcontrol *kcontrol, int event)
{
        int wid;
        int ret;
        struct device *codec_dev = snd_soc_dapm_to_dev(w->dapm);
        struct gbaudio_codec_info *gbcodec = dev_get_drvdata(codec_dev);
        struct gbaudio_module_info *module;
        struct gb_bundle *bundle;

        dev_dbg(codec_dev, "%s %s %d\n", __func__, w->name, event);

        /* Find relevant module */
        module = find_gb_module(gbcodec, w->name);
        if (!module)
                return -EINVAL;

        /* map name to widget id */
        wid = gbaudio_map_widgetname(module, w->name);
        if (wid < 0) {
                dev_err(codec_dev, "Invalid widget name:%s\n", w->name);
                return -EINVAL;
        }

        bundle = to_gb_bundle(module->dev);

        ret = gb_pm_runtime_get_sync(bundle);
        if (ret)
                return ret;

        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
                ret = gb_audio_gb_enable_widget(module->mgmt_connection, wid);
                if (!ret)
                        ret = gbaudio_module_update(gbcodec, w, module, 1);
                break;
        case SND_SOC_DAPM_POST_PMD:
                ret = gb_audio_gb_disable_widget(module->mgmt_connection, wid);
                if (!ret)
                        ret = gbaudio_module_update(gbcodec, w, module, 0);
                break;
        }
        if (ret)
                dev_err_ratelimited(codec_dev,
                                    "%d: widget, event:%d failed:%d\n", wid,
                                    event, ret);

        gb_pm_runtime_put_autosuspend(bundle);

        return ret;
}

static const struct snd_soc_dapm_widget gbaudio_widgets[] = {
        [snd_soc_dapm_spk]      = SND_SOC_DAPM_SPK(NULL, gbcodec_event_spk),
        [snd_soc_dapm_hp]       = SND_SOC_DAPM_HP(NULL, gbcodec_event_hp),
        [snd_soc_dapm_mic]      = SND_SOC_DAPM_MIC(NULL, gbcodec_event_int_mic),
        [snd_soc_dapm_output]   = SND_SOC_DAPM_OUTPUT(NULL),
        [snd_soc_dapm_input]    = SND_SOC_DAPM_INPUT(NULL),
        [snd_soc_dapm_switch]   = SND_SOC_DAPM_SWITCH_E(NULL, SND_SOC_NOPM,
                                        0, 0, NULL,
                                        gbaudio_widget_event,
                                        SND_SOC_DAPM_PRE_PMU |
                                        SND_SOC_DAPM_POST_PMD),
        [snd_soc_dapm_pga]      = SND_SOC_DAPM_PGA_E(NULL, SND_SOC_NOPM,
                                        0, 0, NULL, 0,
                                        gbaudio_widget_event,
                                        SND_SOC_DAPM_PRE_PMU |
                                        SND_SOC_DAPM_POST_PMD),
        [snd_soc_dapm_mixer]    = SND_SOC_DAPM_MIXER_E(NULL, SND_SOC_NOPM,
                                        0, 0, NULL, 0,
                                        gbaudio_widget_event,
                                        SND_SOC_DAPM_PRE_PMU |
                                        SND_SOC_DAPM_POST_PMD),
        [snd_soc_dapm_mux]      = SND_SOC_DAPM_MUX_E(NULL, SND_SOC_NOPM,
                                        0, 0, NULL,
                                        gbaudio_widget_event,
                                        SND_SOC_DAPM_PRE_PMU |
                                        SND_SOC_DAPM_POST_PMD),
        [snd_soc_dapm_aif_in]   = SND_SOC_DAPM_AIF_IN_E(NULL, NULL, 0,
                                        SND_SOC_NOPM, 0, 0,
                                        gbaudio_widget_event,
                                        SND_SOC_DAPM_PRE_PMU |
                                        SND_SOC_DAPM_POST_PMD),
        [snd_soc_dapm_aif_out]  = SND_SOC_DAPM_AIF_OUT_E(NULL, NULL, 0,
                                        SND_SOC_NOPM, 0, 0,
                                        gbaudio_widget_event,
                                        SND_SOC_DAPM_PRE_PMU |
                                        SND_SOC_DAPM_POST_PMD),
};

static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module,
                                      struct snd_soc_dapm_widget *dw,
                                      struct gb_audio_widget *w, int *w_size)
{
        int i, ret, csize;
        struct snd_kcontrol_new *widget_kctls;
        struct gb_audio_control *curr;
        struct gbaudio_control *control, *_control;
        size_t size;
        char temp_name[NAME_SIZE];

        ret = gbaudio_validate_kcontrol_count(w);
        if (ret) {
                dev_err(module->dev, "Invalid kcontrol count=%d for %s\n",
                        w->ncontrols, w->name);
                return ret;
        }

        /* allocate memory for kcontrol */
        if (w->ncontrols) {
                size = sizeof(struct snd_kcontrol_new) * w->ncontrols;
                widget_kctls = devm_kzalloc(module->dev, size, GFP_KERNEL);
                if (!widget_kctls)
                        return -ENOMEM;
        }

        *w_size = sizeof(struct gb_audio_widget);

        /* create relevant kcontrols */
        curr = w->ctl;
        for (i = 0; i < w->ncontrols; i++) {
                ret = gbaudio_tplg_create_wcontrol(module, &widget_kctls[i],
                                                   curr);
                if (ret) {
                        dev_err(module->dev,
                                "%s:%d type widget_ctl not supported\n",
                                curr->name, curr->iface);
                        goto error;
                }
                control = devm_kzalloc(module->dev,
                                       sizeof(struct gbaudio_control),
                                       GFP_KERNEL);
                if (!control) {
                        ret = -ENOMEM;
                        goto error;
                }
                control->id = curr->id;
                control->name = curr->name;
                control->wname = w->name;

                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 += le16_to_cpu(gbenum->names_length);
                        control->texts = (const char * const *)
                                gb_generate_enum_strings(module, gbenum);
                        if (!control->texts) {
                                ret = -ENOMEM;
                                goto error;
                        }
                        control->items = le32_to_cpu(gbenum->items);
                } else {
                        csize = sizeof(struct gb_audio_control);
                }

                *w_size += csize;
                curr = (void *)curr + csize;
                list_add(&control->list, &module->widget_ctl_list);
                dev_dbg(module->dev, "%s: control of type %d created\n",
                        widget_kctls[i].name, widget_kctls[i].iface);
        }

        /* Prefix dev_id to widget control_name */
        strscpy(temp_name, w->name, sizeof(temp_name));
        snprintf(w->name, sizeof(w->name), "GB %d %s", module->dev_id, temp_name);

        switch (w->type) {
        case snd_soc_dapm_spk:
                *dw = gbaudio_widgets[w->type];
                module->op_devices |= GBAUDIO_DEVICE_OUT_SPEAKER;
                break;
        case snd_soc_dapm_hp:
                *dw = gbaudio_widgets[w->type];
                module->op_devices |= (GBAUDIO_DEVICE_OUT_WIRED_HEADSET
                                        | GBAUDIO_DEVICE_OUT_WIRED_HEADPHONE);
                module->ip_devices |= GBAUDIO_DEVICE_IN_WIRED_HEADSET;
                break;
        case snd_soc_dapm_mic:
                *dw = gbaudio_widgets[w->type];
                module->ip_devices |= GBAUDIO_DEVICE_IN_BUILTIN_MIC;
                break;
        case snd_soc_dapm_output:
        case snd_soc_dapm_input:
        case snd_soc_dapm_switch:
        case snd_soc_dapm_pga:
        case snd_soc_dapm_mixer:
        case snd_soc_dapm_mux:
                *dw = gbaudio_widgets[w->type];
                break;
        case snd_soc_dapm_aif_in:
        case snd_soc_dapm_aif_out:
                *dw = gbaudio_widgets[w->type];
                dw->sname = w->sname;
                break;
        default:
                ret = -EINVAL;
                goto error;
        }
        dw->name = w->name;

        dev_dbg(module->dev, "%s: widget of type %d created\n", dw->name,
                dw->id);
        return 0;
error:
        list_for_each_entry_safe(control, _control, &module->widget_ctl_list,
                                 list) {
                list_del(&control->list);
                devm_kfree(module->dev, control);
        }
        return ret;
}

static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module,
                                          struct gb_audio_control *controls)
{
        int i, csize, ret;
        struct snd_kcontrol_new *dapm_kctls;
        struct gb_audio_control *curr;
        struct gbaudio_control *control, *_control;
        size_t size;
        char temp_name[NAME_SIZE];

        size = sizeof(struct snd_kcontrol_new) * module->num_controls;
        dapm_kctls = devm_kzalloc(module->dev, size, GFP_KERNEL);
        if (!dapm_kctls)
                return -ENOMEM;

        curr = controls;
        for (i = 0; i < module->num_controls; i++) {
                ret = gbaudio_tplg_create_kcontrol(module, &dapm_kctls[i],
                                                   curr);
                if (ret) {
                        dev_err(module->dev, "%s:%d type not supported\n",
                                curr->name, curr->iface);
                        goto error;
                }
                control = devm_kzalloc(module->dev, sizeof(struct
                                                           gbaudio_control),
                                      GFP_KERNEL);
                if (!control) {
                        ret = -ENOMEM;
                        goto error;
                }
                control->id = curr->id;
                /* Prefix dev_id to widget_name */
                strscpy(temp_name, curr->name, sizeof(temp_name));
                snprintf(curr->name, sizeof(curr->name), "GB %d %s", module->dev_id,
                         temp_name);
                control->name = curr->name;
                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 += le16_to_cpu(gbenum->names_length);
                        control->texts = (const char * const *)
                                gb_generate_enum_strings(module, gbenum);
                        if (!control->texts) {
                                ret = -ENOMEM;
                                goto error;
                        }
                        control->items = le32_to_cpu(gbenum->items);
                } 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,
                        curr->name, curr->info.type);
                curr = (void *)curr + csize;
        }
        module->controls = dapm_kctls;

        return 0;
error:
        list_for_each_entry_safe(control, _control, &module->ctl_list,
                                 list) {
                list_del(&control->list);
                devm_kfree(module->dev, control);
        }
        devm_kfree(module->dev, dapm_kctls);
        return ret;
}

static int gbaudio_tplg_process_widgets(struct gbaudio_module_info *module,
                                        struct gb_audio_widget *widgets)
{
        int i, ret, w_size;
        struct snd_soc_dapm_widget *dapm_widgets;
        struct gb_audio_widget *curr;
        struct gbaudio_widget *widget, *_widget;
        size_t size;

        size = sizeof(struct snd_soc_dapm_widget) * module->num_dapm_widgets;
        dapm_widgets = devm_kzalloc(module->dev, size, GFP_KERNEL);
        if (!dapm_widgets)
                return -ENOMEM;

        curr = widgets;
        for (i = 0; i < module->num_dapm_widgets; i++) {
                ret = gbaudio_tplg_create_widget(module, &dapm_widgets[i],
                                                 curr, &w_size);
                if (ret) {
                        dev_err(module->dev, "%s:%d type not supported\n",
                                curr->name, curr->type);
                        goto error;
                }
                widget = devm_kzalloc(module->dev, sizeof(struct
                                                           gbaudio_widget),
                                      GFP_KERNEL);
                if (!widget) {
                        ret = -ENOMEM;
                        goto error;
                }
                widget->id = curr->id;
                widget->name = curr->name;
                list_add(&widget->list, &module->widget_list);
                curr = (void *)curr + w_size;
        }
        module->dapm_widgets = dapm_widgets;

        return 0;

error:
        list_for_each_entry_safe(widget, _widget, &module->widget_list,
                                 list) {
                list_del(&widget->list);
                devm_kfree(module->dev, widget);
        }
        devm_kfree(module->dev, dapm_widgets);
        return ret;
}

static int gbaudio_tplg_process_routes(struct gbaudio_module_info *module,
                                       struct gb_audio_route *routes)
{
        int i, ret;
        struct snd_soc_dapm_route *dapm_routes;
        struct gb_audio_route *curr;
        size_t size;

        size = sizeof(struct snd_soc_dapm_route) * module->num_dapm_routes;
        dapm_routes = devm_kzalloc(module->dev, size, GFP_KERNEL);
        if (!dapm_routes)
                return -ENOMEM;

        module->dapm_routes = dapm_routes;
        curr = routes;

        for (i = 0; i < module->num_dapm_routes; i++) {
                dapm_routes->sink =
                        gbaudio_map_widgetid(module, curr->destination_id);
                if (!dapm_routes->sink) {
                        dev_err(module->dev, "%d:%d:%d:%d - Invalid sink\n",
                                curr->source_id, curr->destination_id,
                                curr->control_id, curr->index);
                        ret = -EINVAL;
                        goto error;
                }
                dapm_routes->source =
                        gbaudio_map_widgetid(module, curr->source_id);
                if (!dapm_routes->source) {
                        dev_err(module->dev, "%d:%d:%d:%d - Invalid source\n",
                                curr->source_id, curr->destination_id,
                                curr->control_id, curr->index);
                        ret = -EINVAL;
                        goto error;
                }
                dapm_routes->control =
                        gbaudio_map_controlid(module,
                                              curr->control_id,
                                              curr->index);
                if ((curr->control_id !=  GBAUDIO_INVALID_ID) &&
                    !dapm_routes->control) {
                        dev_err(module->dev, "%d:%d:%d:%d - Invalid control\n",
                                curr->source_id, curr->destination_id,
                                curr->control_id, curr->index);
                        ret = -EINVAL;
                        goto error;
                }
                dev_dbg(module->dev, "Route {%s, %s, %s}\n", dapm_routes->sink,
                        (dapm_routes->control) ? dapm_routes->control : "NULL",
                        dapm_routes->source);
                dapm_routes++;
                curr++;
        }

        return 0;

error:
        devm_kfree(module->dev, module->dapm_routes);
        return ret;
}

static int gbaudio_tplg_process_header(struct gbaudio_module_info *module,
                                       struct gb_audio_topology *tplg_data)
{
        /* fetch no. of kcontrols, widgets & routes */
        module->num_controls = tplg_data->num_controls;
        module->num_dapm_widgets = tplg_data->num_widgets;
        module->num_dapm_routes = tplg_data->num_routes;

        /* update block offset */
        module->dai_offset = (unsigned long)&tplg_data->data;
        module->control_offset = module->dai_offset +
                                        le32_to_cpu(tplg_data->size_dais);
        module->widget_offset = module->control_offset +
                                        le32_to_cpu(tplg_data->size_controls);
        module->route_offset = module->widget_offset +
                                        le32_to_cpu(tplg_data->size_widgets);

        dev_dbg(module->dev, "DAI offset is 0x%lx\n", module->dai_offset);
        dev_dbg(module->dev, "control offset is %lx\n",
                module->control_offset);
        dev_dbg(module->dev, "widget offset is %lx\n", module->widget_offset);
        dev_dbg(module->dev, "route offset is %lx\n", module->route_offset);

        return 0;
}

int gbaudio_tplg_parse_data(struct gbaudio_module_info *module,
                            struct gb_audio_topology *tplg_data)
{
        int ret;
        struct gb_audio_control *controls;
        struct gb_audio_widget *widgets;
        struct gb_audio_route *routes;
        unsigned int jack_type;

        if (!tplg_data)
                return -EINVAL;

        ret = gbaudio_tplg_process_header(module, tplg_data);
        if (ret) {
                dev_err(module->dev, "%d: Error in parsing topology header\n",
                        ret);
                return ret;
        }

        /* process control */
        controls = (struct gb_audio_control *)module->control_offset;
        ret = gbaudio_tplg_process_kcontrols(module, controls);
        if (ret) {
                dev_err(module->dev,
                        "%d: Error in parsing controls data\n", ret);
                return ret;
        }
        dev_dbg(module->dev, "Control parsing finished\n");

        /* process widgets */
        widgets = (struct gb_audio_widget *)module->widget_offset;
        ret = gbaudio_tplg_process_widgets(module, widgets);
        if (ret) {
                dev_err(module->dev,
                        "%d: Error in parsing widgets data\n", ret);
                return ret;
        }
        dev_dbg(module->dev, "Widget parsing finished\n");

        /* process route */
        routes = (struct gb_audio_route *)module->route_offset;
        ret = gbaudio_tplg_process_routes(module, routes);
        if (ret) {
                dev_err(module->dev,
                        "%d: Error in parsing routes data\n", ret);
                return ret;
        }
        dev_dbg(module->dev, "Route parsing finished\n");

        /* parse jack capabilities */
        jack_type = le32_to_cpu(tplg_data->jack_type);
        if (jack_type) {
                module->jack_mask = jack_type & GBCODEC_JACK_MASK;
                module->button_mask = jack_type & GBCODEC_JACK_BUTTON_MASK;
        }

        return ret;
}

void gbaudio_tplg_release(struct gbaudio_module_info *module)
{
        struct gbaudio_control *control, *_control;
        struct gbaudio_widget *widget, *_widget;

        if (!module->topology)
                return;

        /* release kcontrols */
        list_for_each_entry_safe(control, _control, &module->ctl_list,
                                 list) {
                list_del(&control->list);
                devm_kfree(module->dev, control);
        }
        if (module->controls)
                devm_kfree(module->dev, module->controls);

        /* release widget controls */
        list_for_each_entry_safe(control, _control, &module->widget_ctl_list,
                                 list) {
                list_del(&control->list);
                devm_kfree(module->dev, control);
        }

        /* release widgets */
        list_for_each_entry_safe(widget, _widget, &module->widget_list,
                                 list) {
                list_del(&widget->list);
                devm_kfree(module->dev, widget);
        }
        if (module->dapm_widgets)
                devm_kfree(module->dev, module->dapm_widgets);

        /* release routes */
        if (module->dapm_routes)
                devm_kfree(module->dev, module->dapm_routes);
}