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,65 +9,300 @@ #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; } +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 */ static int gbcodec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret; + 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); - struct gbaudio_dai *gb_dai; - struct gbaudio_codec_info *gb = 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; - - /* 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; } - /* 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, - AUDIO_APBRIDGEA_DIRECTION_TX); - dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name, cportid, - ret); + 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; + } - if (!ret) - atomic_inc(&gb_dai->users); - mutex_unlock(&gb->lock); + /* 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); return ret; } @@ -77,60 +312,79 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, { 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); - struct gbaudio_dai *gb_dai; - struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + mutex_lock(&codec->lock); - /* 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; - } - - atomic_dec(&gb_dai->users); - - if (!atomic_read(&gb->is_connected)) { - 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; } - 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) { + 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; + } - 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; + } - if (ret) - dev_err(dai->dev, "%d:Error during deactivate\n", 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, + /* 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); - - 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); - - /* - if (!atomic_read(&gb_dai->users)) - wake_up_interruptible(&gb_dai->wait_queue); - */ - + 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)) + 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; - - /* 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; } /* @@ -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)) + 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; - - /* 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; } - /* deactivate rx/tx */ - data_cport = gb_dai->connection->intf_cport_id; - - 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; + 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; } - ret = gb_audio_apbridgea_set_rx_data_size(gb_dai->connection, 0, - 192); - if (ret) { - dev_err(dai->dev, + + /* 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 */ + 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; + 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_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; - } - 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); + struct gbaudio_data_connection *data; + 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) 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, + 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(data->connection, 0); - } - - else if (start && rx) { - ret = gb_audio_apbridgea_prepare_rx(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_start_rx(gb_dai->connection, - 0); - } - - else if (stop && tx) { - ret = gb_audio_apbridgea_stop_tx(gb_dai->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,11 +735,178 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = { .digital_mute = gbcodec_digital_mute, }; +int gbaudio_register_module(struct gbaudio_module_info *module) +{ + int ret; + struct snd_soc_codec *codec; + + if (!gbcodec) { + dev_err(module->dev, "GB Codec not yet probed\n"); + return -EAGAIN; + } + + codec = gbcodec->codec; + mutex_lock(&gbcodec->lock); + + 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 (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); + } + + list_add(&module->list, &gbcodec->module_list); + dev_dbg(codec->dev, "Registered %s module\n", module->name); + + mutex_unlock(&gbcodec->lock); + return 0; +} +EXPORT_SYMBOL(gbaudio_register_module); + +void gbaudio_codec_cleanup(struct gbaudio_module_info *module) +{ + 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; + + /* locks already acquired */ + if (!pb_state && !cap_state) + return; + + 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); + 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; + } +} + +void gbaudio_unregister_module(struct gbaudio_module_info *module) +{ + struct snd_soc_codec *codec = gbcodec->codec; + struct snd_card *card = codec->card->snd_card; + + dev_dbg(codec->dev, "Unregister %s module\n", module->name); + + /* complete widget processing, if ongoing */ + snd_soc_dapm_sync(&codec->dapm); + + down_write(&card->controls_rwsem); + mutex_lock(&gbcodec->lock); + dev_dbg(codec->dev, "Process Unregister %s module\n", module->name); + mutex_lock(&module->lock); + + if (list_is_last(&module->list, &gbcodec->module_list)) { + dev_dbg(codec->dev, "Last module removed, cleanup APBridge\n"); + gbaudio_codec_cleanup(module); + } + + 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); + } + 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); + } + + mutex_unlock(&module->lock); + + list_del(&module->list); + dev_dbg(codec->dev, "Unregistered %s module\n", module->name); + + mutex_unlock(&gbcodec->lock); + up_write(&card->controls_rwsem); +} +EXPORT_SYMBOL(gbaudio_unregister_module); + /* * codec driver ops */ static int gbcodec_probe(struct snd_soc_codec *codec) { + struct gbaudio_codec_info *info; + + info = devm_kzalloc(codec->dev, sizeof(*info), GFP_KERNEL); + if (!info) + 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; } @@ -424,17 +917,26 @@ static int gbcodec_remove(struct snd_soc_codec *codec) return 0; } +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) { int ret = 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) + BUG_ON(reg >= GBCODEC_REG_COUNT); return 0; gbcodec_reg[reg] = value; @@ -448,14 +950,10 @@ static unsigned int gbcodec_read(struct snd_soc_codec *codec, { 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; + BUG_ON(reg >= GBCODEC_REG_COUNT); val = gbcodec_reg[reg]; dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, val); @@ -463,422 +961,99 @@ static unsigned int gbcodec_read(struct snd_soc_codec *codec, return val; } -/* - * 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_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, - AUDIO_APBRIDGEA_DIRECTION_TX); - if (ret) - dev_info(dev, "%d:Failed during unregister cport\n", - ret); - } - } -} - -static int gbaudio_register_codec(struct gbaudio_codec_info *gbcodec) -{ - 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; - - 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; - } - - ret = gb_connection_enable(connection); - if (ret) { - dev_err(dev, "%d: Error while enabling mgmt connection\n", ret); - return ret; - } - - 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; - } - - /* 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; - } - 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; - } - - /* 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; - } - gbcodec->num_dai_links = 1; - - return 0; - -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; -} - -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); -} - -static int gbaudio_codec_request_handler(struct gb_operation *op) -{ - struct gb_connection *connection = op->connection; - struct gb_audio_streaming_event_request *req = op->request->payload; - - dev_warn(&connection->bundle->dev, - "Audio Event received: cport: %u, event: %u\n", - req->data_cport, req->event); - - return 0; -} - -static int gbaudio_dai_request_handler(struct gb_operation *op) -{ - struct gb_connection *connection = op->connection; - - dev_warn(&connection->bundle->dev, "Audio Event received\n"); - - return 0; -} - -static int gb_audio_add_mgmt_connection(struct gbaudio_codec_info *gbcodec, - struct greybus_descriptor_cport *cport_desc, - struct gb_bundle *bundle) -{ - struct gb_connection *connection; - - /* Management Cport */ - if (gbcodec->mgmt_connection) { - dev_err(&bundle->dev, - "Can't have multiple Management connections\n"); - return -ENODEV; - } - - connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), - gbaudio_codec_request_handler); - if (IS_ERR(connection)) - return PTR_ERR(connection); - - gb_connection_set_data(connection, gbcodec); - gbcodec->mgmt_connection = connection; - - return 0; -} - -static int gb_audio_add_data_connection(struct gbaudio_codec_info *gbcodec, - struct greybus_descriptor_cport *cport_desc, - struct gb_bundle *bundle) -{ - struct gb_connection *connection; - struct gbaudio_dai *dai; - - dai = devm_kzalloc(gbcodec->dev, sizeof(*dai), GFP_KERNEL); - if (!dai) { - dev_err(gbcodec->dev, "DAI Malloc failure\n"); - return -ENOMEM; - } - - 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); - } - - 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); - - return 0; -} -/* - * 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); - /* - * 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; - } - - 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; - } - } - - /* There must be a management cport */ - if (!gbcodec->mgmt_connection) { - ret = -EINVAL; - dev_err(dev, "Missing management connection\n"); - goto destroy_connections; - } - - /* 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); - - 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); - - 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) -{ - struct gbaudio_codec_info *gbcodec = greybus_get_drvdata(bundle); - struct gbaudio_dai *dai, *_dai; - - mutex_lock(&gb_codec_list_lock); - atomic_set(&gbcodec->is_connected, 0); - /* inform uevent to above layers */ - gb_audio_manager_remove(gbcodec->manager_id); - - 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); - - devm_kfree(&bundle->dev, gbcodec); - mutex_unlock(&gb_codec_list_lock); -} - -static const struct greybus_bundle_id gb_audio_id_table[] = { - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO) }, - { } +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, + }, }; -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 snd_soc_codec_driver soc_codec_dev_gbaudio = { + .probe = gbcodec_probe, + .remove = gbcodec_remove, + + .read = gbcodec_read, + .write = gbcodec_write, + + .reg_cache_size = GBCODEC_REG_COUNT, + .reg_cache_default = gbcodec_reg_defaults, + .reg_word_size = 1, + + .idle_bias_off = true, + .ignore_pmdown_time = 1, }; -module_greybus_driver(gb_audio_driver); -MODULE_DESCRIPTION("Greybus Audio codec driver"); + +#ifdef CONFIG_PM +static int gbaudio_codec_suspend(struct device *dev) +{ + dev_dbg(dev, "%s: suspend\n", __func__); + return 0; +} + +static int gbaudio_codec_resume(struct device *dev) +{ + dev_dbg(dev, "%s: resume\n", __func__); + return 0; +} + +static const struct dev_pm_ops gbaudio_codec_pm_ops = { + .suspend = gbaudio_codec_suspend, + .resume = gbaudio_codec_resume, +}; +#endif + +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)); +} + +static int gbaudio_codec_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static const struct of_device_id greybus_asoc_machine_of_match[] = { + { .compatible = "qcom,ara-codec", }, + {}, +}; + +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_platform_driver(gbaudio_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(module->dev, widget); } - devm_kfree(gbcodec->dev, dapm_widgets); + devm_kfree(module->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++; - } - gbcodec->dais = gb_dais; - - return 0; - -error: - devm_kfree(gbcodec->dev, gb_dais); - 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); }