Commit 6dd67645 authored by Vaibhav Agarwal's avatar Vaibhav Agarwal Committed by Greg Kroah-Hartman

greybus: audio: Use single codec driver registration

We have single I2S port via APB1 for communication with all
audio modules. Thus, we should register single codec driver
and manage all individual audio modules internally within
this driver.
Signed-off-by: default avatarVaibhav Agarwal <vaibhav.agarwal@linaro.org>
Reviewed-by: default avatarMark Greer <mark.greer@animalcreek.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parent 0ec30632
/* /*
* Greybus audio driver * audio codec driver
* Copyright 2015 Google Inc. * Copyright 2016 Google Inc.
* Copyright 2015 Linaro Ltd. * Copyright 2016 Linaro Ltd.
* *
* Released under the GPLv2 only. * Released under the GPLv2 only.
*/ */
...@@ -9,65 +9,300 @@ ...@@ -9,65 +9,300 @@
#include <linux/module.h> #include <linux/module.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/msm-dynamic-dailink.h>
#include "audio_codec.h" #include "audio_codec.h"
#include "audio_apbridgea.h" #include "audio_apbridgea.h"
#include "audio_manager.h" #include "audio_manager.h"
static DEFINE_MUTEX(gb_codec_list_lock); static struct gbaudio_codec_info *gbcodec;
static LIST_HEAD(gb_codec_list);
struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec, struct gbaudio_data_connection *find_data(struct gbaudio_module_info *module,
int data_cport, const char *name) const char *name)
{ {
struct gbaudio_dai *dai; struct gbaudio_data_connection *data;
list_for_each_entry(dai, &gbcodec->dai_list, list) { list_for_each_entry(data, &module->data_list, list) {
if (name && !strncmp(dai->name, name, NAME_SIZE)) if (name && !strncmp(data->name, name, NAME_SIZE))
return dai; return data;
if ((data_cport != -1) && (dai->data_cport == data_cport))
return dai;
} }
return NULL; return NULL;
} }
static int find_stream(const char *name)
{
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;
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;
}
/* 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 */
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 */
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);
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);
}
/* 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;
}
int gbaudio_module_update(struct gbaudio_codec_info *codec,
const char *w_name,
struct gbaudio_module_info *module, int enable)
{
int stream, ret = 0;
int pb_state;
dev_dbg(module->dev, "Module update %s sequence\n",
enable ? "Enable":"Disable");
stream = find_stream(w_name);
if (!stream) {
dev_dbg(codec->dev, "No action required for %s\n", w_name);
return 0;
}
/* 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);
}
/* 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 * codec DAI ops
*/ */
static int gbcodec_startup(struct snd_pcm_substream *substream, static int gbcodec_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
int ret; int ret = 0;
__u16 i2s_port, cportid; __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);
struct gbaudio_dai *gb_dai; mutex_lock(&codec->lock);
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
if (!atomic_read(&gb->is_connected)) if (list_empty(&codec->module_list)) {
dev_err(codec->dev, "No codec module available\n");
mutex_unlock(&codec->lock);
return -ENODEV; return -ENODEV;
}
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;
}
/* find the dai */ /* find the dai */
mutex_lock(&gb->lock); data = find_data(module, dai->name);
gb_dai = gbaudio_find_dai(gb, -1, dai->name); if (!data) {
if (!gb_dai) { dev_err(dai->dev, "%s:%s DATA connection missing\n",
dev_err(dai->dev, "%s: DAI not registered\n", dai->name); dai->name, module->name);
mutex_unlock(&gb->lock); mutex_unlock(&module->lock);
return -EINVAL; continue;
} }
/* register cport */ /* register cport */
i2s_port = 0; /* fixed for now */ i2s_port = 0; /* fixed for now */
cportid = gb_dai->connection->hd_cport_id; cportid = data->connection->hd_cport_id;
ret = gb_audio_apbridgea_register_cport(gb_dai->connection, i2s_port, ret = gb_audio_apbridgea_register_cport(data->connection,
cportid, i2s_port, cportid,
AUDIO_APBRIDGEA_DIRECTION_TX); AUDIO_APBRIDGEA_DIRECTION_TX);
dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name, cportid, dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name,
ret); cportid, ret);
state = GBAUDIO_CODEC_STARTUP;
if (!ret) module->ctrlstate[substream->stream] = state;
atomic_inc(&gb_dai->users); dev_dbg(dai->dev, "%s: state:%d\n", module->name, state);
mutex_unlock(&gb->lock); mutex_unlock(&module->lock);
}
codec->stream[substream->stream].state = state;
codec->stream[substream->stream].dai_name = dai->name;
mutex_unlock(&codec->lock);
return ret; return ret;
} }
...@@ -77,60 +312,79 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, ...@@ -77,60 +312,79 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream,
{ {
int ret; int ret;
__u16 i2s_port, cportid; __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);
struct gbaudio_dai *gb_dai; mutex_lock(&codec->lock);
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
/* find the dai */ if (list_empty(&codec->module_list)) {
gb_dai = gbaudio_find_dai(gb, -1, dai->name); dev_err(codec->dev, "No codec module available\n");
if (!gb_dai) { mutex_unlock(&codec->lock);
dev_err(dai->dev, "%s: DAI not registered\n", dai->name); return;
goto func_exit;
} }
atomic_dec(&gb_dai->users); state = codec->stream[substream->stream].state;
list_for_each_entry(module, &codec->module_list, list) {
if (!atomic_read(&gb->is_connected)) { mutex_lock(&module->lock);
if (!atomic_read(&gb_dai->users)) if (!module->is_connected) {
wake_up_interruptible(&gb_dai->wait_queue); dev_err(dai->dev, "%s:%s module not connected\n",
return; __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;
} }
mutex_lock(&gb->lock); /* find the dai */
/* deactivate rx/tx */ data = find_data(module, dai->name);
cportid = gb_dai->connection->intf_cport_id; 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) { switch (substream->stream) {
case SNDRV_PCM_STREAM_CAPTURE: case SNDRV_PCM_STREAM_CAPTURE:
ret = gb_audio_gb_deactivate_rx(gb->mgmt_connection, cportid); 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; break;
case SNDRV_PCM_STREAM_PLAYBACK: case SNDRV_PCM_STREAM_PLAYBACK:
ret = gb_audio_gb_deactivate_tx(gb->mgmt_connection, cportid); ret = gb_audio_gb_deactivate_tx(module->mgmt_connection,
break; cportid);
default: /* unregister cport */
dev_err(dai->dev, "Invalid stream type during shutdown\n");
goto func_exit;
}
if (ret)
dev_err(dai->dev, "%d:Error during deactivate\n", ret);
/* un register cport */
i2s_port = 0; /* fixed for now */ i2s_port = 0; /* fixed for now */
ret = gb_audio_apbridgea_unregister_cport(gb_dai->connection, i2s_port, cportid = data->connection->hd_cport_id;
gb_dai->connection->hd_cport_id, ret = gb_audio_apbridgea_unregister_cport(
data->connection, i2s_port, cportid,
AUDIO_APBRIDGEA_DIRECTION_TX); AUDIO_APBRIDGEA_DIRECTION_TX);
break;
}
dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name, dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name,
gb_dai->connection->hd_cport_id, ret); cportid, ret);
func_exit: state = GBAUDIO_CODEC_SHUTDOWN;
mutex_unlock(&gb->lock); module->ctrlstate[substream->stream] = state;
dev_dbg(dai->dev, "%s: state:%d\n", module->name, state);
/* mutex_unlock(&module->lock);
if (!atomic_read(&gb_dai->users)) }
wake_up_interruptible(&gb_dai->wait_queue); codec->stream[substream->stream].state = state;
*/ codec->stream[substream->stream].dai_name = NULL;
mutex_unlock(&codec->lock);
return; return;
} }
...@@ -142,19 +396,17 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, ...@@ -142,19 +396,17 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream,
uint8_t sig_bits, channels; uint8_t sig_bits, channels;
uint32_t format, rate; uint32_t format, rate;
uint16_t data_cport; uint16_t data_cport;
struct gbaudio_dai *gb_dai; struct gbaudio_module_info *module;
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); struct gbaudio_data_connection *data;
int state;
struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
if (!atomic_read(&gb->is_connected)) mutex_lock(&codec->lock);
return -ENODEV;
/* find the dai */ if (list_empty(&codec->module_list)) {
mutex_lock(&gb->lock); dev_err(codec->dev, "No codec module available\n");
gb_dai = gbaudio_find_dai(gb, -1, dai->name); mutex_unlock(&codec->lock);
if (!gb_dai) { return -ENODEV;
dev_err(dai->dev, "%s: DAI not registered\n", dai->name);
ret = -EINVAL;
goto func_exit;
} }
/* /*
...@@ -164,54 +416,86 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, ...@@ -164,54 +416,86 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream,
if (params_channels(hwparams) != 2) { if (params_channels(hwparams) != 2) {
dev_err(dai->dev, "Invalid channel count:%d\n", dev_err(dai->dev, "Invalid channel count:%d\n",
params_channels(hwparams)); params_channels(hwparams));
ret = -EINVAL; mutex_unlock(&codec->lock);
goto func_exit; return -EINVAL;
} }
channels = params_channels(hwparams); channels = params_channels(hwparams);
if (params_rate(hwparams) != 48000) { if (params_rate(hwparams) != 48000) {
dev_err(dai->dev, "Invalid sampling rate:%d\n", dev_err(dai->dev, "Invalid sampling rate:%d\n",
params_rate(hwparams)); params_rate(hwparams));
ret = -EINVAL; mutex_unlock(&codec->lock);
goto func_exit; return -EINVAL;
} }
rate = GB_AUDIO_PCM_RATE_48000; rate = GB_AUDIO_PCM_RATE_48000;
if (params_format(hwparams) != SNDRV_PCM_FORMAT_S16_LE) { if (params_format(hwparams) != SNDRV_PCM_FORMAT_S16_LE) {
dev_err(dai->dev, "Invalid format:%d\n", dev_err(dai->dev, "Invalid format:%d\n",
params_format(hwparams)); params_format(hwparams));
ret = -EINVAL; mutex_unlock(&codec->lock);
goto func_exit; return -EINVAL;
} }
format = GB_AUDIO_PCM_FMT_S16_LE; format = GB_AUDIO_PCM_FMT_S16_LE;
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) {
dev_err(dai->dev, "%s:%s module not connected\n",
__func__, module->name);
ret = -ENODEV;
mutex_unlock(&module->lock);
continue;
}
/* 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 /* XXX check impact of sig_bit
* it should not change ideally * it should not change ideally
*/ */
dev_dbg(dai->dev,
dev_dbg(dai->dev, "cport:%d, rate:%d, channel %d, format %d, sig_bits:%d\n", "cport:%d, rate:%d, channel %d, format %d, sig_bits:%d\n",
data_cport, rate, channels, format, sig_bits); data_cport, rate, channels, format, sig_bits);
ret = gb_audio_gb_set_pcm(gb->mgmt_connection, data_cport, format, ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport,
rate, channels, sig_bits); format, rate, channels, sig_bits);
if (ret) { if (ret) {
dev_err(dai->dev, "%d: Error during set_pcm\n", ret); dev_err(dai->dev, "%d: Error during set_pcm\n", ret);
mutex_unlock(&module->lock);
goto func_exit; goto func_exit;
} }
if (state < GBAUDIO_CODEC_HWPARAMS) {
/* ret = gb_audio_apbridgea_set_config(data->connection, 0,
* 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_FMT_16,
AUDIO_APBRIDGEA_PCM_RATE_48000, AUDIO_APBRIDGEA_PCM_RATE_48000,
6144000); 6144000);
if (ret) if (ret) {
dev_err(dai->dev, "%d: Error during set_config\n", 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;
func_exit: func_exit:
mutex_unlock(&gb->lock); mutex_unlock(&codec->lock);
return ret; return ret;
} }
...@@ -220,76 +504,110 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, ...@@ -220,76 +504,110 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream,
{ {
int ret; int ret;
uint16_t data_cport; uint16_t data_cport;
struct gbaudio_dai *gb_dai; struct gbaudio_data_connection *data;
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); struct gbaudio_module_info *module;
int state;
struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
mutex_lock(&codec->lock);
if (!atomic_read(&gb->is_connected)) if (list_empty(&codec->module_list)) {
dev_err(codec->dev, "No codec module available\n");
mutex_unlock(&codec->lock);
return -ENODEV; return -ENODEV;
}
/* find the dai */ state = codec->stream[substream->stream].state;
mutex_lock(&gb->lock); list_for_each_entry(module, &codec->module_list, list) {
gb_dai = gbaudio_find_dai(gb, -1, dai->name); mutex_lock(&module->lock);
if (!gb_dai) { if (!module->is_connected) {
dev_err(dai->dev, "%s: DAI not registered\n", dai->name); mutex_unlock(&module->lock);
ret = -EINVAL; continue;
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;
}
/* deactivate rx/tx */ /* deactivate rx/tx */
data_cport = gb_dai->connection->intf_cport_id; data_cport = data->connection->intf_cport_id;
switch (substream->stream) { switch (substream->stream) {
case SNDRV_PCM_STREAM_CAPTURE: case SNDRV_PCM_STREAM_CAPTURE:
ret = gb_audio_gb_set_rx_data_size(gb->mgmt_connection, ret = gb_audio_gb_set_rx_data_size(
module->mgmt_connection,
data_cport, 192); data_cport, 192);
if (ret) { if (ret) {
dev_err(dai->dev, dev_err(dai->dev,
"%d:Error during set_rx_data_size, cport:%d\n", "%d:Error during set_rx_data_size, cport:%d\n",
ret, data_cport); ret, data_cport);
mutex_unlock(&module->lock);
goto func_exit; goto func_exit;
} }
ret = gb_audio_apbridgea_set_rx_data_size(gb_dai->connection, 0, if (state < GBAUDIO_CODEC_PREPARE) {
ret = gb_audio_apbridgea_set_rx_data_size(
data->connection, 0,
192); 192);
if (ret) { if (ret) {
dev_err(dai->dev, dev_err(dai->dev,
"%d:Error during apbridgea_set_rx_data_size\n", "%d:Error during apbridgea_set_rx_data_size\n",
ret); ret);
mutex_unlock(&module->lock);
goto func_exit; goto func_exit;
} }
ret = gb_audio_gb_activate_rx(gb->mgmt_connection, data_cport); }
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; break;
case SNDRV_PCM_STREAM_PLAYBACK: case SNDRV_PCM_STREAM_PLAYBACK:
ret = gb_audio_gb_set_tx_data_size(gb->mgmt_connection, ret = gb_audio_gb_set_tx_data_size(
module->mgmt_connection,
data_cport, 192); data_cport, 192);
if (ret) { if (ret) {
dev_err(dai->dev, dev_err(dai->dev,
"%d:Error during module set_tx_data_size, cport:%d\n", "%d:Error during module set_tx_data_size, cport:%d\n",
ret, data_cport); ret, data_cport);
mutex_unlock(&module->lock);
goto func_exit; goto func_exit;
} }
ret = gb_audio_apbridgea_set_tx_data_size(gb_dai->connection, 0, if (state < GBAUDIO_CODEC_PREPARE) {
ret = gb_audio_apbridgea_set_tx_data_size(
data->connection, 0,
192); 192);
if (ret) { if (ret) {
dev_err(dai->dev, dev_err(dai->dev,
"%d:Error during apbridgea set_tx_data_size, cport\n", "%d:Error during apbridgea set_tx_data_size, cport\n",
ret); ret);
mutex_unlock(&module->lock);
goto func_exit; goto func_exit;
} }
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;
} }
ret = gb_audio_gb_activate_tx(module->mgmt_connection,
data_cport);
if (ret) if (ret)
dev_err(dai->dev, "%d: Error during activate stream\n", ret); dev_err(dai->dev,
"%s:Error during activate stream,%d\n",
module->name, ret);
break;
}
state = GBAUDIO_CODEC_PREPARE;
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;
func_exit: func_exit:
mutex_unlock(&gb->lock); mutex_unlock(&codec->lock);
return ret; return 0;
} }
static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd,
...@@ -297,24 +615,19 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -297,24 +615,19 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd,
{ {
int ret; int ret;
int tx, rx, start, stop; int tx, rx, start, stop;
struct gbaudio_dai *gb_dai; struct gbaudio_data_connection *data;
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); struct gbaudio_module_info *module;
struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
if (!atomic_read(&gb->is_connected)) {
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) if (cmd == SNDRV_PCM_TRIGGER_STOP)
return 0; return 0;
return -ENODEV; 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; tx = rx = start = stop = 0;
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
...@@ -345,47 +658,60 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -345,47 +658,60 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd,
goto func_exit; 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) { if (start && tx) {
ret = gb_audio_apbridgea_prepare_tx(gb_dai->connection, 0); ret = gb_audio_apbridgea_prepare_tx(data->connection,
0);
if (!ret) if (!ret)
ret = gb_audio_apbridgea_start_tx(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); 0);
}
else if (start && rx) {
ret = gb_audio_apbridgea_prepare_rx(gb_dai->connection, 0);
if (!ret) if (!ret)
ret = gb_audio_apbridgea_start_rx(gb_dai->connection, ret = gb_audio_apbridgea_start_rx(data->connection,
0); 0);
} codec->stream[substream->stream].state = GBAUDIO_CODEC_START;
} else if (stop && tx) {
else if (stop && tx) { ret = gb_audio_apbridgea_stop_tx(data->connection, 0);
ret = gb_audio_apbridgea_stop_tx(gb_dai->connection, 0);
if (!ret) if (!ret)
ret = gb_audio_apbridgea_shutdown_tx(gb_dai->connection, ret = gb_audio_apbridgea_shutdown_tx(data->connection,
0); 0);
} codec->stream[substream->stream].state = GBAUDIO_CODEC_STOP;
} else if (stop && rx) {
else if (stop && rx) { ret = gb_audio_apbridgea_stop_rx(data->connection, 0);
ret = gb_audio_apbridgea_stop_rx(gb_dai->connection, 0);
if (!ret) if (!ret)
ret = gb_audio_apbridgea_shutdown_rx(gb_dai->connection, ret = gb_audio_apbridgea_shutdown_rx(data->connection,
0); 0);
} codec->stream[substream->stream].state = GBAUDIO_CODEC_STOP;
} else
else
ret = -EINVAL; ret = -EINVAL;
if (ret) if (ret)
dev_err(dai->dev, "%d:Error during %s stream\n", ret, dev_err(dai->dev, "%s:Error during %s stream:%d\n",
start ? "Start" : "Stop"); module->name, start ? "Start" : "Stop", ret);
mutex_unlock(&module->lock);
/* in case device removed, return 0 for stop trigger */
if (stop && (ret == -ENODEV))
ret = 0;
func_exit: func_exit:
mutex_unlock(&gb->lock); mutex_unlock(&codec->lock);
return ret; return ret;
} }
...@@ -409,476 +735,325 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = { ...@@ -409,476 +735,325 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = {
.digital_mute = gbcodec_digital_mute, .digital_mute = gbcodec_digital_mute,
}; };
/* int gbaudio_register_module(struct gbaudio_module_info *module)
* codec driver ops
*/
static int gbcodec_probe(struct snd_soc_codec *codec)
{
/* Empty function for now */
return 0;
}
static int gbcodec_remove(struct snd_soc_codec *codec)
{
/* Empty function for now */
return 0;
}
static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{ {
int ret = 0; int ret;
struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); struct snd_soc_codec *codec;
u8 *gbcodec_reg = gbcodec->reg;
if (reg == SND_SOC_NOPM)
return 0;
if (reg >= GBCODEC_REG_COUNT)
return 0;
gbcodec_reg[reg] = value; if (!gbcodec) {
dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, value); dev_err(module->dev, "GB Codec not yet probed\n");
return -EAGAIN;
}
return ret; codec = gbcodec->codec;
} mutex_lock(&gbcodec->lock);
static unsigned int gbcodec_read(struct snd_soc_codec *codec, if (module->num_dais) {
unsigned int reg) dev_err(gbcodec->dev,
{ "%d:DAIs not supported via gbcodec driver\n",
unsigned int val = 0; module->num_dais);
mutex_unlock(&gbcodec->lock);
return -EINVAL;
}
struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); if (module->dapm_widgets)
u8 *gbcodec_reg = gbcodec->reg; 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);
}
if (reg == SND_SOC_NOPM) list_add(&module->list, &gbcodec->module_list);
return 0; dev_dbg(codec->dev, "Registered %s module\n", module->name);
if (reg >= GBCODEC_REG_COUNT) mutex_unlock(&gbcodec->lock);
return 0; return 0;
val = gbcodec_reg[reg];
dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, val);
return val;
} }
EXPORT_SYMBOL(gbaudio_register_module);
/* void gbaudio_codec_cleanup(struct gbaudio_module_info *module)
* gb_snd management functions
*/
/* 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_data_connection *data;
struct gbaudio_dai *gb_dai; int pb_state = gbcodec->stream[0].state;
struct gb_connection *connection; int cap_state = gbcodec->stream[1].state;
long timeout = msecs_to_jiffies(50); /* 50ms */ int ret;
struct device *dev = gb->dev; uint16_t i2s_port, cportid;
list_for_each_entry(gb_dai, &gb->dai_list, list) { /* locks already acquired */
/* if (!pb_state && !cap_state)
* In case of BE dailink, need to deactivate APBridge return;
* manually
*/ if (pb_state == GBAUDIO_CODEC_START) {
if (atomic_read(&gb_dai->users)) { /* cleanup PB path, only APBridge specific */
/* schedule a wait event */ data = find_data(module, gbcodec->stream[0].dai_name);
timeout_result = if (!data) {
wait_event_interruptible_timeout( dev_err(gbcodec->dev, "%s: Missing data pointer\n",
gb_dai->wait_queue, __func__);
!atomic_read(&gb_dai->users), return;
timeout); }
if (!timeout_result) ret = gb_audio_apbridgea_stop_tx(data->connection, 0);
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) if (ret)
dev_info(dev, "%d:Failed during APBridge shutdown_tx\n", return;
ret); ret = gb_audio_apbridgea_shutdown_tx(data->connection, 0);
cportid = connection->intf_cport_id;
ret = gb_audio_gb_deactivate_tx(gb->mgmt_connection,
cportid);
if (ret) if (ret)
dev_info(dev, return;
"%d:Failed during deactivate_tx\n", i2s_port = 0; /* fixed for now */
ret); cportid = data->connection->hd_cport_id;
cportid = connection->hd_cport_id; ret = gb_audio_apbridgea_unregister_cport(data->connection,
ret = gb_audio_apbridgea_unregister_cport(connection, 0, i2s_port, cportid,
cportid,
AUDIO_APBRIDGEA_DIRECTION_TX); AUDIO_APBRIDGEA_DIRECTION_TX);
if (ret) gbcodec->stream[0].state = GBAUDIO_CODEC_SHUTDOWN;
dev_info(dev, "%d:Failed during unregister cport\n", }
ret);
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 snd_soc_codec *codec = gbcodec->codec;
struct device *dev = gbcodec->dev; struct snd_card *card = codec->card->snd_card;
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;
soc_codec_dev_gbcodec = devm_kzalloc(gbcodec->dev, dev_dbg(codec->dev, "Unregister %s module\n", module->name);
sizeof(*soc_codec_dev_gbcodec),
GFP_KERNEL);
if (!soc_codec_dev_gbcodec) {
dev_err(gbcodec->dev, "Malloc failed for codec_driver\n");
return -ENOMEM;
}
ret = gb_connection_enable(connection); /* complete widget processing, if ongoing */
if (ret) { snd_soc_dapm_sync(&codec->dapm);
dev_err(dev, "%d: Error while enabling mgmt connection\n", ret);
return ret;
}
gbcodec->dev_id = connection->intf->interface_id; down_write(&card->controls_rwsem);
/* fetch topology data */ mutex_lock(&gbcodec->lock);
ret = gb_audio_gb_get_topology(connection, &topology); dev_dbg(codec->dev, "Process Unregister %s module\n", module->name);
if (ret) { mutex_lock(&module->lock);
dev_err(dev, "%d:Error while fetching topology\n", ret);
goto tplg_fetch_err;
}
/* process topology data */ if (list_is_last(&module->list, &gbcodec->module_list)) {
ret = gbaudio_tplg_parse_data(gbcodec, topology); dev_dbg(codec->dev, "Last module removed, cleanup APBridge\n");
if (ret) { gbaudio_codec_cleanup(module);
dev_err(dev, "%d:Error while parsing topology data\n",
ret);
goto tplg_parse_err;
} }
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; module->is_connected = 0;
soc_codec_dev_gbcodec->num_controls = gbcodec->num_kcontrols; if (module->dapm_routes) {
soc_codec_dev_gbcodec->dapm_widgets = gbcodec->widgets; dev_dbg(codec->dev, "Removing %d routes\n",
soc_codec_dev_gbcodec->num_dapm_widgets = gbcodec->num_dapm_widgets; module->num_dapm_routes);
soc_codec_dev_gbcodec->dapm_routes = gbcodec->routes; snd_soc_dapm_del_routes(&codec->dapm, module->dapm_routes,
soc_codec_dev_gbcodec->num_dapm_routes = gbcodec->num_dapm_routes; module->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;
} }
if (module->controls) {
/* update DAI links in response to this codec */ dev_dbg(codec->dev, "Removing %d controls\n",
ret = msm8994_add_dailink("msm8994-tomtom-mtp-snd-card", gbcodec->name, module->num_controls);
gbcodec->dais[0].name, 1); soc_remove_codec_controls(codec, module->controls,
if (ret) { module->num_controls);
dev_err(dev, "%d: Failed to add DAI links\n", ret); }
goto add_dailink_err; 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: list_del(&module->list);
snd_soc_unregister_codec(dev); dev_dbg(codec->dev, "Unregistered %s module\n", module->name);
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;
}
static void gbaudio_unregister_codec(struct gbaudio_codec_info *gbcodec) mutex_unlock(&gbcodec->lock);
{ up_write(&card->controls_rwsem);
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);
} }
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 gbaudio_codec_info *info;
struct gb_audio_streaming_event_request *req = op->request->payload;
dev_warn(&connection->bundle->dev, info = devm_kzalloc(codec->dev, sizeof(*info), GFP_KERNEL);
"Audio Event received: cport: %u, event: %u\n", if (!info)
req->data_cport, req->event); return -ENOMEM;
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; 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; /* Empty function for now */
dev_warn(&connection->bundle->dev, "Audio Event received\n");
return 0; return 0;
} }
static int gb_audio_add_mgmt_connection(struct gbaudio_codec_info *gbcodec, static u8 gbcodec_reg[GBCODEC_REG_COUNT] = {
struct greybus_descriptor_cport *cport_desc, [GBCODEC_CTL_REG] = GBCODEC_CTL_REG_DEFAULT,
struct gb_bundle *bundle) [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 (reg == SND_SOC_NOPM)
if (gbcodec->mgmt_connection) { return 0;
dev_err(&bundle->dev,
"Can't have multiple Management connections\n");
return -ENODEV;
}
connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), BUG_ON(reg >= GBCODEC_REG_COUNT);
gbaudio_codec_request_handler); return 0;
if (IS_ERR(connection))
return PTR_ERR(connection);
gb_connection_set_data(connection, gbcodec); gbcodec_reg[reg] = value;
gbcodec->mgmt_connection = connection; 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, static unsigned int gbcodec_read(struct snd_soc_codec *codec,
struct greybus_descriptor_cport *cport_desc, unsigned int reg)
struct gb_bundle *bundle)
{ {
struct gb_connection *connection; unsigned int val = 0;
struct gbaudio_dai *dai;
dai = devm_kzalloc(gbcodec->dev, sizeof(*dai), GFP_KERNEL); if (reg == SND_SOC_NOPM)
if (!dai) { return 0;
dev_err(gbcodec->dev, "DAI Malloc failure\n");
return -ENOMEM;
}
connection = gb_connection_create_flags(bundle, BUG_ON(reg >= GBCODEC_REG_COUNT);
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);
}
gb_connection_set_data(connection, gbcodec); val = gbcodec_reg[reg];
atomic_set(&dai->users, 0); dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, val);
init_waitqueue_head(&dai->wait_queue);
dai->data_cport = connection->intf_cport_id;
dai->connection = connection;
list_add(&dai->list, &gbcodec->dai_list);
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;
mutex_lock(&gb_codec_list_lock); static struct snd_soc_dai_driver gbaudio_dai[] = {
/* {
* There can be only one Management connection and any number of data .name = "greybus-apb1",
* connections. .id = 0,
*/ .playback = {
gbcodec = devm_kzalloc(dev, sizeof(*gbcodec), GFP_KERNEL); .stream_name = "GB Audio Playback",
if (!gbcodec) { .rates = SNDRV_PCM_RATE_48000,
mutex_unlock(&gb_codec_list_lock); .formats = SNDRV_PCM_FORMAT_S16_LE,
return -ENOMEM; .rate_max = 48000,
} .rate_min = 48000,
.channels_min = 1,
gbcodec->num_data_connections = bundle->num_cports - 1; .channels_max = 2,
mutex_init(&gbcodec->lock); },
INIT_LIST_HEAD(&gbcodec->dai_list); .capture = {
INIT_LIST_HEAD(&gbcodec->widget_list); .stream_name = "GB Audio Capture",
INIT_LIST_HEAD(&gbcodec->codec_ctl_list); .rates = SNDRV_PCM_RATE_48000,
INIT_LIST_HEAD(&gbcodec->widget_ctl_list); .formats = SNDRV_PCM_FORMAT_S16_LE,
gbcodec->dev = dev; .rate_max = 48000,
snprintf(gbcodec->name, NAME_SIZE, "%s.%s", dev->driver->name, .rate_min = 48000,
dev_name(dev)); .channels_min = 1,
greybus_set_drvdata(bundle, gbcodec); .channels_max = 2,
},
/* Create all connections */ .ops = &gbcodec_dai_ops,
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;
}
}
/* There must be a management cport */ static struct snd_soc_codec_driver soc_codec_dev_gbaudio = {
if (!gbcodec->mgmt_connection) { .probe = gbcodec_probe,
ret = -EINVAL; .remove = gbcodec_remove,
dev_err(dev, "Missing management connection\n");
goto destroy_connections;
}
/* Initialize management connection */ .read = gbcodec_read,
ret = gbaudio_register_codec(gbcodec); .write = gbcodec_write,
if (ret)
goto destroy_connections;
/* Initialize data connections */ .reg_cache_size = GBCODEC_REG_COUNT,
list_for_each_entry(dai, &gbcodec->dai_list, list) { .reg_cache_default = gbcodec_reg_defaults,
ret = gb_connection_enable(dai->connection); .reg_word_size = 1,
if (ret)
goto remove_dai;
}
/* inform above layer for uevent */ .idle_bias_off = true,
dev_dbg(dev, "Inform set_event:%d to above layer\n", 1); .ignore_pmdown_time = 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);
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; 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); dev_dbg(dev, "%s: resume\n", __func__);
struct gbaudio_dai *dai, *_dai; return 0;
}
mutex_lock(&gb_codec_list_lock); static const struct dev_pm_ops gbaudio_codec_pm_ops = {
atomic_set(&gbcodec->is_connected, 0); .suspend = gbaudio_codec_suspend,
/* inform uevent to above layers */ .resume = gbaudio_codec_resume,
gb_audio_manager_remove(gbcodec->manager_id); };
#endif
mutex_lock(&gbcodec->lock); static int gbaudio_codec_probe(struct platform_device *pdev)
list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) {
gb_connection_disable(dai->connection); return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_gbaudio,
gbaudio_unregister_codec(gbcodec); gbaudio_dai, ARRAY_SIZE(gbaudio_dai));
}
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);
devm_kfree(&bundle->dev, gbcodec); static int gbaudio_codec_remove(struct platform_device *pdev)
mutex_unlock(&gb_codec_list_lock); {
snd_soc_unregister_codec(&pdev->dev);
return 0;
} }
static const struct greybus_bundle_id gb_audio_id_table[] = { static const struct of_device_id greybus_asoc_machine_of_match[] = {
{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO) }, { .compatible = "qcom,ara-codec", },
{ } {},
}; };
MODULE_DEVICE_TABLE(greybus, gb_audio_id_table);
static struct greybus_driver gb_audio_driver = { static struct platform_driver gbaudio_codec_driver = {
.name = "gb-audio", .driver = {
.probe = gb_audio_probe, .name = "gb-codec",
.disconnect = gb_audio_disconnect, .owner = THIS_MODULE,
.id_table = gb_audio_id_table, #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_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@linaro.org>");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:gbaudio-codec"); MODULE_ALIAS("platform:gbaudio-codec");
...@@ -68,6 +68,31 @@ static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = { ...@@ -68,6 +68,31 @@ static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = {
GBCODEC_APB2_MUX_REG_DEFAULT, GBCODEC_APB2_MUX_REG_DEFAULT,
}; };
enum gbaudio_codec_state {
GBAUDIO_CODEC_SHUTDOWN = 0,
GBAUDIO_CODEC_STARTUP,
GBAUDIO_CODEC_HWPARAMS,
GBAUDIO_CODEC_PREPARE,
GBAUDIO_CODEC_START,
GBAUDIO_CODEC_STOP,
};
struct gbaudio_stream {
const char *dai_name;
int state;
uint8_t sig_bits, channels;
uint32_t format, rate;
};
struct gbaudio_codec_info {
struct device *dev;
struct snd_soc_codec *codec;
struct list_head module_list;
struct gbaudio_stream stream[2]; /* PB/CAP */
struct mutex lock;
u8 reg[GBCODEC_REG_COUNT];
};
struct gbaudio_widget { struct gbaudio_widget {
__u8 id; __u8 id;
const char *name; const char *name;
...@@ -81,81 +106,80 @@ struct gbaudio_control { ...@@ -81,81 +106,80 @@ struct gbaudio_control {
struct list_head list; struct list_head list;
}; };
struct gbaudio_dai { struct gbaudio_data_connection {
__le16 data_cport; __le16 data_cport;
int cport_configured;
char name[NAME_SIZE]; char name[NAME_SIZE];
/* DAI users */
atomic_t users;
struct gb_connection *connection; struct gb_connection *connection;
struct list_head list; struct list_head list;
wait_queue_head_t wait_queue;
}; };
struct gbaudio_codec_info { /* stream direction */
#define GB_PLAYBACK BIT(0)
#define GB_CAPTURE BIT(1)
enum gbaudio_module_state {
GBAUDIO_MODULE_OFF = 0,
GBAUDIO_MODULE_ON,
};
struct gbaudio_module_info {
/* module info */ /* module info */
struct device *dev;
int dev_id; /* check if it should be bundle_id/hd_cport_id */ int dev_id; /* check if it should be bundle_id/hd_cport_id */
int vid; int vid;
int pid; int pid;
int slot; int slot;
int type; int type;
int dai_added;
int codec_registered;
int set_uevent; int set_uevent;
char vstr[NAME_SIZE]; char vstr[NAME_SIZE];
char pstr[NAME_SIZE]; char pstr[NAME_SIZE];
struct list_head list; struct list_head list;
struct gb_audio_topology *topology;
/* need to share this info to above user space */ /* need to share this info to above user space */
int manager_id; int manager_id;
char name[NAME_SIZE]; char name[NAME_SIZE];
/* /* used by codec_ops */
* there can be a rece condition between gb_audio_disconnect()
* and dai->trigger from above ASoC layer.
* To avoid any deadlock over codec_info->lock, atomic variable
* is used.
*/
atomic_t is_connected;
struct mutex lock; struct mutex lock;
int is_connected;
int ctrlstate[2]; /* PB/CAP */
/* soc related data */ /* connection info */
struct snd_soc_codec *codec;
struct device *dev;
u8 reg[GBCODEC_REG_COUNT];
/* dai_link related */
char card_name[NAME_SIZE];
char *dailink_name[MAX_DAIS];
int num_dai_links;
struct gb_connection *mgmt_connection; struct gb_connection *mgmt_connection;
size_t num_data_connections; size_t num_data_connections;
struct list_head data_list;
/* topology related */ /* topology related */
int num_dais; int num_dais;
int num_kcontrols; int num_controls;
int num_dapm_widgets; int num_dapm_widgets;
int num_dapm_routes; int num_dapm_routes;
unsigned long dai_offset; unsigned long dai_offset;
unsigned long widget_offset; unsigned long widget_offset;
unsigned long control_offset; unsigned long control_offset;
unsigned long route_offset; unsigned long route_offset;
struct snd_kcontrol_new *kctls; struct snd_kcontrol_new *controls;
struct snd_soc_dapm_widget *widgets; struct snd_soc_dapm_widget *dapm_widgets;
struct snd_soc_dapm_route *routes; struct snd_soc_dapm_route *dapm_routes;
struct snd_soc_dai_driver *dais; struct snd_soc_dai_driver *dais;
/* lists */
struct list_head dai_list;
struct list_head widget_list; struct list_head widget_list;
struct list_head codec_ctl_list; struct list_head ctl_list;
struct list_head widget_ctl_list; struct list_head widget_ctl_list;
struct gb_audio_topology *topology;
}; };
struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec, int gbaudio_tplg_parse_data(struct gbaudio_module_info *module,
int data_cport, const char *name);
int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec,
struct gb_audio_topology *tplg_data); struct gb_audio_topology *tplg_data);
void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec); void gbaudio_tplg_release(struct gbaudio_module_info *module);
int gbaudio_module_update(struct gbaudio_codec_info *codec,
const char *w_name,
struct gbaudio_module_info *module,
int enable);
int gbaudio_register_module(struct gbaudio_module_info *module);
void gbaudio_unregister_module(struct gbaudio_module_info *module);
/* protocol related */ /* protocol related */
extern int gb_audio_gb_get_topology(struct gb_connection *connection, extern int gb_audio_gb_get_topology(struct gb_connection *connection,
......
...@@ -25,7 +25,32 @@ struct gbaudio_ctl_pvt { ...@@ -25,7 +25,32 @@ struct gbaudio_ctl_pvt {
struct gb_audio_ctl_elem_info *info; 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) __u8 control_id, __u8 index)
{ {
struct gbaudio_control *control; struct gbaudio_control *control;
...@@ -33,14 +58,14 @@ static const char *gbaudio_map_controlid(struct gbaudio_codec_info *gbcodec, ...@@ -33,14 +58,14 @@ static const char *gbaudio_map_controlid(struct gbaudio_codec_info *gbcodec,
if (control_id == GBAUDIO_INVALID_ID) if (control_id == GBAUDIO_INVALID_ID)
return NULL; 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 (control->id == control_id) {
if (index == GBAUDIO_INVALID_ID) if (index == GBAUDIO_INVALID_ID)
return control->name; return control->name;
return control->texts[index]; 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 (control->id == control_id) {
if (index == GBAUDIO_INVALID_ID) if (index == GBAUDIO_INVALID_ID)
return control->name; return control->name;
...@@ -50,34 +75,23 @@ static const char *gbaudio_map_controlid(struct gbaudio_codec_info *gbcodec, ...@@ -50,34 +75,23 @@ static const char *gbaudio_map_controlid(struct gbaudio_codec_info *gbcodec,
return NULL; 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) const char *name)
{ {
struct gbaudio_widget *widget; struct gbaudio_widget *widget;
char widget_name[NAME_SIZE]; list_for_each_entry(widget, &module->widget_list, list) {
char prefix_name[NAME_SIZE]; if (!strncmp(widget->name, 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))
return widget->id; return widget->id;
} }
return -EINVAL; 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) __u8 widget_id)
{ {
struct gbaudio_widget *widget; 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) if (widget->id == widget_id)
return widget->name; return widget->name;
} }
...@@ -91,14 +105,16 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, ...@@ -91,14 +105,16 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol,
const char *name; const char *name;
struct gbaudio_ctl_pvt *data; struct gbaudio_ctl_pvt *data;
struct gb_audio_ctl_elem_info *info; struct gb_audio_ctl_elem_info *info;
struct gbaudio_module_info *module;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); 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; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
info = (struct gb_audio_ctl_elem_info *)data->info; info = (struct gb_audio_ctl_elem_info *)data->info;
if (!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; return -EINVAL;
} }
...@@ -118,7 +134,10 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, ...@@ -118,7 +134,10 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol,
uinfo->value.enumerated.items = max; uinfo->value.enumerated.items = max;
if (uinfo->value.enumerated.item > max - 1) if (uinfo->value.enumerated.item > max - 1)
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); uinfo->value.enumerated.item);
strlcpy(uinfo->value.enumerated.name, name, NAME_SIZE); strlcpy(uinfo->value.enumerated.name, name, NAME_SIZE);
break; break;
...@@ -137,16 +156,19 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, ...@@ -137,16 +156,19 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol,
struct gb_audio_ctl_elem_info *info; struct gb_audio_ctl_elem_info *info;
struct gbaudio_ctl_pvt *data; struct gbaudio_ctl_pvt *data;
struct gb_audio_ctl_elem_value gbvalue; struct gb_audio_ctl_elem_value gbvalue;
struct gbaudio_module_info *module;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
if (!atomic_read(&gb->is_connected)) dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
return -ENODEV; module = find_gb_module(gb, kcontrol->id.name);
if (!module)
return -EINVAL;
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
info = (struct gb_audio_ctl_elem_info *)data->info; 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); GB_AUDIO_INVALID_INDEX, &gbvalue);
if (ret) { if (ret) {
dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__,
...@@ -187,11 +209,14 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, ...@@ -187,11 +209,14 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol,
struct gb_audio_ctl_elem_info *info; struct gb_audio_ctl_elem_info *info;
struct gbaudio_ctl_pvt *data; struct gbaudio_ctl_pvt *data;
struct gb_audio_ctl_elem_value gbvalue; struct gb_audio_ctl_elem_value gbvalue;
struct gbaudio_module_info *module;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
if (!atomic_read(&gb->is_connected)) dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
return -ENODEV; module = find_gb_module(gb, kcontrol->id.name);
if (!module)
return -EINVAL;
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
info = (struct gb_audio_ctl_elem_info *)data->info; info = (struct gb_audio_ctl_elem_info *)data->info;
...@@ -223,7 +248,7 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, ...@@ -223,7 +248,7 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol,
if (ret) if (ret)
return 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); GB_AUDIO_INVALID_INDEX, &gbvalue);
if (ret) { if (ret) {
dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__,
...@@ -250,7 +275,11 @@ static int gbcodec_mixer_dapm_ctl_info(struct snd_kcontrol *kcontrol, ...@@ -250,7 +275,11 @@ static int gbcodec_mixer_dapm_ctl_info(struct snd_kcontrol *kcontrol,
int platform_max, platform_min; int platform_max, platform_min;
struct gbaudio_ctl_pvt *data; struct gbaudio_ctl_pvt *data;
struct gb_audio_ctl_elem_info *info; 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; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
info = (struct gb_audio_ctl_elem_info *)data->info; info = (struct gb_audio_ctl_elem_info *)data->info;
...@@ -282,13 +311,16 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, ...@@ -282,13 +311,16 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol,
struct gb_audio_ctl_elem_info *info; struct gb_audio_ctl_elem_info *info;
struct gbaudio_ctl_pvt *data; struct gbaudio_ctl_pvt *data;
struct gb_audio_ctl_elem_value gbvalue; 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_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec; struct snd_soc_codec *codec = widget->codec;
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
if (!atomic_read(&gb->is_connected)) dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
return -ENODEV; module = find_gb_module(gb, kcontrol->id.name);
if (!module)
return -EINVAL;
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
info = (struct gb_audio_ctl_elem_info *)data->info; info = (struct gb_audio_ctl_elem_info *)data->info;
...@@ -298,7 +330,7 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, ...@@ -298,7 +330,7 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol,
"GB: Control '%s' is stereo, which is not supported\n", "GB: Control '%s' is stereo, which is not supported\n",
kcontrol->id.name); 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); GB_AUDIO_INVALID_INDEX, &gbvalue);
if (ret) { if (ret) {
dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__,
...@@ -319,13 +351,16 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, ...@@ -319,13 +351,16 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol,
struct gb_audio_ctl_elem_info *info; struct gb_audio_ctl_elem_info *info;
struct gbaudio_ctl_pvt *data; struct gbaudio_ctl_pvt *data;
struct gb_audio_ctl_elem_value gbvalue; 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_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec; struct snd_soc_codec *codec = widget->codec;
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
if (!atomic_read(&gb->is_connected)) dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
return -ENODEV; module = find_gb_module(gb, kcontrol->id.name);
if (!module)
return -EINVAL;
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
info = (struct gb_audio_ctl_elem_info *)data->info; info = (struct gb_audio_ctl_elem_info *)data->info;
...@@ -352,7 +387,7 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, ...@@ -352,7 +387,7 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol,
} }
gbvalue.value.integer_value[0] = gbvalue.value.integer_value[0] =
ucontrol->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, data->ctl_id,
GB_AUDIO_INVALID_INDEX, &gbvalue); GB_AUDIO_INVALID_INDEX, &gbvalue);
if (ret) { if (ret) {
...@@ -420,7 +455,7 @@ static int gbaudio_validate_kcontrol_count(struct gb_audio_widget *w) ...@@ -420,7 +455,7 @@ static int gbaudio_validate_kcontrol_count(struct gb_audio_widget *w)
return 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 snd_kcontrol_new *kctl,
struct gb_audio_control *ctl) struct gb_audio_control *ctl)
{ {
...@@ -452,23 +487,23 @@ static int gbaudio_tplg_create_kcontrol(struct gbaudio_codec_info *gb, ...@@ -452,23 +487,23 @@ static int gbaudio_tplg_create_kcontrol(struct gbaudio_codec_info *gb,
static const char * const gbtexts[] = {"Stereo", "Left", "Right"}; static const char * const gbtexts[] = {"Stereo", "Left", "Right"};
static const SOC_ENUM_SINGLE_DECL( 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( 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 snd_kcontrol_new *kctl,
struct gb_audio_control *ctl) struct gb_audio_control *ctl)
{ {
switch (ctl->id) { switch (ctl->id) {
case 8: case 8:
*kctl = (struct snd_kcontrol_new) *kctl = (struct snd_kcontrol_new)
SOC_DAPM_ENUM(ctl->name, gbcodec_apb1_rx_enum); SOC_DAPM_ENUM(ctl->name, module_apb1_rx_enum);
break; break;
case 9: case 9:
*kctl = (struct snd_kcontrol_new) *kctl = (struct snd_kcontrol_new)
SOC_DAPM_ENUM(ctl->name, gbcodec_mic_enum); SOC_DAPM_ENUM(ctl->name, module_mic_enum);
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -477,7 +512,7 @@ static int gbaudio_tplg_create_enum_ctl(struct gbaudio_codec_info *gb, ...@@ -477,7 +512,7 @@ static int gbaudio_tplg_create_enum_ctl(struct gbaudio_codec_info *gb,
return 0; 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 snd_kcontrol_new *kctl,
struct gb_audio_control *ctl) struct gb_audio_control *ctl)
{ {
...@@ -498,7 +533,7 @@ static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_codec_info *gb, ...@@ -498,7 +533,7 @@ static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_codec_info *gb,
return 0; 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 snd_kcontrol_new *kctl,
struct gb_audio_control *ctl) struct gb_audio_control *ctl)
{ {
...@@ -532,11 +567,17 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, ...@@ -532,11 +567,17 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w,
int ret; int ret;
struct snd_soc_codec *codec = w->codec; struct snd_soc_codec *codec = w->codec;
struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(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); 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 */ /* map name to widget id */
wid = gbaudio_map_widgetname(gbcodec, w->name); wid = gbaudio_map_widgetname(module, w->name);
if (wid < 0) { if (wid < 0) {
dev_err(codec->dev, "Invalid widget name:%s\n", w->name); dev_err(codec->dev, "Invalid widget name:%s\n", w->name);
return -EINVAL; return -EINVAL;
...@@ -544,10 +585,16 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, ...@@ -544,10 +585,16 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w,
switch (event) { switch (event) {
case SND_SOC_DAPM_PRE_PMU: 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; break;
case SND_SOC_DAPM_POST_PMD: 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; break;
} }
if (ret) if (ret)
...@@ -556,7 +603,7 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, ...@@ -556,7 +603,7 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w,
return 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 snd_soc_dapm_widget *dw,
struct gb_audio_widget *w) struct gb_audio_widget *w)
{ {
...@@ -565,10 +612,11 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, ...@@ -565,10 +612,11 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec,
struct gb_audio_control *curr; struct gb_audio_control *curr;
struct gbaudio_control *control, *_control; struct gbaudio_control *control, *_control;
size_t size; size_t size;
char temp_name[NAME_SIZE];
ret = gbaudio_validate_kcontrol_count(w); ret = gbaudio_validate_kcontrol_count(w);
if (ret) { 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); w->ncontrols, w->name);
return ret; return ret;
} }
...@@ -576,7 +624,7 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, ...@@ -576,7 +624,7 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec,
/* allocate memory for kcontrol */ /* allocate memory for kcontrol */
if (w->ncontrols) { if (w->ncontrols) {
size = sizeof(struct snd_kcontrol_new) * 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) if (!widget_kctls)
return -ENOMEM; return -ENOMEM;
} }
...@@ -584,15 +632,15 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, ...@@ -584,15 +632,15 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec,
/* create relevant kcontrols */ /* create relevant kcontrols */
for (i = 0; i < w->ncontrols; i++) { for (i = 0; i < w->ncontrols; i++) {
curr = &w->ctl[i]; curr = &w->ctl[i];
ret = gbaudio_tplg_create_wcontrol(gbcodec, &widget_kctls[i], ret = gbaudio_tplg_create_wcontrol(module, &widget_kctls[i],
curr); curr);
if (ret) { if (ret) {
dev_err(gbcodec->dev, dev_err(module->dev,
"%s:%d type widget_ctl not supported\n", "%s:%d type widget_ctl not supported\n",
curr->name, curr->iface); curr->name, curr->iface);
goto error; goto error;
} }
control = devm_kzalloc(gbcodec->dev, control = devm_kzalloc(module->dev,
sizeof(struct gbaudio_control), sizeof(struct gbaudio_control),
GFP_KERNEL); GFP_KERNEL);
if (!control) { if (!control) {
...@@ -604,11 +652,15 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, ...@@ -604,11 +652,15 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec,
if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED)
control->texts = (const char * const *) control->texts = (const char * const *)
curr->info.value.enumerated.names; curr->info.value.enumerated.names;
list_add(&control->list, &gbcodec->widget_ctl_list); list_add(&control->list, &module->widget_ctl_list);
dev_dbg(gbcodec->dev, "%s: control of type %d created\n", dev_dbg(module->dev, "%s: control of type %d created\n",
widget_kctls[i].name, widget_kctls[i].iface); 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) { switch (w->type) {
case snd_soc_dapm_spk: case snd_soc_dapm_spk:
*dw = (struct snd_soc_dapm_widget) *dw = (struct snd_soc_dapm_widget)
...@@ -677,45 +729,19 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, ...@@ -677,45 +729,19 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec,
goto error; 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); dw->id);
return 0; return 0;
error: 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) {
list_del(&control->list); list_del(&control->list);
devm_kfree(gbcodec->dev, control); devm_kfree(module->dev, control);
} }
return ret; return ret;
} }
static int gbaudio_tplg_create_dai(struct gbaudio_codec_info *gbcodec, static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module,
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,
struct gb_audio_control *controls) struct gb_audio_control *controls)
{ {
int i, ret; int i, ret;
...@@ -723,22 +749,23 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_codec_info *gbcodec, ...@@ -723,22 +749,23 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_codec_info *gbcodec,
struct gb_audio_control *curr; struct gb_audio_control *curr;
struct gbaudio_control *control, *_control; struct gbaudio_control *control, *_control;
size_t size; size_t size;
char temp_name[NAME_SIZE];
size = sizeof(struct snd_kcontrol_new) * gbcodec->num_kcontrols; size = sizeof(struct snd_kcontrol_new) * module->num_controls;
dapm_kctls = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); dapm_kctls = devm_kzalloc(module->dev, size, GFP_KERNEL);
if (!dapm_kctls) if (!dapm_kctls)
return -ENOMEM; return -ENOMEM;
curr = controls; curr = controls;
for (i = 0; i < gbcodec->num_kcontrols; i++) { for (i = 0; i < module->num_controls; i++) {
ret = gbaudio_tplg_create_kcontrol(gbcodec, &dapm_kctls[i], ret = gbaudio_tplg_create_kcontrol(module, &dapm_kctls[i],
curr); curr);
if (ret) { 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); curr->name, curr->iface);
goto error; goto error;
} }
control = devm_kzalloc(gbcodec->dev, sizeof(struct control = devm_kzalloc(module->dev, sizeof(struct
gbaudio_control), gbaudio_control),
GFP_KERNEL); GFP_KERNEL);
if (!control) { if (!control) {
...@@ -746,29 +773,33 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_codec_info *gbcodec, ...@@ -746,29 +773,33 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_codec_info *gbcodec,
goto error; goto error;
} }
control->id = curr->id; 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; control->name = curr->name;
if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED)
control->texts = (const char * const *) control->texts = (const char * const *)
curr->info.value.enumerated.names; curr->info.value.enumerated.names;
list_add(&control->list, &gbcodec->codec_ctl_list); list_add(&control->list, &module->ctl_list);
dev_dbg(gbcodec->dev, "%d:%s created of type %d\n", curr->id, dev_dbg(module->dev, "%d:%s created of type %d\n", curr->id,
curr->name, curr->info.type); curr->name, curr->info.type);
curr++; curr++;
} }
gbcodec->kctls = dapm_kctls; module->controls = dapm_kctls;
return 0; return 0;
error: error:
list_for_each_entry_safe(control, _control, &gbcodec->codec_ctl_list, list_for_each_entry_safe(control, _control, &module->ctl_list,
list) { list) {
list_del(&control->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; 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) struct gb_audio_widget *widgets)
{ {
int i, ret, ncontrols; int i, ret, ncontrols;
...@@ -777,21 +808,21 @@ static int gbaudio_tplg_process_widgets(struct gbaudio_codec_info *gbcodec, ...@@ -777,21 +808,21 @@ static int gbaudio_tplg_process_widgets(struct gbaudio_codec_info *gbcodec,
struct gbaudio_widget *widget, *_widget; struct gbaudio_widget *widget, *_widget;
size_t size; size_t size;
size = sizeof(struct snd_soc_dapm_widget) * gbcodec->num_dapm_widgets; size = sizeof(struct snd_soc_dapm_widget) * module->num_dapm_widgets;
dapm_widgets = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); dapm_widgets = devm_kzalloc(module->dev, size, GFP_KERNEL);
if (!dapm_widgets) if (!dapm_widgets)
return -ENOMEM; return -ENOMEM;
curr = widgets; curr = widgets;
for (i = 0; i < gbcodec->num_dapm_widgets; i++) { for (i = 0; i < module->num_dapm_widgets; i++) {
ret = gbaudio_tplg_create_widget(gbcodec, &dapm_widgets[i], ret = gbaudio_tplg_create_widget(module, &dapm_widgets[i],
curr); curr);
if (ret) { 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); curr->name, curr->type);
goto error; goto error;
} }
widget = devm_kzalloc(gbcodec->dev, sizeof(struct widget = devm_kzalloc(module->dev, sizeof(struct
gbaudio_widget), gbaudio_widget),
GFP_KERNEL); GFP_KERNEL);
if (!widget) { if (!widget) {
...@@ -800,69 +831,26 @@ static int gbaudio_tplg_process_widgets(struct gbaudio_codec_info *gbcodec, ...@@ -800,69 +831,26 @@ static int gbaudio_tplg_process_widgets(struct gbaudio_codec_info *gbcodec,
} }
widget->id = curr->id; widget->id = curr->id;
widget->name = curr->name; widget->name = curr->name;
list_add(&widget->list, &gbcodec->widget_list); list_add(&widget->list, &module->widget_list);
ncontrols = curr->ncontrols; ncontrols = curr->ncontrols;
curr++; curr++;
curr += ncontrols * sizeof(struct gb_audio_control); curr += ncontrols * sizeof(struct gb_audio_control);
} }
gbcodec->widgets = dapm_widgets; module->dapm_widgets = dapm_widgets;
return 0; return 0;
error: error:
list_for_each_entry_safe(widget, _widget, &gbcodec->widget_list, list_for_each_entry_safe(widget, _widget, &module->widget_list,
list) { list) {
list_del(&widget->list); list_del(&widget->list);
devm_kfree(gbcodec->dev, widget); devm_kfree(module->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 */ devm_kfree(module->dev, dapm_widgets);
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++;
}
gbcodec->dais = gb_dais;
return 0;
error:
devm_kfree(gbcodec->dev, gb_dais);
return ret; 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) struct gb_audio_route *routes)
{ {
int i, ret; int i, ret;
...@@ -870,47 +858,47 @@ static int gbaudio_tplg_process_routes(struct gbaudio_codec_info *gbcodec, ...@@ -870,47 +858,47 @@ static int gbaudio_tplg_process_routes(struct gbaudio_codec_info *gbcodec,
struct gb_audio_route *curr; struct gb_audio_route *curr;
size_t size; size_t size;
size = sizeof(struct snd_soc_dapm_route) * gbcodec->num_dapm_routes; size = sizeof(struct snd_soc_dapm_route) * module->num_dapm_routes;
dapm_routes = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); dapm_routes = devm_kzalloc(module->dev, size, GFP_KERNEL);
if (!dapm_routes) if (!dapm_routes)
return -ENOMEM; return -ENOMEM;
gbcodec->routes = dapm_routes; module->dapm_routes = dapm_routes;
curr = routes; curr = routes;
for (i = 0; i < gbcodec->num_dapm_routes; i++) { for (i = 0; i < module->num_dapm_routes; i++) {
dapm_routes->sink = dapm_routes->sink =
gbaudio_map_widgetid(gbcodec, curr->destination_id); gbaudio_map_widgetid(module, curr->destination_id);
if (!dapm_routes->sink) { 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->source_id, curr->destination_id,
curr->control_id, curr->index); curr->control_id, curr->index);
ret = -EINVAL; ret = -EINVAL;
goto error; goto error;
} }
dapm_routes->source = dapm_routes->source =
gbaudio_map_widgetid(gbcodec, curr->source_id); gbaudio_map_widgetid(module, curr->source_id);
if (!dapm_routes->source) { 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->source_id, curr->destination_id,
curr->control_id, curr->index); curr->control_id, curr->index);
ret = -EINVAL; ret = -EINVAL;
goto error; goto error;
} }
dapm_routes->control = dapm_routes->control =
gbaudio_map_controlid(gbcodec, gbaudio_map_controlid(module,
curr->control_id, curr->control_id,
curr->index); curr->index);
if ((curr->control_id != GBAUDIO_INVALID_ID) && if ((curr->control_id != GBAUDIO_INVALID_ID) &&
!dapm_routes->control) { !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->source_id, curr->destination_id,
curr->control_id, curr->index); curr->control_id, curr->index);
ret = -EINVAL; ret = -EINVAL;
goto error; 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->control) ? dapm_routes->control:"NULL",
dapm_routes->source); dapm_routes->source);
dapm_routes++; dapm_routes++;
...@@ -920,41 +908,39 @@ static int gbaudio_tplg_process_routes(struct gbaudio_codec_info *gbcodec, ...@@ -920,41 +908,39 @@ static int gbaudio_tplg_process_routes(struct gbaudio_codec_info *gbcodec,
return 0; return 0;
error: error:
devm_kfree(gbcodec->dev, dapm_routes); devm_kfree(module->dev, dapm_routes);
return ret; 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) struct gb_audio_topology *tplg_data)
{ {
/* fetch no. of kcontrols, widgets & routes */ /* fetch no. of kcontrols, widgets & routes */
gbcodec->num_dais = tplg_data->num_dais; module->num_controls = tplg_data->num_controls;
gbcodec->num_kcontrols = tplg_data->num_controls; module->num_dapm_widgets = tplg_data->num_widgets;
gbcodec->num_dapm_widgets = tplg_data->num_widgets; module->num_dapm_routes = tplg_data->num_routes;
gbcodec->num_dapm_routes = tplg_data->num_routes;
/* update block offset */ /* update block offset */
gbcodec->dai_offset = (unsigned long)&tplg_data->data; module->dai_offset = (unsigned long)&tplg_data->data;
gbcodec->control_offset = gbcodec->dai_offset + tplg_data->size_dais; module->control_offset = module->dai_offset + tplg_data->size_dais;
gbcodec->widget_offset = gbcodec->control_offset + module->widget_offset = module->control_offset +
tplg_data->size_controls; tplg_data->size_controls;
gbcodec->route_offset = gbcodec->widget_offset + module->route_offset = module->widget_offset +
tplg_data->size_widgets; tplg_data->size_widgets;
dev_dbg(gbcodec->dev, "DAI offset is 0x%lx\n", gbcodec->dai_offset); dev_dbg(module->dev, "DAI offset is 0x%lx\n", module->dai_offset);
dev_dbg(gbcodec->dev, "control offset is %lx\n", dev_dbg(module->dev, "control offset is %lx\n",
gbcodec->control_offset); module->control_offset);
dev_dbg(gbcodec->dev, "widget offset is %lx\n", gbcodec->widget_offset); dev_dbg(module->dev, "widget offset is %lx\n", module->widget_offset);
dev_dbg(gbcodec->dev, "route offset is %lx\n", gbcodec->route_offset); dev_dbg(module->dev, "route offset is %lx\n", module->route_offset);
return 0; 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) struct gb_audio_topology *tplg_data)
{ {
int ret; int ret;
struct gb_audio_dai *dais;
struct gb_audio_control *controls; struct gb_audio_control *controls;
struct gb_audio_widget *widgets; struct gb_audio_widget *widgets;
struct gb_audio_route *routes; struct gb_audio_route *routes;
...@@ -962,90 +948,80 @@ int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, ...@@ -962,90 +948,80 @@ int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec,
if (!tplg_data) if (!tplg_data)
return -EINVAL; return -EINVAL;
ret = gbaudio_tplg_process_header(gbcodec, tplg_data); ret = gbaudio_tplg_process_header(module, tplg_data);
if (ret) { 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); ret);
return ret; return ret;
} }
/* process control */ /* process control */
controls = (struct gb_audio_control *)gbcodec->control_offset; controls = (struct gb_audio_control *)module->control_offset;
ret = gbaudio_tplg_process_kcontrols(gbcodec, controls); ret = gbaudio_tplg_process_kcontrols(module, controls);
if (ret) { if (ret) {
dev_err(gbcodec->dev, dev_err(module->dev,
"%d: Error in parsing controls data\n", ret); "%d: Error in parsing controls data\n", ret);
return ret; return ret;
} }
dev_dbg(gbcodec->dev, "Control parsing finished\n"); dev_dbg(module->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");
/* process widgets */ /* process widgets */
widgets = (struct gb_audio_widget *)gbcodec->widget_offset; widgets = (struct gb_audio_widget *)module->widget_offset;
ret = gbaudio_tplg_process_widgets(gbcodec, widgets); ret = gbaudio_tplg_process_widgets(module, widgets);
if (ret) { if (ret) {
dev_err(gbcodec->dev, dev_err(module->dev,
"%d: Error in parsing widgets data\n", ret); "%d: Error in parsing widgets data\n", ret);
return ret; return ret;
} }
dev_dbg(gbcodec->dev, "Widget parsing finished\n"); dev_dbg(module->dev, "Widget parsing finished\n");
/* process route */ /* process route */
routes = (struct gb_audio_route *)gbcodec->route_offset; routes = (struct gb_audio_route *)module->route_offset;
ret = gbaudio_tplg_process_routes(gbcodec, routes); ret = gbaudio_tplg_process_routes(module, routes);
if (ret) { if (ret) {
dev_err(gbcodec->dev, dev_err(module->dev,
"%d: Error in parsing routes data\n", ret); "%d: Error in parsing routes data\n", ret);
return ret; return ret;
} }
dev_dbg(gbcodec->dev, "Route parsing finished\n"); dev_dbg(module->dev, "Route parsing finished\n");
return ret; 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_control *control, *_control;
struct gbaudio_widget *widget, *_widget; struct gbaudio_widget *widget, *_widget;
if (!gbcodec->topology) if (!module->topology)
return; return;
/* release kcontrols */ /* 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) {
list_del(&control->list); list_del(&control->list);
devm_kfree(gbcodec->dev, control); devm_kfree(module->dev, control);
} }
if (gbcodec->kctls) if (module->controls)
devm_kfree(gbcodec->dev, gbcodec->kctls); devm_kfree(module->dev, module->controls);
/* release widget 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) {
list_del(&control->list); list_del(&control->list);
devm_kfree(gbcodec->dev, control); devm_kfree(module->dev, control);
} }
/* release widgets */ /* release widgets */
list_for_each_entry_safe(widget, _widget, &gbcodec->widget_list, list_for_each_entry_safe(widget, _widget, &module->widget_list,
list) { list) {
list_del(&widget->list); list_del(&widget->list);
devm_kfree(gbcodec->dev, widget); devm_kfree(module->dev, widget);
} }
if (gbcodec->widgets) if (module->dapm_widgets)
devm_kfree(gbcodec->dev, gbcodec->widgets); devm_kfree(module->dev, module->dapm_widgets);
/* release routes */ /* release routes */
if (gbcodec->routes) if (module->dapm_routes)
devm_kfree(gbcodec->dev, gbcodec->routes); devm_kfree(module->dev, module->dapm_routes);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment