ASoC: pcm: Create PCM streams for ASOC backend

Change creates a PCM stream for ASOC backend which will only be used
internally by kernel drivers.It provides existing ASoC components
drivers with a substream and access to any private data.
This commit is contained in:
Gopikrishnaiah Anandan 2013-02-15 12:36:00 -05:00 committed by Stephen Boyd
parent a97a269128
commit 9e3cd79005
2 changed files with 145 additions and 56 deletions

View file

@ -476,9 +476,9 @@ extern const struct file_operations snd_pcm_f_ops[2];
int snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count,
struct snd_pcm **rpcm);
int snd_pcm_new_internal(struct snd_card *card, const char *id, int device,
int snd_pcm_new_soc_be(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count,
struct snd_pcm **rpcm);
struct snd_pcm ** rpcm);
int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count);
int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree);
@ -785,8 +785,7 @@ void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interva
unsigned int k, struct snd_interval *c);
void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
const struct snd_interval *b, struct snd_interval *c);
int snd_interval_list(struct snd_interval *i, unsigned int count,
const unsigned int *list, unsigned int mask);
int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask);
int snd_interval_ratnum(struct snd_interval *i,
unsigned int rats_count, struct snd_ratnum *rats,
unsigned int *nump, unsigned int *denp);

View file

