From 796fad441cb248c1eac88bfb3a5929bb1a10fabb Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Thu, 28 Jan 2016 21:15:39 +0530 Subject: [PATCH] greybus: audio: codec driver cleanup audio codec driver is now moved to bundle driver approach. This resolved many race conditions related to audio mgmt & data connection init/exit sequence. Thus, a lot of helper functions can now be safely removed. Signed-off-by: Vaibhav Agarwal Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 622 ++++++++--------------- drivers/staging/greybus/audio_codec.h | 21 +- drivers/staging/greybus/audio_topology.c | 99 +--- 3 files changed, 230 insertions(+), 512 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index b43b5432d7a6..ad28c10fa154 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -18,30 +18,38 @@ static DEFINE_MUTEX(gb_codec_list_lock); static LIST_HEAD(gb_codec_list); +struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec, + int data_cport, const char *name) +{ + struct gbaudio_dai *dai; + + 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; + } + return NULL; +} + /* * codec DAI ops */ static int gbcodec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret, found; + int ret; __u16 i2s_port, cportid; struct gbaudio_dai *gb_dai; - struct gb_audio *audio = dev_get_drvdata(dai->dev); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); /* find the dai */ - found = 0; - list_for_each_entry(gb_dai, &gb->dai_list, list) { - if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { - found = 1; - break; - } - } - - if (!found) { + 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; } @@ -54,7 +62,8 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, ret); if (!ret) - atomic_inc(&gb->users); + atomic_inc(&gb_dai->users); + mutex_unlock(&gb->lock); return ret; } @@ -62,28 +71,21 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, static void gbcodec_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret, found; + int ret; __u16 i2s_port, cportid; struct gbaudio_dai *gb_dai; - struct gb_audio *audio = dev_get_drvdata(dai->dev); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); /* find the dai */ - found = 0; - list_for_each_entry(gb_dai, &gb->dai_list, list) { - if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { - found = 1; - break; - } - } - - if (!found) { + 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); - return; + goto func_exit; } - atomic_dec(&gb->users); + atomic_dec(&gb_dai->users); /* deactivate rx/tx */ cportid = gb_dai->connection->intf_cport_id; @@ -97,7 +99,7 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, break; default: dev_err(dai->dev, "Invalid stream type during shutdown\n"); - return; + goto func_exit; } if (ret) @@ -110,6 +112,8 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, 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); return; } @@ -118,26 +122,20 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hwparams, struct snd_soc_dai *dai) { - int ret, found; + int ret; uint8_t sig_bits, channels; uint32_t format, rate; uint16_t data_cport; struct gbaudio_dai *gb_dai; - struct gb_audio *audio = dev_get_drvdata(dai->dev); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); /* find the dai */ - found = 0; - list_for_each_entry(gb_dai, &gb->dai_list, list) { - if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { - found = 1; - break; - } - } - - if (!found) { + 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); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } /* @@ -147,21 +145,24 @@ 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)); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } channels = params_channels(hwparams); if (params_rate(hwparams) != 48000) { dev_err(dai->dev, "Invalid sampling rate:%d\n", params_rate(hwparams)); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } 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)); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } format = GB_AUDIO_PCM_FMT_S16_LE; @@ -176,7 +177,7 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, rate, channels, sig_bits); if (ret) { dev_err(dai->dev, "%d: Error during set_pcm\n", ret); - return ret; + goto func_exit; } /* @@ -190,31 +191,26 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, 6144000); if (ret) dev_err(dai->dev, "%d: Error during set_config\n", ret); - +func_exit: + mutex_unlock(&gb->lock); return ret; } static int gbcodec_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret, found; + int ret; uint16_t data_cport; struct gbaudio_dai *gb_dai; - struct gb_audio *audio = dev_get_drvdata(dai->dev); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); /* find the dai */ - found = 0; - list_for_each_entry(gb_dai, &gb->dai_list, list) { - if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { - found = 1; - break; - } - } - - if (!found) { + 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); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } /* deactivate rx/tx */ @@ -228,7 +224,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, dev_err(dai->dev, "%d:Error during set_rx_data_size, cport:%d\n", ret, data_cport); - return ret; + goto func_exit; } ret = gb_audio_apbridgea_set_rx_data_size(gb_dai->connection, 0, 192); @@ -236,7 +232,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, dev_err(dai->dev, "%d:Error during apbridgea_set_rx_data_size\n", ret); - return ret; + goto func_exit; } ret = gb_audio_gb_activate_rx(gb->mgmt_connection, data_cport); break; @@ -247,7 +243,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, dev_err(dai->dev, "%d:Error during module set_tx_data_size, cport:%d\n", ret, data_cport); - return ret; + goto func_exit; } ret = gb_audio_apbridgea_set_tx_data_size(gb_dai->connection, 0, 192); @@ -255,43 +251,40 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, dev_err(dai->dev, "%d:Error during apbridgea set_tx_data_size, cport\n", ret); - return 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); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } if (ret) dev_err(dai->dev, "%d: Error during activate stream\n", ret); +func_exit: + mutex_unlock(&gb->lock); return ret; } static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - int ret, found; + int ret; int tx, rx, start, stop; struct gbaudio_dai *gb_dai; - struct gb_audio *audio = dev_get_drvdata(dai->dev); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); /* find the dai */ - found = 0; - list_for_each_entry(gb_dai, &gb->dai_list, list) { - if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { - found = 1; - break; - } - } - - if (!found) { + 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); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } tx = rx = start = stop = 0; @@ -306,7 +299,8 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, break; default: dev_err(dai->dev, "Invalid tigger cmd:%d\n", cmd); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } switch (substream->stream) { @@ -319,7 +313,8 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, default: dev_err(dai->dev, "Invalid stream type:%d\n", substream->stream); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } if (start && tx) @@ -341,6 +336,8 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, dev_err(dai->dev, "%d:Error during %s stream\n", ret, start ? "Start" : "Stop"); +func_exit: + mutex_unlock(&gb->lock); return ret; } @@ -383,8 +380,7 @@ static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { int ret = 0; - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gbcodec = audio->gbcodec; + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); u8 *gbcodec_reg = gbcodec->reg; if (reg == SND_SOC_NOPM) @@ -404,8 +400,7 @@ static unsigned int gbcodec_read(struct snd_soc_codec *codec, { unsigned int val = 0; - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gbcodec = audio->gbcodec; + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); u8 *gbcodec_reg = gbcodec->reg; if (reg == SND_SOC_NOPM) @@ -447,28 +442,15 @@ static struct snd_soc_dai_link gbaudio_dailink = { .be_id = 34, }; -static void gbaudio_remove_dailinks(struct gbaudio_codec_info *gbcodec) -{ - int i; - - for (i = 0; i < gbcodec->num_dai_links; i++) { - dev_dbg(gbcodec->dev, "Remove %s: DAI link\n", - gbcodec->dailink_name[i]); - devm_kfree(gbcodec->dev, gbcodec->dailink_name[i]); - gbcodec->dailink_name[i] = NULL; - } - gbcodec->num_dai_links = 0; -} - static int gbaudio_add_dailinks(struct gbaudio_codec_info *gbcodec) { int ret, i; char *dai_link_name; - struct snd_soc_dai_link *dai; + struct snd_soc_dai_link *dailink; struct device *dev = gbcodec->dev; - dai = &gbaudio_dailink; - dai->codec_name = gbcodec->name; + dailink = &gbaudio_dailink; + dailink->codec_name = gbcodec->name; /* FIXME * allocate memory for DAI links based on count. @@ -481,98 +463,20 @@ static int gbaudio_add_dailinks(struct gbaudio_codec_info *gbcodec) devm_kzalloc(dev, NAME_SIZE, GFP_KERNEL); snprintf(dai_link_name, NAME_SIZE, "GB %d.%d PRI_MI2S_RX", gbcodec->dev_id, i); - dai->name = dai_link_name; - dai->codec_dai_name = gbcodec->dais[i].name; + dailink->name = dai_link_name; + dailink->codec_dai_name = gbcodec->dais[i].name; } - ret = msm8994_add_dailink("msm8994-tomtom-mtp-snd-card", dai, 1); - if (ret) { + ret = msm8994_add_dailink("msm8994-tomtom-mtp-snd-card", dailink, 1); + if (ret) dev_err(dev, "%d:Error while adding DAI link\n", ret); - goto err_dai_link; - } return ret; - -err_dai_link: - gbcodec->num_dai_links = i; - gbaudio_remove_dailinks(gbcodec); - return ret; } /* * gb_snd management functions */ -static struct gbaudio_codec_info *gbaudio_find_codec(struct device *dev, - int dev_id) -{ - struct gbaudio_codec_info *tmp, *ret; - - mutex_lock(&gb_codec_list_lock); - list_for_each_entry_safe(ret, tmp, &gb_codec_list, list) { - dev_dbg(dev, "%d:device found\n", ret->dev_id); - if (ret->dev_id == dev_id) { - mutex_unlock(&gb_codec_list_lock); - return ret; - } - } - mutex_unlock(&gb_codec_list_lock); - return NULL; -} - -static struct gbaudio_codec_info *gbaudio_get_codec(struct device *dev, - int dev_id) -{ - struct gbaudio_codec_info *gbcodec; - struct gb_audio *audio = dev_get_drvdata(dev); - - gbcodec = gbaudio_find_codec(dev, dev_id); - if (gbcodec) - return gbcodec; - - gbcodec = devm_kzalloc(dev, sizeof(*gbcodec), GFP_KERNEL); - if (!gbcodec) - return NULL; - - mutex_init(&gbcodec->lock); - INIT_LIST_HEAD(&gbcodec->dai_list); - INIT_LIST_HEAD(&gbcodec->widget_list); - INIT_LIST_HEAD(&gbcodec->codec_ctl_list); - INIT_LIST_HEAD(&gbcodec->widget_ctl_list); - gbcodec->dev_id = dev_id; - audio->gbcodec = gbcodec; - gbcodec->dev = dev; - snprintf(gbcodec->name, NAME_SIZE, "%s.%s", dev->driver->name, - dev_name(dev)); - - mutex_lock(&gb_codec_list_lock); - list_add(&gbcodec->list, &gb_codec_list); - mutex_unlock(&gb_codec_list_lock); - dev_dbg(dev, "%d:%s Added to codec list\n", gbcodec->dev_id, - gbcodec->name); - - return gbcodec; -} - -static void gbaudio_free_codec(struct device *dev, - struct gbaudio_codec_info *gbcodec) -{ - struct gb_audio *audio = dev_get_drvdata(dev); - - mutex_lock(&gb_codec_list_lock); - if (!gbcodec->mgmt_connection && - list_empty(&gbcodec->dai_list)) { - list_del(&gbcodec->list); - mutex_unlock(&gb_codec_list_lock); - audio->gbcodec = NULL; - devm_kfree(dev, gbcodec); - } else { - mutex_unlock(&gb_codec_list_lock); - } -} - -/* - * This is the basic hook get things initialized and registered w/ gb - */ /* XXX * since BE DAI path is not yet properly closed from above layer, @@ -593,7 +497,7 @@ static void gb_audio_cleanup(struct gbaudio_codec_info *gb) * In case of BE dailink, need to deactivate APBridge * manually */ - if (gbaudio_dailink.no_pcm && atomic_read(&gb->users)) { + if (atomic_read(&gb_dai->users)) { connection = gb_dai->connection; /* PB active */ ret = gb_audio_apbridgea_stop_tx(connection, 0); @@ -613,38 +517,34 @@ static void gb_audio_cleanup(struct gbaudio_codec_info *gb) if (ret) dev_info(dev, "%d:Failed during unregister cport\n", ret); - atomic_dec(&gb->users); + atomic_dec(&gb_dai->users); } } } -static int gbaudio_codec_probe(struct gb_connection *connection) +static int gbaudio_register_codec(struct gbaudio_codec_info *gbcodec) { int ret, i; - struct gbaudio_codec_info *gbcodec; + struct device *dev = gbcodec->dev; + struct gb_connection *connection = gbcodec->mgmt_connection; + /* + * FIXME: malloc for topology happens via audio_gb driver + * should be done within codec driver itself + */ struct gb_audio_topology *topology; - struct gb_audio_manager_module_descriptor desc; - struct device *dev = &connection->bundle->dev; - int dev_id = connection->intf->interface_id; - - dev_dbg(dev, "Add device:%d:%s\n", dev_id, dev_name(dev)); - /* get gbcodec data */ - gbcodec = gbaudio_get_codec(dev, dev_id); - if (!gbcodec) - return -ENOMEM; - - gbcodec->mgmt_connection = connection; ret = gb_connection_enable(connection); - if (ret) - goto base_error; + 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(gbcodec->dev, - "%d:Error while fetching topology\n", ret); - goto err_connection_disable; + dev_err(dev, "%d:Error while fetching topology\n", ret); + goto tplg_fetch_err; } /* process topology data */ @@ -652,7 +552,7 @@ static int gbaudio_codec_probe(struct gb_connection *connection) if (ret) { dev_err(dev, "%d:Error while parsing topology data\n", ret); - goto topology_error; + goto tplg_parse_err; } gbcodec->topology = topology; @@ -673,100 +573,39 @@ static int gbaudio_codec_probe(struct gb_connection *connection) gbcodec->dais, 1); if (ret) { dev_err(dev, "%d:Failed to register codec\n", ret); - goto parse_error; + goto codec_reg_err; } /* update DAI links in response to this codec */ ret = gbaudio_add_dailinks(gbcodec); if (ret) { dev_err(dev, "%d: Failed to add DAI links\n", ret); - goto codec_reg_error; + goto add_dailink_err; } - /* set registered flag */ - mutex_lock(&gbcodec->lock); - gbcodec->codec_registered = 1; - - /* codec cleanup related */ - atomic_set(&gbcodec->users, 0); - - /* inform above layer for uevent */ - if (!gbcodec->set_uevent && - (gbcodec->dai_added == gbcodec->num_dais)) { - 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); /* todo */ - 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); - gbcodec->set_uevent = 1; - } - mutex_unlock(&gbcodec->lock); - - return ret; + return 0; -codec_reg_error: +add_dailink_err: snd_soc_unregister_codec(dev); - dev->driver = NULL; -parse_error: +codec_reg_err: gbaudio_tplg_release(gbcodec); gbcodec->topology = NULL; -topology_error: +tplg_parse_err: kfree(topology); -err_connection_disable: - gb_connection_disable(connection); -base_error: - gbcodec->mgmt_connection = NULL; - gbaudio_free_codec(dev, gbcodec); +tplg_fetch_err: + gb_connection_disable(gbcodec->mgmt_connection); return ret; } -static void gbaudio_codec_remove(struct gb_connection *connection) +static void gbaudio_unregister_codec(struct gbaudio_codec_info *gbcodec) { - struct gbaudio_codec_info *gbcodec; - struct device *dev = &connection->bundle->dev; - int dev_id = connection->intf->interface_id; - - dev_dbg(dev, "Remove device:%d:%s\n", dev_id, dev_name(dev)); - - /* get gbcodec data */ - gbcodec = gbaudio_find_codec(dev, dev_id); - if (!gbcodec) - return; - - /* inform uevent to above layers */ - mutex_lock(&gbcodec->lock); - if (gbcodec->set_uevent) { - /* notify the audio manager */ - dev_dbg(dev, "Inform set_event:%d to above layer\n", 0); - gb_audio_manager_remove(gbcodec->manager_id); - gbcodec->set_uevent = 0; - } - mutex_unlock(&gbcodec->lock); - - if (atomic_read(&gbcodec->users)) { - dev_err(dev, "Cleanup Error: BE stream not yet closed\n"); - gb_audio_cleanup(gbcodec); - } - + gb_audio_cleanup(gbcodec); msm8994_remove_dailink("msm8994-tomtom-mtp-snd-card", &gbaudio_dailink, 1); - gbaudio_remove_dailinks(gbcodec); - - snd_soc_unregister_codec(dev); - dev->driver = NULL; + snd_soc_unregister_codec(gbcodec->dev); gbaudio_tplg_release(gbcodec); kfree(gbcodec->topology); - gb_connection_disable(connection); - gbcodec->mgmt_connection = NULL; - mutex_lock(&gbcodec->lock); - gbcodec->codec_registered = 0; - mutex_unlock(&gbcodec->lock); - gbaudio_free_codec(dev, gbcodec); + gb_connection_disable(gbcodec->mgmt_connection); } static int gbaudio_codec_request_handler(struct gb_operation *op) @@ -781,93 +620,6 @@ static int gbaudio_codec_request_handler(struct gb_operation *op) return 0; } -static int gbaudio_dai_probe(struct gb_connection *connection) -{ - struct gbaudio_dai *dai; - struct device *dev = &connection->bundle->dev; - int dev_id = connection->intf->interface_id; - struct gbaudio_codec_info *gbcodec; - struct gb_audio_manager_module_descriptor desc; - int ret; - - dev_dbg(dev, "Add DAI device:%d:%s\n", dev_id, dev_name(dev)); - - /* get gbcodec data */ - gbcodec = gbaudio_get_codec(dev, dev_id); - if (!gbcodec) - return -ENOMEM; - - ret = gb_connection_enable(connection); - if (ret) - goto err_free_codec; - - /* add/update dai_list*/ - dai = gbaudio_add_dai(gbcodec, connection->intf_cport_id, connection, - NULL); - if (!dai) { - ret = -ENOMEM; - goto err_connection_disable; - } - - /* update dai_added count */ - mutex_lock(&gbcodec->lock); - gbcodec->dai_added++; - - /* inform above layer for uevent */ - if (!gbcodec->set_uevent && gbcodec->codec_registered && - (gbcodec->dai_added == gbcodec->num_dais)) { - /* prepare for the audio manager */ - dev_dbg(dev, "Inform set_event:%d to above layer\n", 1); - strlcpy(desc.name, gbcodec->name, - GB_AUDIO_MANAGER_MODULE_NAME_LEN); /* todo */ - 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); - gbcodec->set_uevent = 1; - } - mutex_unlock(&gbcodec->lock); - - return 0; - -err_connection_disable: - gb_connection_disable(connection); -err_free_codec: - gbaudio_free_codec(dev, gbcodec); - return ret; -} - -static void gbaudio_dai_remove(struct gb_connection *connection) -{ - struct device *dev = &connection->bundle->dev; - int dev_id = connection->intf->interface_id; - struct gbaudio_codec_info *gbcodec; - - dev_dbg(dev, "Remove DAI device:%d:%s\n", dev_id, dev_name(dev)); - - /* get gbcodec data */ - gbcodec = gbaudio_find_codec(dev, dev_id); - if (!gbcodec) - return; - - /* inform uevent to above layers */ - mutex_lock(&gbcodec->lock); - if (gbcodec->set_uevent) { - /* notify the audio manager */ - dev_dbg(dev, "Inform set_event:%d to above layer\n", 0); - gb_audio_manager_remove(gbcodec->manager_id); - gbcodec->set_uevent = 0; - } - /* update dai_added count */ - gbcodec->dai_added--; - mutex_unlock(&gbcodec->lock); - - gb_connection_disable(connection); - gbaudio_free_codec(dev, gbcodec); -} - static int gbaudio_dai_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; @@ -877,14 +629,14 @@ static int gbaudio_dai_request_handler(struct gb_operation *op) return 0; } -static int gb_audio_add_mgmt_connection(struct gb_audio *audio, +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 (audio->mgmt_connection) { + if (gbcodec->mgmt_connection) { dev_err(&bundle->dev, "Can't have multiple Management connections\n"); return -ENODEV; @@ -895,74 +647,99 @@ static int gb_audio_add_mgmt_connection(struct gb_audio *audio, if (IS_ERR(connection)) return PTR_ERR(connection); - connection->private = audio; - audio->mgmt_connection = connection; + connection->private = gbcodec; + gbcodec->mgmt_connection = connection; return 0; } -static int gb_audio_add_data_connection(struct gb_audio *audio, +static int gb_audio_add_data_connection(struct gbaudio_codec_info *gbcodec, struct greybus_descriptor_cport *cport_desc, - struct gb_bundle *bundle, int index) + 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(bundle, le16_to_cpu(cport_desc->id), gbaudio_dai_request_handler); - if (IS_ERR(connection)) + if (IS_ERR(connection)) { + devm_kfree(gbcodec->dev, dai); return PTR_ERR(connection); + } - connection->private = audio; - audio->data_connection[index] = connection; + connection->private = gbcodec; + atomic_set(&dai->users, 0); + 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 *audio; + struct gb_audio_manager_module_descriptor desc; + struct gbaudio_dai *dai, *_dai; int ret, i; - int count = bundle->num_cports - 1; /* 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. */ - audio = kzalloc(sizeof(*audio) + - count * sizeof(*audio->data_connection), GFP_KERNEL); - if (!audio) + gbcodec = devm_kzalloc(dev, sizeof(*gbcodec), GFP_KERNEL); + if (!gbcodec) { + mutex_unlock(&gb_codec_list_lock); return -ENOMEM; + } - audio->num_data_connections = count; - greybus_set_drvdata(bundle, audio); + 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 (count = 0, i = 0; i < bundle->num_cports; i++) { + 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(audio, cport_desc, + 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(audio, cport_desc, - bundle, count); + ret = gb_audio_add_data_connection(gbcodec, cport_desc, + bundle); if (ret) goto destroy_connections; - - count++; break; default: - dev_err(&bundle->dev, "Unsupported protocol: 0x%02x\n", + dev_err(dev, "Unsupported protocol: 0x%02x\n", cport_desc->protocol_id); ret = -ENODEV; goto destroy_connections; @@ -970,57 +747,88 @@ static int gb_audio_probe(struct gb_bundle *bundle, } /* There must be a management cport */ - if (!audio->mgmt_connection) { + if (!gbcodec->mgmt_connection) { ret = -EINVAL; - dev_err(&bundle->dev, "Missing management connection\n"); + dev_err(dev, "Missing management connection\n"); goto destroy_connections; } /* Initialize management connection */ - ret = gbaudio_codec_probe(audio->mgmt_connection); + ret = gbaudio_register_codec(gbcodec); if (ret) goto destroy_connections; /* Initialize data connections */ - for (i = 0; i < audio->num_data_connections; i++) { - ret = gbaudio_dai_probe(audio->data_connection[i]); + 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); + + list_add(&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: - while (i--) - gbaudio_dai_remove(audio->data_connection[i]); + list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) + gb_connection_disable(dai->connection); - gbaudio_codec_remove(audio->mgmt_connection); + gbaudio_unregister_codec(gbcodec); destroy_connections: - while (count--) - gb_connection_destroy(audio->data_connection[count]); + 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 (audio->mgmt_connection) - gb_connection_destroy(audio->mgmt_connection); + if (gbcodec->mgmt_connection) + gb_connection_destroy(gbcodec->mgmt_connection); - kfree(audio); + devm_kfree(dev, gbcodec); + mutex_unlock(&gb_codec_list_lock); return ret; } static void gb_audio_disconnect(struct gb_bundle *bundle) { - struct gb_audio *audio = greybus_get_drvdata(bundle); - int i; + struct gbaudio_codec_info *gbcodec = greybus_get_drvdata(bundle); + struct gbaudio_dai *dai, *_dai; - for (i = audio->num_data_connections - 1; i >= 0; i--) { - gbaudio_dai_remove(audio->data_connection[i]); - gb_connection_destroy(audio->data_connection[i]); - } + mutex_lock(&gb_codec_list_lock); + list_del(&gbcodec->list); + /* inform uevent to above layers */ + gb_audio_manager_remove(gbcodec->manager_id); - gbaudio_codec_remove(audio->mgmt_connection); - gb_connection_destroy(audio->mgmt_connection); + mutex_lock(&gbcodec->lock); + list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) + gb_connection_disable(dai->connection); + gbaudio_unregister_codec(gbcodec); - kfree(audio); + 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; + 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[] = { diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index bba48a59bf4d..56110913b70e 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -84,6 +84,8 @@ struct gbaudio_control { struct gbaudio_dai { __le16 data_cport; char name[NAME_SIZE]; + /* DAI users */ + atomic_t users; struct gb_connection *connection; struct list_head list; }; @@ -116,8 +118,9 @@ struct gbaudio_codec_info { char *dailink_name[MAX_DAIS]; int num_dai_links; - /* topology related */ struct gb_connection *mgmt_connection; + size_t num_data_connections; + /* topology related */ int num_dais; int num_kcontrols; int num_dapm_widgets; @@ -131,9 +134,6 @@ struct gbaudio_codec_info { struct snd_soc_dapm_route *routes; struct snd_soc_dai_driver *dais; - /* codec users */ - atomic_t users; - /* lists */ struct list_head dai_list; struct list_head widget_list; @@ -142,17 +142,8 @@ struct gbaudio_codec_info { struct mutex lock; }; -struct gb_audio { - struct gb_connection *mgmt_connection; - size_t num_data_connections; - struct gbaudio_codec_info *gbcodec; - struct gb_connection *data_connection[0]; -}; - -struct gbaudio_dai *gbaudio_add_dai(struct gbaudio_codec_info *gbcodec, - int data_cport, - struct gb_connection *connection, - const char *name); +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, struct gb_audio_topology *tplg_data); void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec); diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 90d2148392ef..5fab393130a5 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -92,8 +92,7 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_info *info; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gbcodec = audio->gbcodec; + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -139,8 +138,7 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_value gbvalue; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -187,8 +185,7 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_value gbvalue; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -282,8 +279,7 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, 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 gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -317,8 +313,7 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, 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 gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -524,8 +519,7 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, int wid; int ret; struct snd_soc_codec *codec = w->codec; - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gbcodec = audio->gbcodec; + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); @@ -819,9 +813,9 @@ static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec, int i, ret; struct snd_soc_dai_driver *gb_dais; struct gb_audio_dai *curr; - struct gbaudio_dai *dai, *_dai; 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); @@ -839,10 +833,10 @@ static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec, /* append dev_id to dai_name */ snprintf(dai_name, NAME_SIZE, "%s.%d", curr->name, gbcodec->dev_id); - dai = gbaudio_add_dai(gbcodec, curr->data_cport, NULL, - dai_name); + 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++; @@ -852,10 +846,6 @@ static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec, return 0; error: - list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { - list_del(&dai->list); - devm_kfree(gbcodec->dev, dai); - } devm_kfree(gbcodec->dev, gb_dais); return ret; } @@ -948,68 +938,6 @@ static int gbaudio_tplg_process_header(struct gbaudio_codec_info *gbcodec, return 0; } -static struct gbaudio_dai *gbaudio_allocate_dai(struct gbaudio_codec_info *gb, - int data_cport, - struct gb_connection *connection, - const char *name) -{ - struct gbaudio_dai *dai; - - mutex_lock(&gb->lock); - dai = devm_kzalloc(gb->dev, sizeof(*dai), GFP_KERNEL); - if (!dai) { - dev_err(gb->dev, "%s:DAI Malloc failure\n", name); - mutex_unlock(&gb->lock); - return NULL; - } - - dai->data_cport = data_cport; - dai->connection = connection; - - /* update name */ - if (name) - strlcpy(dai->name, name, NAME_SIZE); - list_add(&dai->list, &gb->dai_list); - dev_dbg(gb->dev, "%d:%s: DAI added\n", data_cport, dai->name); - mutex_unlock(&gb->lock); - - return dai; -} - -struct gbaudio_dai *gbaudio_add_dai(struct gbaudio_codec_info *gbcodec, - int data_cport, - struct gb_connection *connection, - const char *name) -{ - struct gbaudio_dai *dai, *_dai; - - /* FIXME need to take care for multiple DAIs */ - mutex_lock(&gbcodec->lock); - if (list_empty(&gbcodec->dai_list)) { - mutex_unlock(&gbcodec->lock); - return gbaudio_allocate_dai(gbcodec, data_cport, connection, - name); - } - - list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { - if (dai->data_cport == data_cport) { - if (connection) - dai->connection = connection; - - if (name) - strlcpy(dai->name, name, NAME_SIZE); - dev_dbg(gbcodec->dev, "%d:%s: DAI updated\n", - data_cport, dai->name); - mutex_unlock(&gbcodec->lock); - return dai; - } - } - - dev_err(gbcodec->dev, "%s:DAI not found\n", name); - mutex_unlock(&gbcodec->lock); - return NULL; -} - int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, struct gb_audio_topology *tplg_data) { @@ -1074,7 +1002,6 @@ int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec) { - struct gbaudio_dai *dai, *_dai; struct gbaudio_control *control, *_control; struct gbaudio_widget *widget, *_widget; @@ -1109,12 +1036,4 @@ void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec) /* release routes */ if (gbcodec->routes) devm_kfree(gbcodec->dev, gbcodec->routes); - - /* release DAIs */ - mutex_lock(&gbcodec->lock); - list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { - list_del(&dai->list); - devm_kfree(gbcodec->dev, dai); - } - mutex_unlock(&gbcodec->lock); } -- 2.39.2