ASoC: dpcm: Add Dynamic PCM core operations.

The Dynamic PCM core allows digital audio data to be dynamically
routed between different ALSA PCMs and DAI links on SoC CPUs with
on chip DSP devices. e.g. audio data could be played on pcm:0,0 and
routed to any (or all) SoC DAI links.

Dynamic PCM introduces the concept of Front End (FE) PCMs and Back
End (BE) PCMs. The FE PCMs are normal ALSA PCM devices except that
they can dynamically route digital audio data to any supported BE
PCM. A BE PCM has no ALSA device, but represents a DAI link and it's
substream and audio HW parameters.

Signed-off-by: Gopikrishnaiah Anandan <agopik@codeaurora.org>
This commit is contained in:
Gopikrishnaiah Anandan 2013-02-15 14:21:01 -05:00 committed by Stephen Boyd
parent 9e3cd79005
commit 1f42244c54
8 changed files with 2301 additions and 135 deletions

View File

@ -264,7 +264,7 @@ struct snd_pcm_hw_constraint_ratdens {
struct snd_pcm_hw_constraint_list {
unsigned int count;
const unsigned int *list;
unsigned int *list;
unsigned int mask;
};
@ -454,7 +454,6 @@ struct snd_pcm {
void *private_data;
void (*private_free) (struct snd_pcm *pcm);
struct device *dev; /* actual hw device this belongs to */
bool internal; /* pcm is for internal use only */
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
struct snd_pcm_oss oss;
#endif

View File

@ -173,6 +173,8 @@ struct snd_soc_dai_ops {
struct snd_soc_dai *);
int (*trigger)(struct snd_pcm_substream *, int,
struct snd_soc_dai *);
int (*bespoke_trigger)(struct snd_pcm_substream *, int,
struct snd_soc_dai *);
/*
* For hardware based FIFO caused delay reporting.
* Optional.
@ -287,4 +289,98 @@ 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)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int ret = 0;
mutex_lock(&rtd->pcm_mutex);
if (dai->driver->ops->startup)
ret = dai->driver->ops->startup(substream, dai);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
dai->playback_active++;
else
dai->capture_active++;
dai->active++;
mutex_unlock(&rtd->pcm_mutex);
return ret;
}
static inline void snd_soc_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
mutex_lock(&rtd->pcm_mutex);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
dai->playback_active--;
else
dai->capture_active--;
dai->active--;
if (dai->driver->ops->shutdown)
dai->driver->ops->shutdown(substream, dai);
mutex_unlock(&rtd->pcm_mutex);
}
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)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int ret = 0;
mutex_lock(&rtd->pcm_mutex);
if (dai->driver->ops->hw_params)
ret = dai->driver->ops->hw_params(substream, hw_params, dai);
mutex_unlock(&rtd->pcm_mutex);
return ret;
}
static inline int snd_soc_dai_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int ret = 0;
mutex_lock(&rtd->pcm_mutex);
if (dai->driver->ops->hw_free)
ret = dai->driver->ops->hw_free(substream, dai);
mutex_unlock(&rtd->pcm_mutex);
return ret;
}
static inline int snd_soc_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int ret = 0;
mutex_lock(&rtd->pcm_mutex);
if (dai->driver->ops->prepare)
ret = dai->driver->ops->prepare(substream, dai);
mutex_unlock(&rtd->pcm_mutex);
return ret;
}
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

View File

@ -13,11 +13,10 @@
#ifndef __LINUX_SND_SOC_DAPM_H
#define __LINUX_SND_SOC_DAPM_H
#include <linux/device.h>
#include <linux/types.h>
#include <sound/control.h>
struct device;
/* widget has no PM register bit */
#define SND_SOC_NOPM -1
@ -369,8 +368,10 @@ int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num);
/* dapm events */
int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
struct snd_soc_dai *dai, int event);
int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd,
const char *stream, int event);
void snd_soc_dapm_rtd_stream_event(struct snd_soc_pcm_runtime *rtd,
int stream, int event);
void snd_soc_dapm_shutdown(struct snd_soc_card *card);
/* external DAPM widget events */

109
include/sound/soc-dpcm.h Normal file
View File

