summaryrefslogtreecommitdiffstats
path: root/drivers/staging/greybus/audio_codec.c
diff options
context:
space:
mode:
authorVaibhav Agarwal <vaibhav.agarwal@linaro.org>2016-01-13 14:07:51 -0700
committerGreg Kroah-Hartman <gregkh@google.com>2016-01-13 16:41:36 -0800
commit6339d2322c47f4b8ebabf9daf0130328ed72648b (patch)
treeadc601e095d625334fdf81fc38508dd73c9ed631 /drivers/staging/greybus/audio_codec.c
parent2a70e49f9183d72287e84ac4d6a4080e3f2a6475 (diff)
downloadop-kernel-dev-6339d2322c47f4b8ebabf9daf0130328ed72648b.zip
op-kernel-dev-6339d2322c47f4b8ebabf9daf0130328ed72648b.tar.gz
greybus: audio: Add topology parser for GB codec
For each GB codec module inserted, DAPM widgets, kcontrols, routes and DAIs can be fetched through greybus in a binary chunk and parsed locally to create & populate DAPM graph for the specific module. It is required by each codec module to populate a minimum set of kcontrols with fixed names to support basic audio usecase. To support advanced features of codec module, the same can be polpulated with existing topology parser. However, to use them for different usecase separate mechanism (may be via MSP) is required to inform userspace about their configuration value & enable/disable sequence. ToDos: Currently, support for enumerated kcontrol/dapm control is hardcoded. Need to add complete logic within the parser. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Diffstat (limited to 'drivers/staging/greybus/audio_codec.c')
-rw-r--r--drivers/staging/greybus/audio_codec.c254
1 files changed, 45 insertions, 209 deletions
diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c
index fd94042..239a9b6 100644
--- a/drivers/staging/greybus/audio_codec.c
+++ b/drivers/staging/greybus/audio_codec.c
@@ -19,124 +19,6 @@
static DEFINE_MUTEX(gb_codec_list_lock);
static LIST_HEAD(gb_codec_list);
-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 const struct snd_kcontrol_new gbcodec_snd_controls[] = {
- SOC_DOUBLE("Playback Mute", GBCODEC_MUTE_REG, 0, 1, 1, 1),
- SOC_DOUBLE("Capture Mute", GBCODEC_MUTE_REG, 4, 5, 1, 1),
- SOC_DOUBLE_R("Playback Volume", GBCODEC_PB_LVOL_REG,
- GBCODEC_PB_RVOL_REG, 0, 127, 0),
- SOC_DOUBLE_R("Capture Volume", GBCODEC_CAP_LVOL_REG,
- GBCODEC_CAP_RVOL_REG, 0, 127, 0),
-};
-
-static const struct snd_kcontrol_new spk_amp_ctl =
- SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 0, 1, 0);
-
-static const struct snd_kcontrol_new hp_amp_ctl =
- SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 1, 1, 0);
-
-static const struct snd_kcontrol_new mic_adc_ctl =
- SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 4, 1, 0);
-
-/* APB1-GBSPK source */
-static const char * const gbcodec_apb1_src[] = {"Stereo", "Left", "Right"};
-
-static const SOC_ENUM_SINGLE_DECL(
- gbcodec_apb1_rx_enum, GBCODEC_APB1_MUX_REG, 0, gbcodec_apb1_src);
-
-static const struct snd_kcontrol_new gbcodec_apb1_rx_mux =
- SOC_DAPM_ENUM("APB1 source", gbcodec_apb1_rx_enum);
-
-static const SOC_ENUM_SINGLE_DECL(
- gbcodec_mic_enum, GBCODEC_APB1_MUX_REG, 4, gbcodec_apb1_src);
-
-static const struct snd_kcontrol_new gbcodec_mic_mux =
- SOC_DAPM_ENUM("MIC source", gbcodec_mic_enum);
-
-static const struct snd_soc_dapm_widget gbcodec_dapm_widgets[] = {
- SND_SOC_DAPM_SPK("Spk", gbcodec_event_spk),
- SND_SOC_DAPM_SPK("HP", gbcodec_event_hp),
- SND_SOC_DAPM_MIC("Int Mic", gbcodec_event_int_mic),
-
- SND_SOC_DAPM_OUTPUT("SPKOUT"),
- SND_SOC_DAPM_OUTPUT("HPOUT"),
-
- SND_SOC_DAPM_INPUT("MIC"),
- SND_SOC_DAPM_INPUT("HSMIC"),
-
- SND_SOC_DAPM_SWITCH("SPK Amp", SND_SOC_NOPM, 0, 0, &spk_amp_ctl),
- SND_SOC_DAPM_SWITCH("HP Amp", SND_SOC_NOPM, 0, 0, &hp_amp_ctl),
- SND_SOC_DAPM_SWITCH("MIC ADC", SND_SOC_NOPM, 0, 0, &mic_adc_ctl),
-
- SND_SOC_DAPM_PGA("SPK DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("HP DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
-
- SND_SOC_DAPM_MIXER("SPK Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("APB1_TX Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
-
- SND_SOC_DAPM_MUX("APB1_RX Mux", SND_SOC_NOPM, 0, 0,
- &gbcodec_apb1_rx_mux),
- SND_SOC_DAPM_MUX("MIC Mux", SND_SOC_NOPM, 0, 0, &gbcodec_mic_mux),
-
- SND_SOC_DAPM_AIF_IN("APB1RX", "APBridgeA1 Playback", 0, SND_SOC_NOPM, 0,
- 0),
- SND_SOC_DAPM_AIF_OUT("APB1TX", "APBridgeA1 Capture", 0, SND_SOC_NOPM, 0,
- 0),
-};
-
-static const struct snd_soc_dapm_route gbcodec_dapm_routes[] = {
- /* Playback path */
- {"Spk", NULL, "SPKOUT"},
- {"SPKOUT", NULL, "SPK Amp"},
- {"SPK Amp", "Switch", "SPK DAC"},
- {"SPK DAC", NULL, "SPK Mixer"},
-
- {"HP", NULL, "HPOUT"},
- {"HPOUT", NULL, "HP Amp"},
- {"HP Amp", "Switch", "HP DAC"},
- {"HP DAC", NULL, "HP Mixer"},
-
- {"SPK Mixer", NULL, "APB1_RX Mux"},
- {"HP Mixer", NULL, "APB1_RX Mux"},
-
- {"APB1_RX Mux", "Left", "APB1RX"},
- {"APB1_RX Mux", "Right", "APB1RX"},
- {"APB1_RX Mux", "Stereo", "APB1RX"},
-
- /* Capture path */
- {"MIC", NULL, "Int Mic"},
- {"MIC", NULL, "MIC Mux"},
- {"MIC Mux", "Left", "MIC ADC"},
- {"MIC Mux", "Right", "MIC ADC"},
- {"MIC Mux", "Stereo", "MIC ADC"},
- {"MIC ADC", "Switch", "APB1_TX Mixer"},
- {"APB1_TX Mixer", NULL, "APB1TX"}
-};
-
static int gbcodec_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -180,24 +62,9 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = {
.digital_mute = gbcodec_digital_mute,
};
-static struct snd_soc_dai_driver gbcodec_dai = {
- .playback = {
- .stream_name = "APBridgeA1 Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "APBridgeA1 Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &gbcodec_dai_ops,
-};
-
+/*
+ * codec driver ops
+ */
static int gbcodec_probe(struct snd_soc_codec *codec)
{
/* Empty function for now */
@@ -261,13 +128,6 @@ static struct snd_soc_codec_driver soc_codec_dev_gbcodec = {
.reg_word_size = 1,
.idle_bias_off = true,
-
- .controls = gbcodec_snd_controls,
- .num_controls = ARRAY_SIZE(gbcodec_snd_controls),
- .dapm_widgets = gbcodec_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(gbcodec_dapm_widgets),
- .dapm_routes = gbcodec_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(gbcodec_dapm_routes),
};
/*
@@ -369,6 +229,9 @@ static struct gbaudio_codec_info *gbaudio_get_codec(struct device *dev,
mutex_init(&gbcodec->lock);
INIT_LIST_HEAD(&gbcodec->dai_list);
+ INIT_LIST_HEAD(&gbcodec->widget_list);
+ INIT_LIST_HEAD(&gbcodec->codec_ctl_list);
+ INIT_LIST_HEAD(&gbcodec->widget_ctl_list);
gbcodec->dev_id = dev_id;
dev_set_drvdata(dev, gbcodec);
gbcodec->dev = dev;
@@ -412,8 +275,9 @@ struct device_driver gb_codec_driver = {
static int gbaudio_codec_probe(struct gb_connection *connection)
{
- int ret;
+ int ret, i;
struct gbaudio_codec_info *gbcodec;
+ struct gb_audio_topology *topology;
struct device *dev = &connection->bundle->dev;
int dev_id = connection->bundle->id;
@@ -425,8 +289,35 @@ static int gbaudio_codec_probe(struct gb_connection *connection)
gbcodec->mgmt_connection = connection;
+ /* fetch topology data */
+ ret = gb_audio_gb_get_topology(connection, &topology);
+ if (ret) {
+ dev_err(gbcodec->dev,
+ "%d:Error while fetching topology\n", ret);
+ goto base_error;
+ }
+
+ /* process topology data */
+ ret = gbaudio_tplg_parse_data(gbcodec, topology);
+ if (ret) {
+ dev_err(dev, "%d:Error while parsing topology data\n",
+ ret);
+ goto topology_error;
+ }
+ gbcodec->topology = topology;
+
+ /* update codec info */
+ soc_codec_dev_gbcodec.controls = gbcodec->kctls;
+ soc_codec_dev_gbcodec.num_controls = gbcodec->num_kcontrols;
+ soc_codec_dev_gbcodec.dapm_widgets = gbcodec->widgets;
+ soc_codec_dev_gbcodec.num_dapm_widgets = gbcodec->num_dapm_widgets;
+ soc_codec_dev_gbcodec.dapm_routes = gbcodec->routes;
+ soc_codec_dev_gbcodec.num_dapm_routes = gbcodec->num_dapm_routes;
+
/* update DAI info */
- gbcodec->dais = &gbcodec_dai;
+ for (i = 0; i < gbcodec->num_dais; i++)
+ gbcodec->dais[i].ops = &gbcodec_dai_ops;
+
/* FIXME */
dev->driver = &gb_codec_driver;
@@ -435,7 +326,7 @@ static int gbaudio_codec_probe(struct gb_connection *connection)
gbcodec->dais, 1);
if (ret) {
dev_err(dev, "%d:Failed to register codec\n", ret);
- goto base_error;
+ goto parse_error;
}
/* update DAI links in response to this codec */
@@ -455,8 +346,13 @@ static int gbaudio_codec_probe(struct gb_connection *connection)
codec_reg_error:
snd_soc_unregister_codec(dev);
-base_error:
dev->driver = NULL;
+parse_error:
+ gbaudio_tplg_release(gbcodec);
+ gbcodec->topology = NULL;
+topology_error:
+ kfree(topology);
+base_error:
gbcodec->mgmt_connection = NULL;
return ret;
}
@@ -480,6 +376,8 @@ static void gbaudio_codec_remove(struct gb_connection *connection)
snd_soc_unregister_codec(dev);
dev->driver = NULL;
+ gbaudio_tplg_release(gbcodec);
+ kfree(gbcodec->topology);
gbcodec->mgmt_connection = NULL;
mutex_lock(&gbcodec->lock);
gbcodec->codec_registered = 0;
@@ -509,68 +407,6 @@ static struct gb_protocol gb_audio_mgmt_protocol = {
.request_recv = gbaudio_codec_report_event_recv,
};
-static struct gbaudio_dai *gbaudio_allocate_dai(struct gbaudio_codec_info *gb,
- int data_cport,
- struct gb_connection *connection,
- const char *name)
-{
- struct gbaudio_dai *dai;
-
- mutex_lock(&gb->lock);
- dai = devm_kzalloc(gb->dev, sizeof(*dai), GFP_KERNEL);
- if (!dai) {
- dev_err(gb->dev, "%s:DAI Malloc failure\n", name);
- mutex_unlock(&gb->lock);
- return NULL;
- }
-
- dai->data_cport = data_cport;
- dai->connection = connection;
-
- /* update name */
- if (name)
- strlcpy(dai->name, name, NAME_SIZE);
- list_add(&dai->list, &gb->dai_list);
- dev_dbg(gb->dev, "%d:%s: DAI added\n", data_cport, dai->name);
- mutex_unlock(&gb->lock);
-
- return dai;
-}
-
-struct gbaudio_dai *gbaudio_add_dai(struct gbaudio_codec_info *gbcodec,
- int data_cport,
- struct gb_connection *connection,
- const char *name)
-{
- struct gbaudio_dai *dai, *_dai;
-
- /* FIXME need to take care for multiple DAIs */
- mutex_lock(&gbcodec->lock);
- if (list_empty(&gbcodec->dai_list)) {
- mutex_unlock(&gbcodec->lock);
- return gbaudio_allocate_dai(gbcodec, data_cport, connection,
- name);
- }
-
- list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) {
- if (dai->data_cport == data_cport) {
- if (connection)
- dai->connection = connection;
-
- if (name)
- strlcpy(dai->name, name, NAME_SIZE);
- dev_dbg(gbcodec->dev, "%d:%s: DAI updated\n",
- data_cport, dai->name);
- mutex_unlock(&gbcodec->lock);
- return dai;
- }
- }
-
- dev_err(gbcodec->dev, "%s:DAI not found\n", name);
- mutex_unlock(&gbcodec->lock);
- return NULL;
-}
-
static int gbaudio_dai_probe(struct gb_connection *connection)
{
struct gbaudio_dai *dai;
OpenPOWER on IntegriCloud