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 #include #include +#include #include #include #include @@ -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 @@ static int audio_set_hw_params(struct snd_pcm_hardware *pcm_hw, 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); }