@ -0,0 +1,109 @@
/*
* linux/sound/soc-dpcm.h -- ALSA SoC Dynamic PCM Support
*
* Author: Liam Girdwood <lrg@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __LINUX_SND_SOC_DPCM_H
#define __LINUX_SND_SOC_DPCM_H
#include <sound/pcm.h>
/*
* Types of runtime_update to perform (e.g. originated from FE PCM ops
* or audio route changes triggered by muxes/mixers.
*/
#define SND_SOC_DPCM_UPDATE_NO 0
#define SND_SOC_DPCM_UPDATE_BE 1
#define SND_SOC_DPCM_UPDATE_FE 2
/*
* Dynamic PCM Frontend -> Backend link state.
*/
enum snd_soc_dpcm_link_state {
SND_SOC_DPCM_LINK_STATE_NEW = 0, /* newly created path */
SND_SOC_DPCM_LINK_STATE_FREE, /* path to be dismantled */
};
/*
* Dynamic PCM params link
* This links together a FE and BE DAI at runtime and stores the link
* state information and the hw_params configuration.
*/
struct snd_soc_dpcm_params {
/* FE and BE DAIs*/
struct snd_soc_pcm_runtime *be;
struct snd_soc_pcm_runtime *fe;
/* link state */
enum snd_soc_dpcm_link_state state;
struct list_head list_be;
struct list_head list_fe;
/* hw params for this link - may be different for each link */
struct snd_pcm_hw_params hw_params;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_state;
#endif
};
/*
* Bespoke Trigger() Helper API
*/
/* is the PCM operation for this FE ? */
static inline int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe,
int stream)
{
return (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE);
}
/* is the PCM operation for this BE ? */
static inline int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe,
struct snd_soc_pcm_runtime *be, int stream)
{
if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) ||
((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) &&
be->dpcm[stream].runtime_update))
return 1;
else
return 0;
}
/* trigger platform driver only */
static inline int
snd_soc_dpcm_platform_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_platform *platform)
{
if (platform->driver->ops->trigger)
return platform->driver->ops->trigger(substream, cmd);
return 0;
}
int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
struct snd_soc_pcm_runtime *be, int stream);
static inline struct snd_pcm_substream *
snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream)
{
return be->pcm->streams[stream].substream;
}
static inline enum snd_soc_dpcm_state
snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream)
{
return be->dpcm[stream].state;
}
static inline void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be,
int stream, enum snd_soc_dpcm_state state)
{
be->dpcm[stream].state = state;
}
#endif

View File

