diff options
author | 2018-12-17 15:10:12 +0100 | |
---|---|---|
committer | 2018-12-17 16:10:20 +0100 | |
commit | 15600aea2754c3f0cd3440b95b91bbf1602a3fca (patch) | |
tree | 0ef2e838cc0da7a54f483c733ebdd544b46b41dc /drivers/staging/most | |
parent | staging: vc04_services: Remove VCHIU_QUEUE_T typedef (diff) | |
download | linux-15600aea2754c3f0cd3440b95b91bbf1602a3fca.tar.gz linux-15600aea2754c3f0cd3440b95b91bbf1602a3fca.tar.bz2 linux-15600aea2754c3f0cd3440b95b91bbf1602a3fca.zip |
staging: most: sound: create one sound card w/ multiple PCM devices per MOST device
This patch avoids that a sound card is created and registered with ALSA
every time a channel is being linked. Instead the channels are hooked on
the same card, which is registered not until the final link has been added
to the component. The string provided by user space that used to be the
card name becomes the PCM device name. The user space API to add a link is
being expanded by a "create" flag to trigger the registration.
Signed-off-by: Christian Gromm <christian.gromm@microchip.com>
Reviewed-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/staging/most')
-rw-r--r-- | drivers/staging/most/sound/sound.c | 130 |
1 files changed, 89 insertions, 41 deletions
diff --git a/drivers/staging/most/sound/sound.c b/drivers/staging/most/sound/sound.c index 89b02fc305b8..9f848080330c 100644 --- a/drivers/staging/most/sound/sound.c +++ b/drivers/staging/most/sound/sound.c @@ -10,6 +10,7 @@ #include <linux/module.h> #include <linux/printk.h> #include <linux/kernel.h> +#include <linux/slab.h> #include <linux/init.h> #include <sound/core.h> #include <sound/pcm.h> @@ -20,7 +21,6 @@ #define DRIVER_NAME "sound" -static struct list_head dev_list; static struct core_component comp; /** @@ -56,6 +56,17 @@ struct channel { void (*copy_fn)(void *alsa, void *most, unsigned int bytes); }; +struct sound_adapter { + struct list_head dev_list; + struct most_interface *iface; + struct snd_card *card; + struct list_head list; + bool registered; + int pcm_dev_idx; +}; + +static struct list_head adpt_list; + #define MOST_PCM_INFO (SNDRV_PCM_INFO_MMAP | \ SNDRV_PCM_INFO_MMAP_VALID | \ SNDRV_PCM_INFO_BATCH | \ @@ -157,9 +168,10 @@ static void most_to_alsa_copy32(void *alsa, void *most, unsigned int bytes) static struct channel *get_channel(struct most_interface *iface, int channel_id) { + struct sound_adapter *adpt = iface->priv; struct channel *channel, *tmp; - list_for_each_entry_safe(channel, tmp, &dev_list, list) { + list_for_each_entry_safe(channel, tmp, &adpt->dev_list, list) { if ((channel->iface == iface) && (channel->id == channel_id)) return channel; } @@ -460,7 +472,7 @@ static const struct snd_pcm_ops pcm_ops = { }; static int split_arg_list(char *buf, char **card_name, u16 *ch_num, - char **sample_res) + char **sample_res, u8 *create) { char *num; int ret; @@ -479,6 +491,9 @@ static int split_arg_list(char *buf, char **card_name, u16 *ch_num, *sample_res = strsep(&buf, ".\n"); if (!*sample_res) goto err; + + if (buf && !strcmp(buf, "create")) + *create = 1; return 0; err: @@ -536,6 +551,20 @@ found: return 0; } +static void release_adapter(struct sound_adapter *adpt) +{ + struct channel *channel, *tmp; + + list_for_each_entry_safe(channel, tmp, &adpt->dev_list, list) { + list_del(&channel->list); + kfree(channel); + } + if (adpt->card) + snd_card_free(adpt->card); + list_del(&adpt->list); + kfree(adpt); +} + /** * audio_probe_channel - probe function of the driver module * @iface: pointer to interface instance @@ -553,7 +582,7 @@ static int audio_probe_channel(struct most_interface *iface, int channel_id, char *arg_list) { struct channel *channel; - struct snd_card *card; + struct sound_adapter *adpt; struct snd_pcm *pcm; int playback_count = 0; int capture_count = 0; @@ -561,6 +590,7 @@ static int audio_probe_channel(struct most_interface *iface, int channel_id, int direction; char *card_name; u16 ch_num; + u8 create = 0; char *sample_res; if (!iface) @@ -571,6 +601,39 @@ static int audio_probe_channel(struct most_interface *iface, int channel_id, return -EINVAL; } + ret = split_arg_list(arg_list, &card_name, &ch_num, &sample_res, + &create); + if (ret < 0) + return ret; + + list_for_each_entry(adpt, &adpt_list, list) { + if (adpt->iface != iface) + continue; + if (adpt->registered) + return -ENOSPC; + adpt->pcm_dev_idx++; + goto skip_adpt_alloc; + } + adpt = kzalloc(sizeof(*adpt), GFP_KERNEL); + if (!adpt) + return -ENOMEM; + + adpt->iface = iface; + INIT_LIST_HEAD(&adpt->dev_list); + iface->priv = adpt; + list_add_tail(&adpt->list, &adpt_list); + ret = snd_card_new(&iface->dev, -1, card_name, THIS_MODULE, + sizeof(*channel), &adpt->card); + if (ret < 0) + goto err_free_card; + snprintf(adpt->card->driver, sizeof(adpt->card->driver), + "%s", DRIVER_NAME); + snprintf(adpt->card->shortname, sizeof(adpt->card->shortname), + "Microchip MOST:%d", adpt->card->number); + snprintf(adpt->card->longname, sizeof(adpt->card->longname), + "%s at %s, ch %d", adpt->card->shortname, iface->description, + channel_id); +skip_adpt_alloc: if (get_channel(iface, channel_id)) { pr_err("channel (%s:%d) is already linked\n", iface->description, channel_id); @@ -584,53 +647,43 @@ static int audio_probe_channel(struct most_interface *iface, int channel_id, capture_count = 1; direction = SNDRV_PCM_STREAM_CAPTURE; } - - ret = split_arg_list(arg_list, &card_name, &ch_num, &sample_res); - if (ret < 0) - return ret; - - ret = snd_card_new(&iface->dev, -1, card_name, THIS_MODULE, - sizeof(*channel), &card); - if (ret < 0) - return ret; - - channel = card->private_data; - channel->card = card; + channel = kzalloc(sizeof(*channel), GFP_KERNEL); + if (!channel) { + ret = -ENOMEM; + goto err_free_card; + } + channel->card = adpt->card; channel->cfg = cfg; channel->iface = iface; channel->id = channel_id; init_waitqueue_head(&channel->playback_waitq); + list_add_tail(&channel->list, &adpt->dev_list); ret = audio_set_hw_params(&channel->pcm_hardware, ch_num, sample_res, cfg); if (ret) goto err_free_card; - snprintf(card->driver, sizeof(card->driver), "%s", DRIVER_NAME); - snprintf(card->shortname, sizeof(card->shortname), "Microchip MOST:%d", - card->number); - snprintf(card->longname, sizeof(card->longname), "%s at %s, ch %d", - card->shortname, iface->description, channel_id); + ret = snd_pcm_new(adpt->card, card_name, adpt->pcm_dev_idx, + playback_count, capture_count, &pcm); - ret = snd_pcm_new(card, card_name, 0, playback_count, - capture_count, &pcm); if (ret < 0) goto err_free_card; pcm->private_data = channel; - + snprintf(pcm->name, sizeof(pcm->name), card_name); snd_pcm_set_ops(pcm, direction, &pcm_ops); - ret = snd_card_register(card); - if (ret < 0) - goto err_free_card; - - list_add_tail(&channel->list, &dev_list); - + if (create) { + ret = snd_card_register(adpt->card); + if (ret < 0) + goto err_free_card; + adpt->registered = true; + } return 0; err_free_card: - snd_card_free(card); + release_adapter(adpt); return ret; } @@ -647,6 +700,7 @@ static int audio_disconnect_channel(struct most_interface *iface, int channel_id) { struct channel *channel; + struct sound_adapter *adpt = iface->priv; channel = get_channel(iface, channel_id); if (!channel) { @@ -656,8 +710,10 @@ static int audio_disconnect_channel(struct most_interface *iface, } list_del(&channel->list); - snd_card_free(channel->card); + kfree(channel); + if (list_empty(&adpt->dev_list)) + release_adapter(adpt); return 0; } @@ -733,22 +789,14 @@ static int __init audio_init(void) { pr_info("init()\n"); - INIT_LIST_HEAD(&dev_list); + INIT_LIST_HEAD(&adpt_list); return most_register_component(&comp); } static void __exit audio_exit(void) { - struct channel *channel, *tmp; - pr_info("exit()\n"); - - list_for_each_entry_safe(channel, tmp, &dev_list, list) { - list_del(&channel->list); - snd_card_free(channel->card); - } - most_deregister_component(&comp); } |