From 6dd67645f22cfeb55a32e9a08c92deb297d06935 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Tue, 29 Mar 2016 23:31:41 +0530 Subject: [PATCH] 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: Vaibhav Agarwal Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 1413 ++++++++++++---------- drivers/staging/greybus/audio_codec.h | 96 +- drivers/staging/greybus/audio_topology.c | 408 +++---- 3 files changed, 1046 insertions(+), 871 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 30b381ab8a1f..ab24ec8c5ab8 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -1,7 +1,7 @@ /* - * 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. */ @@ -9,128 +9,382 @@ #include #include #include -#include #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; } @@ -142,19 +396,17 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, 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; } /* @@ -164,54 +416,86 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, 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; } @@ -220,76 +504,110 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, { 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, @@ -297,24 +615,19 @@ 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: @@ -345,47 +658,60 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, 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; } @@ -409,476 +735,325 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = { .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 "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:gbaudio-codec"); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 9197adcbc32e..a2697dd62949 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -68,6 +68,31 @@ static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = { 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 { __u8 id; const char *name; @@ -81,81 +106,80 @@ struct gbaudio_control { struct list_head list; }; -struct gbaudio_dai { +struct gbaudio_data_connection { __le16 data_cport; + int cport_configured; char name[NAME_SIZE]; - /* DAI users */ - atomic_t users; struct gb_connection *connection; 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 */ + struct device *dev; int dev_id; /* check if it should be bundle_id/hd_cport_id */ int vid; int pid; int slot; int type; - int dai_added; - int codec_registered; int set_uevent; char vstr[NAME_SIZE]; char pstr[NAME_SIZE]; struct list_head list; - struct gb_audio_topology *topology; /* need to share this info to above user space */ int manager_id; char name[NAME_SIZE]; - /* - * 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; + /* used by codec_ops */ struct mutex lock; + int is_connected; + int ctrlstate[2]; /* PB/CAP */ - /* soc related data */ - 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; - + /* connection info */ struct gb_connection *mgmt_connection; size_t num_data_connections; + struct list_head data_list; + /* topology related */ int num_dais; - int num_kcontrols; + int num_controls; int num_dapm_widgets; int num_dapm_routes; unsigned long dai_offset; unsigned long widget_offset; unsigned long control_offset; unsigned long route_offset; - struct snd_kcontrol_new *kctls; - struct snd_soc_dapm_widget *widgets; - struct snd_soc_dapm_route *routes; + struct snd_kcontrol_new *controls; + struct snd_soc_dapm_widget *dapm_widgets; + struct snd_soc_dapm_route *dapm_routes; struct snd_soc_dai_driver *dais; - /* lists */ - struct list_head dai_list; struct list_head widget_list; - struct list_head codec_ctl_list; + struct list_head 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 data_cport, const char *name); -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); -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 */ extern int gb_audio_gb_get_topology(struct gb_connection *connection, diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 1651c14c87ba..4901348a2ada 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -25,7 +25,32 @@ struct gbaudio_ctl_pvt { 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; @@ -33,14 +58,14 @@ static const char *gbaudio_map_controlid(struct gbaudio_codec_info *gbcodec, 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; @@ -50,34 +75,23 @@ static const char *gbaudio_map_controlid(struct gbaudio_codec_info *gbcodec, 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; } @@ -91,14 +105,16 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, 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; } @@ -118,7 +134,10 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, 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; @@ -137,16 +156,19 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, 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__, @@ -187,11 +209,14 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, 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; @@ -223,7 +248,7 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, 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__, @@ -250,7 +275,11 @@ static int gbcodec_mixer_dapm_ctl_info(struct snd_kcontrol *kcontrol, 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; @@ -282,13 +311,16 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, 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; @@ -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", 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__, @@ -319,13 +351,16 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, 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; @@ -352,7 +387,7 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, } 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) { @@ -420,7 +455,7 @@ static int gbaudio_validate_kcontrol_count(struct gb_audio_widget *w) 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) { @@ -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 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; @@ -477,7 +512,7 @@ static int gbaudio_tplg_create_enum_ctl(struct gbaudio_codec_info *gb, 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) { @@ -498,7 +533,7 @@ static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_codec_info *gb, 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) { @@ -532,11 +567,17 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, 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; @@ -544,10 +585,16 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, 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) @@ -556,7 +603,7 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, 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) { @@ -565,10 +612,11 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, 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; } @@ -576,7 +624,7 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, /* 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; } @@ -584,15 +632,15 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, /* 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) { @@ -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) 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) @@ -677,45 +729,19 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, 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; @@ -723,22 +749,23 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_codec_info *gbcodec, 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) { @@ -746,29 +773,33 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_codec_info *gbcodec, 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; @@ -777,21 +808,21 @@ static int gbaudio_tplg_process_widgets(struct gbaudio_codec_info *gbcodec, 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) { @@ -800,69 +831,26 @@ static int gbaudio_tplg_process_widgets(struct gbaudio_codec_info *gbcodec, } 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; @@ -870,47 +858,47 @@ static int gbaudio_tplg_process_routes(struct gbaudio_codec_info *gbcodec, 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++; @@ -920,41 +908,39 @@ static int gbaudio_tplg_process_routes(struct gbaudio_codec_info *gbcodec, 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; @@ -962,90 +948,80 @@ int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, 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); } -- 2.34.1