@ -24,7 +24,6 @@
#include <linux/module.h>
#include <linux/time.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/pcm.h>
@ -42,6 +41,7 @@ static DEFINE_MUTEX(register_mutex);
static int snd_pcm_free(struct snd_pcm *pcm);
static int snd_pcm_dev_free(struct snd_device *device);
static int snd_pcm_dev_register(struct snd_device *device);
static int snd_pcm_dev_register_soc_be(struct snd_device *device);
static int snd_pcm_dev_disconnect(struct snd_device *device);
static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device)
@ -651,7 +651,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
pstr->stream = stream;
pstr->pcm = pcm;
pstr->substream_count = substream_count;
if (substream_count > 0 && !pcm->internal) {
if (substream_count > 0) {
err = snd_pcm_stream_proc_init(pstr);
if (err < 0) {
snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n");
@ -675,18 +675,15 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
pstr->substream = substream;
else
prev->next = substream;
if (!pcm->internal) {
err = snd_pcm_substream_proc_init(substream);
if (err < 0) {
snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n");
if (prev == NULL)
pstr->substream = NULL;
else
prev->next = NULL;
kfree(substream);
return err;
}
err = snd_pcm_substream_proc_init(substream);
if (err < 0) {
snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n");
if (prev == NULL)
pstr->substream = NULL;
else
prev->next = NULL;
kfree(substream);
return err;
}
substream->group = &substream->self_group;
spin_lock_init(&substream->self_group.lock);
@ -700,9 +697,25 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
EXPORT_SYMBOL(snd_pcm_new_stream);
static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count, bool internal,
struct snd_pcm **rpcm)
/**
* snd_pcm_new - create a new PCM instance
* @card: the card instance
* @id: the id string
* @device: the device index (zero based)
* @playback_count: the number of substreams for playback
* @capture_count: the number of substreams for capture
* @rpcm: the pointer to store the new pcm instance
*
* Creates a new PCM instance.
*
* The pcm operators have to be set afterwards to the new instance
* via snd_pcm_set_ops().
*
* Returns zero if successful, or a negative error code on failure.
*/
int snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count,
struct snd_pcm ** rpcm)
{
struct snd_pcm *pcm;
int err;
@ -723,7 +736,7 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
}
pcm->card = card;
pcm->device = device;
pcm->internal = internal;
if (id)
strlcpy(pcm->id, id, sizeof(pcm->id));
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
@ -745,32 +758,49 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
return 0;
}
/**
* snd_pcm_new - create a new PCM instance
* @card: the card instance
* @id: the id string
* @device: the device index (zero based)
* @playback_count: the number of substreams for playback
* @capture_count: the number of substreams for capture
* @rpcm: the pointer to store the new pcm instance
*
* Creates a new PCM instance.
*
* The pcm operators have to be set afterwards to the new instance
* via snd_pcm_set_ops().
*
* Returns zero if successful, or a negative error code on failure.
*/
int snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count, struct snd_pcm **rpcm)
{
return _snd_pcm_new(card, id, device, playback_count, capture_count,
false, rpcm);
}
EXPORT_SYMBOL(snd_pcm_new);
static int snd_pcm_new_stream_soc_be(struct snd_pcm *pcm, int stream,
int substream_count)
{
int idx;
struct snd_pcm_str *pstr = &pcm->streams[stream];
struct snd_pcm_substream *substream, *prev;
pstr->stream = stream;
pstr->pcm = pcm;
pstr->substream_count = substream_count;
prev = NULL;
for (idx = 0, prev = NULL; idx < substream_count; idx++) {
substream = kzalloc(sizeof(*substream), GFP_KERNEL);
if (substream == NULL) {
snd_printk(KERN_ERR "Cannot allocate BE PCM substream\n");
return -ENOMEM;
}
substream->pcm = pcm;
substream->pstr = pstr;
substream->number = idx;
substream->stream = stream;
sprintf(substream->name, "subdevice #%i", idx);
substream->buffer_bytes_max = UINT_MAX;
if (prev == NULL)
pstr->substream = substream;
else
prev->next = substream;
substream->group = &substream->self_group;
spin_lock_init(&substream->self_group.lock);
INIT_LIST_HEAD(&substream->self_group.substreams);
list_add_tail(&substream->link_list, &substream->self_group.substreams);
atomic_set(&substream->mmap_count, 0);
prev = substream;
}
return 0;
}
/**
* snd_pcm_new_internal - create a new internal PCM instance
* snd_pcm_new_soc_be - create a new PCM instance for ASoC BE DAI link
* @card: the card instance
* @id: the id string
* @device: the device index (zero based - shared with normal PCMs)
@ -778,25 +808,62 @@ EXPORT_SYMBOL(snd_pcm_new);
* @capture_count: the number of substreams for capture
* @rpcm: the pointer to store the new pcm instance
*
* Creates a new internal PCM instance with no userspace device or procfs
* entries. This is used by ASoC Back End PCMs in order to create a PCM that
* will only be used internally by kernel drivers. i.e. it cannot be opened
* by userspace. It provides existing ASoC components drivers with a substream
* and access to any private data.
* Creates a new PCM instance with no userspace device or procfs entries.
* This is used by ASoC Back End PCMs in order to create a PCM that will only
* be used internally by kernel drivers. i.e. it cannot be opened by userspace.
* It also provides existing ASoC components drivers with a substream and
* access to any private data.
*
* The pcm operators have to be set afterwards to the new instance
* via snd_pcm_set_ops().
*
* Returns zero if successful, or a negative error code on failure.
*/
int snd_pcm_new_internal(struct snd_card *card, const char *id, int device,
int snd_pcm_new_soc_be(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count,
struct snd_pcm **rpcm)
struct snd_pcm ** rpcm)
{
return _snd_pcm_new(card, id, device, playback_count, capture_count,
true, rpcm);
struct snd_pcm *pcm;
int err;
static struct snd_device_ops ops = {
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register_soc_be,
.dev_disconnect = snd_pcm_dev_disconnect,
};
if (snd_BUG_ON(!card))
return -ENXIO;
if (rpcm)
*rpcm = NULL;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
if (pcm == NULL) {
snd_printk(KERN_ERR "Cannot allocate virtual PCM\n");
return -ENOMEM;
}
pcm->card = card;
pcm->device = device;
if (id)
strlcpy(pcm->id, id, sizeof(pcm->id));
if ((err = snd_pcm_new_stream_soc_be(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
snd_pcm_free(pcm);
return err;
}
if ((err = snd_pcm_new_stream_soc_be(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {
snd_pcm_free(pcm);
return err;
}
if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {
snd_pcm_free(pcm);
return err;
}
if (rpcm)
*rpcm = pcm;
return 0;
}
EXPORT_SYMBOL(snd_pcm_new_internal);
EXPORT_SYMBOL(snd_pcm_new_soc_be);
static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
{
@ -1034,7 +1101,7 @@ static int snd_pcm_dev_register(struct snd_device *device)
}
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
if (pcm->streams[cidx].substream == NULL || pcm->internal)
if (pcm->streams[cidx].substream == NULL)
continue;
switch (cidx) {
case SNDRV_PCM_STREAM_PLAYBACK:
@ -1075,6 +1142,29 @@ static int snd_pcm_dev_register(struct snd_device *device)
return 0;
}
static int snd_pcm_dev_register_soc_be(struct snd_device *device)
{
int err;
struct snd_pcm_notify *notify;
struct snd_pcm *pcm;
if (snd_BUG_ON(!device || !device->device_data))
return -ENXIO;
pcm = device->device_data;
mutex_lock(&register_mutex);
err = snd_pcm_add(pcm);
if (err) {
mutex_unlock(&register_mutex);
return err;
}
list_for_each_entry(notify, &snd_pcm_notify_list, list)
notify->n_register(pcm);
mutex_unlock(&register_mutex);
return 0;
}
static int snd_pcm_dev_disconnect(struct snd_device *device)
{
struct snd_pcm *pcm = device->device_data;