@ -217,6 +217,12 @@
#define SOC_VALUE_ENUM_SINGLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \
SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues)
/* DAI Link Host Mode Support */
#define SND_SOC_DAI_LINK_NO_HOST 0x1
#define SND_SOC_DAI_LINK_OPT_HOST 0x2
/*
* Component probe and remove ordering levels for components with runtime
* dependencies.
@ -263,6 +269,7 @@ struct snd_soc_jack;
struct snd_soc_jack_zone;
struct snd_soc_jack_pin;
struct snd_soc_cache_ops;
struct snd_soc_dpcm_link;
#include <sound/soc-dapm.h>
#ifdef CONFIG_GPIOLIB
@ -288,6 +295,35 @@ enum snd_soc_pcm_subclass {
SND_SOC_PCM_CLASS_BE = 1,
};
/*
* Dynamic PCM DAI link states.
*/
enum snd_soc_dpcm_state {
SND_SOC_DPCM_STATE_NEW = 0,
SND_SOC_DPCM_STATE_OPEN,
SND_SOC_DPCM_STATE_HW_PARAMS,
SND_SOC_DPCM_STATE_PREPARE,
SND_SOC_DPCM_STATE_START,
SND_SOC_DPCM_STATE_STOP,
SND_SOC_DPCM_STATE_PAUSED,
SND_SOC_DPCM_STATE_SUSPEND,
SND_SOC_DPCM_STATE_HW_FREE,
SND_SOC_DPCM_STATE_CLOSE,
};
/*
* Dynamic PCM trigger ordering. Triggering flexibility is required as some
* DSPs require triggering before/after their clients/hosts.
*
* i.e. some clients may want to manually order this call in their PCM
* trigger() whilst others will just use the regular core ordering.
*/
enum snd_soc_dpcm_trigger {
SND_SOC_DPCM_TRIGGER_PRE = 0,
SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_BESPOKE,
};
int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir);
int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
@ -333,6 +369,11 @@ int snd_soc_platform_write(struct snd_soc_platform *platform,
unsigned int reg, unsigned int val);
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
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);
@ -664,8 +705,6 @@ struct snd_soc_codec_driver {
/* codec stream completion event */
int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
bool ignore_pmdown_time; /* Doesn't benefit from pmdown delay */
/* probe ordering - for components with runtime dependencies */
int probe_order;
int remove_order;
@ -711,6 +750,8 @@ struct snd_soc_platform_driver {
/* platform IO - used for platform DAPM */
unsigned int (*read)(struct snd_soc_platform *, unsigned int);
int (*write)(struct snd_soc_platform *, unsigned int, unsigned int);
int (*bespoke_trigger)(struct snd_pcm_substream *, int);
};
struct snd_soc_platform {
@ -718,7 +759,6 @@ struct snd_soc_platform {
int id;
struct device *dev;
struct snd_soc_platform_driver *driver;
struct mutex mutex;
unsigned int suspended:1; /* platform is suspended */
unsigned int probed:1;
@ -749,11 +789,21 @@ struct snd_soc_dai_link {
unsigned int dai_fmt; /* format to set on init */
enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */
/* Keep DAI active over suspend */
unsigned int ignore_suspend:1;
/* 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 has a Backend ID */
unsigned int be_id;
/* This DAI can support no host IO (no pcm data is copied to from host) */
unsigned int no_host_mode:2;
/* pmdown_time is ignored at stop */
unsigned int ignore_pmdown_time:1;
@ -761,6 +811,10 @@ struct snd_soc_dai_link {
/* codec/machine specific init - e.g. add machine controls */
int (*init)(struct snd_soc_pcm_runtime *rtd);
/* hw_params re-writing for BE and FE sync */
int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params);
/* machine stream operations */
struct snd_soc_ops *ops;
};
@ -800,6 +854,12 @@ struct snd_soc_card {
struct list_head list;
struct mutex mutex;
struct mutex dpcm_mutex;
struct mutex dapm_mutex;
struct mutex dapm_power_mutex;
struct mutex dsp_mutex;
spinlock_t dsp_spinlock;
bool instantiated;
@ -880,6 +940,17 @@ struct snd_soc_card {
void *drvdata;
};
/* DSP runtime data */
struct snd_soc_dpcm_runtime {
struct list_head be_clients;
struct list_head fe_clients;
int users;
struct snd_pcm_runtime *runtime;
struct snd_pcm_hw_params hw_params;
int runtime_update;
enum snd_soc_dpcm_state state;
};
/* SoC machine DAI configuration, glues a codec and cpu DAI together */
struct snd_soc_pcm_runtime {
struct device *dev;
@ -892,6 +963,9 @@ struct snd_soc_pcm_runtime {
unsigned int complete:1;
unsigned int dev_registered:1;
/* Dynamic PCM BE runtime data */
struct snd_soc_dpcm_runtime dpcm[2];
long pmdown_time;
/* runtime devices */
@ -902,6 +976,11 @@ struct snd_soc_pcm_runtime {
struct snd_soc_dai *cpu_dai;
struct delayed_work delayed_work;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_dpcm_root;
struct dentry *debugfs_dpcm_state;
#endif
};
/* mixer control */

View File

@ -39,6 +39,7 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dpcm.h>
#include <sound/initval.h>
#define CREATE_TRACE_POINTS
@ -59,12 +60,22 @@ static LIST_HEAD(dai_list);
static LIST_HEAD(platform_list);
static LIST_HEAD(codec_list);
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd);
int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute);
int soc_dpcm_be_ac97_cpu_dai_suspend(struct snd_soc_pcm_runtime *fe);
int soc_dpcm_be_ac97_cpu_dai_resume(struct snd_soc_pcm_runtime *fe);
int soc_dpcm_be_cpu_dai_resume(struct snd_soc_pcm_runtime *fe);
int soc_dpcm_be_cpu_dai_suspend(struct snd_soc_pcm_runtime *fe);
int soc_dpcm_be_platform_suspend(struct snd_soc_pcm_runtime *fe);
int soc_dpcm_be_platform_resume(struct snd_soc_pcm_runtime *fe);
/*
* This is a timeout to do a DAPM powerdown after a stream is closed().
* It can be used to eliminate pops between different playback streams, e.g.
* between two audio tracks.
*/
static int pmdown_time = 5000;
static int pmdown_time;
module_param(pmdown_time, int, 0);
MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
@ -258,7 +269,7 @@ static ssize_t codec_reg_write_file(struct file *file,
}
static const struct file_operations codec_reg_fops = {
.open = simple_open,
.open = codec_reg_open_file,
.read = codec_reg_read_file,
.write = codec_reg_write_file,
.llseek = default_llseek,
@ -465,6 +476,35 @@ static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card)
}
#endif
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_SND_SOC_AC97_BUS
/* unregister ac97 codec */
static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
@ -530,8 +570,12 @@ int snd_soc_suspend(struct device *dev)
if (card->rtd[i].dai_link->ignore_suspend)
continue;
if (drv->ops->digital_mute && dai->playback_active)
drv->ops->digital_mute(dai, 1);
if (card->rtd[i].dai_link->dynamic)
soc_dpcm_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 */
@ -552,11 +596,16 @@ int snd_soc_suspend(struct device *dev)
if (card->rtd[i].dai_link->ignore_suspend)
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_dpcm_be_cpu_dai_suspend(&card->rtd[i]);
soc_dpcm_be_platform_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;
}
}
}
@ -567,20 +616,18 @@ int snd_soc_suspend(struct device *dev)
}
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
struct snd_soc_dai_driver *driver = card->rtd[i].codec_dai->driver;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
snd_soc_dapm_stream_event(&card->rtd[i],
SNDRV_PCM_STREAM_PLAYBACK,
codec_dai,
SND_SOC_DAPM_STREAM_SUSPEND);
if (driver->playback.stream_name != NULL)
snd_soc_dapm_stream_event(&card->rtd[i], driver->playback.stream_name,
SND_SOC_DAPM_STREAM_SUSPEND);
snd_soc_dapm_stream_event(&card->rtd[i],
SNDRV_PCM_STREAM_CAPTURE,
codec_dai,
SND_SOC_DAPM_STREAM_SUSPEND);
if (driver->capture.stream_name != NULL)
snd_soc_dapm_stream_event(&card->rtd[i], driver->capture.stream_name,
SND_SOC_DAPM_STREAM_SUSPEND);
}
/* suspend all CODECs */
@ -590,17 +637,6 @@ int snd_soc_suspend(struct device *dev)
if (!codec->suspended && codec->driver->suspend) {
switch (codec->dapm.bias_level) {
case SND_SOC_BIAS_STANDBY:
/*
* If the CODEC is capable of idle
* bias off then being in STANDBY
* means it's doing something,
* otherwise fall through.
*/
if (codec->dapm.idle_bias_off) {
dev_dbg(codec->dev,
"idle_bias_off CODEC on over suspend\n");
break;
}
case SND_SOC_BIAS_OFF:
codec->driver->suspend(codec);
codec->suspended = 1;
@ -619,8 +655,11 @@ int snd_soc_suspend(struct device *dev)
if (card->rtd[i].dai_link->ignore_suspend)
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_dpcm_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)
@ -659,8 +698,11 @@ static void soc_resume_deferred(struct work_struct *work)
if (card->rtd[i].dai_link->ignore_suspend)
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_dpcm_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) {
@ -683,18 +725,18 @@ static void soc_resume_deferred(struct work_struct *work)
}
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
struct snd_soc_dai_driver *driver = card->rtd[i].codec_dai->driver;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
snd_soc_dapm_stream_event(&card->rtd[i],
SNDRV_PCM_STREAM_PLAYBACK, codec_dai,
SND_SOC_DAPM_STREAM_RESUME);
if (driver->playback.stream_name != NULL)
snd_soc_dapm_stream_event(&card->rtd[i], driver->playback.stream_name,
SND_SOC_DAPM_STREAM_RESUME);
snd_soc_dapm_stream_event(&card->rtd[i],
SNDRV_PCM_STREAM_CAPTURE, codec_dai,
SND_SOC_DAPM_STREAM_RESUME);
if (driver->capture.stream_name != NULL)
snd_soc_dapm_stream_event(&card->rtd[i], driver->capture.stream_name,
SND_SOC_DAPM_STREAM_RESUME);
}
/* unmute any active DACs */
@ -705,8 +747,12 @@ static void soc_resume_deferred(struct work_struct *work)
if (card->rtd[i].dai_link->ignore_suspend)
continue;
if (drv->ops->digital_mute && dai->playback_active)
drv->ops->digital_mute(dai, 0);
if (card->rtd[i].dai_link->dynamic)
soc_dpcm_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++) {
@ -716,11 +762,16 @@ static void soc_resume_deferred(struct work_struct *work)
if (card->rtd[i].dai_link->ignore_suspend)
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_dpcm_be_cpu_dai_resume(&card->rtd[i]);
soc_dpcm_be_platform_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;
}
}
}
@ -1007,7 +1058,6 @@ static int soc_probe_codec(struct snd_soc_card *card,
{
int ret = 0;
const struct snd_soc_codec_driver *driver = codec->driver;
struct snd_soc_dai *dai;
codec->card = card;
codec->dapm.card = card;
@ -1022,14 +1072,6 @@ static int soc_probe_codec(struct snd_soc_card *card,
snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
driver->num_dapm_widgets);
/* Create DAPM widgets for each DAI stream */
list_for_each_entry(dai, &dai_list, list) {
if (dai->dev != codec->dev)
continue;
snd_soc_dapm_new_dai_widgets(&codec->dapm, dai);
}
codec->dapm.idle_bias_off = driver->idle_bias_off;
if (driver->probe) {
@ -1081,8 +1123,6 @@ static int soc_probe_platform(struct snd_soc_card *card,
snd_soc_dapm_new_controls(&platform->dapm,
driver->dapm_widgets, driver->num_dapm_widgets);
platform->dapm.idle_bias_off = 1;
if (driver->probe) {
ret = driver->probe(platform);
if (ret < 0) {
@ -1170,6 +1210,10 @@ static int soc_post_component_init(struct snd_soc_card *card,
rtd->dev->init_name = name;
dev_set_drvdata(rtd->dev, rtd);
mutex_init(&rtd->pcm_mutex);
INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients);
INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients);
INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients);
INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients);
ret = device_add(rtd->dev);
if (ret < 0) {
dev_err(card->dev,
@ -1510,8 +1554,6 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
}
}
snd_soc_dapm_link_dai_widgets(card);
if (card->controls)
snd_soc_add_card_controls(card, card->controls, card->num_controls);
@ -1527,14 +1569,14 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
if (dai_link->dai_fmt) {
ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai,
dai_link->dai_fmt);
if (ret != 0 && ret != -ENOTSUPP)
if (ret != 0)
dev_warn(card->rtd[i].codec_dai->dev,
"Failed to set DAI format: %d\n",
ret);
ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
dai_link->dai_fmt);
if (ret != 0 && ret != -ENOTSUPP)
if (ret != 0)
dev_warn(card->rtd[i].cpu_dai->dev,
"Failed to set DAI format: %d\n",
ret);
@ -1641,10 +1683,6 @@ static int soc_probe(struct platform_device *pdev)
if (!card)
return -EINVAL;
dev_warn(&pdev->dev,
"ASoC machine %s should use snd_soc_register_card()\n",
card->name);
/* Bodge while we unpick instantiation */
card->dev = &pdev->dev;
@ -1682,6 +1720,7 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
snd_soc_dapm_free(&card->dapm);
kfree(card->rtd);
snd_card_free(card->snd_card);
return 0;
@ -1720,10 +1759,7 @@ EXPORT_SYMBOL_GPL(snd_soc_poweroff);
const struct dev_pm_ops snd_soc_pm_ops = {
.suspend = snd_soc_suspend,
.resume = snd_soc_resume,
.freeze = snd_soc_suspend,
.thaw = snd_soc_resume,
.poweroff = snd_soc_poweroff,
.restore = snd_soc_resume,
};
EXPORT_SYMBOL_GPL(snd_soc_pm_ops);
@ -2015,6 +2051,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;
@ -2966,11 +3004,10 @@ EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll);
*/
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
if (dai->driver == NULL)
if (dai->driver && dai->driver->ops->set_fmt)
return dai->driver->ops->set_fmt(dai, fmt);
else
return -EINVAL;
if (dai->driver->ops->set_fmt == NULL)
return -ENOTSUPP;
return dai->driver->ops->set_fmt(dai, fmt);
}
EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
@ -3061,6 +3098,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;
@ -3107,10 +3145,9 @@ int snd_soc_register_card(struct snd_soc_card *card)
soc_init_card_debugfs(card);
card->rtd = devm_kzalloc(card->dev,
sizeof(struct snd_soc_pcm_runtime) *
(card->num_links + card->num_aux_devs),
GFP_KERNEL);
card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) *
(card->num_links + card->num_aux_devs),
GFP_KERNEL);
if (card->rtd == NULL)
return -ENOMEM;
card->num_rtd = 0;
@ -3123,6 +3160,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
INIT_LIST_HEAD(&card->dapm_dirty);
card->instantiated = 0;
mutex_init(&card->mutex);
mutex_init(&card->dpcm_mutex);
mutex_lock(&client_mutex);
list_add(&card->list, &card_list);
@ -3131,7 +3169,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
dev_dbg(card->dev, "Registered card '%s'\n", card->name);
return 0;
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_card);
@ -3310,6 +3348,7 @@ int snd_soc_register_dais(struct device *dev,
dai->dev = dev;
dai->driver = &dai_drv[i];
if (dai->driver->id)
dai->id = dai->driver->id;
else
@ -3380,7 +3419,6 @@ int snd_soc_register_platform(struct device *dev,
platform->dapm.dev = dev;
platform->dapm.platform = platform;
platform->dapm.stream_event = platform_drv->stream_event;
mutex_init(&platform->mutex);
mutex_lock(&client_mutex);
list_add(&platform->list, &platform_list);
@ -3489,7 +3527,6 @@ int snd_soc_register_codec(struct device *dev,
codec->volatile_register = codec_drv->volatile_register;
codec->readable_register = codec_drv->readable_register;
codec->writable_register = codec_drv->writable_register;
codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time;
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
codec->dapm.dev = dev;
codec->dapm.codec = codec;

View File

@ -2967,6 +2967,30 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
dapm_power_widgets(dapm, event);
}
void snd_soc_dapm_rtd_stream_event(struct snd_soc_pcm_runtime *rtd,
int stream, int event)
{
struct snd_soc_dapm_context *pdapm = &rtd->platform->dapm;
struct snd_soc_dapm_context *cdapm = &rtd->codec->dapm;
dev_dbg(rtd->dev, "rtd stream %d event %d\n", stream, event);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
widget_stream_event(pdapm, rtd->cpu_dai->playback_aif, event);
widget_stream_event(cdapm, rtd->codec_dai->playback_aif, event);
} else {
widget_stream_event(pdapm, rtd->cpu_dai->capture_aif, event);
widget_stream_event(cdapm, rtd->codec_dai->capture_aif, event);
}
/* do we need to notify any clients that DAPM stream is complete */
if (pdapm->stream_event)
pdapm->stream_event(pdapm, event);
if (cdapm->stream_event)
cdapm->stream_event(cdapm, event);
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_rtd_stream_event);
/**
* snd_soc_dapm_stream_event - send a stream event to the dapm core
* @rtd: PCM runtime data
@ -2978,14 +3002,18 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
*
* Returns 0 for success else error.
*/
int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
struct snd_soc_dai *dai, int event)
int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd,
const char *stream, int event)
{
struct snd_soc_codec *codec = rtd->codec;
mutex_lock(&codec->mutex);
soc_dapm_stream_event(&codec->dapm, stream, dai, event);
mutex_unlock(&codec->mutex);
if (stream == NULL)
return 0;
//mutex_lock(&codec->mutex);
soc_dapm_stream_event(&codec->dapm, stream, event);
//mutex_unlock(&codec->mutex);
return 0;
}

File diff suppressed because it is too large Load Diff