summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiam Girdwood <lrg@slimlogic.co.uk>2011-04-07 08:38:46 +0100
committerAndy Green <andy.green@linaro.org>2011-04-07 08:38:46 +0100
commitbe054eadeaf4e2de3139dc2d9859b2001be47e51 (patch)
tree4b7443cd1ffe275f856cbcc82aa7d76b3144ab11
parentf6e4e17fc6f5684f961921dfe8255ea68ca1fad5 (diff)
ASoC: core: Add support for back end DAIs
Add support for backend DAIs. Change-Id: Id067678682ecb480c63af142b85b73a93ebc6c69 Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk> Signed-off-by: Axel Castaneda Gonzalez <x0055901@ti.com> Signed-off-by: Misael Lopez Cruz <misael.lopez@ti.com> Signed-off-by: Margarita Olaya Cabrera <magi.olaya@ti.com>
-rw-r--r--include/sound/soc-dai.h49
-rw-r--r--include/sound/soc.h26
-rw-r--r--sound/soc/soc-core.c220
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;