diff options
| -rw-r--r-- | include/sound/soc-dai.h | 49 | ||||
| -rw-r--r-- | include/sound/soc.h | 26 | ||||
| -rw-r--r-- | sound/soc/soc-core.c | 220 |
3 files changed, 248 insertions, 47 deletions
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 1bafe95dcf4..af9a5519e33 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -277,4 +277,53 @@ static inline void *snd_soc_dai_get_drvdata(struct snd_soc_dai *dai) return dev_get_drvdata(dai->dev); } +/* Backend DAI PCM ops */ +static inline int snd_soc_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (dai->driver->ops->startup) + return dai->driver->ops->startup(substream, dai); + return 0; +} + +static inline void snd_soc_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (dai->driver->ops->shutdown) + dai->driver->ops->shutdown(substream, dai); +} + +static inline int snd_soc_dai_hw_params(struct snd_pcm_substream * substream, + struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) +{ + if (dai->driver->ops->hw_params) + return dai->driver->ops->hw_params(substream, hw_params, dai); + return 0; +} + +static inline int snd_soc_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (dai->driver->ops->hw_free) + return dai->driver->ops->hw_free(substream, dai); + return 0; +} + +static inline int snd_soc_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (dai->driver->ops->prepare) + return dai->driver->ops->prepare(substream, dai); + return 0; +} + +static inline int snd_soc_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + if (dai->driver->ops->trigger) + return dai->driver->ops->trigger(substream, cmd, dai); + return 0; +} + + #endif diff --git a/include/sound/soc.h b/include/sound/soc.h index d787268e533..a89e611d490 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -278,6 +278,24 @@ int snd_soc_cache_write(struct snd_soc_codec *codec, int snd_soc_cache_read(struct snd_soc_codec *codec, unsigned int reg, unsigned int *value); +/* pcm <-> DAI connect */ +void snd_soc_free_pcms(struct snd_soc_codec *codec); +int snd_soc_new_pcms(struct snd_soc_codec *codec, int idx, const char *xid); + +/* DAI operations - for backend DAIs */ +int snd_soc_pcm_open(struct snd_pcm_substream *substream); +int snd_soc_pcm_close(struct snd_pcm_substream *substream); +int snd_soc_pcm_prepare(struct snd_pcm_substream *substream); +int snd_soc_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +int snd_soc_pcm_hw_free(struct snd_pcm_substream *substream); +int snd_soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd); +snd_pcm_uframes_t snd_soc_pcm_pointer(struct snd_pcm_substream *substream); +struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, + const char *dai_link, int stream); +struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, + const char *dai_link); + /* Utility functions to get clock rates from various things */ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params); @@ -584,6 +602,12 @@ struct snd_soc_dai_link { /* Symmetry requirements */ unsigned int symmetric_rates:1; + /* No PCM created for this DAI link */ + unsigned int no_pcm:1; + /* This DAI link can change CODEC and platform at runtime*/ + unsigned int dynamic:1; + /* This DAI link has no codec side driver*/ + unsigned int no_codec:1; /* codec/machine specific init - e.g. add machine controls */ int (*init)(struct snd_soc_pcm_runtime *rtd); @@ -688,6 +712,7 @@ struct snd_soc_pcm_runtime { struct device dev; struct snd_soc_card *card; struct snd_soc_dai_link *dai_link; + struct mutex pcm_mutex; unsigned int complete:1; unsigned int dev_registered:1; @@ -702,6 +727,7 @@ struct snd_soc_pcm_runtime { struct snd_soc_platform *platform; struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; + int current_fe; struct delayed_work delayed_work; }; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 922c26557bd..cb3d7a5921b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -44,7 +44,6 @@ #define NAME_SIZE 32 -static DEFINE_MUTEX(pcm_mutex); static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); #ifdef CONFIG_DEBUG_FS @@ -459,7 +458,8 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream) * then initialized and any private data can be allocated. This also calls * startup for the cpu DAI, platform, machine and codec DAI. */ -static int soc_pcm_open(struct snd_pcm_substream *substream) + +int snd_soc_pcm_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; @@ -470,7 +470,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver; int ret = 0; - mutex_lock(&pcm_mutex); + mutex_lock(&rtd->pcm_mutex); /* startup the audio subsystem */ if (cpu_dai->driver->ops->startup) { @@ -507,6 +507,9 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } + if (rtd->dai_link->no_pcm) + goto no_pcm; + /* Check that the codec and cpu DAIs are compatible */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { runtime->hw.rate_min = @@ -587,7 +590,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) runtime->hw.channels_max); pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min, runtime->hw.rate_max); - +no_pcm: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { cpu_dai->playback_active++; codec_dai->playback_active++; @@ -598,7 +601,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) cpu_dai->active++; codec_dai->active++; rtd->codec->active++; - mutex_unlock(&pcm_mutex); + mutex_unlock(&rtd->pcm_mutex); return 0; config_err: @@ -617,9 +620,10 @@ platform_err: if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); out: - mutex_unlock(&pcm_mutex); + mutex_unlock(&rtd->pcm_mutex); return ret; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_open); /* * Power down the audio subsystem pmdown_time msecs after close is called. @@ -631,8 +635,9 @@ static void close_delayed_work(struct work_struct *work) struct snd_soc_pcm_runtime *rtd = container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - mutex_lock(&pcm_mutex); + mutex_lock(&rtd->pcm_mutex); pr_debug("pop wq checking: %s status: %s waiting: %s\n", codec_dai->driver->playback.stream_name, @@ -642,12 +647,17 @@ static void close_delayed_work(struct work_struct *work) /* are we waiting on this codec DAI stream */ if (codec_dai->pop_wait == 1) { codec_dai->pop_wait = 0; - snd_soc_dapm_stream_event(rtd, - codec_dai->driver->playback.stream_name, - SND_SOC_DAPM_STREAM_STOP); + if (rtd->dai_link->dynamic) + snd_soc_dapm_stream_event(rtd, + cpu_dai->driver->playback.stream_name, + SND_SOC_DAPM_STREAM_STOP); + else + snd_soc_dapm_stream_event(rtd, + codec_dai->driver->playback.stream_name, + SND_SOC_DAPM_STREAM_STOP); } - mutex_unlock(&pcm_mutex); + mutex_unlock(&rtd->pcm_mutex); } /* @@ -655,7 +665,7 @@ static void close_delayed_work(struct work_struct *work) * freed here. The cpu DAI, codec DAI, machine and platform are also * shutdown. */ -static int soc_codec_close(struct snd_pcm_substream *substream) +int snd_soc_pcm_close(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; @@ -663,7 +673,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_codec *codec = rtd->codec; - mutex_lock(&pcm_mutex); + mutex_lock(&rtd->pcm_mutex); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { cpu_dai->playback_active--; @@ -703,21 +713,27 @@ static int soc_codec_close(struct snd_pcm_substream *substream) msecs_to_jiffies(rtd->pmdown_time)); } else { /* capture streams can be powered down now */ - snd_soc_dapm_stream_event(rtd, - codec_dai->driver->capture.stream_name, - SND_SOC_DAPM_STREAM_STOP); + if (rtd->dai_link->dynamic) + snd_soc_dapm_stream_event(rtd, + cpu_dai->driver->capture.stream_name, + SND_SOC_DAPM_STREAM_STOP); + else + snd_soc_dapm_stream_event(rtd, + codec_dai->driver->capture.stream_name, + SND_SOC_DAPM_STREAM_STOP); } - mutex_unlock(&pcm_mutex); + mutex_unlock(&rtd->pcm_mutex); return 0; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_close); /* * Called by ALSA when the PCM substream is prepared, can set format, sample * rate, etc. This function is non atomic and can be called multiple times, * it can refer to the runtime info. */ -static int soc_pcm_prepare(struct snd_pcm_substream *substream) +int snd_soc_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; @@ -725,7 +741,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; - mutex_lock(&pcm_mutex); + mutex_lock(&rtd->pcm_mutex); if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) { ret = rtd->dai_link->ops->prepare(substream); @@ -766,28 +782,39 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) cancel_delayed_work(&rtd->delayed_work); } - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dapm_stream_event(rtd, + if (rtd->dai_link->dynamic) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dapm_stream_event(rtd, + cpu_dai->driver->playback.stream_name, + SND_SOC_DAPM_STREAM_START); + else + snd_soc_dapm_stream_event(rtd, + cpu_dai->driver->capture.stream_name, + SND_SOC_DAPM_STREAM_START); + } else { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dapm_stream_event(rtd, codec_dai->driver->playback.stream_name, SND_SOC_DAPM_STREAM_START); - else - snd_soc_dapm_stream_event(rtd, + else + snd_soc_dapm_stream_event(rtd, codec_dai->driver->capture.stream_name, SND_SOC_DAPM_STREAM_START); - + } snd_soc_dai_digital_mute(codec_dai, 0); out: - mutex_unlock(&pcm_mutex); + mutex_unlock(&rtd->pcm_mutex); return ret; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_prepare); /* * Called by ALSA when the hardware params are set by application. This * function can also be called multiple times and can allocate buffers * (using snd_pcm_lib_* ). It's non-atomic. */ -static int soc_pcm_hw_params(struct snd_pcm_substream *substream, +int snd_soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -796,7 +823,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; - mutex_lock(&pcm_mutex); + mutex_lock(&rtd->pcm_mutex); if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { ret = rtd->dai_link->ops->hw_params(substream, params); @@ -836,7 +863,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, rtd->rate = params_rate(params); out: - mutex_unlock(&pcm_mutex); + mutex_unlock(&rtd->pcm_mutex); return ret; platform_err: @@ -851,14 +878,15 @@ codec_err: if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) rtd->dai_link->ops->hw_free(substream); - mutex_unlock(&pcm_mutex); + mutex_unlock(&rtd->pcm_mutex);; return ret; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_hw_params); /* * Frees resources allocated by hw_params, can be called multiple times */ -static int soc_pcm_hw_free(struct snd_pcm_substream *substream) +int snd_soc_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; @@ -866,7 +894,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_codec *codec = rtd->codec; - mutex_lock(&pcm_mutex); + mutex_lock(&rtd->pcm_mutex); /* apply codec digital mute */ if (!codec->active) @@ -887,11 +915,12 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) if (cpu_dai->driver->ops->hw_free) cpu_dai->driver->ops->hw_free(substream, cpu_dai); - mutex_unlock(&pcm_mutex); + mutex_unlock(&rtd->pcm_mutex); return 0; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_hw_free); -static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +int snd_soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; @@ -918,13 +947,14 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } return 0; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_trigger); /* * soc level wrapper for pointer callback * If cpu_dai, codec_dai, platform driver has the delay callback, than * the runtime->delay will be updated accordingly. */ -static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) +snd_pcm_uframes_t snd_soc_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; @@ -950,18 +980,48 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) return offset; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_pointer); /* ASoC PCM operations */ static struct snd_pcm_ops soc_pcm_ops = { - .open = soc_pcm_open, - .close = soc_codec_close, - .hw_params = soc_pcm_hw_params, - .hw_free = soc_pcm_hw_free, - .prepare = soc_pcm_prepare, - .trigger = soc_pcm_trigger, - .pointer = soc_pcm_pointer, + .open = snd_soc_pcm_open, + .close = snd_soc_pcm_close, + .hw_params = snd_soc_pcm_hw_params, + .hw_free = snd_soc_pcm_hw_free, + .prepare = snd_soc_pcm_prepare, + .trigger = snd_soc_pcm_trigger, + .pointer = snd_soc_pcm_pointer, }; +struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, + const char *dai_link, int stream) +{ + int i; + + for (i = 0; i < card->num_links; i++) { + if (card->rtd[i].dai_link->no_pcm && + !strcmp(card->rtd[i].dai_link->name, dai_link)) + return card->rtd[i].pcm->streams[stream].substream; + } + dev_dbg(card->dev, "failed to find dai link %s\n", dai_link); + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream); + +struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, + const char *dai_link) +{ + int i; + + for (i = 0; i < card->num_links; i++) { + if (!strcmp(card->rtd[i].dai_link->name, dai_link)) + return &card->rtd[i]; + } + dev_dbg(card->dev, "failed to find rtd %s\n", dai_link); + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); + #ifdef CONFIG_PM /* powers down audio subsystem for suspend */ static int soc_suspend(struct device *dev) @@ -1215,9 +1275,32 @@ static int soc_resume(struct device *dev) #define soc_resume NULL #endif +#define NULL_FORMATS \ + (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 |\ + SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 |\ + SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32) + static struct snd_soc_dai_ops null_dai_ops = { }; +static struct snd_soc_dai_driver null_codec_dai_drv = { + .name = "null-codec-dai", + .ops = &null_dai_ops, + .capture = { + .channels_min = 1 , + .channels_max = 16, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = NULL_FORMATS, + }, + .playback = { + .channels_min = 1 , + .channels_max = 16, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = NULL_FORMATS, + }, +}; +static struct snd_soc_codec_driver null_codec_drv = {}; + static int soc_bind_dai_link(struct snd_soc_card *card, int num) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; @@ -1261,7 +1344,7 @@ find_codec: /* CODEC found, so find CODEC DAI from registered DAIs from this CODEC*/ list_for_each_entry(codec_dai, &dai_list, list) { - if (codec->dev == codec_dai->dev && + if ((codec->dev == codec_dai->dev || codec->driver == &null_codec_drv) && !strcmp(codec_dai->name, dai_link->codec_dai_name)) { rtd->codec_dai = codec_dai; goto find_platform; @@ -1591,6 +1674,7 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) /* DAPM dai link stream work */ INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + mutex_init(&rtd->pcm_mutex); ret = soc_post_component_init(card, codec, num, 0); if (ret) @@ -2008,6 +2092,15 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->pcm = pcm; pcm->private_data = rtd; + + if (rtd->dai_link->no_pcm) { + if (playback) + pcm->streams[0].substream->private_data = rtd; + if (capture) + pcm->streams[1].substream->private_data = rtd; + goto out; + } + soc_pcm_ops.mmap = platform->driver->ops->mmap; soc_pcm_ops.pointer = platform->driver->ops->pointer; soc_pcm_ops.ioctl = platform->driver->ops->ioctl; @@ -2028,6 +2121,7 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) return ret; } +out: pcm->private_free = platform->driver->pcm_free; printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name, cpu_dai->name); @@ -3149,7 +3243,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); */ static int snd_soc_register_card(struct snd_soc_card *card) { - int i; + int i, ret = 0; if (!card->name || !card->dev) return -EINVAL; @@ -3161,8 +3255,36 @@ static int snd_soc_register_card(struct snd_soc_card *card) return -ENOMEM; card->rtd_aux = &card->rtd[card->num_links]; - for (i = 0; i < card->num_links; i++) + for (i = 0; i < card->num_links; i++) { + /* create virtual CODEC for dynamic links */ + dev_dbg(card->dev, "DAI create runtime %s\n", card->dai_link[i].name); card->rtd[i].dai_link = &card->dai_link[i]; + if (card->rtd[i].dai_link->dynamic) { + + card->rtd[i].dai_link->codec_name = "null-codec"; + card->rtd[i].dai_link->codec_dai_name = "null-codec-dai"; + ret = snd_soc_register_codec(card->dev, &null_codec_drv, + &null_codec_dai_drv, 1); + if (ret < 0) { + printk(KERN_ERR "%s: failed to register dynamic DAI link %d\n", + __func__, ret); + goto out; + } + continue; + } + if (card->rtd[i].dai_link->no_codec) { + card->rtd[i].dai_link->codec_name = "null-codec"; + + ret = snd_soc_register_codec(card->dev, &null_codec_drv, + &null_codec_dai_drv, 1); + if (ret < 0) { + printk(KERN_ERR "%s: failed to register dynamic DAI link %d\n", + __func__, ret); + goto out; + } + continue; + } + } INIT_LIST_HEAD(&card->list); card->instantiated = 0; @@ -3174,8 +3296,8 @@ static int snd_soc_register_card(struct snd_soc_card *card) mutex_unlock(&client_mutex); dev_dbg(card->dev, "Registered card '%s'\n", card->name); - - return 0; +out: + return ret; } /** @@ -3515,7 +3637,11 @@ int snd_soc_register_codec(struct device *dev, return -ENOMEM; /* create CODEC component name */ - codec->name = fmt_single_name(dev, &codec->id); + if (codec_drv == &null_codec_drv) + codec->name = kstrdup("null-codec", GFP_KERNEL); + else + codec->name = fmt_single_name(dev, &codec->id); + if (codec->name == NULL) { kfree(codec); return -ENOMEM; |
