aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/soc-core.c')
-rw-r--r--sound/soc/soc-core.c611
1 files changed, 495 insertions, 116 deletions
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e2bfe1d..c171182 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -38,6 +38,7 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/soc-dsp.h>
#include <sound/initval.h>
#define CREATE_TRACE_POINTS
@@ -45,7 +46,6 @@
#define NAME_SIZE 32
-static DEFINE_MUTEX(pcm_mutex);
static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
#ifdef CONFIG_DEBUG_FS
@@ -60,6 +60,7 @@ static LIST_HEAD(platform_list);
static LIST_HEAD(codec_list);
static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
+int soc_dsp_debugfs_add(struct snd_soc_pcm_runtime *rtd);
/*
* This is a timeout to do a DAPM powerdown after a stream is closed().
@@ -123,6 +124,24 @@ static int format_register_str(struct snd_soc_codec *codec,
return 0;
}
+/* ASoC no host IO hardware.
+ * TODO: fine tune these values for all host less transfers.
+ */
+static const struct snd_pcm_hardware no_host_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = PAGE_SIZE >> 2,
+ .period_bytes_max = PAGE_SIZE >> 1,
+ .periods_min = 2,
+ .periods_max = 4,
+ .buffer_bytes_max = PAGE_SIZE,
+};
+
/* codec register dump */
static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf,
size_t count, loff_t pos)
@@ -527,7 +546,7 @@ 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 soc_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -538,7 +557,19 @@ 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_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+ if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST)
+ snd_soc_set_runtime_hwparams(substream, &no_host_hardware);
+
+ if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
+ ret = rtd->dai_link->ops->startup(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: %s startup failed\n",
+ rtd->dai_link->name);
+ goto machine_err;
+ }
+ }
/* startup the audio subsystem */
if (cpu_dai->driver->ops->startup) {
@@ -546,7 +577,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
if (ret < 0) {
printk(KERN_ERR "asoc: can't open interface %s\n",
cpu_dai->name);
- goto out;
+ goto cpu_err;
}
}
@@ -567,13 +598,9 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
}
}
- if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
- ret = rtd->dai_link->ops->startup(substream);
- if (ret < 0) {
- printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name);
- goto machine_err;
- }
- }
+ /* DSP DAI links compat checks are different */
+ if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
+ goto dynamic;
/* Check that the codec and cpu DAIs are compatible */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -658,6 +685,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
runtime->hw.rate_max);
+dynamic:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
cpu_dai->playback_active++;
codec_dai->playback_active++;
@@ -668,14 +696,11 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
cpu_dai->active++;
codec_dai->active++;
rtd->codec->active++;
- mutex_unlock(&pcm_mutex);
+ rtd->dai_link->active++;
+ mutex_unlock(&rtd->pcm_mutex);
return 0;
config_err:
- if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
- rtd->dai_link->ops->shutdown(substream);
-
-machine_err:
if (codec_dai->driver->ops->shutdown)
codec_dai->driver->ops->shutdown(substream, codec_dai);
@@ -686,8 +711,12 @@ codec_dai_err:
platform_err:
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
-out:
- mutex_unlock(&pcm_mutex);
+cpu_err:
+ if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
+ rtd->dai_link->ops->shutdown(substream);
+
+machine_err:
+ mutex_unlock(&rtd->pcm_mutex);
return ret;
}
@@ -702,7 +731,7 @@ static void close_delayed_work(struct work_struct *work)
container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- mutex_lock(&pcm_mutex);
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
pr_debug("pop wq checking: %s status: %s waiting: %s\n",
codec_dai->driver->playback.stream_name,
@@ -717,7 +746,7 @@ static void close_delayed_work(struct work_struct *work)
SND_SOC_DAPM_STREAM_STOP);
}
- mutex_unlock(&pcm_mutex);
+ mutex_unlock(&rtd->pcm_mutex);
}
/*
@@ -725,7 +754,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 soc_pcm_close(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
@@ -733,7 +762,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_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
cpu_dai->playback_active--;
@@ -746,6 +775,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
cpu_dai->active--;
codec_dai->active--;
codec->active--;
+ rtd->dai_link->active--;
/* Muting the DAC suppresses artifacts caused during digital
* shutdown, for example from stopping clocks.
@@ -759,11 +789,12 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
if (codec_dai->driver->ops->shutdown)
codec_dai->driver->ops->shutdown(substream, codec_dai);
+ if (platform->driver->ops && platform->driver->ops->close)
+ platform->driver->ops->close(substream);
+
if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
- if (platform->driver->ops && platform->driver->ops->close)
- platform->driver->ops->close(substream);
cpu_dai->runtime = NULL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -778,7 +809,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
SND_SOC_DAPM_STREAM_STOP);
}
- mutex_unlock(&pcm_mutex);
+ mutex_unlock(&rtd->pcm_mutex);
return 0;
}
@@ -787,7 +818,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
* 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 soc_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
@@ -795,7 +826,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_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
ret = rtd->dai_link->ops->prepare(substream);
@@ -848,7 +879,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
snd_soc_dai_digital_mute(codec_dai, 0);
out:
- mutex_unlock(&pcm_mutex);
+ mutex_unlock(&rtd->pcm_mutex);
return ret;
}
@@ -857,7 +888,7 @@ out:
* 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 soc_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -866,7 +897,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_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
ret = rtd->dai_link->ops->hw_params(substream, params);
@@ -905,8 +936,21 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
rtd->rate = params_rate(params);
+ /* malloc a page for hostless IO.
+ * FIXME: rework with alsa-lib changes so that this malloc is not required.
+ */
+ if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) {
+ substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV;
+ substream->dma_buffer.dev.dev = &rtd->dev;
+ substream->dma_buffer.dev.dev->coherent_dma_mask = ISA_DMA_THRESHOLD;
+ substream->dma_buffer.private_data = NULL;
+
+ ret = snd_pcm_lib_malloc_pages(substream, PAGE_SIZE);
+ if (ret < 0)
+ goto platform_err;
+ }
out:
- mutex_unlock(&pcm_mutex);
+ mutex_unlock(&rtd->pcm_mutex);
return ret;
platform_err:
@@ -921,14 +965,14 @@ 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;
}
/*
* Frees resources allocated by hw_params, can be called multiple times
*/
-static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
+int 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;
@@ -936,7 +980,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_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
/* apply codec digital mute */
if (!codec->active)
@@ -957,11 +1001,13 @@ 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);
+ if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST)
+ snd_pcm_lib_free_pages(substream);
+ mutex_unlock(&rtd->pcm_mutex);
return 0;
}
-static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+int 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;
@@ -989,12 +1035,40 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
+int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ if (codec_dai->driver->ops->bespoke_trigger) {
+ ret = codec_dai->driver->ops->bespoke_trigger(substream, cmd, codec_dai);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (platform->driver->bespoke_trigger) {
+ ret = platform->driver->bespoke_trigger(substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (cpu_dai->driver->ops->bespoke_trigger) {
+ ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
/*
* 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 soc_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
@@ -1021,16 +1095,74 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
return offset;
}
-/* 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,
-};
+static int soc_pcm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+
+ if (platform->driver->ops->ioctl)
+ return platform->driver->ops->ioctl(substream, cmd, arg);
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+struct snd_soc_codec *snd_soc_card_get_codec(struct snd_soc_card *card,
+ const char *codec_name)
+{
+ struct snd_soc_codec *codec = NULL;
+
+ list_for_each_entry(codec, &card->codec_dev_list, card_list) {
+ if (!strcmp(codec->name, codec_name))
+ return codec;
+ }
+
+ return codec;
+}
+EXPORT_SYMBOL(snd_soc_card_get_codec);
+
+int snd_soc_card_active_links(struct snd_soc_card *card)
+{
+ int i;
+ int count = 0;
+
+ for (i = 0; i < card->num_rtd; i++) {
+ /* count FEs: dynamic and legacy */
+ if (!card->rtd[i].dai_link->no_pcm)
+ count += card->rtd[i].dai_link->active;
+ }
+
+ return count;
+}
+EXPORT_SYMBOL(snd_soc_card_active_links);
+
+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_SLEEP
/* powers down audio subsystem for suspend */
@@ -1040,6 +1172,9 @@ int snd_soc_suspend(struct device *dev)
struct snd_soc_codec *codec;
int i;
+ /* cancel pending deferred resume if any */
+ cancel_work_sync(&card->deferred_resume_work);
+
/* If the initialization of this soc device failed, there is no codec
* associated with it. Just bail out in this case.
*/
@@ -1061,16 +1196,22 @@ int snd_soc_suspend(struct device *dev)
struct snd_soc_dai *dai = card->rtd[i].codec_dai;
struct snd_soc_dai_driver *drv = dai->driver;
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (card->rtd[i].dai_link->ignore_suspend ||
+ card->rtd[i].dai_link->no_pcm)
continue;
- if (drv->ops->digital_mute && dai->playback_active)
- drv->ops->digital_mute(dai, 1);
+ if (card->rtd[i].dai_link->dynamic)
+ soc_dsp_be_digital_mute(&card->rtd[i], 1);
+ else {
+ if (drv->ops->digital_mute && dai->playback_active)
+ drv->ops->digital_mute(dai, 1);
+ }
}
/* suspend all pcms */
for (i = 0; i < card->num_rtd; i++) {
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (card->rtd[i].dai_link->ignore_suspend ||
+ card->rtd[i].dai_link->no_pcm)
continue;
snd_pcm_suspend_all(card->rtd[i].pcm);
@@ -1083,14 +1224,19 @@ int snd_soc_suspend(struct device *dev)
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
struct snd_soc_platform *platform = card->rtd[i].platform;
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (card->rtd[i].dai_link->ignore_suspend ||
+ card->rtd[i].dai_link->no_pcm)
continue;
- if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control)
- cpu_dai->driver->suspend(cpu_dai);
- if (platform->driver->suspend && !platform->suspended) {
- platform->driver->suspend(cpu_dai);
- platform->suspended = 1;
+ if (card->rtd[i].dai_link->dynamic) {
+ soc_dsp_fe_suspend(&card->rtd[i]);
+ } else {
+ if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control)
+ cpu_dai->driver->suspend(cpu_dai);
+ if (platform->driver->suspend && !platform->suspended) {
+ platform->driver->suspend(cpu_dai);
+ platform->suspended = 1;
+ }
}
}
@@ -1103,7 +1249,8 @@ int snd_soc_suspend(struct device *dev)
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai_driver *driver = card->rtd[i].codec_dai->driver;
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (card->rtd[i].dai_link->ignore_suspend ||
+ card->rtd[i].dai_link->no_pcm)
continue;
if (driver->playback.stream_name != NULL)
@@ -1137,11 +1284,15 @@ int snd_soc_suspend(struct device *dev)
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (card->rtd[i].dai_link->ignore_suspend ||
+ card->rtd[i].dai_link->no_pcm)
continue;
- if (cpu_dai->driver->suspend && cpu_dai->driver->ac97_control)
- cpu_dai->driver->suspend(cpu_dai);
+ if (card->rtd[i].dai_link->dynamic)
+ soc_dsp_be_ac97_cpu_dai_suspend(&card->rtd[i]);
+ else
+ if (cpu_dai->driver->suspend && cpu_dai->driver->ac97_control)
+ cpu_dai->driver->suspend(cpu_dai);
}
if (card->suspend_post)
@@ -1177,11 +1328,15 @@ static void soc_resume_deferred(struct work_struct *work)
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (card->rtd[i].dai_link->ignore_suspend ||
+ card->rtd[i].dai_link->no_pcm)
continue;
- if (cpu_dai->driver->resume && cpu_dai->driver->ac97_control)
- cpu_dai->driver->resume(cpu_dai);
+ if (card->rtd[i].dai_link->dynamic)
+ soc_dsp_be_ac97_cpu_dai_resume(&card->rtd[i]);
+ else
+ if (cpu_dai->driver->resume && cpu_dai->driver->ac97_control)
+ cpu_dai->driver->resume(cpu_dai);
}
list_for_each_entry(codec, &card->codec_dev_list, card_list) {
@@ -1206,7 +1361,8 @@ static void soc_resume_deferred(struct work_struct *work)
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai_driver *driver = card->rtd[i].codec_dai->driver;
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (card->rtd[i].dai_link->ignore_suspend ||
+ card->rtd[i].dai_link->no_pcm)
continue;
if (driver->playback.stream_name != NULL)
@@ -1223,25 +1379,35 @@ static void soc_resume_deferred(struct work_struct *work)
struct snd_soc_dai *dai = card->rtd[i].codec_dai;
struct snd_soc_dai_driver *drv = dai->driver;
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (card->rtd[i].dai_link->ignore_suspend ||
+ card->rtd[i].dai_link->no_pcm)
continue;
- if (drv->ops->digital_mute && dai->playback_active)
- drv->ops->digital_mute(dai, 0);
+ if (card->rtd[i].dai_link->dynamic)
+ soc_dsp_be_digital_mute(&card->rtd[i], 0);
+ else {
+ if (drv->ops->digital_mute && dai->playback_active)
+ drv->ops->digital_mute(dai, 0);
+ }
}
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
struct snd_soc_platform *platform = card->rtd[i].platform;
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (card->rtd[i].dai_link->ignore_suspend ||
+ card->rtd[i].dai_link->no_pcm)
continue;
- if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control)
- cpu_dai->driver->resume(cpu_dai);
- if (platform->driver->resume && platform->suspended) {
- platform->driver->resume(cpu_dai);
- platform->suspended = 0;
+ if (card->rtd[i].dai_link->dynamic) {
+ soc_dsp_fe_resume(&card->rtd[i]);
+ } else {
+ if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control)
+ cpu_dai->driver->resume(cpu_dai);
+ if (platform->driver->resume && platform->suspended) {
+ platform->driver->resume(cpu_dai);
+ platform->suspended = 0;
+ }
}
}
@@ -1286,8 +1452,30 @@ EXPORT_SYMBOL_GPL(snd_soc_resume);
#define snd_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)
{
@@ -1329,7 +1517,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;
@@ -1396,7 +1584,7 @@ static void soc_remove_codec(struct snd_soc_codec *codec)
module_put(codec->dev->driver->owner);
}
-static void soc_remove_dai_link(struct snd_soc_card *card, int num)
+static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
{
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_codec *codec = rtd->codec;
@@ -1413,7 +1601,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
}
/* remove the CODEC DAI */
- if (codec_dai && codec_dai->probed) {
+ if (codec_dai && codec_dai->probed &&
+ codec_dai->driver->remove_order == order) {
if (codec_dai->driver->remove) {
err = codec_dai->driver->remove(codec_dai);
if (err < 0)
@@ -1421,10 +1610,12 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
}
codec_dai->probed = 0;
list_del(&codec_dai->card_list);
+ module_put(codec_dai->dev->driver->owner);
}
/* remove the platform */
- if (platform && platform->probed) {
+ if (platform && platform->probed &&
+ platform->driver->remove_order == order) {
if (platform->driver->remove) {
err = platform->driver->remove(platform);
if (err < 0)
@@ -1436,11 +1627,13 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
}
/* remove the CODEC */
- if (codec && codec->probed)
+ if (codec && codec->probed &&
+ codec->driver->remove_order == order)
soc_remove_codec(codec);
/* remove the cpu_dai */
- if (cpu_dai && cpu_dai->probed) {
+ if (cpu_dai && cpu_dai->probed &&
+ cpu_dai->driver->remove_order == order) {
if (cpu_dai->driver->remove) {
err = cpu_dai->driver->remove(cpu_dai);
if (err < 0)
@@ -1454,11 +1647,13 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
static void soc_remove_dai_links(struct snd_soc_card *card)
{
- int i;
-
- for (i = 0; i < card->num_rtd; i++)
- soc_remove_dai_link(card, i);
+ int dai, order;
+ for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
+ order++) {
+ for (dai = 0; dai < card->num_rtd; dai++)
+ soc_remove_dai_link(card, dai, order);
+ }
card->num_rtd = 0;
}
@@ -1575,6 +1770,11 @@ static int soc_post_component_init(struct snd_soc_card *card,
rtd->dev.parent = card->dev;
rtd->dev.release = rtd_release;
rtd->dev.init_name = name;
+ mutex_init(&rtd->pcm_mutex);
+ INIT_LIST_HEAD(&rtd->dsp[SNDRV_PCM_STREAM_PLAYBACK].be_clients);
+ INIT_LIST_HEAD(&rtd->dsp[SNDRV_PCM_STREAM_CAPTURE].be_clients);
+ INIT_LIST_HEAD(&rtd->dsp[SNDRV_PCM_STREAM_PLAYBACK].fe_clients);
+ INIT_LIST_HEAD(&rtd->dsp[SNDRV_PCM_STREAM_CAPTURE].fe_clients);
ret = device_register(&rtd->dev);
if (ret < 0) {
dev_err(card->dev,
@@ -1596,10 +1796,21 @@ static int soc_post_component_init(struct snd_soc_card *card,
dev_err(codec->dev,
"asoc: failed to add codec sysfs files: %d\n", ret);
+#ifdef CONFIG_DEBUG_FS
+ /* add DSP sysfs entries */
+ if (!dai_link->dynamic)
+ goto out;
+
+ ret = soc_dsp_debugfs_add(rtd);
+ if (ret < 0)
+ dev_err(&rtd->dev, "asoc: failed to add dsp sysfs entries: %d\n", ret);
+
+out:
+#endif
return 0;
}
-static int soc_probe_dai_link(struct snd_soc_card *card, int num)
+static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
{
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
@@ -1608,19 +1819,22 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
int ret;
- dev_dbg(card->dev, "probe %s dai link %d\n", card->name, num);
+ dev_dbg(card->dev, "probe %s dai link %d late %d\n",
+ card->name, num, order);
/* config components */
codec_dai->codec = codec;
cpu_dai->platform = platform;
codec_dai->card = card;
cpu_dai->card = card;
+ codec->dapm.card = platform->dapm.card = card;
/* set default power off timeout */
rtd->pmdown_time = pmdown_time;
/* probe the cpu_dai */
- if (!cpu_dai->probed) {
+ if (!cpu_dai->probed &&
+ cpu_dai->driver->probe_order == order) {
if (!try_module_get(cpu_dai->dev->driver->owner))
return -ENODEV;
@@ -1639,17 +1853,20 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
}
/* probe the CODEC */
- if (!codec->probed) {
+ if (!codec->probed &&
+ codec->driver->probe_order == order) {
ret = soc_probe_codec(card, codec);
if (ret < 0)
return ret;
}
/* probe the platform */
- if (!platform->probed) {
+ if (!platform->probed &&
+ platform->driver->probe_order == order) {
if (!try_module_get(platform->dev->driver->owner))
return -ENODEV;
+ platform->card = card;
if (platform->driver->probe) {
ret = platform->driver->probe(platform);
if (ret < 0) {
@@ -1665,12 +1882,15 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
}
/* probe the CODEC DAI */
- if (!codec_dai->probed) {
+ if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
+ if (!try_module_get(codec_dai->dev->driver->owner))
+ return -ENODEV;
if (codec_dai->driver->probe) {
ret = codec_dai->driver->probe(codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: failed to probe CODEC DAI %s\n",
codec_dai->name);
+ module_put(codec_dai->dev->driver->owner);
return ret;
}
}
@@ -1680,6 +1900,10 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
list_add(&codec_dai->card_list, &card->dai_dev_list);
}
+ /* complete DAI probe during last probe */
+ if (order != SND_SOC_COMP_ORDER_LAST)
+ return 0;
+
/* DAPM dai link stream work */
INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
@@ -1820,7 +2044,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
struct snd_soc_codec *codec;
struct snd_soc_codec_conf *codec_conf;
enum snd_soc_compress_type compress_type;
- int ret, i;
+ int ret, i, order;
mutex_lock(&card->mutex);
@@ -1876,6 +2100,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
card->dapm.bias_level = SND_SOC_BIAS_OFF;
card->dapm.dev = card->dev;
card->dapm.card = card;
+ card->dapm.stream_event = card->stream_event;
list_add(&card->dapm.list, &card->dapm_list);
#ifdef CONFIG_DEBUG_FS
@@ -1898,12 +2123,16 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
goto card_probe_error;
}
- for (i = 0; i < card->num_links; i++) {
- ret = soc_probe_dai_link(card, i);
- if (ret < 0) {
- pr_err("asoc: failed to instantiate card %s: %d\n",
+ /* early DAI link probe */
+ for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
+ order++) {
+ for (i = 0; i < card->num_links; i++) {
+ ret = soc_probe_dai_link(card, i, order);
+ if (ret < 0) {
+ pr_err("asoc: failed to instantiate card %s: %d\n",
card->name, ret);
- goto probe_dai_err;
+ goto probe_dai_err;
+ }
}
}
@@ -2092,6 +2321,11 @@ int snd_soc_poweroff(struct device *dev)
}
EXPORT_SYMBOL_GPL(snd_soc_poweroff);
+void soc_shutdown(struct platform_device *pdev)
+{
+ snd_soc_poweroff(&pdev->dev);
+}
+
const struct dev_pm_ops snd_soc_pm_ops = {
.suspend = snd_soc_suspend,
.resume = snd_soc_resume,
@@ -2108,6 +2342,7 @@ static struct platform_driver soc_driver = {
},
.probe = soc_probe,
.remove = soc_remove,
+ .shutdown = soc_shutdown,
};
/* create a new pcm */
@@ -2117,6 +2352,7 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_pcm_substream *substream[2];
struct snd_pcm *pcm;
char new_name[64];
int ret = 0, playback = 0, capture = 0;
@@ -2125,10 +2361,15 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
snprintf(new_name, sizeof(new_name), "%s %s-%d",
rtd->dai_link->stream_name, codec_dai->name, num);
- if (codec_dai->driver->playback.channels_min)
- playback = 1;
- if (codec_dai->driver->capture.channels_min)
- capture = 1;
+ if (rtd->dai_link->dynamic) {
+ playback = rtd->dai_link->dsp_link->playback;
+ capture = rtd->dai_link->dsp_link->capture;
+ } else {
+ if (codec_dai->driver->playback.channels_min)
+ playback = 1;
+ if (codec_dai->driver->capture.channels_min)
+ capture = 1;
+ }
dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
ret = snd_pcm_new(rtd->card->snd_card, new_name,
@@ -2140,25 +2381,67 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
rtd->pcm = pcm;
pcm->private_data = rtd;
+
+ substream[SNDRV_PCM_STREAM_PLAYBACK] =
+ pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ substream[SNDRV_PCM_STREAM_CAPTURE] =
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+
+ if (rtd->dai_link->no_pcm) {
+ if (playback)
+ substream[SNDRV_PCM_STREAM_PLAYBACK]->private_data = rtd;
+ if (capture)
+ substream[SNDRV_PCM_STREAM_CAPTURE]->private_data = rtd;
+ goto out;
+ }
+
+ /* setup any hostless PCMs - i.e. no host IO is performed */
+ if (rtd->dai_link->no_host_mode) {
+ substream[SNDRV_PCM_STREAM_PLAYBACK]->hw_no_buffer = 1;
+ substream[SNDRV_PCM_STREAM_CAPTURE]->hw_no_buffer = 1;
+ snd_soc_set_runtime_hwparams(substream[SNDRV_PCM_STREAM_PLAYBACK],
+ &no_host_hardware);
+ snd_soc_set_runtime_hwparams(substream[SNDRV_PCM_STREAM_CAPTURE],
+ &no_host_hardware);
+ }
+
+ /* ASoC PCM operations */
+ if (rtd->dai_link->dynamic) {
+ rtd->ops.open = soc_dsp_fe_dai_open;
+ rtd->ops.hw_params = soc_dsp_fe_dai_hw_params;
+ rtd->ops.prepare = soc_dsp_fe_dai_prepare;
+ rtd->ops.trigger = soc_dsp_fe_dai_trigger;
+ rtd->ops.hw_free = soc_dsp_fe_dai_hw_free;
+ rtd->ops.close = soc_dsp_fe_dai_close;
+ rtd->ops.pointer = soc_pcm_pointer;
+ rtd->ops.ioctl = soc_pcm_ioctl;
+ } else {
+ rtd->ops.open = soc_pcm_open;
+ rtd->ops.hw_params = soc_pcm_hw_params;
+ rtd->ops.prepare = soc_pcm_prepare;
+ rtd->ops.trigger = soc_pcm_trigger;
+ rtd->ops.hw_free = soc_pcm_hw_free;
+ rtd->ops.close = soc_pcm_close;
+ rtd->ops.pointer = soc_pcm_pointer;
+ rtd->ops.ioctl = soc_pcm_ioctl;
+ }
+
if (platform->driver->ops) {
- soc_pcm_ops.mmap = platform->driver->ops->mmap;
- soc_pcm_ops.pointer = platform->driver->ops->pointer;
- soc_pcm_ops.ioctl = platform->driver->ops->ioctl;
- soc_pcm_ops.copy = platform->driver->ops->copy;
- soc_pcm_ops.silence = platform->driver->ops->silence;
- soc_pcm_ops.ack = platform->driver->ops->ack;
- soc_pcm_ops.page = platform->driver->ops->page;
+ rtd->ops.ack = platform->driver->ops->ack;
+ rtd->ops.copy = platform->driver->ops->copy;
+ rtd->ops.silence = platform->driver->ops->silence;
+ rtd->ops.page = platform->driver->ops->page;
+ rtd->ops.mmap = platform->driver->ops->mmap;
}
if (playback)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
if (capture)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
if (platform->driver->pcm_new) {
- ret = platform->driver->pcm_new(rtd->card->snd_card,
- codec_dai, pcm);
+ ret = platform->driver->pcm_new(rtd);
if (ret < 0) {
pr_err("asoc: platform pcm constructor failed\n");
return ret;
@@ -2166,6 +2449,7 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
}
pcm->private_free = platform->driver->pcm_free;
+out:
printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
cpu_dai->name);
return ret;
@@ -2189,6 +2473,28 @@ int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register);
+unsigned int snd_soc_platform_read(struct snd_soc_platform *platform,
+ unsigned int reg)
+{
+ unsigned int ret;
+
+ ret = platform->driver->read(platform, reg);
+ dev_dbg(platform->dev, "read %x => %x\n", reg, ret);
+ trace_snd_soc_preg_read(platform, reg, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_platform_read);
+
+unsigned int snd_soc_platform_write(struct snd_soc_platform *platform,
+ unsigned int reg, unsigned int val)
+{
+ dev_dbg(platform->dev, "write %x = %x\n", reg, val);
+ trace_snd_soc_preg_write(platform, reg, val);
+ return platform->driver->write(platform, reg, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_platform_write);
+
/**
* snd_soc_codec_readable_register: Report if a register is readable.
*
@@ -2411,6 +2717,8 @@ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
const struct snd_pcm_hardware *hw)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ if (!runtime)
+ return 0;
runtime->hw.info = hw->info;
runtime->hw.formats = hw->formats;
runtime->hw.period_bytes_min = hw->period_bytes_min;
@@ -2504,6 +2812,36 @@ int snd_soc_add_controls(struct snd_soc_codec *codec,
EXPORT_SYMBOL_GPL(snd_soc_add_controls);
/**
+ * snd_soc_add_platform_controls - add an array of controls to a platform.
+ * Convienience function to add a list of controls.
+ *
+ * @platform: platform to add controls to
+ * @controls: array of controls to add
+ * @num_controls: number of elements in the array
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_add_platform_controls(struct snd_soc_platform *platform,
+ const struct snd_kcontrol_new *controls, int num_controls)
+{
+ struct snd_card *card = platform->card->snd_card;
+ int err, i;
+
+ for (i = 0; i < num_controls; i++) {
+ const struct snd_kcontrol_new *control = &controls[i];
+ err = snd_ctl_add(card, snd_soc_cnew(control, platform,
+ control->name, NULL));
+ if (err < 0) {
+ dev_err(platform->dev, "Failed to add %s %d\n",control->name, err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls);
+
+/**
* snd_soc_info_enum_double - enumerated double mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
@@ -2525,7 +2863,8 @@ int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
if (uinfo->value.enumerated.item > e->max - 1)
uinfo->value.enumerated.item = e->max - 1;
strcpy(uinfo->value.enumerated.name,
- e->texts[uinfo->value.enumerated.item]);
+ snd_soc_get_enum_text(e, uinfo->value.enumerated.item));
+
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
@@ -2689,7 +3028,7 @@ int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
if (uinfo->value.enumerated.item > e->max - 1)
uinfo->value.enumerated.item = e->max - 1;
strcpy(uinfo->value.enumerated.name,
- e->texts[uinfo->value.enumerated.item]);
+ snd_soc_get_enum_text(e, uinfo->value.enumerated.item));
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext);
@@ -3370,6 +3709,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
int snd_soc_register_card(struct snd_soc_card *card)
{
int i;
+ int ret = 0;
if (!card->name || !card->dev)
return -EINVAL;
@@ -3387,12 +3727,43 @@ 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++) {
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;
mutex_init(&card->mutex);
+ mutex_init(&card->dapm_mutex);
+ mutex_init(&card->dsp_mutex);
+ mutex_init(&card->power_mutex);
mutex_lock(&client_mutex);
list_add(&card->list, &card_list);
@@ -3401,7 +3772,8 @@ int snd_soc_register_card(struct snd_soc_card *card)
dev_dbg(card->dev, "Registered card '%s'\n", card->name);
- return 0;
+out:
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_card);
@@ -3646,7 +4018,10 @@ int snd_soc_register_platform(struct device *dev,
}
platform->dev = dev;
+ platform->dapm.platform = platform;
platform->driver = platform_drv;
+ platform->dapm.dev = dev;
+ platform->dapm.stream_event = platform_drv->stream_event;
mutex_lock(&client_mutex);
list_add(&platform->list, &platform_list);
@@ -3739,7 +4114,10 @@ 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;
@@ -3759,6 +4137,7 @@ int snd_soc_register_codec(struct device *dev,
codec->dapm.dev = dev;
codec->dapm.codec = codec;
codec->dapm.seq_notifier = codec_drv->seq_notifier;
+ codec->dapm.stream_event = codec_drv->stream_event;
codec->dev = dev;
codec->driver = codec_drv;
codec->num_dai = num_dai;