summaryrefslogtreecommitdiffstats
path: root/drivers/staging
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging')
-rw-r--r--drivers/staging/most/sound/sound.c130
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);
}