linux-stable/drivers/staging/greybus/audio_topology.c
Greg Kroah-Hartman 863dbc52e7 staging: greybus: Remove redundant license text
Now that the SPDX tag is in all greybus files, that identifies the
license in a specific and legally-defined manner.  So the extra GPL text
wording can be removed as it is no longer needed at all.

This is done on a quest to remove the 700+ different ways that files in
the kernel describe the GPL license text.  And there's unneeded stuff
like the address (sometimes incorrect) for the FSF which is never
needed.

No copyright headers or other non-license-description text was removed.

Cc: Vaibhav Hiremath <hvaibhav.linux@gmail.com>
Reviewed-by: Alex Elder <elder@linaro.org>
Acked-by: Vaibhav Agarwal <vaibhav.sr@gmail.com>
Acked-by: David Lin <dtwlin@gmail.com>
Acked-by: Johan Hovold <johan@kernel.org>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Acked-by: Mark Greer <mgreer@animalcreek.com>
Acked-by: Rui Miguel Silva <rmfrfs@gmail.com>
Acked-by: "Bryan O'Donoghue" <pure.logic@nexus-software.ie>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-11 14:46:21 +01:00

1448 lines
39 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Greybus audio driver
* Copyright 2015-2016 Google Inc.
* Copyright 2015-2016 Linaro Ltd.
*/
#include "audio_codec.h"
#include "greybus_protocols.h"
#define GBAUDIO_INVALID_ID 0xFF
/* mixer control */
struct gb_mixer_control {
int min, max;
unsigned int reg, rreg, shift, rshift, invert;
};
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, ret;
char begin[NAME_SIZE];
struct gbaudio_module_info *module;
if (!name)
return NULL;
ret = sscanf(name, "%s %d", begin, &dev_id);
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_kzalloc(gb->dev, sizeof(char *) * items, GFP_KERNEL);
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_codec *codec = snd_kcontrol_chip(kcontrol);
struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->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(codec->dev, "NULL info for %s\n", uinfo->id.name);
return -EINVAL;
}
/* update uinfo */
uinfo->access = data->access;
uinfo->count = data->vcount;
uinfo->type = (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);
strlcpy(uinfo->value.enumerated.name, name, NAME_SIZE);
break;
default:
dev_err(codec->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_codec *codec = snd_kcontrol_chip(kcontrol);
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
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);
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 */
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(codec->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_codec *codec = snd_kcontrol_chip(kcontrol);
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
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);
/* 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(codec->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(codec->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;
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
dev_dbg(codec->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;
/* 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", NAME_SIZE))
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 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 snd_soc_codec *codec = widget->codec;
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
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(widget->dapm->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 snd_soc_codec *codec = widget->codec;
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
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(widget->dapm->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;
/* update ucontrol */
if (gbvalue.value.integer_value[0] != val) {
for (wi = 0; wi < wlist->num_widgets; wi++) {
widget = wlist->widgets[wi];
widget->value = val;
widget->dapm->update = NULL;
snd_soc_dapm_mixer_update_power(widget, kcontrol,
connect);
}
gbvalue.value.integer_value[0] =
cpu_to_le32(ucontrol->value.integer.value[0]);
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(codec->dev,
"%d:Error in %s for %s\n", ret,
__func__, kcontrol->id.name);
return ret;
}
}
return 0;
}
#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_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);
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(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_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);
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->max - 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->max - 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(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 = le32_to_cpu(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", gbe->max,
le16_to_cpu(gb_enum->names_length));
for (i = 0; i < gbe->max; 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:
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 = 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;
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] = 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_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;
struct gb_bundle *bundle;
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;
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;
mask = e->mask << e->shift_l;
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_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];
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)
{
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 = le32_to_cpu(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", gbe->max,
le16_to_cpu(gb_enum->names_length));
for (i = 0; i < gbe->max; 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 = 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 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 snd_soc_codec *codec = w->codec;
struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec);
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 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, "Inavlid 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);
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 */
strlcpy(temp_name, w->name, NAME_SIZE);
snprintf(w->name, NAME_SIZE, "GB %d %s", module->dev_id, temp_name);
switch (w->type) {
case snd_soc_dapm_spk:
*dw = (struct snd_soc_dapm_widget)
SND_SOC_DAPM_SPK(w->name, gbcodec_event_spk);
module->op_devices |= GBAUDIO_DEVICE_OUT_SPEAKER;
break;
case snd_soc_dapm_hp:
*dw = (struct snd_soc_dapm_widget)
SND_SOC_DAPM_HP(w->name, gbcodec_event_hp);
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 = (struct snd_soc_dapm_widget)
SND_SOC_DAPM_MIC(w->name, gbcodec_event_int_mic);
module->ip_devices |= GBAUDIO_DEVICE_IN_BUILTIN_MIC;
break;
case snd_soc_dapm_output:
*dw = (struct snd_soc_dapm_widget)SND_SOC_DAPM_OUTPUT(w->name);
break;
case snd_soc_dapm_input:
*dw = (struct snd_soc_dapm_widget)SND_SOC_DAPM_INPUT(w->name);
break;
case snd_soc_dapm_switch:
*dw = (struct snd_soc_dapm_widget)
SND_SOC_DAPM_SWITCH_E(w->name, SND_SOC_NOPM, 0, 0,
widget_kctls, gbaudio_widget_event,
SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMD);
break;
case snd_soc_dapm_pga:
*dw = (struct snd_soc_dapm_widget)
SND_SOC_DAPM_PGA_E(w->name, SND_SOC_NOPM, 0, 0, NULL, 0,
gbaudio_widget_event,
SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMD);
break;
case snd_soc_dapm_mixer:
*dw = (struct snd_soc_dapm_widget)
SND_SOC_DAPM_MIXER_E(w->name, SND_SOC_NOPM, 0, 0, NULL,
0, gbaudio_widget_event,
SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMD);
break;
case snd_soc_dapm_mux:
*dw = (struct snd_soc_dapm_widget)
SND_SOC_DAPM_MUX_E(w->name, SND_SOC_NOPM, 0, 0,
widget_kctls, gbaudio_widget_event,
SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMD);
break;
case snd_soc_dapm_aif_in:
*dw = (struct snd_soc_dapm_widget)
SND_SOC_DAPM_AIF_IN_E(w->name, w->sname, 0,
SND_SOC_NOPM,
0, 0, gbaudio_widget_event,
SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMD);
break;
case snd_soc_dapm_aif_out:
*dw = (struct snd_soc_dapm_widget)
SND_SOC_DAPM_AIF_OUT_E(w->name, w->sname, 0,
SND_SOC_NOPM,
0, 0, gbaudio_widget_event,
SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMD);
break;
default:
ret = -EINVAL;
goto error;
}
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 */
strlcpy(temp_name, curr->name, NAME_SIZE);
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) {
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);
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);
}