/*
- * Greybus audio driver
- * Copyright 2015 Google Inc.
- * Copyright 2015 Linaro Ltd.
+ * audio codec driver
+ * Copyright 2016 Google Inc.
+ * Copyright 2016 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#include <linux/module.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
-#include <sound/msm-dynamic-dailink.h>
#include "audio_codec.h"
#include "audio_apbridgea.h"
#include "audio_manager.h"
-static DEFINE_MUTEX(gb_codec_list_lock);
-static LIST_HEAD(gb_codec_list);
+static struct gbaudio_codec_info *gbcodec;
-struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec,
- int data_cport, const char *name)
+struct gbaudio_data_connection *find_data(struct gbaudio_module_info *module,
+ const char *name)
{
- struct gbaudio_dai *dai;
+ struct gbaudio_data_connection *data;
- list_for_each_entry(dai, &gbcodec->dai_list, list) {
- if (name && !strncmp(dai->name, name, NAME_SIZE))
- return dai;
- if ((data_cport != -1) && (dai->data_cport == data_cport))
- return dai;
+ list_for_each_entry(data, &module->data_list, list) {
+ if (name && !strncmp(data->name, name, NAME_SIZE))
+ return data;
}
return NULL;
}
-/*
- * codec DAI ops
- */
-static int gbcodec_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int find_stream(const char *name)
{
- int ret;
- __u16 i2s_port, cportid;
+ int stream = 0;
+
+ if (strnstr(name, "SPK Amp", NAME_SIZE))
+ stream |= GB_PLAYBACK;
+
+ return stream;
+}
+
+static int gbaudio_module_disable(struct gbaudio_codec_info *codec,
+ struct gbaudio_module_info *module,
+ int dir)
+{
+ int ret = 0;
+ uint16_t data_cport, cportid, i2s_port;
+ int codec_state, module_state;
+ struct gbaudio_data_connection *data;
+ const char *dai_name;
+
+ mutex_lock(&codec->lock);
+
+ codec_state = codec->stream[dir].state;
+ if (codec_state == GBAUDIO_CODEC_SHUTDOWN) {
+ mutex_unlock(&codec->lock);
+ return 0;
+ }
+
+ dai_name = codec->stream[dir].dai_name;
- struct gbaudio_dai *gb_dai;
- struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
+ mutex_lock(&module->lock);
+ module_state = module->ctrlstate[dir];
+ if (module_state == GBAUDIO_CODEC_SHUTDOWN) {
+ dev_dbg(codec->dev, "%s: module already configured\n",
+ module->name);
+ goto func_exit;
+ }
- if (!atomic_read(&gb->is_connected))
+ /* find the dai */
+ data = find_data(module, dai_name);
+ if (!data) {
+ dev_err(codec->dev, "%s:%s DATA connection missing\n",
+ dai_name, module->name);
+ mutex_unlock(&module->lock);
+ mutex_unlock(&codec->lock);
return -ENODEV;
+ }
+ if (codec_state > GBAUDIO_CODEC_HWPARAMS) {
+ data_cport = data->connection->intf_cport_id;
+ ret = gb_audio_gb_deactivate_tx(module->mgmt_connection,
+ data_cport);
+ if (ret) {
+ dev_err(codec->dev, "deactivate_tx for %s failed:%d\n",
+ module->name, ret);
+ goto func_exit;
+ }
+ dev_dbg(codec->dev, "Dynamic deactivate %s:%d DAI\n", dai_name,
+ data_cport);
+ }
+ if (codec_state > GBAUDIO_CODEC_SHUTDOWN) {
+ cportid = data->connection->hd_cport_id;
+ ret = gb_audio_apbridgea_unregister_cport(data->connection,
+ i2s_port, cportid,
+ AUDIO_APBRIDGEA_DIRECTION_TX);
+ if (ret) {
+ dev_err(codec->dev, "unregister_cport for %s failed:%d\n",
+ module->name, ret);
+ goto func_exit;
+ }
+ dev_dbg(codec->dev, "Dynamic Unregister %s:%d DAI\n", dai_name,
+ cportid);
+ }
+ module->ctrlstate[dir] = GBAUDIO_CODEC_SHUTDOWN;
+
+func_exit:
+ mutex_unlock(&module->lock);
+ mutex_unlock(&codec->lock);
+ return ret;
+}
+
+static int gbaudio_module_enable(struct gbaudio_codec_info *codec,
+ struct gbaudio_module_info *module, int dir)
+{
+ int ret = 0;
+ __u16 i2s_port, cportid;
+ int codec_state, module_state;
+ uint16_t data_cport;
+ uint8_t sig_bits, channels;
+ uint32_t format, rate;
+ struct gbaudio_data_connection *data;
+ const char *dai_name;
+
+ mutex_lock(&codec->lock);
+
+ codec_state = codec->stream[dir].state;
+ if (codec_state == GBAUDIO_CODEC_SHUTDOWN) {
+ mutex_unlock(&codec->lock);
+ return 0;
+ }
+
+ dai_name = codec->stream[dir].dai_name;
+ format = codec->stream[dir].format;
+ channels = codec->stream[dir].channels;
+ rate = codec->stream[dir].rate;
+ sig_bits = codec->stream[dir].sig_bits;
+
+ mutex_lock(&module->lock);
+ module_state = module->ctrlstate[dir];
+ if (module_state == codec_state) {
+ dev_dbg(codec->dev, "%s: module already configured\n",
+ module->name);
+ goto func_exit;
+ }
/* find the dai */
- mutex_lock(&gb->lock);
- gb_dai = gbaudio_find_dai(gb, -1, dai->name);
- if (!gb_dai) {
- dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
- mutex_unlock(&gb->lock);
- return -EINVAL;
+ data = find_data(module, dai_name);
+ if (!data) {
+ dev_err(codec->dev, "%s:%s DATA connection missing\n",
+ dai_name, module->name);
+ mutex_unlock(&module->lock);
+ mutex_unlock(&codec->lock);
+ return -ENODEV;
}
/* register cport */
- i2s_port = 0; /* fixed for now */
- cportid = gb_dai->connection->hd_cport_id;
- ret = gb_audio_apbridgea_register_cport(gb_dai->connection, i2s_port,
- cportid,
+ if (module_state < codec_state) {
+ i2s_port = 0; /* fixed for now */
+ cportid = data->connection->hd_cport_id;
+ ret = gb_audio_apbridgea_register_cport(data->connection,
+ i2s_port, cportid,
AUDIO_APBRIDGEA_DIRECTION_TX);
- dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name, cportid,
- ret);
+ if (ret) {
+ dev_err(codec->dev, "reg_cport for %s\n", module->name);
+ goto func_exit;
+ }
+ module_state = GBAUDIO_CODEC_STARTUP;
+ dev_dbg(codec->dev, "Dynamic Register %s:%d DAI\n", dai_name,
+ cportid);
+ }
+
+ /* hw_params */
+ if (module_state < codec_state) {
+ data_cport = data->connection->intf_cport_id;
+ ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport,
+ format, rate, channels, sig_bits);
+ if (ret) {
+ dev_err(codec->dev, "set_pcm for %s\n", module->name);
+ goto func_exit;
+ }
+ module_state = GBAUDIO_CODEC_HWPARAMS;
+ dev_dbg(codec->dev, "Dynamic hw_params %s:%d DAI\n", dai_name,
+ data_cport);
+ }
- if (!ret)
- atomic_inc(&gb_dai->users);
- mutex_unlock(&gb->lock);
+ /* prepare */
+ if (module_state < codec_state) {
+ data_cport = data->connection->intf_cport_id;
+ ret = gb_audio_gb_set_tx_data_size(module->mgmt_connection,
+ data_cport, 192);
+ if (ret) {
+ dev_err(codec->dev, "set_tx_data_size for %s\n",
+ module->name);
+ goto func_exit;
+ }
+ ret = gb_audio_gb_activate_tx(module->mgmt_connection,
+ data_cport);
+ if (ret) {
+ dev_err(codec->dev, "activate_tx for %s\n",
+ module->name);
+ goto func_exit;
+ }
+ module_state = GBAUDIO_CODEC_PREPARE;
+ dev_dbg(codec->dev, "Dynamic prepare %s:%d DAI\n", dai_name,
+ data_cport);
+ }
+func_exit:
+ module->ctrlstate[dir] = module_state;
+ mutex_unlock(&module->lock);
+ mutex_unlock(&codec->lock);
return ret;
}
-static void gbcodec_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+int gbaudio_module_update(struct gbaudio_codec_info *codec,
+ const char *w_name,
+ struct gbaudio_module_info *module, int enable)
{
- int ret;
- __u16 i2s_port, cportid;
+ int stream, ret = 0;
+ int pb_state;
- struct gbaudio_dai *gb_dai;
- struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
+ dev_dbg(module->dev, "Module update %s sequence\n",
+ enable ? "Enable":"Disable");
- /* find the dai */
- gb_dai = gbaudio_find_dai(gb, -1, dai->name);
- if (!gb_dai) {
- dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
- goto func_exit;
+ stream = find_stream(w_name);
+ if (!stream) {
+ dev_dbg(codec->dev, "No action required for %s\n", w_name);
+ return 0;
}
- atomic_dec(&gb_dai->users);
+ /* check if playback active */
+ pb_state = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].state;
+ if ((stream & GB_PLAYBACK) && (pb_state > GBAUDIO_CODEC_SHUTDOWN)) {
+ if (enable)
+ ret = gbaudio_module_enable(codec, module,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ else
+ ret = gbaudio_module_disable(codec, module,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
- if (!atomic_read(&gb->is_connected)) {
- if (!atomic_read(&gb_dai->users))
- wake_up_interruptible(&gb_dai->wait_queue);
- return;
+ /* TBD
+ * check if capture active
+ * cap_state = codec->stream[SNDRV_PCM_STREAM_CAPTURE].state;
+ * if ((stream & GB_CAPTURE) && (cap_state > GBAUDIO_CODEC_SHUTDOWN))
+ *
+ */
+
+ return ret;
+}
+EXPORT_SYMBOL(gbaudio_module_update);
+
+/*
+ * codec DAI ops
+ */
+static int gbcodec_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+ __u16 i2s_port, cportid;
+ int state;
+ struct gbaudio_data_connection *data;
+ struct gbaudio_module_info *module;
+ struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
+
+ mutex_lock(&codec->lock);
+
+ if (list_empty(&codec->module_list)) {
+ dev_err(codec->dev, "No codec module available\n");
+ mutex_unlock(&codec->lock);
+ return -ENODEV;
}
- mutex_lock(&gb->lock);
- /* deactivate rx/tx */
- cportid = gb_dai->connection->intf_cport_id;
+ state = codec->stream[substream->stream].state;
+ list_for_each_entry(module, &codec->module_list, list) {
+ mutex_lock(&module->lock);
+ if (!module->is_connected) {
+ mutex_unlock(&module->lock);
+ continue;
+ }
- switch (substream->stream) {
- case SNDRV_PCM_STREAM_CAPTURE:
- ret = gb_audio_gb_deactivate_rx(gb->mgmt_connection, cportid);
- break;
- case SNDRV_PCM_STREAM_PLAYBACK:
- ret = gb_audio_gb_deactivate_tx(gb->mgmt_connection, cportid);
- break;
- default:
- dev_err(dai->dev, "Invalid stream type during shutdown\n");
- goto func_exit;
+ /* find the dai */
+ data = find_data(module, dai->name);
+ if (!data) {
+ dev_err(dai->dev, "%s:%s DATA connection missing\n",
+ dai->name, module->name);
+ mutex_unlock(&module->lock);
+ continue;
+ }
+
+ /* register cport */
+ i2s_port = 0; /* fixed for now */
+ cportid = data->connection->hd_cport_id;
+ ret = gb_audio_apbridgea_register_cport(data->connection,
+ i2s_port, cportid,
+ AUDIO_APBRIDGEA_DIRECTION_TX);
+ dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name,
+ cportid, ret);
+ state = GBAUDIO_CODEC_STARTUP;
+ module->ctrlstate[substream->stream] = state;
+ dev_dbg(dai->dev, "%s: state:%d\n", module->name, state);
+ mutex_unlock(&module->lock);
}
+ codec->stream[substream->stream].state = state;
+ codec->stream[substream->stream].dai_name = dai->name;
+ mutex_unlock(&codec->lock);
- if (ret)
- dev_err(dai->dev, "%d:Error during deactivate\n", ret);
+ return ret;
+}
- /* un register cport */
- i2s_port = 0; /* fixed for now */
- ret = gb_audio_apbridgea_unregister_cport(gb_dai->connection, i2s_port,
- gb_dai->connection->hd_cport_id,
- AUDIO_APBRIDGEA_DIRECTION_TX);
+static void gbcodec_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+ __u16 i2s_port, cportid;
+ int state, module_state;
+ struct gbaudio_module_info *module;
+ struct gbaudio_data_connection *data;
+ struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
- dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name,
- gb_dai->connection->hd_cport_id, ret);
-func_exit:
- mutex_unlock(&gb->lock);
+ mutex_lock(&codec->lock);
- /*
- if (!atomic_read(&gb_dai->users))
- wake_up_interruptible(&gb_dai->wait_queue);
- */
+ if (list_empty(&codec->module_list)) {
+ dev_err(codec->dev, "No codec module available\n");
+ mutex_unlock(&codec->lock);
+ return;
+ }
+
+ state = codec->stream[substream->stream].state;
+ list_for_each_entry(module, &codec->module_list, list) {
+ mutex_lock(&module->lock);
+ if (!module->is_connected) {
+ dev_err(dai->dev, "%s:%s module not connected\n",
+ __func__, module->name);
+ mutex_unlock(&module->lock);
+ continue;
+ }
+ module_state = module->ctrlstate[substream->stream];
+ if (module_state == GBAUDIO_CODEC_SHUTDOWN) {
+ dev_dbg(codec->dev, "%s: module already configured\n",
+ module->name);
+ mutex_unlock(&module->lock);
+ continue;
+ }
+
+ /* find the dai */
+ data = find_data(module, dai->name);
+ if (!data) {
+ dev_err(dai->dev, "%s:%s DATA connection missing\n",
+ dai->name, module->name);
+ mutex_unlock(&module->lock);
+ continue;
+ }
+ /* deactivate */
+ cportid = data->connection->intf_cport_id;
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_CAPTURE:
+ ret = gb_audio_gb_deactivate_rx(module->mgmt_connection,
+ cportid);
+ /* unregister cport */
+ i2s_port = 0; /* fixed for now */
+ cportid = data->connection->hd_cport_id;
+ ret = gb_audio_apbridgea_unregister_cport(
+ data->connection, i2s_port, cportid,
+ AUDIO_APBRIDGEA_DIRECTION_RX);
+ break;
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = gb_audio_gb_deactivate_tx(module->mgmt_connection,
+ cportid);
+ /* unregister cport */
+ i2s_port = 0; /* fixed for now */
+ cportid = data->connection->hd_cport_id;
+ ret = gb_audio_apbridgea_unregister_cport(
+ data->connection, i2s_port, cportid,
+ AUDIO_APBRIDGEA_DIRECTION_TX);
+ break;
+ }
+ dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name,
+ cportid, ret);
+ state = GBAUDIO_CODEC_SHUTDOWN;
+ module->ctrlstate[substream->stream] = state;
+ dev_dbg(dai->dev, "%s: state:%d\n", module->name, state);
+ mutex_unlock(&module->lock);
+ }
+ codec->stream[substream->stream].state = state;
+ codec->stream[substream->stream].dai_name = NULL;
+ mutex_unlock(&codec->lock);
return;
}
uint8_t sig_bits, channels;
uint32_t format, rate;
uint16_t data_cport;
- struct gbaudio_dai *gb_dai;
- struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
+ struct gbaudio_module_info *module;
+ struct gbaudio_data_connection *data;
+ int state;
+ struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
- if (!atomic_read(&gb->is_connected))
- return -ENODEV;
+ mutex_lock(&codec->lock);
- /* find the dai */
- mutex_lock(&gb->lock);
- gb_dai = gbaudio_find_dai(gb, -1, dai->name);
- if (!gb_dai) {
- dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
- ret = -EINVAL;
- goto func_exit;
+ if (list_empty(&codec->module_list)) {
+ dev_err(codec->dev, "No codec module available\n");
+ mutex_unlock(&codec->lock);
+ return -ENODEV;
}
/*
if (params_channels(hwparams) != 2) {
dev_err(dai->dev, "Invalid channel count:%d\n",
params_channels(hwparams));
- ret = -EINVAL;
- goto func_exit;
+ mutex_unlock(&codec->lock);
+ return -EINVAL;
}
channels = params_channels(hwparams);
if (params_rate(hwparams) != 48000) {
dev_err(dai->dev, "Invalid sampling rate:%d\n",
params_rate(hwparams));
- ret = -EINVAL;
- goto func_exit;
+ mutex_unlock(&codec->lock);
+ return -EINVAL;
}
rate = GB_AUDIO_PCM_RATE_48000;
if (params_format(hwparams) != SNDRV_PCM_FORMAT_S16_LE) {
dev_err(dai->dev, "Invalid format:%d\n",
params_format(hwparams));
- ret = -EINVAL;
- goto func_exit;
+ mutex_unlock(&codec->lock);
+ return -EINVAL;
}
format = GB_AUDIO_PCM_FMT_S16_LE;
- data_cport = gb_dai->connection->intf_cport_id;
- /* XXX check impact of sig_bit
- * it should not change ideally
- */
+ state = codec->stream[substream->stream].state;
+ list_for_each_entry(module, &codec->module_list, list) {
+ mutex_lock(&module->lock);
+ if (!module->is_connected) {
+ dev_err(dai->dev, "%s:%s module not connected\n",
+ __func__, module->name);
+ ret = -ENODEV;
+ mutex_unlock(&module->lock);
+ continue;
+ }
- dev_dbg(dai->dev, "cport:%d, rate:%d, channel %d, format %d, sig_bits:%d\n",
- data_cport, rate, channels, format, sig_bits);
- ret = gb_audio_gb_set_pcm(gb->mgmt_connection, data_cport, format,
- rate, channels, sig_bits);
- if (ret) {
- dev_err(dai->dev, "%d: Error during set_pcm\n", ret);
- goto func_exit;
+ /* find the data connection */
+ data = find_data(module, dai->name);
+ if (!data) {
+ dev_err(dai->dev, "%s:%s DATA connection missing\n",
+ dai->name, module->name);
+ mutex_unlock(&module->lock);
+ continue;
+ }
+
+ data_cport = data->connection->intf_cport_id;
+ /* XXX check impact of sig_bit
+ * it should not change ideally
+ */
+ dev_dbg(dai->dev,
+ "cport:%d, rate:%d, channel %d, format %d, sig_bits:%d\n",
+ data_cport, rate, channels, format, sig_bits);
+ ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport,
+ format, rate, channels, sig_bits);
+ if (ret) {
+ dev_err(dai->dev, "%d: Error during set_pcm\n", ret);
+ mutex_unlock(&module->lock);
+ goto func_exit;
+ }
+ if (state < GBAUDIO_CODEC_HWPARAMS) {
+ ret = gb_audio_apbridgea_set_config(data->connection, 0,
+ AUDIO_APBRIDGEA_PCM_FMT_16,
+ AUDIO_APBRIDGEA_PCM_RATE_48000,
+ 6144000);
+ if (ret) {
+ dev_err(dai->dev,
+ "%d: Error during set_config\n", ret);
+ mutex_unlock(&module->lock);
+ goto func_exit;
+ }
+ }
+ state = GBAUDIO_CODEC_HWPARAMS;
+ module->ctrlstate[substream->stream] = state;
+ dev_dbg(dai->dev, "%s: state:%d\n", module->name, state);
+ mutex_unlock(&module->lock);
}
+ codec->stream[substream->stream].state = state;
+ codec->stream[substream->stream].format = format;
+ codec->stream[substream->stream].rate = rate;
+ codec->stream[substream->stream].channels = channels;
+ codec->stream[substream->stream].sig_bits = sig_bits;
- /*
- * XXX need to check if
- * set config is always required
- * check for mclk_freq as well
- */
- ret = gb_audio_apbridgea_set_config(gb_dai->connection, 0,
- AUDIO_APBRIDGEA_PCM_FMT_16,
- AUDIO_APBRIDGEA_PCM_RATE_48000,
- 6144000);
- if (ret)
- dev_err(dai->dev, "%d: Error during set_config\n", ret);
func_exit:
- mutex_unlock(&gb->lock);
+ mutex_unlock(&codec->lock);
return ret;
}
{
int ret;
uint16_t data_cport;
- struct gbaudio_dai *gb_dai;
- struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
+ struct gbaudio_data_connection *data;
+ struct gbaudio_module_info *module;
+ int state;
+ struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
- if (!atomic_read(&gb->is_connected))
- return -ENODEV;
+ mutex_lock(&codec->lock);
- /* find the dai */
- mutex_lock(&gb->lock);
- gb_dai = gbaudio_find_dai(gb, -1, dai->name);
- if (!gb_dai) {
- dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
- ret = -EINVAL;
- goto func_exit;
+ if (list_empty(&codec->module_list)) {
+ dev_err(codec->dev, "No codec module available\n");
+ mutex_unlock(&codec->lock);
+ return -ENODEV;
}
- /* deactivate rx/tx */
- data_cport = gb_dai->connection->intf_cport_id;
+ state = codec->stream[substream->stream].state;
+ list_for_each_entry(module, &codec->module_list, list) {
+ mutex_lock(&module->lock);
+ if (!module->is_connected) {
+ mutex_unlock(&module->lock);
+ continue;
+ }
- switch (substream->stream) {
- case SNDRV_PCM_STREAM_CAPTURE:
- ret = gb_audio_gb_set_rx_data_size(gb->mgmt_connection,
- data_cport, 192);
- if (ret) {
- dev_err(dai->dev,
- "%d:Error during set_rx_data_size, cport:%d\n",
- ret, data_cport);
- goto func_exit;
+ /* find the dai */
+ data = find_data(module, dai->name);
+ if (!data) {
+ dev_err(dai->dev, "%s:%s DATA connection missing\n",
+ dai->name, module->name);
+ mutex_unlock(&module->lock);
+ continue;
}
- ret = gb_audio_apbridgea_set_rx_data_size(gb_dai->connection, 0,
- 192);
- if (ret) {
- dev_err(dai->dev,
+ /* deactivate rx/tx */
+ data_cport = data->connection->intf_cport_id;
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_CAPTURE:
+ ret = gb_audio_gb_set_rx_data_size(
+ module->mgmt_connection,
+ data_cport, 192);
+ if (ret) {
+ dev_err(dai->dev,
+ "%d:Error during set_rx_data_size, cport:%d\n",
+ ret, data_cport);
+ mutex_unlock(&module->lock);
+ goto func_exit;
+ }
+ if (state < GBAUDIO_CODEC_PREPARE) {
+ ret = gb_audio_apbridgea_set_rx_data_size(
+ data->connection, 0,
+ 192);
+ if (ret) {
+ dev_err(dai->dev,
"%d:Error during apbridgea_set_rx_data_size\n",
ret);
- goto func_exit;
- }
- ret = gb_audio_gb_activate_rx(gb->mgmt_connection, data_cport);
- break;
- case SNDRV_PCM_STREAM_PLAYBACK:
- ret = gb_audio_gb_set_tx_data_size(gb->mgmt_connection,
- data_cport, 192);
- if (ret) {
- dev_err(dai->dev,
- "%d:Error during module set_tx_data_size, cport:%d\n",
- ret, data_cport);
- goto func_exit;
- }
- ret = gb_audio_apbridgea_set_tx_data_size(gb_dai->connection, 0,
- 192);
- if (ret) {
- dev_err(dai->dev,
- "%d:Error during apbridgea set_tx_data_size, cport\n",
- ret);
- goto func_exit;
+ mutex_unlock(&module->lock);
+ goto func_exit;
+ }
+ }
+ ret = gb_audio_gb_activate_rx(module->mgmt_connection,
+ data_cport);
+ if (ret)
+ dev_err(dai->dev,
+ "%s:Error during activate stream,%d\n",
+ module->name, ret);
+ break;
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = gb_audio_gb_set_tx_data_size(
+ module->mgmt_connection,
+ data_cport, 192);
+ if (ret) {
+ dev_err(dai->dev,
+ "%d:Error during module set_tx_data_size, cport:%d\n",
+ ret, data_cport);
+ mutex_unlock(&module->lock);
+ goto func_exit;
+ }
+ if (state < GBAUDIO_CODEC_PREPARE) {
+ ret = gb_audio_apbridgea_set_tx_data_size(
+ data->connection, 0,
+ 192);
+ if (ret) {
+ dev_err(dai->dev,
+ "%d:Error during apbridgea set_tx_data_size, cport\n",
+ ret);
+ mutex_unlock(&module->lock);
+ goto func_exit;
+ }
+ }
+ ret = gb_audio_gb_activate_tx(module->mgmt_connection,
+ data_cport);
+ if (ret)
+ dev_err(dai->dev,
+ "%s:Error during activate stream,%d\n",
+ module->name, ret);
+ break;
}
- ret = gb_audio_gb_activate_tx(gb->mgmt_connection, data_cport);
- break;
- default:
- dev_err(dai->dev, "Invalid stream type %d during prepare\n",
- substream->stream);
- ret = -EINVAL;
- goto func_exit;
+ state = GBAUDIO_CODEC_PREPARE;
+ module->ctrlstate[substream->stream] = state;
+ dev_dbg(dai->dev, "%s: state:%d\n", module->name, state);
+ mutex_unlock(&module->lock);
}
-
- if (ret)
- dev_err(dai->dev, "%d: Error during activate stream\n", ret);
+ codec->stream[substream->stream].state = state;
func_exit:
- mutex_unlock(&gb->lock);
- return ret;
+ mutex_unlock(&codec->lock);
+ return 0;
}
static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd,
{
int ret;
int tx, rx, start, stop;
- struct gbaudio_dai *gb_dai;
- struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
-
- if (!atomic_read(&gb->is_connected)) {
+ struct gbaudio_data_connection *data;
+ struct gbaudio_module_info *module;
+ struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
+
+ mutex_lock(&codec->lock);
+ if (list_empty(&codec->module_list)) {
+ dev_err(codec->dev, "No codec module available\n");
+ mutex_unlock(&codec->lock);
if (cmd == SNDRV_PCM_TRIGGER_STOP)
return 0;
return -ENODEV;
}
- /* find the dai */
- mutex_lock(&gb->lock);
- gb_dai = gbaudio_find_dai(gb, -1, dai->name);
- if (!gb_dai) {
- dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
- ret = -EINVAL;
- goto func_exit;
- }
-
tx = rx = start = stop = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
goto func_exit;
}
+ list_for_each_entry(module, &codec->module_list, list) {
+ mutex_lock(&module->lock);
+ if (!module->is_connected) {
+ mutex_unlock(&module->lock);
+ continue;
+ }
+
+ /* find the dai */
+ data = find_data(module, dai->name);
+ if (data)
+ break;
+ }
+ if (!data) {
+ dev_err(dai->dev, "%s:%s DATA connection missing\n",
+ dai->name, module->name);
+ ret = -ENODEV;
+ mutex_unlock(&module->lock);
+ goto func_exit;
+ }
if (start && tx) {
- ret = gb_audio_apbridgea_prepare_tx(gb_dai->connection, 0);
+ ret = gb_audio_apbridgea_prepare_tx(data->connection,
+ 0);
if (!ret)
- ret = gb_audio_apbridgea_start_tx(gb_dai->connection, 0,
- 0);
- }
-
- else if (start && rx) {
- ret = gb_audio_apbridgea_prepare_rx(gb_dai->connection, 0);
+ ret = gb_audio_apbridgea_start_tx(data->connection,
+ 0, 0);
+ codec->stream[substream->stream].state = GBAUDIO_CODEC_START;
+ } else if (start && rx) {
+ ret = gb_audio_apbridgea_prepare_rx(data->connection,
+ 0);
if (!ret)
- ret = gb_audio_apbridgea_start_rx(gb_dai->connection,
+ ret = gb_audio_apbridgea_start_rx(data->connection,
0);
- }
-
- else if (stop && tx) {
- ret = gb_audio_apbridgea_stop_tx(gb_dai->connection, 0);
+ codec->stream[substream->stream].state = GBAUDIO_CODEC_START;
+ } else if (stop && tx) {
+ ret = gb_audio_apbridgea_stop_tx(data->connection, 0);
if (!ret)
- ret = gb_audio_apbridgea_shutdown_tx(gb_dai->connection,
+ ret = gb_audio_apbridgea_shutdown_tx(data->connection,
0);
- }
-
- else if (stop && rx) {
- ret = gb_audio_apbridgea_stop_rx(gb_dai->connection, 0);
+ codec->stream[substream->stream].state = GBAUDIO_CODEC_STOP;
+ } else if (stop && rx) {
+ ret = gb_audio_apbridgea_stop_rx(data->connection, 0);
if (!ret)
- ret = gb_audio_apbridgea_shutdown_rx(gb_dai->connection,
+ ret = gb_audio_apbridgea_shutdown_rx(data->connection,
0);
- }
-
- else
+ codec->stream[substream->stream].state = GBAUDIO_CODEC_STOP;
+ } else
ret = -EINVAL;
-
if (ret)
- dev_err(dai->dev, "%d:Error during %s stream\n", ret,
- start ? "Start" : "Stop");
-
- /* in case device removed, return 0 for stop trigger */
- if (stop && (ret == -ENODEV))
- ret = 0;
+ dev_err(dai->dev, "%s:Error during %s stream:%d\n",
+ module->name, start ? "Start" : "Stop", ret);
+ mutex_unlock(&module->lock);
func_exit:
- mutex_unlock(&gb->lock);
+ mutex_unlock(&codec->lock);
return ret;
}
.digital_mute = gbcodec_digital_mute,
};
-/*
- * codec driver ops
- */
-static int gbcodec_probe(struct snd_soc_codec *codec)
+int gbaudio_register_module(struct gbaudio_module_info *module)
{
- /* Empty function for now */
- return 0;
-}
+ int ret;
+ struct snd_soc_codec *codec;
-static int gbcodec_remove(struct snd_soc_codec *codec)
-{
- /* Empty function for now */
- return 0;
-}
+ if (!gbcodec) {
+ dev_err(module->dev, "GB Codec not yet probed\n");
+ return -EAGAIN;
+ }
-static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- int ret = 0;
- struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec);
- u8 *gbcodec_reg = gbcodec->reg;
+ codec = gbcodec->codec;
+ mutex_lock(&gbcodec->lock);
- if (reg == SND_SOC_NOPM)
- return 0;
+ if (module->num_dais) {
+ dev_err(gbcodec->dev,
+ "%d:DAIs not supported via gbcodec driver\n",
+ module->num_dais);
+ mutex_unlock(&gbcodec->lock);
+ return -EINVAL;
+ }
- if (reg >= GBCODEC_REG_COUNT)
- return 0;
+ if (module->dapm_widgets)
+ snd_soc_dapm_new_controls(&codec->dapm, module->dapm_widgets,
+ module->num_dapm_widgets);
+ if (module->controls)
+ snd_soc_add_codec_controls(codec, module->controls,
+ module->num_controls);
+ if (module->dapm_routes)
+ snd_soc_dapm_add_routes(&codec->dapm, module->dapm_routes,
+ module->num_dapm_routes);
+
+ /* card already instantiated, create widgets here only */
+ if (codec->card->instantiated) {
+ ret = snd_soc_dapm_new_widgets(&codec->dapm);
+ if (!ret)
+ snd_soc_dapm_link_dai_widgets_component(codec->card,
+ &codec->dapm);
+ }
- gbcodec_reg[reg] = value;
- dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, value);
+ list_add(&module->list, &gbcodec->module_list);
+ dev_dbg(codec->dev, "Registered %s module\n", module->name);
- return ret;
+ mutex_unlock(&gbcodec->lock);
+ return 0;
}
+EXPORT_SYMBOL(gbaudio_register_module);
-static unsigned int gbcodec_read(struct snd_soc_codec *codec,
- unsigned int reg)
+void gbaudio_codec_cleanup(struct gbaudio_module_info *module)
{
- unsigned int val = 0;
-
- struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec);
- u8 *gbcodec_reg = gbcodec->reg;
-
- if (reg == SND_SOC_NOPM)
- return 0;
-
- if (reg >= GBCODEC_REG_COUNT)
- return 0;
-
- val = gbcodec_reg[reg];
- dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, val);
-
- return val;
-}
+ struct gbaudio_data_connection *data;
+ int pb_state = gbcodec->stream[0].state;
+ int cap_state = gbcodec->stream[1].state;
+ int ret;
+ uint16_t i2s_port, cportid;
-/*
- * gb_snd management functions
- */
+ /* locks already acquired */
+ if (!pb_state && !cap_state)
+ return;
-/* XXX
- * since BE DAI path is not yet properly closed from above layer,
- * dsp dai.mi2s_dai_data.status_mask is still set to STATUS_PORT_STARTED
- * this causes immediate playback/capture to fail in case relevant mixer
- * control is not turned OFF
- * user need to try once again after failure to recover DSP state.
- */
-static void gb_audio_cleanup(struct gbaudio_codec_info *gb)
-{
- int cportid, ret, timeout_result;
- struct gbaudio_dai *gb_dai;
- struct gb_connection *connection;
- long timeout = msecs_to_jiffies(50); /* 50ms */
- struct device *dev = gb->dev;
-
- list_for_each_entry(gb_dai, &gb->dai_list, list) {
- /*
- * In case of BE dailink, need to deactivate APBridge
- * manually
- */
- if (atomic_read(&gb_dai->users)) {
- /* schedule a wait event */
- timeout_result =
- wait_event_interruptible_timeout(
- gb_dai->wait_queue,
- !atomic_read(&gb_dai->users),
- timeout);
- if (!timeout_result)
- dev_warn(dev, "%s:DAI still in use.\n",
- gb_dai->name);
-
- connection = gb_dai->connection;
- /* PB active */
- ret = gb_audio_apbridgea_stop_tx(connection, 0);
- if (ret)
- dev_info(dev, "%d:Failed during APBridge stop_tx\n",
- ret);
- ret = gb_audio_apbridgea_shutdown_tx(connection, 0);
- if (ret)
- dev_info(dev, "%d:Failed during APBridge shutdown_tx\n",
- ret);
- cportid = connection->intf_cport_id;
- ret = gb_audio_gb_deactivate_tx(gb->mgmt_connection,
- cportid);
- if (ret)
- dev_info(dev,
- "%d:Failed during deactivate_tx\n",
- ret);
- cportid = connection->hd_cport_id;
- ret = gb_audio_apbridgea_unregister_cport(connection, 0,
- cportid,
+ if (pb_state == GBAUDIO_CODEC_START) {
+ /* cleanup PB path, only APBridge specific */
+ data = find_data(module, gbcodec->stream[0].dai_name);
+ if (!data) {
+ dev_err(gbcodec->dev, "%s: Missing data pointer\n",
+ __func__);
+ return;
+ }
+ ret = gb_audio_apbridgea_stop_tx(data->connection, 0);
+ if (ret)
+ return;
+ ret = gb_audio_apbridgea_shutdown_tx(data->connection, 0);
+ if (ret)
+ return;
+ i2s_port = 0; /* fixed for now */
+ cportid = data->connection->hd_cport_id;
+ ret = gb_audio_apbridgea_unregister_cport(data->connection,
+ i2s_port, cportid,
AUDIO_APBRIDGEA_DIRECTION_TX);
- if (ret)
- dev_info(dev, "%d:Failed during unregister cport\n",
- ret);
+ gbcodec->stream[0].state = GBAUDIO_CODEC_SHUTDOWN;
+ }
+
+ if (cap_state == GBAUDIO_CODEC_START) {
+ /* cleanup CAP path, only APBridge specific */
+ data = find_data(module, gbcodec->stream[1].dai_name);
+ if (!data) {
+ dev_err(gbcodec->dev, "%s: Missing data pointer\n",
+ __func__);
+ return;
}
+ ret = gb_audio_apbridgea_stop_rx(data->connection, 0);
+ if (ret)
+ return;
+ ret = gb_audio_apbridgea_shutdown_rx(data->connection, 0);
+ if (ret)
+ return;
+ i2s_port = 0; /* fixed for now */
+ cportid = data->connection->hd_cport_id;
+ ret = gb_audio_apbridgea_unregister_cport(data->connection,
+ i2s_port, cportid,
+ AUDIO_APBRIDGEA_DIRECTION_RX);
+ gbcodec->stream[1].state = GBAUDIO_CODEC_SHUTDOWN;
}
}
-static int gbaudio_register_codec(struct gbaudio_codec_info *gbcodec)
+void gbaudio_unregister_module(struct gbaudio_module_info *module)
{
- int ret, i;
- struct device *dev = gbcodec->dev;
- struct gb_connection *connection = gbcodec->mgmt_connection;
- struct snd_soc_codec_driver *soc_codec_dev_gbcodec;
- /*
- * FIXME: malloc for topology happens via audio_gb driver
- * should be done within codec driver itself
- */
- struct gb_audio_topology *topology;
+ struct snd_soc_codec *codec = gbcodec->codec;
+ struct snd_card *card = codec->card->snd_card;
- soc_codec_dev_gbcodec = devm_kzalloc(gbcodec->dev,
- sizeof(*soc_codec_dev_gbcodec),
- GFP_KERNEL);
- if (!soc_codec_dev_gbcodec) {
- dev_err(gbcodec->dev, "Malloc failed for codec_driver\n");
- return -ENOMEM;
- }
+ dev_dbg(codec->dev, "Unregister %s module\n", module->name);
- ret = gb_connection_enable(connection);
- if (ret) {
- dev_err(dev, "%d: Error while enabling mgmt connection\n", ret);
- return ret;
- }
+ /* complete widget processing, if ongoing */
+ snd_soc_dapm_sync(&codec->dapm);
- gbcodec->dev_id = connection->intf->interface_id;
- /* fetch topology data */
- ret = gb_audio_gb_get_topology(connection, &topology);
- if (ret) {
- dev_err(dev, "%d:Error while fetching topology\n", ret);
- goto tplg_fetch_err;
- }
+ down_write(&card->controls_rwsem);
+ mutex_lock(&gbcodec->lock);
+ dev_dbg(codec->dev, "Process Unregister %s module\n", module->name);
+ mutex_lock(&module->lock);
- /* process topology data */
- ret = gbaudio_tplg_parse_data(gbcodec, topology);
- if (ret) {
- dev_err(dev, "%d:Error while parsing topology data\n",
- ret);
- goto tplg_parse_err;
+ if (list_is_last(&module->list, &gbcodec->module_list)) {
+ dev_dbg(codec->dev, "Last module removed, cleanup APBridge\n");
+ gbaudio_codec_cleanup(module);
}
- gbcodec->topology = topology;
-
- /* update codec info */
- soc_codec_dev_gbcodec->probe = gbcodec_probe,
- soc_codec_dev_gbcodec->remove = gbcodec_remove,
-
- soc_codec_dev_gbcodec->read = gbcodec_read,
- soc_codec_dev_gbcodec->write = gbcodec_write,
-
- soc_codec_dev_gbcodec->reg_cache_size = GBCODEC_REG_COUNT,
- soc_codec_dev_gbcodec->reg_cache_default = gbcodec_reg_defaults,
- soc_codec_dev_gbcodec->reg_word_size = 1,
- soc_codec_dev_gbcodec->idle_bias_off = true,
- soc_codec_dev_gbcodec->ignore_pmdown_time = 1,
-
- 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 */
- for (i = 0; i < gbcodec->num_dais; i++)
- gbcodec->dais[i].ops = &gbcodec_dai_ops;
-
- /* register codec */
- ret = snd_soc_register_codec(dev, soc_codec_dev_gbcodec,
- gbcodec->dais, 1);
- if (ret) {
- dev_err(dev, "%d:Failed to register codec\n", ret);
- goto codec_reg_err;
+ module->is_connected = 0;
+ if (module->dapm_routes) {
+ dev_dbg(codec->dev, "Removing %d routes\n",
+ module->num_dapm_routes);
+ snd_soc_dapm_del_routes(&codec->dapm, module->dapm_routes,
+ module->num_dapm_routes);
}
-
- /* update DAI links in response to this codec */
- ret = msm8994_add_dailink("msm8994-tomtom-mtp-snd-card", gbcodec->name,
- gbcodec->dais[0].name, 1);
- if (ret) {
- dev_err(dev, "%d: Failed to add DAI links\n", ret);
- goto add_dailink_err;
+ if (module->controls) {
+ dev_dbg(codec->dev, "Removing %d controls\n",
+ module->num_controls);
+ soc_remove_codec_controls(codec, module->controls,
+ module->num_controls);
+ }
+ if (module->dapm_widgets) {
+ dev_dbg(codec->dev, "Removing %d widgets\n",
+ module->num_dapm_widgets);
+ snd_soc_dapm_free_controls(&codec->dapm, module->dapm_widgets,
+ module->num_dapm_widgets);
}
- gbcodec->num_dai_links = 1;
- return 0;
+ mutex_unlock(&module->lock);
-add_dailink_err:
- snd_soc_unregister_codec(dev);
-codec_reg_err:
- gbaudio_tplg_release(gbcodec);
- gbcodec->topology = NULL;
-tplg_parse_err:
- kfree(topology);
-tplg_fetch_err:
- gb_connection_disable(gbcodec->mgmt_connection);
- return ret;
-}
+ list_del(&module->list);
+ dev_dbg(codec->dev, "Unregistered %s module\n", module->name);
-static void gbaudio_unregister_codec(struct gbaudio_codec_info *gbcodec)
-{
- gb_audio_cleanup(gbcodec);
- msm8994_remove_dailink("msm8994-tomtom-mtp-snd-card", gbcodec->name,
- gbcodec->dais[0].name, 1);
- snd_soc_unregister_codec(gbcodec->dev);
- gbaudio_tplg_release(gbcodec);
- kfree(gbcodec->topology);
- gb_connection_disable(gbcodec->mgmt_connection);
+ mutex_unlock(&gbcodec->lock);
+ up_write(&card->controls_rwsem);
}
+EXPORT_SYMBOL(gbaudio_unregister_module);
-static int gbaudio_codec_request_handler(struct gb_operation *op)
+/*
+ * codec driver ops
+ */
+static int gbcodec_probe(struct snd_soc_codec *codec)
{
- struct gb_connection *connection = op->connection;
- struct gb_audio_streaming_event_request *req = op->request->payload;
+ struct gbaudio_codec_info *info;
+
+ info = devm_kzalloc(codec->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
- dev_warn(&connection->bundle->dev,
- "Audio Event received: cport: %u, event: %u\n",
- req->data_cport, req->event);
+ info->dev = codec->dev;
+ INIT_LIST_HEAD(&info->module_list);
+ mutex_init(&info->lock);
+ info->codec = codec;
+ snd_soc_codec_set_drvdata(codec, info);
+ gbcodec = info;
+ /* Empty function for now */
return 0;
}
-static int gbaudio_dai_request_handler(struct gb_operation *op)
+static int gbcodec_remove(struct snd_soc_codec *codec)
{
- struct gb_connection *connection = op->connection;
-
- dev_warn(&connection->bundle->dev, "Audio Event received\n");
-
+ /* Empty function for now */
return 0;
}
-static int gb_audio_add_mgmt_connection(struct gbaudio_codec_info *gbcodec,
- struct greybus_descriptor_cport *cport_desc,
- struct gb_bundle *bundle)
+static u8 gbcodec_reg[GBCODEC_REG_COUNT] = {
+ [GBCODEC_CTL_REG] = GBCODEC_CTL_REG_DEFAULT,
+ [GBCODEC_MUTE_REG] = GBCODEC_MUTE_REG_DEFAULT,
+ [GBCODEC_PB_LVOL_REG] = GBCODEC_PB_VOL_REG_DEFAULT,
+ [GBCODEC_PB_RVOL_REG] = GBCODEC_PB_VOL_REG_DEFAULT,
+ [GBCODEC_CAP_LVOL_REG] = GBCODEC_CAP_VOL_REG_DEFAULT,
+ [GBCODEC_CAP_RVOL_REG] = GBCODEC_CAP_VOL_REG_DEFAULT,
+ [GBCODEC_APB1_MUX_REG] = GBCODEC_APB1_MUX_REG_DEFAULT,
+ [GBCODEC_APB2_MUX_REG] = GBCODEC_APB2_MUX_REG_DEFAULT,
+};
+
+static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
{
- struct gb_connection *connection;
+ int ret = 0;
- /* Management Cport */
- if (gbcodec->mgmt_connection) {
- dev_err(&bundle->dev,
- "Can't have multiple Management connections\n");
- return -ENODEV;
- }
+ if (reg == SND_SOC_NOPM)
+ return 0;
- connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
- gbaudio_codec_request_handler);
- if (IS_ERR(connection))
- return PTR_ERR(connection);
+ BUG_ON(reg >= GBCODEC_REG_COUNT);
+ return 0;
- gb_connection_set_data(connection, gbcodec);
- gbcodec->mgmt_connection = connection;
+ gbcodec_reg[reg] = value;
+ dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, value);
- return 0;
+ return ret;
}
-static int gb_audio_add_data_connection(struct gbaudio_codec_info *gbcodec,
- struct greybus_descriptor_cport *cport_desc,
- struct gb_bundle *bundle)
+static unsigned int gbcodec_read(struct snd_soc_codec *codec,
+ unsigned int reg)
{
- struct gb_connection *connection;
- struct gbaudio_dai *dai;
+ unsigned int val = 0;
- dai = devm_kzalloc(gbcodec->dev, sizeof(*dai), GFP_KERNEL);
- if (!dai) {
- dev_err(gbcodec->dev, "DAI Malloc failure\n");
- return -ENOMEM;
- }
+ if (reg == SND_SOC_NOPM)
+ return 0;
- connection = gb_connection_create_flags(bundle,
- le16_to_cpu(cport_desc->id),
- gbaudio_dai_request_handler,
- GB_CONNECTION_FLAG_CSD);
- if (IS_ERR(connection)) {
- devm_kfree(gbcodec->dev, dai);
- return PTR_ERR(connection);
- }
+ BUG_ON(reg >= GBCODEC_REG_COUNT);
- gb_connection_set_data(connection, gbcodec);
- atomic_set(&dai->users, 0);
- init_waitqueue_head(&dai->wait_queue);
- dai->data_cport = connection->intf_cport_id;
- dai->connection = connection;
- list_add(&dai->list, &gbcodec->dai_list);
+ val = gbcodec_reg[reg];
+ dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, val);
- return 0;
+ return val;
}
-/*
- * This is the basic hook get things initialized and registered w/ gb
- */
-static int gb_audio_probe(struct gb_bundle *bundle,
- const struct greybus_bundle_id *id)
-{
- struct device *dev = &bundle->dev;
- struct gbaudio_codec_info *gbcodec;
- struct greybus_descriptor_cport *cport_desc;
- struct gb_audio_manager_module_descriptor desc;
- struct gbaudio_dai *dai, *_dai;
- int ret, i;
-
- /* There should be at least one Management and one Data cport */
- if (bundle->num_cports < 2)
- return -ENODEV;
+static struct snd_soc_dai_driver gbaudio_dai[] = {
+ {
+ .name = "greybus-apb1",
+ .id = 0,
+ .playback = {
+ .stream_name = "GB Audio Playback",
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FORMAT_S16_LE,
+ .rate_max = 48000,
+ .rate_min = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .capture = {
+ .stream_name = "GB Audio Capture",
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FORMAT_S16_LE,
+ .rate_max = 48000,
+ .rate_min = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &gbcodec_dai_ops,
+ },
+};
- mutex_lock(&gb_codec_list_lock);
- /*
- * There can be only one Management connection and any number of data
- * connections.
- */
- gbcodec = devm_kzalloc(dev, sizeof(*gbcodec), GFP_KERNEL);
- if (!gbcodec) {
- mutex_unlock(&gb_codec_list_lock);
- return -ENOMEM;
- }
+static struct snd_soc_codec_driver soc_codec_dev_gbaudio = {
+ .probe = gbcodec_probe,
+ .remove = gbcodec_remove,
- gbcodec->num_data_connections = bundle->num_cports - 1;
- 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 = dev;
- snprintf(gbcodec->name, NAME_SIZE, "%s.%s", dev->driver->name,
- dev_name(dev));
- greybus_set_drvdata(bundle, gbcodec);
-
- /* Create all connections */
- for (i = 0; i < bundle->num_cports; i++) {
- cport_desc = &bundle->cport_desc[i];
-
- switch (cport_desc->protocol_id) {
- case GREYBUS_PROTOCOL_AUDIO_MGMT:
- ret = gb_audio_add_mgmt_connection(gbcodec, cport_desc,
- bundle);
- if (ret)
- goto destroy_connections;
- break;
- case GREYBUS_PROTOCOL_AUDIO_DATA:
- ret = gb_audio_add_data_connection(gbcodec, cport_desc,
- bundle);
- if (ret)
- goto destroy_connections;
- break;
- default:
- dev_err(dev, "Unsupported protocol: 0x%02x\n",
- cport_desc->protocol_id);
- ret = -ENODEV;
- goto destroy_connections;
- }
- }
+ .read = gbcodec_read,
+ .write = gbcodec_write,
- /* There must be a management cport */
- if (!gbcodec->mgmt_connection) {
- ret = -EINVAL;
- dev_err(dev, "Missing management connection\n");
- goto destroy_connections;
- }
+ .reg_cache_size = GBCODEC_REG_COUNT,
+ .reg_cache_default = gbcodec_reg_defaults,
+ .reg_word_size = 1,
- /* Initialize management connection */
- ret = gbaudio_register_codec(gbcodec);
- if (ret)
- goto destroy_connections;
-
- /* Initialize data connections */
- list_for_each_entry(dai, &gbcodec->dai_list, list) {
- ret = gb_connection_enable(dai->connection);
- if (ret)
- goto remove_dai;
- }
-
- /* inform above layer for uevent */
- dev_dbg(dev, "Inform set_event:%d to above layer\n", 1);
- /* prepare for the audio manager */
- strlcpy(desc.name, gbcodec->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN);
- desc.slot = 1; /* todo */
- desc.vid = 2; /* todo */
- desc.pid = 3; /* todo */
- desc.cport = gbcodec->dev_id;
- desc.devices = 0x2; /* todo */
- gbcodec->manager_id = gb_audio_manager_add(&desc);
+ .idle_bias_off = true,
+ .ignore_pmdown_time = 1,
+};
- atomic_set(&gbcodec->is_connected, 1);
- list_add_tail(&gbcodec->list, &gb_codec_list);
- dev_dbg(dev, "Add GB Audio device:%s\n", gbcodec->name);
- mutex_unlock(&gb_codec_list_lock);
+#ifdef CONFIG_PM
+static int gbaudio_codec_suspend(struct device *dev)
+{
+ dev_dbg(dev, "%s: suspend\n", __func__);
return 0;
-
-remove_dai:
- list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list)
- gb_connection_disable(dai->connection);
-
- gbaudio_unregister_codec(gbcodec);
-destroy_connections:
- list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) {
- gb_connection_destroy(dai->connection);
- list_del(&dai->list);
- devm_kfree(dev, dai);
- }
-
- if (gbcodec->mgmt_connection)
- gb_connection_destroy(gbcodec->mgmt_connection);
-
- devm_kfree(dev, gbcodec);
- mutex_unlock(&gb_codec_list_lock);
-
- return ret;
}
-static void gb_audio_disconnect(struct gb_bundle *bundle)
+static int gbaudio_codec_resume(struct device *dev)
{
- struct gbaudio_codec_info *gbcodec = greybus_get_drvdata(bundle);
- struct gbaudio_dai *dai, *_dai;
+ dev_dbg(dev, "%s: resume\n", __func__);
+ return 0;
+}
- mutex_lock(&gb_codec_list_lock);
- atomic_set(&gbcodec->is_connected, 0);
- /* inform uevent to above layers */
- gb_audio_manager_remove(gbcodec->manager_id);
+static const struct dev_pm_ops gbaudio_codec_pm_ops = {
+ .suspend = gbaudio_codec_suspend,
+ .resume = gbaudio_codec_resume,
+};
+#endif
- mutex_lock(&gbcodec->lock);
- list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list)
- gb_connection_disable(dai->connection);
- gbaudio_unregister_codec(gbcodec);
-
- list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) {
- gb_connection_destroy(dai->connection);
- list_del(&dai->list);
- devm_kfree(gbcodec->dev, dai);
- }
- gb_connection_destroy(gbcodec->mgmt_connection);
- gbcodec->mgmt_connection = NULL;
- list_del(&gbcodec->list);
- mutex_unlock(&gbcodec->lock);
+static int gbaudio_codec_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_gbaudio,
+ gbaudio_dai, ARRAY_SIZE(gbaudio_dai));
+}
- devm_kfree(&bundle->dev, gbcodec);
- mutex_unlock(&gb_codec_list_lock);
+static int gbaudio_codec_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
}
-static const struct greybus_bundle_id gb_audio_id_table[] = {
- { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO) },
- { }
+static const struct of_device_id greybus_asoc_machine_of_match[] = {
+ { .compatible = "qcom,ara-codec", },
+ {},
};
-MODULE_DEVICE_TABLE(greybus, gb_audio_id_table);
-static struct greybus_driver gb_audio_driver = {
- .name = "gb-audio",
- .probe = gb_audio_probe,
- .disconnect = gb_audio_disconnect,
- .id_table = gb_audio_id_table,
+static struct platform_driver gbaudio_codec_driver = {
+ .driver = {
+ .name = "gb-codec",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &gbaudio_codec_pm_ops,
+#endif
+ .of_match_table = greybus_asoc_machine_of_match,
+ },
+ .probe = gbaudio_codec_probe,
+ .remove = gbaudio_codec_remove,
};
-module_greybus_driver(gb_audio_driver);
+module_platform_driver(gbaudio_codec_driver);
-MODULE_DESCRIPTION("Greybus Audio codec driver");
+MODULE_DESCRIPTION("Greybus codec driver");
MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@linaro.org>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:gbaudio-codec");
struct gb_audio_ctl_elem_info *info;
};
-static const char *gbaudio_map_controlid(struct gbaudio_codec_info *gbcodec,
+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);
+ 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, &gbcodec->codec_ctl_list, list) {
+ list_for_each_entry(control, &module->ctl_list, list) {
if (control->id == control_id) {
if (index == GBAUDIO_INVALID_ID)
return control->name;
return control->texts[index];
}
}
- list_for_each_entry(control, &gbcodec->widget_ctl_list, list) {
+ list_for_each_entry(control, &module->widget_ctl_list, list) {
if (control->id == control_id) {
if (index == GBAUDIO_INVALID_ID)
return control->name;
return NULL;
}
-static int gbaudio_map_widgetname(struct gbaudio_codec_info *gbcodec,
+static int gbaudio_map_widgetname(struct gbaudio_module_info *module,
const char *name)
{
struct gbaudio_widget *widget;
- char widget_name[NAME_SIZE];
- char prefix_name[NAME_SIZE];
-
- snprintf(prefix_name, NAME_SIZE, "GB %d ", gbcodec->dev_id);
- if (strncmp(name, prefix_name, strlen(prefix_name)))
- return -EINVAL;
-
- strlcpy(widget_name, name+strlen(prefix_name), NAME_SIZE);
- dev_dbg(gbcodec->dev, "widget_name:%s, truncated widget_name:%s\n",
- name, widget_name);
-
- list_for_each_entry(widget, &gbcodec->widget_list, list) {
- if (!strncmp(widget->name, widget_name, NAME_SIZE))
+ list_for_each_entry(widget, &module->widget_list, list) {
+ if (!strncmp(widget->name, name, NAME_SIZE))
return widget->id;
}
return -EINVAL;
}
-static const char *gbaudio_map_widgetid(struct gbaudio_codec_info *gbcodec,
+static const char *gbaudio_map_widgetid(struct gbaudio_module_info *module,
__u8 widget_id)
{
struct gbaudio_widget *widget;
- list_for_each_entry(widget, &gbcodec->widget_list, list) {
+ list_for_each_entry(widget, &module->widget_list, list) {
if (widget->id == widget_id)
return widget->name;
}
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(gbcodec->dev, "NULL info for %s\n", uinfo->id.name);
+ dev_err(module->dev, "NULL info for %s\n", uinfo->id.name);
return -EINVAL;
}
uinfo->value.enumerated.items = max;
if (uinfo->value.enumerated.item > max - 1)
uinfo->value.enumerated.item = max - 1;
- name = gbaudio_map_controlid(gbcodec, data->ctl_id,
+ 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;
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);
- if (!atomic_read(&gb->is_connected))
- return -ENODEV;
+ 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;
- ret = gb_audio_gb_get_control(gb->mgmt_connection, data->ctl_id,
+ ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id,
GB_AUDIO_INVALID_INDEX, &gbvalue);
if (ret) {
dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__,
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);
- if (!atomic_read(&gb->is_connected))
- return -ENODEV;
+ 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;
if (ret)
return ret;
- ret = gb_audio_gb_set_control(gb->mgmt_connection, data->ctl_id,
+ ret = gb_audio_gb_set_control(module->mgmt_connection, data->ctl_id,
GB_AUDIO_INVALID_INDEX, &gbvalue);
if (ret) {
dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__,
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;
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);
- if (!atomic_read(&gb->is_connected))
- return -ENODEV;
+ 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;
"GB: Control '%s' is stereo, which is not supported\n",
kcontrol->id.name);
- ret = gb_audio_gb_get_control(gb->mgmt_connection, data->ctl_id,
+ ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id,
GB_AUDIO_INVALID_INDEX, &gbvalue);
if (ret) {
dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__,
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);
- if (!atomic_read(&gb->is_connected))
- return -ENODEV;
+ 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;
}
gbvalue.value.integer_value[0] =
ucontrol->value.integer.value[0];
- ret = gb_audio_gb_set_control(gb->mgmt_connection,
+ ret = gb_audio_gb_set_control(module->mgmt_connection,
data->ctl_id,
GB_AUDIO_INVALID_INDEX, &gbvalue);
if (ret) {
return ret;
}
-static int gbaudio_tplg_create_kcontrol(struct gbaudio_codec_info *gb,
+static int gbaudio_tplg_create_kcontrol(struct gbaudio_module_info *gb,
struct snd_kcontrol_new *kctl,
struct gb_audio_control *ctl)
{
static const char * const gbtexts[] = {"Stereo", "Left", "Right"};
static const SOC_ENUM_SINGLE_DECL(
- gbcodec_apb1_rx_enum, GBCODEC_APB1_MUX_REG, 0, gbtexts);
+ module_apb1_rx_enum, GBCODEC_APB1_MUX_REG, 0, gbtexts);
static const SOC_ENUM_SINGLE_DECL(
- gbcodec_mic_enum, GBCODEC_APB1_MUX_REG, 4, gbtexts);
+ module_mic_enum, GBCODEC_APB1_MUX_REG, 4, gbtexts);
-static int gbaudio_tplg_create_enum_ctl(struct gbaudio_codec_info *gb,
+static int gbaudio_tplg_create_enum_ctl(struct gbaudio_module_info *gb,
struct snd_kcontrol_new *kctl,
struct gb_audio_control *ctl)
{
switch (ctl->id) {
case 8:
*kctl = (struct snd_kcontrol_new)
- SOC_DAPM_ENUM(ctl->name, gbcodec_apb1_rx_enum);
+ SOC_DAPM_ENUM(ctl->name, module_apb1_rx_enum);
break;
case 9:
*kctl = (struct snd_kcontrol_new)
- SOC_DAPM_ENUM(ctl->name, gbcodec_mic_enum);
+ SOC_DAPM_ENUM(ctl->name, module_mic_enum);
break;
default:
return -EINVAL;
return 0;
}
-static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_codec_info *gb,
+static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_module_info *gb,
struct snd_kcontrol_new *kctl,
struct gb_audio_control *ctl)
{
return 0;
}
-static int gbaudio_tplg_create_wcontrol(struct gbaudio_codec_info *gb,
+static int gbaudio_tplg_create_wcontrol(struct gbaudio_module_info *gb,
struct snd_kcontrol_new *kctl,
struct gb_audio_control *ctl)
{
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;
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(gbcodec, w->name);
+ wid = gbaudio_map_widgetname(module, w->name);
if (wid < 0) {
dev_err(codec->dev, "Invalid widget name:%s\n", w->name);
return -EINVAL;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- ret = gb_audio_gb_enable_widget(gbcodec->mgmt_connection, wid);
+ ret = gb_audio_gb_enable_widget(module->mgmt_connection, wid);
+ if (!ret)
+ ret = gbaudio_module_update(gbcodec, w->name, module,
+ 1);
break;
case SND_SOC_DAPM_POST_PMD:
- ret = gb_audio_gb_disable_widget(gbcodec->mgmt_connection, wid);
+ ret = gb_audio_gb_disable_widget(module->mgmt_connection, wid);
+ if (!ret)
+ ret = gbaudio_module_update(gbcodec, w->name, module,
+ 0);
break;
}
if (ret)
return ret;
}
-static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec,
+static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module,
struct snd_soc_dapm_widget *dw,
struct gb_audio_widget *w)
{
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(gbcodec->dev, "Inavlid kcontrol count=%d for %s\n",
+ 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(gbcodec->dev, size, GFP_KERNEL);
+ widget_kctls = devm_kzalloc(module->dev, size, GFP_KERNEL);
if (!widget_kctls)
return -ENOMEM;
}
/* create relevant kcontrols */
for (i = 0; i < w->ncontrols; i++) {
curr = &w->ctl[i];
- ret = gbaudio_tplg_create_wcontrol(gbcodec, &widget_kctls[i],
+ ret = gbaudio_tplg_create_wcontrol(module, &widget_kctls[i],
curr);
if (ret) {
- dev_err(gbcodec->dev,
+ dev_err(module->dev,
"%s:%d type widget_ctl not supported\n",
curr->name, curr->iface);
goto error;
}
- control = devm_kzalloc(gbcodec->dev,
+ control = devm_kzalloc(module->dev,
sizeof(struct gbaudio_control),
GFP_KERNEL);
if (!control) {
if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED)
control->texts = (const char * const *)
curr->info.value.enumerated.names;
- list_add(&control->list, &gbcodec->widget_ctl_list);
- dev_dbg(gbcodec->dev, "%s: control of type %d created\n",
+ 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)
goto error;
}
- dev_dbg(gbcodec->dev, "%s: widget of type %d created\n", dw->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, &gbcodec->widget_ctl_list,
+ list_for_each_entry_safe(control, _control, &module->widget_ctl_list,
list) {
list_del(&control->list);
- devm_kfree(gbcodec->dev, control);
+ devm_kfree(module->dev, control);
}
return ret;
}
-static int gbaudio_tplg_create_dai(struct gbaudio_codec_info *gbcodec,
- struct snd_soc_dai_driver *gb_dai,
- struct gb_audio_dai *dai)
-{
- /*
- * do not update name here,
- * append dev_id before assigning it here
- */
-
- gb_dai->playback.stream_name = dai->playback.stream_name;
- gb_dai->playback.channels_min = dai->playback.chan_min;
- gb_dai->playback.channels_max = dai->playback.chan_max;
- gb_dai->playback.formats = dai->playback.formats;
- gb_dai->playback.rates = dai->playback.rates;
- gb_dai->playback.sig_bits = dai->playback.sig_bits;
-
- gb_dai->capture.stream_name = dai->capture.stream_name;
- gb_dai->capture.channels_min = dai->capture.chan_min;
- gb_dai->capture.channels_max = dai->capture.chan_max;
- gb_dai->capture.formats = dai->capture.formats;
- gb_dai->capture.rates = dai->capture.rates;
- gb_dai->capture.sig_bits = dai->capture.sig_bits;
-
- return 0;
-}
-
-static int gbaudio_tplg_process_kcontrols(struct gbaudio_codec_info *gbcodec,
+static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module,
struct gb_audio_control *controls)
{
int i, ret;
struct gb_audio_control *curr;
struct gbaudio_control *control, *_control;
size_t size;
+ char temp_name[NAME_SIZE];
- size = sizeof(struct snd_kcontrol_new) * gbcodec->num_kcontrols;
- dapm_kctls = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL);
+ 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 < gbcodec->num_kcontrols; i++) {
- ret = gbaudio_tplg_create_kcontrol(gbcodec, &dapm_kctls[i],
+ for (i = 0; i < module->num_controls; i++) {
+ ret = gbaudio_tplg_create_kcontrol(module, &dapm_kctls[i],
curr);
if (ret) {
- dev_err(gbcodec->dev, "%s:%d type not supported\n",
+ dev_err(module->dev, "%s:%d type not supported\n",
curr->name, curr->iface);
goto error;
}
- control = devm_kzalloc(gbcodec->dev, sizeof(struct
+ control = devm_kzalloc(module->dev, sizeof(struct
gbaudio_control),
GFP_KERNEL);
if (!control) {
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)
control->texts = (const char * const *)
curr->info.value.enumerated.names;
- list_add(&control->list, &gbcodec->codec_ctl_list);
- dev_dbg(gbcodec->dev, "%d:%s created of type %d\n", curr->id,
+ 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++;
}
- gbcodec->kctls = dapm_kctls;
+ module->controls = dapm_kctls;
return 0;
error:
- list_for_each_entry_safe(control, _control, &gbcodec->codec_ctl_list,
+ list_for_each_entry_safe(control, _control, &module->ctl_list,
list) {
list_del(&control->list);
- devm_kfree(gbcodec->dev, control);
+ devm_kfree(module->dev, control);
}
- devm_kfree(gbcodec->dev, dapm_kctls);
+ devm_kfree(module->dev, dapm_kctls);
return ret;
}
-static int gbaudio_tplg_process_widgets(struct gbaudio_codec_info *gbcodec,
+static int gbaudio_tplg_process_widgets(struct gbaudio_module_info *module,
struct gb_audio_widget *widgets)
{
int i, ret, ncontrols;
struct gbaudio_widget *widget, *_widget;
size_t size;
- size = sizeof(struct snd_soc_dapm_widget) * gbcodec->num_dapm_widgets;
- dapm_widgets = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL);
+ 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 < gbcodec->num_dapm_widgets; i++) {
- ret = gbaudio_tplg_create_widget(gbcodec, &dapm_widgets[i],
+ for (i = 0; i < module->num_dapm_widgets; i++) {
+ ret = gbaudio_tplg_create_widget(module, &dapm_widgets[i],
curr);
if (ret) {
- dev_err(gbcodec->dev, "%s:%d type not supported\n",
+ dev_err(module->dev, "%s:%d type not supported\n",
curr->name, curr->type);
goto error;
}
- widget = devm_kzalloc(gbcodec->dev, sizeof(struct
+ widget = devm_kzalloc(module->dev, sizeof(struct
gbaudio_widget),
GFP_KERNEL);
if (!widget) {
}
widget->id = curr->id;
widget->name = curr->name;
- list_add(&widget->list, &gbcodec->widget_list);
+ list_add(&widget->list, &module->widget_list);
ncontrols = curr->ncontrols;
curr++;
curr += ncontrols * sizeof(struct gb_audio_control);
}
- gbcodec->widgets = dapm_widgets;
+ module->dapm_widgets = dapm_widgets;
return 0;
error:
- list_for_each_entry_safe(widget, _widget, &gbcodec->widget_list,
+ list_for_each_entry_safe(widget, _widget, &module->widget_list,
list) {
list_del(&widget->list);
- devm_kfree(gbcodec->dev, widget);
- }
- devm_kfree(gbcodec->dev, dapm_widgets);
- return ret;
-}
-
-static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec,
- struct gb_audio_dai *dais)
-{
- int i, ret;
- struct snd_soc_dai_driver *gb_dais;
- struct gb_audio_dai *curr;
- size_t size;
- char dai_name[NAME_SIZE];
- struct gbaudio_dai *dai;
-
- size = sizeof(struct snd_soc_dai_driver) * gbcodec->num_dais;
- gb_dais = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL);
- if (!gb_dais)
- return -ENOMEM;
-
- curr = dais;
- for (i = 0; i < gbcodec->num_dais; i++) {
- ret = gbaudio_tplg_create_dai(gbcodec, &gb_dais[i], curr);
- if (ret) {
- dev_err(gbcodec->dev, "%s failed to create\n",
- curr->name);
- goto error;
- }
- /* append dev_id to dai_name */
- snprintf(dai_name, NAME_SIZE, "%s.%d", curr->name,
- gbcodec->dev_id);
- dai = gbaudio_find_dai(gbcodec, curr->data_cport, NULL);
- if (!dai)
- goto error;
- strlcpy(dai->name, dai_name, NAME_SIZE);
- dev_dbg(gbcodec->dev, "%s:DAI added\n", dai->name);
- gb_dais[i].name = dai->name;
- curr++;
+ devm_kfree(module->dev, widget);
}
- gbcodec->dais = gb_dais;
-
- return 0;
-
-error:
- devm_kfree(gbcodec->dev, gb_dais);
+ devm_kfree(module->dev, dapm_widgets);
return ret;
}
-static int gbaudio_tplg_process_routes(struct gbaudio_codec_info *gbcodec,
+static int gbaudio_tplg_process_routes(struct gbaudio_module_info *module,
struct gb_audio_route *routes)
{
int i, ret;
struct gb_audio_route *curr;
size_t size;
- size = sizeof(struct snd_soc_dapm_route) * gbcodec->num_dapm_routes;
- dapm_routes = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL);
+ 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;
- gbcodec->routes = dapm_routes;
+ module->dapm_routes = dapm_routes;
curr = routes;
- for (i = 0; i < gbcodec->num_dapm_routes; i++) {
+ for (i = 0; i < module->num_dapm_routes; i++) {
dapm_routes->sink =
- gbaudio_map_widgetid(gbcodec, curr->destination_id);
+ gbaudio_map_widgetid(module, curr->destination_id);
if (!dapm_routes->sink) {
- dev_err(gbcodec->dev, "%d:%d:%d:%d - Invalid sink\n",
+ 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(gbcodec, curr->source_id);
+ gbaudio_map_widgetid(module, curr->source_id);
if (!dapm_routes->source) {
- dev_err(gbcodec->dev, "%d:%d:%d:%d - Invalid source\n",
+ 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(gbcodec,
+ gbaudio_map_controlid(module,
curr->control_id,
curr->index);
if ((curr->control_id != GBAUDIO_INVALID_ID) &&
!dapm_routes->control) {
- dev_err(gbcodec->dev, "%d:%d:%d:%d - Invalid control\n",
+ 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(gbcodec->dev, "Route {%s, %s, %s}\n", dapm_routes->sink,
+ dev_dbg(module->dev, "Route {%s, %s, %s}\n", dapm_routes->sink,
(dapm_routes->control) ? dapm_routes->control:"NULL",
dapm_routes->source);
dapm_routes++;
return 0;
error:
- devm_kfree(gbcodec->dev, dapm_routes);
+ devm_kfree(module->dev, dapm_routes);
return ret;
}
-static int gbaudio_tplg_process_header(struct gbaudio_codec_info *gbcodec,
+static int gbaudio_tplg_process_header(struct gbaudio_module_info *module,
struct gb_audio_topology *tplg_data)
{
/* fetch no. of kcontrols, widgets & routes */
- gbcodec->num_dais = tplg_data->num_dais;
- gbcodec->num_kcontrols = tplg_data->num_controls;
- gbcodec->num_dapm_widgets = tplg_data->num_widgets;
- gbcodec->num_dapm_routes = tplg_data->num_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 */
- gbcodec->dai_offset = (unsigned long)&tplg_data->data;
- gbcodec->control_offset = gbcodec->dai_offset + tplg_data->size_dais;
- gbcodec->widget_offset = gbcodec->control_offset +
+ module->dai_offset = (unsigned long)&tplg_data->data;
+ module->control_offset = module->dai_offset + tplg_data->size_dais;
+ module->widget_offset = module->control_offset +
tplg_data->size_controls;
- gbcodec->route_offset = gbcodec->widget_offset +
+ module->route_offset = module->widget_offset +
tplg_data->size_widgets;
- dev_dbg(gbcodec->dev, "DAI offset is 0x%lx\n", gbcodec->dai_offset);
- dev_dbg(gbcodec->dev, "control offset is %lx\n",
- gbcodec->control_offset);
- dev_dbg(gbcodec->dev, "widget offset is %lx\n", gbcodec->widget_offset);
- dev_dbg(gbcodec->dev, "route offset is %lx\n", gbcodec->route_offset);
+ 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_codec_info *gbcodec,
+int gbaudio_tplg_parse_data(struct gbaudio_module_info *module,
struct gb_audio_topology *tplg_data)
{
int ret;
- struct gb_audio_dai *dais;
struct gb_audio_control *controls;
struct gb_audio_widget *widgets;
struct gb_audio_route *routes;
if (!tplg_data)
return -EINVAL;
- ret = gbaudio_tplg_process_header(gbcodec, tplg_data);
+ ret = gbaudio_tplg_process_header(module, tplg_data);
if (ret) {
- dev_err(gbcodec->dev, "%d: Error in parsing topology header\n",
+ dev_err(module->dev, "%d: Error in parsing topology header\n",
ret);
return ret;
}
/* process control */
- controls = (struct gb_audio_control *)gbcodec->control_offset;
- ret = gbaudio_tplg_process_kcontrols(gbcodec, controls);
+ controls = (struct gb_audio_control *)module->control_offset;
+ ret = gbaudio_tplg_process_kcontrols(module, controls);
if (ret) {
- dev_err(gbcodec->dev,
+ dev_err(module->dev,
"%d: Error in parsing controls data\n", ret);
return ret;
}
- dev_dbg(gbcodec->dev, "Control parsing finished\n");
-
- /* process DAI */
- dais = (struct gb_audio_dai *)gbcodec->dai_offset;
- ret = gbaudio_tplg_process_dais(gbcodec, dais);
- if (ret) {
- dev_err(gbcodec->dev,
- "%d: Error in parsing DAIs data\n", ret);
- return ret;
- }
- dev_dbg(gbcodec->dev, "DAI parsing finished\n");
+ dev_dbg(module->dev, "Control parsing finished\n");
/* process widgets */
- widgets = (struct gb_audio_widget *)gbcodec->widget_offset;
- ret = gbaudio_tplg_process_widgets(gbcodec, widgets);
+ widgets = (struct gb_audio_widget *)module->widget_offset;
+ ret = gbaudio_tplg_process_widgets(module, widgets);
if (ret) {
- dev_err(gbcodec->dev,
+ dev_err(module->dev,
"%d: Error in parsing widgets data\n", ret);
return ret;
}
- dev_dbg(gbcodec->dev, "Widget parsing finished\n");
+ dev_dbg(module->dev, "Widget parsing finished\n");
/* process route */
- routes = (struct gb_audio_route *)gbcodec->route_offset;
- ret = gbaudio_tplg_process_routes(gbcodec, routes);
+ routes = (struct gb_audio_route *)module->route_offset;
+ ret = gbaudio_tplg_process_routes(module, routes);
if (ret) {
- dev_err(gbcodec->dev,
+ dev_err(module->dev,
"%d: Error in parsing routes data\n", ret);
return ret;
}
- dev_dbg(gbcodec->dev, "Route parsing finished\n");
+ dev_dbg(module->dev, "Route parsing finished\n");
return ret;
}
-void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec)
+void gbaudio_tplg_release(struct gbaudio_module_info *module)
{
struct gbaudio_control *control, *_control;
struct gbaudio_widget *widget, *_widget;
- if (!gbcodec->topology)
+ if (!module->topology)
return;
/* release kcontrols */
- list_for_each_entry_safe(control, _control, &gbcodec->codec_ctl_list,
+ list_for_each_entry_safe(control, _control, &module->ctl_list,
list) {
list_del(&control->list);
- devm_kfree(gbcodec->dev, control);
+ devm_kfree(module->dev, control);
}
- if (gbcodec->kctls)
- devm_kfree(gbcodec->dev, gbcodec->kctls);
+ if (module->controls)
+ devm_kfree(module->dev, module->controls);
/* release widget controls */
- list_for_each_entry_safe(control, _control, &gbcodec->widget_ctl_list,
+ list_for_each_entry_safe(control, _control, &module->widget_ctl_list,
list) {
list_del(&control->list);
- devm_kfree(gbcodec->dev, control);
+ devm_kfree(module->dev, control);
}
/* release widgets */
- list_for_each_entry_safe(widget, _widget, &gbcodec->widget_list,
+ list_for_each_entry_safe(widget, _widget, &module->widget_list,
list) {
list_del(&widget->list);
- devm_kfree(gbcodec->dev, widget);
+ devm_kfree(module->dev, widget);
}
- if (gbcodec->widgets)
- devm_kfree(gbcodec->dev, gbcodec->widgets);
+ if (module->dapm_widgets)
+ devm_kfree(module->dev, module->dapm_widgets);
/* release routes */
- if (gbcodec->routes)
- devm_kfree(gbcodec->dev, gbcodec->routes);
+ if (module->dapm_routes)
+ devm_kfree(module->dev, module->dapm_routes);
}