aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorDan Murphy <dmurphy@ti.com>2011-08-31 07:32:59 -0500
committerDan Murphy <dmurphy@ti.com>2011-08-31 07:32:59 -0500
commitd21743dc00ad185044ae01c6217a8fac9714c525 (patch)
tree7004932c624ac2b668496a7c41b1a49de66365e3 /sound
parentddbaa04f52ec447cbe86fb132b1da0cd8980c019 (diff)
parent87050c34e61d0ec1b0e72713ceb3ec15cffb9928 (diff)
downloadkernel_samsung_espresso10-d21743dc00ad185044ae01c6217a8fac9714c525.zip
kernel_samsung_espresso10-d21743dc00ad185044ae01c6217a8fac9714c525.tar.gz
kernel_samsung_espresso10-d21743dc00ad185044ae01c6217a8fac9714c525.tar.bz2
Merge branch 'android-omap-3.0' into p-android-omap-3.0
Change-Id: I1160c3de134db29f5af54d55e2704bd4c52b0c6e
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/twl6040.c158
-rw-r--r--sound/soc/omap/Kconfig4
-rw-r--r--sound/soc/omap/Makefile2
-rw-r--r--sound/soc/omap/omap-abe-dsp.c107
-rw-r--r--sound/soc/omap/omap-abe-dsp.h2
-rw-r--r--sound/soc/omap/omap-abe.c50
-rw-r--r--sound/soc/omap/omap-mcasp.c678
-rw-r--r--sound/soc/omap/omap-mcasp.h36
-rw-r--r--sound/soc/omap/omap-mcpdm.c65
-rw-r--r--sound/soc/omap/sdp4430.c28
-rw-r--r--sound/soc/soc-core.c18
-rw-r--r--sound/soc/soc-dapm.c37
-rw-r--r--sound/soc/soc-dsp.c389
13 files changed, 1239 insertions, 335 deletions
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 2dcc8fe..f217421 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -164,59 +164,59 @@ static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = {
0x00, /* TWL6040_STATUS (ro) 0x2E */
};
-/*
- * twl6040 vio/gnd registers:
- * registers under vio/gnd supply can be accessed
- * before the power-up sequence, after NRESPWRON goes high
- */
-static const int twl6040_vio_reg[TWL6040_VIOREGNUM] = {
- TWL6040_REG_ASICID,
- TWL6040_REG_ASICREV,
- TWL6040_REG_INTID,
- TWL6040_REG_INTMR,
- TWL6040_REG_NCPCTL,
- TWL6040_REG_LDOCTL,
- TWL6040_REG_AMICBCTL,
- TWL6040_REG_DMICBCTL,
- TWL6040_REG_HKCTL1,
- TWL6040_REG_HKCTL2,
- TWL6040_REG_GPOCTL,
- TWL6040_REG_TRIM1,
- TWL6040_REG_TRIM2,
- TWL6040_REG_TRIM3,
- TWL6040_REG_HSOTRIM,
- TWL6040_REG_HFOTRIM,
- TWL6040_REG_ACCCTL,
- TWL6040_REG_STATUS,
-};
-/*
- * twl6040 vdd/vss registers:
- * registers under vdd/vss supplies can only be accessed
- * after the power-up sequence
- */
-static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = {
- TWL6040_REG_HPPLLCTL,
- TWL6040_REG_LPPLLCTL,
- TWL6040_REG_LPPLLDIV,
- TWL6040_REG_MICLCTL,
- TWL6040_REG_MICRCTL,
- TWL6040_REG_MICGAIN,
- TWL6040_REG_LINEGAIN,
- TWL6040_REG_HSLCTL,
- TWL6040_REG_HSRCTL,
- TWL6040_REG_HSGAIN,
- TWL6040_REG_EARCTL,
- TWL6040_REG_HFLCTL,
- TWL6040_REG_HFLGAIN,
- TWL6040_REG_HFRCTL,
- TWL6040_REG_HFRGAIN,
- TWL6040_REG_VIBCTLL,
- TWL6040_REG_VIBDATL,
- TWL6040_REG_VIBCTLR,
- TWL6040_REG_VIBDATR,
- TWL6040_REG_ALB,
- TWL6040_REG_DLB,
+/* twl6040 vio/gnd registers: registers under vio/gnd supply can be accessed
+ * twl6040 vdd/vss registers: registers under vdd/vss supplies can only be
+ * accessed after the power-up sequence */
+
+static const u8 twl6040_reg_supply[TWL6040_CACHEREGNUM] = {
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_VIO_SUPPLY, /* TWL6040_ASICID (ro) */
+ TWL6040_VIO_SUPPLY, /* TWL6040_ASICREV (ro) */
+ TWL6040_VIO_SUPPLY, /* TWL6040_INTID */
+ TWL6040_VIO_SUPPLY, /* TWL6040_INTMR */
+ TWL6040_VIO_SUPPLY, /* TWL6040_NCPCTRL */
+ TWL6040_VIO_SUPPLY, /* TWL6040_LDOCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HPPLLCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_LPPLLCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_LPPLLDIV */
+ TWL6040_VIO_SUPPLY, /* TWL6040_AMICBCTL */
+ TWL6040_VIO_SUPPLY, /* TWL6040_DMICBCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_MICLCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_MICRCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_MICGAIN */
+ TWL6040_VDD_SUPPLY, /* TWL6040_LINEGAIN */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HSLCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HSRCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HSGAIN */
+ TWL6040_VDD_SUPPLY, /* TWL6040_EARCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HFLCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HFLGAIN */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HFRCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HFRGAIN */
+ TWL6040_VDD_SUPPLY, /* TWL6040_VIBCTLL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_VIBDATL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_VIBCTLR */
+ TWL6040_VDD_SUPPLY, /* TWL6040_VIBDATR */
+ TWL6040_VIO_SUPPLY, /* TWL6040_HKCTL1 */
+ TWL6040_VIO_SUPPLY, /* TWL6040_HKCTL2 */
+ TWL6040_VIO_SUPPLY, /* TWL6040_GPOCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_ALB */
+ TWL6040_VDD_SUPPLY, /* TWL6040_DLB */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_VIO_SUPPLY, /* TWL6040_TRIM1 */
+ TWL6040_VIO_SUPPLY, /* TWL6040_TRIM2 */
+ TWL6040_VIO_SUPPLY, /* TWL6040_TRIM3 */
+ TWL6040_VIO_SUPPLY, /* TWL6040_HSOTRIM */
+ TWL6040_VIO_SUPPLY, /* TWL6040_HFOTRIM */
+ TWL6040_VIO_SUPPLY, /* TWL6040_ACCCTL */
+ TWL6040_VIO_SUPPLY, /* TWL6040_STATUS (ro) */
};
/*
@@ -250,14 +250,20 @@ static inline void twl6040_write_reg_cache(struct snd_soc_codec *codec,
* read from twl6040 hardware register
*/
static int twl6040_read_reg_volatile(struct snd_soc_codec *codec,
- unsigned int reg)
+ unsigned int reg)
{
struct twl6040 *twl6040 = codec->control_data;
- u8 value;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ u8 value = 0;
if (reg >= TWL6040_CACHEREGNUM)
return -EIO;
+ /* read access not supported while in sleep state */
+ if ((twl6040_reg_supply[reg] == TWL6040_VDD_SUPPLY) &&
+ !priv->codec_powered)
+ return -EINVAL;
+
value = twl6040_reg_read(twl6040, reg);
twl6040_write_reg_cache(codec, reg, value);
@@ -271,21 +277,32 @@ static int twl6040_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value)
{
struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
if (reg >= TWL6040_CACHEREGNUM)
return -EIO;
twl6040_write_reg_cache(codec, reg, value);
- return twl6040_reg_write(twl6040, reg, value);
+
+ if ((twl6040_reg_supply[reg] == TWL6040_VIO_SUPPLY) ||
+ priv->codec_powered)
+ ret = twl6040_reg_write(twl6040, reg, value);
+ else
+ dev_dbg(codec->dev, "deferring register 0x%02x write: %02x\n",
+ reg, value);
+
+ return ret;
}
static void twl6040_init_vio_regs(struct snd_soc_codec *codec)
{
u8 *cache = codec->reg_cache;
- int reg, i;
+ int reg;
- for (i = 0; i < TWL6040_VIOREGNUM; i++) {
- reg = twl6040_vio_reg[i];
+ for (reg = 0; reg < TWL6040_CACHEREGNUM; reg++) {
+ if (twl6040_reg_supply[reg] != TWL6040_VIO_SUPPLY)
+ continue;
/*
* skip read-only registers (ASICID, ASICREV, STATUS)
* and registers shared among MFD children
@@ -311,10 +328,11 @@ static void twl6040_init_vio_regs(struct snd_soc_codec *codec)
static void twl6040_init_vdd_regs(struct snd_soc_codec *codec)
{
u8 *cache = codec->reg_cache;
- int reg, i;
+ int reg;
- for (i = 0; i < TWL6040_VDDREGNUM; i++) {
- reg = twl6040_vdd_reg[i];
+ for (reg = 0; reg < TWL6040_CACHEREGNUM; reg++) {
+ if (twl6040_reg_supply[reg] != TWL6040_VDD_SUPPLY)
+ continue;
/* skip vibra and pll registers */
switch (reg) {
case TWL6040_REG_VIBCTLL:
@@ -1510,6 +1528,8 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
int ret;
+ priv->sysclk = twl6040_get_sysclk(twl6040);
+
switch (clk_id) {
case TWL6040_SYSCLK_SEL_LPPLL:
ret = twl6040_set_pll(twl6040, TWL6040_LPPLL_ID,
@@ -1539,11 +1559,26 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
+static int twl6040_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ /*
+ * pop-noise reduction sequence requires to shutdown
+ * analog side before CPU DAI
+ */
+ if (mute)
+ snd_soc_dapm_codec_stream_event(dai->codec,
+ dai->driver->playback.stream_name,
+ SND_SOC_DAPM_STREAM_STOP);
+
+ return 0;
+}
+
static struct snd_soc_dai_ops twl6040_dai_ops = {
.startup = twl6040_startup,
.hw_params = twl6040_hw_params,
.prepare = twl6040_prepare,
.set_sysclk = twl6040_set_dai_sysclk,
+ .digital_mute = twl6040_digital_mute,
};
static struct snd_soc_dai_driver twl6040_dai[] = {
@@ -1627,6 +1662,7 @@ static int twl6040_probe(struct snd_soc_codec *codec)
priv->codec = codec;
codec->control_data = dev_get_drvdata(codec->dev->parent);
+ codec->dapm.idle_bias_off = 1;
if (pdata && pdata->hs_left_step && pdata->hs_right_step) {
priv->hs_left_step = pdata->hs_left_step;
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 91425b3..2adf1f5 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -6,6 +6,10 @@ config SND_OMAP_SOC_ABE_DSP
tristate
select SND_DYNAMIC_MINORS
+config SND_OMAP_SOC_MCASP
+ tristate
+ select SND_SOC_SPDIF
+
config SND_OMAP_SOC_MCBSP
tristate
select OMAP_MCBSP
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 7cc549d..2ada65c 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -1,5 +1,6 @@
# OMAP Platform Support
snd-soc-omap-objs := omap-pcm.o
+snd-soc-omap-mcasp-objs := omap-mcasp.o
snd-soc-omap-mcbsp-objs := omap-mcbsp.o
snd-soc-omap-mcpdm-objs := omap-mcpdm.o
snd-soc-omap-dmic-objs := omap-dmic.o
@@ -7,6 +8,7 @@ snd-soc-omap-abe-objs := omap-abe.o
snd-soc-omap-abe-dsp-objs := omap-abe-dsp.o
obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
+obj-$(CONFIG_SND_OMAP_SOC_MCASP) += snd-soc-omap-mcasp.o
obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o
obj-$(CONFIG_SND_OMAP_SOC_DMIC) += snd-soc-omap-dmic.o
diff --git a/sound/soc/omap/omap-abe-dsp.c b/sound/soc/omap/omap-abe-dsp.c
index 5f4eecd..940c42c 100644
--- a/sound/soc/omap/omap-abe-dsp.c
+++ b/sound/soc/omap/omap-abe-dsp.c
@@ -98,6 +98,12 @@ struct fw_header {
u32 num_equ; /* number of equalizers */
};
+struct abe_opp_req {
+ struct device *dev;
+ struct list_head node;
+ int opp;
+};
+
/*
* ABE private data.
*/
@@ -108,6 +114,7 @@ struct abe_data {
struct delayed_work delayed_work;
struct mutex mutex;
struct mutex opp_mutex;
+ struct mutex opp_req_mutex;
struct clk *clk;
void __iomem *io_base[5];
int irq;
@@ -129,6 +136,9 @@ struct abe_data {
/* DAPM mixer config - TODO: some of this can be replaced with HAL update */
u32 widget_opp[ABE_NUM_DAPM_REG + 1];
+ struct list_head opp_req;
+ int opp_req_count;
+
u16 router[16];
int loss_count;
@@ -172,6 +182,8 @@ struct abe_data {
static struct abe_data *the_abe;
+static int aess_set_runtime_opp_level(struct abe_data *abe);
+
// TODO: map to the new version of HAL
static unsigned int abe_dsp_read(struct snd_soc_platform *platform,
unsigned int reg)
@@ -1806,6 +1818,91 @@ static const struct snd_pcm_hardware omap_abe_hardware = {
.buffer_bytes_max = 24 * 1024 * 2,
};
+static struct abe_opp_req *abe_opp_req_lookup(struct abe_data *abe,
+ struct device *dev)
+{
+ struct abe_opp_req *req, *tmp_req;
+
+ req = NULL;
+ list_for_each_entry(tmp_req, &abe->opp_req, node) {
+ if (tmp_req->dev == dev) {
+ req = tmp_req;
+ break;
+ }
+ }
+
+ return req;
+}
+
+static int abe_get_opp_req(struct abe_data *abe)
+{
+ struct abe_opp_req *req;
+ int opp = 0;
+
+ list_for_each_entry(req, &abe->opp_req, node)
+ opp |= req->opp;
+
+ opp = (1 << (fls(opp) - 1)) * 25;
+
+ return opp;
+}
+
+int abe_add_opp_req(struct device *dev, int opp)
+{
+ struct abe_opp_req *req;
+ int ret = 0;
+
+ mutex_lock(&the_abe->opp_req_mutex);
+
+ req = abe_opp_req_lookup(the_abe, dev);
+ if (!req) {
+ req = kzalloc(sizeof(struct abe_opp_req), GFP_KERNEL);
+ if (!req) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ req->dev = dev;
+ /* use the same convention as ABE DSP DAPM */
+ req->opp = 1 << opp;
+ list_add(&req->node, &the_abe->opp_req);
+ the_abe->opp_req_count++;
+ } else {
+ req->opp = opp;
+ }
+
+ aess_set_runtime_opp_level(the_abe);
+
+out:
+ mutex_unlock(&the_abe->opp_req_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(abe_add_opp_req);
+
+int abe_remove_opp_req(struct device *dev)
+{
+ struct abe_opp_req *req;
+ int ret = 0;
+
+ mutex_lock(&the_abe->opp_req_mutex);
+
+ req = abe_opp_req_lookup(the_abe, dev);
+ if (!req) {
+ dev_err(dev, "trying to remove an invalid opp req\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ list_del(&req->node);
+ the_abe->opp_req_count--;
+ kfree(req);
+
+ aess_set_runtime_opp_level(the_abe);
+
+out:
+ mutex_unlock(&the_abe->opp_req_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(abe_remove_opp_req);
static int abe_set_opp_mode(struct abe_data *abe, int opp)
{
@@ -1882,7 +1979,7 @@ err_scale:
static int aess_set_runtime_opp_level(struct abe_data *abe)
{
- int i, opp = 0;
+ int i, req_opp, opp = 0;
mutex_lock(&abe->opp_mutex);
@@ -1896,8 +1993,11 @@ static int aess_set_runtime_opp_level(struct abe_data *abe)
}
opp = (1 << (fls(opp) - 1)) * 25;
+ /* opps requested outside ABE DSP driver (e.g. McPDM) */
+ req_opp = abe_get_opp_req(abe);
+
pm_runtime_get_sync(abe->dev);
- abe_set_opp_mode(abe, opp);
+ abe_set_opp_mode(abe, max(opp, req_opp));
pm_runtime_put_sync(abe->dev);
mutex_unlock(&abe->opp_mutex);
@@ -2625,6 +2725,9 @@ static int __devinit abe_engine_probe(struct platform_device *pdev)
abe->dev = &pdev->dev;
mutex_init(&abe->mutex);
mutex_init(&abe->opp_mutex);
+ mutex_init(&abe->opp_req_mutex);
+ INIT_LIST_HEAD(&abe->opp_req);
+ abe->opp_req_count = 0;
ret = snd_soc_register_platform(abe->dev,
&omap_aess_platform);
diff --git a/sound/soc/omap/omap-abe-dsp.h b/sound/soc/omap/omap-abe-dsp.h
index 2e8ac53..5dae62a 100644
--- a/sound/soc/omap/omap-abe-dsp.h
+++ b/sound/soc/omap/omap-abe-dsp.h
@@ -168,5 +168,7 @@
void abe_dsp_shutdown(void);
void abe_dsp_pm_get(void);
void abe_dsp_pm_put(void);
+int abe_add_opp_req(struct device *dev, int opp);
+int abe_remove_opp_req(struct device *dev);
#endif /* End of __OMAP_ABE_DSP_H__ */
diff --git a/sound/soc/omap/omap-abe.c b/sound/soc/omap/omap-abe.c
index 740e553..c418e66 100644
--- a/sound/soc/omap/omap-abe.c
+++ b/sound/soc/omap/omap-abe.c
@@ -503,6 +503,7 @@ static void mute_fe_port(struct snd_pcm_substream *substream,
abe_mute_gain(MIXDL1, MIX_DL1_INPUT_MM_DL);
break;
case ABE_FRONTEND_DAI_VOICE:
+ case ABE_FRONTEND_DAI_MODEM:
if (omap_abe_port_is_enabled(abe_priv->abe,
abe_priv->port[OMAP_ABE_BE_PORT_PDM_DL2]))
abe_mute_gain(MIXDL2, MIX_DL2_INPUT_VX_DL);
@@ -543,6 +544,7 @@ static void unmute_fe_port(struct snd_pcm_substream *substream,
abe_unmute_gain(MIXDL1, MIX_DL1_INPUT_MM_DL);
break;
case ABE_FRONTEND_DAI_VOICE:
+ case ABE_FRONTEND_DAI_MODEM:
if (omap_abe_port_is_enabled(abe_priv->abe,
abe_priv->port[OMAP_ABE_BE_PORT_PDM_DL2]))
abe_unmute_gain(MIXDL2, MIX_DL2_INPUT_VX_DL);
@@ -585,11 +587,11 @@ static void capture_trigger(struct snd_pcm_substream *substream,
if (!snd_soc_dsp_is_op_for_be(fe, be, stream))
continue;
- /* is the BE already in the trigger START state ? */
- if (dsp_params->state == SND_SOC_DSP_LINK_STATE_START)
+ if ((be->dsp[stream].state != SND_SOC_DSP_STATE_PREPARE) &&
+ (be->dsp[stream].state != SND_SOC_DSP_STATE_STOP))
continue;
- be_substream = snd_soc_dsp_get_substream(dsp_params->be, stream);
+ be_substream = snd_soc_dsp_get_substream(be, stream);
/* mute the BE port */
mute_be(be, dai, stream);
@@ -602,6 +604,8 @@ static void capture_trigger(struct snd_pcm_substream *substream,
/* trigger the BE port */
snd_soc_dai_trigger(be_substream, cmd, be->cpu_dai);
+
+ be->dsp[stream].state = SND_SOC_DSP_STATE_START;
}
/* does this trigger() apply to the FE ? */
@@ -650,12 +654,15 @@ static void capture_trigger(struct snd_pcm_substream *substream,
if (!snd_soc_dsp_is_op_for_be(fe, be, stream))
continue;
- /* only STOP BE in FREE state */
- /* REVISIT: Investigate the appropriate state to check against */
- //if (dsp_params->state != SND_SOC_DSP_LINK_STATE_FREE)
- // continue;
+ if (be->dsp[stream].state != SND_SOC_DSP_STATE_START)
+ continue;
- be_substream = snd_soc_dsp_get_substream(dsp_params->be, stream);
+ /* only stop if last running user */
+ if (soc_dsp_fe_state_count(be, stream,
+ SND_SOC_DSP_STATE_START) > 1)
+ continue;
+
+ be_substream = snd_soc_dsp_get_substream(be, stream);
/* disable the BE port */
disable_be_port(be, dai, stream);
@@ -665,6 +672,8 @@ static void capture_trigger(struct snd_pcm_substream *substream,
/* trigger BE port */
snd_soc_dai_trigger(be_substream, cmd, be->cpu_dai);
+
+ be->dsp[stream].state = SND_SOC_DSP_STATE_STOP;
}
break;
default:
@@ -693,11 +702,11 @@ static void playback_trigger(struct snd_pcm_substream *substream,
if (!snd_soc_dsp_is_op_for_be(fe, be, stream))
continue;
- /* is the BE already in the trigger START state ? */
- if (dsp_params->state == SND_SOC_DSP_LINK_STATE_START)
+ if ((be->dsp[stream].state != SND_SOC_DSP_STATE_PREPARE) &&
+ (be->dsp[stream].state != SND_SOC_DSP_STATE_STOP))
continue;
- be_substream = snd_soc_dsp_get_substream(dsp_params->be, stream);
+ be_substream = snd_soc_dsp_get_substream(be, stream);
/* mute BE port */
mute_be(be, dai, stream);
@@ -713,6 +722,8 @@ static void playback_trigger(struct snd_pcm_substream *substream,
/* unmute the BE port */
unmute_be(be, dai, stream);
+
+ be->dsp[stream].state = SND_SOC_DSP_STATE_START;
}
/* does this trigger() apply to the FE ? */
@@ -763,11 +774,15 @@ static void playback_trigger(struct snd_pcm_substream *substream,
if (!snd_soc_dsp_is_op_for_be(fe, be, stream))
continue;
- /* only STOP BE in FREE state */
- if (dsp_params->state != SND_SOC_DSP_LINK_STATE_FREE)
+ if (be->dsp[stream].state != SND_SOC_DSP_STATE_START)
+ continue;
+
+ /* only stop if last running user */
+ if (soc_dsp_fe_state_count(be, stream,
+ SND_SOC_DSP_STATE_START) > 1)
continue;
- be_substream = snd_soc_dsp_get_substream(dsp_params->be, stream);
+ be_substream = snd_soc_dsp_get_substream(be, stream);
/* disable the BE */
disable_be_port(be, dai, stream);
@@ -777,6 +792,8 @@ static void playback_trigger(struct snd_pcm_substream *substream,
/* trigger the BE port */
snd_soc_dai_trigger(be_substream, cmd, be->cpu_dai);
+
+ be->dsp[stream].state = SND_SOC_DSP_STATE_STOP;
}
break;
default:
@@ -794,6 +811,8 @@ static int omap_abe_dai_startup(struct snd_pcm_substream *substream,
abe_priv->active_dais++;
+ abe_dsp_pm_get();
+
if (dai->id == ABE_FRONTEND_DAI_MODEM) {
ret = modem_get_dai(substream, dai);
@@ -1078,6 +1097,9 @@ static void omap_abe_dai_shutdown(struct snd_pcm_substream *substream,
abe_priv->modem_dai);
}
+ abe_dsp_shutdown();
+ abe_dsp_pm_put();
+
abe_priv->active_dais--;
}
diff --git a/sound/soc/omap/omap-mcasp.c b/sound/soc/omap/omap-mcasp.c
new file mode 100644
index 0000000..2f680c2
--- /dev/null
+++ b/sound/soc/omap/omap-mcasp.c
@@ -0,0 +1,678 @@
+/*
+ * ALSA SoC McASP Audio Layer for TI OMAP processor
+ *
+ * Multi-channel Audio Serial Port Driver
+ *
+ * Author: Jon Hunter <jon-hunter@ti.com>,
+ * Dan Milea <dan.milea@ti.com>,
+ *
+ * Based upon McASP driver written for TI DaVinci
+ *
+ * Copyright: (C) 2011 Texas Instruments
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <plat/omap_hwmod.h>
+#include <plat/clock.h>
+#include <plat/dma.h>
+#include <plat/dma-44xx.h>
+
+#include "omap-pcm.h"
+#include "omap-mcasp.h"
+
+/*
+ * McASP register definitions
+ */
+#define OMAP_MCASP_PID_REG 0x00
+#define OMAP_MCASP_SYSCONFIG_REG 0x04
+
+#define OMAP_MCASP_PFUNC_REG 0x10
+#define OMAP_MCASP_PDIR_REG 0x14
+#define OMAP_MCASP_PDOUT_REG 0x18
+#define OMAP_MCASP_PDIN_REG 0x1c
+#define OMAP_MCASP_PDSET_REG 0x1c
+#define OMAP_MCASP_PDCLR_REG 0x20
+
+#define OMAP_MCASP_GBLCTL_REG 0x44
+#define OMAP_MCASP_AMUTE_REG 0x48
+
+#define OMAP_MCASP_TXDITCTL_REG 0x50
+
+#define OMAP_MCASP_TXMASK_REG 0xa4
+#define OMAP_MCASP_TXFMT_REG 0xa8
+#define OMAP_MCASP_TXFMCTL_REG 0xac
+
+#define OMAP_MCASP_ACLKXCTL_REG 0xb0
+#define OMAP_MCASP_AHCLKXCTL_REG 0xb4
+#define OMAP_MCASP_TXTDM_REG 0xb8
+#define OMAP_MCASP_EVTCTLX_REG 0xbc
+
+#define OMAP_MCASP_TXSTAT_REG 0xc0
+#define OMAP_MCASP_TXSTAT_MASK 0x1ff
+
+#define OMAP_MCASP_TXTDMSLOT_REG 0xc4
+#define OMAP_MCASP_TXCLKCHK_REG 0xc8
+#define OMAP_MCASP_TXEVTCTL_REG 0xcc
+
+/* Left(even TDM Slot) Channel Status Register File */
+#define OMAP_MCASP_DITCSRA_REG 0x100
+/* Right(odd TDM slot) Channel Status Register File */
+#define OMAP_MCASP_DITCSRB_REG 0x118
+/* Left(even TDM slot) User Data Register File */
+#define OMAP_MCASP_DITUDRA_REG 0x130
+/* Right(odd TDM Slot) User Data Register File */
+#define OMAP_MCASP_DITUDRB_REG 0x148
+
+/* Serializer n Control Register */
+#define OMAP_MCASP_XRSRCTL0_REG 0x180
+
+/* Transmit Buffer for Serializer */
+#define OMAP_MCASP_TXBUF0_REG 0x200
+
+/*
+ * OMAP_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits
+ */
+#define AXR0 BIT(0)
+#define PFUNC_AMUTE BIT(25)
+#define ACLKX BIT(26)
+#define AHCLKX BIT(27)
+#define AFSX BIT(28)
+
+/*
+ * OMAP_MCASP_PDIR_REG - Pin Direction Register Bits
+ */
+#define AXR0 BIT(0)
+#define PDIR_AMUTE BIT(25)
+#define ACLKX BIT(26)
+#define AHCLKX BIT(27)
+#define AFSX BIT(28)
+
+/*
+ * OMAP_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits
+ */
+#define DITEN BIT(0) /* Transmit DIT mode enable/disable */
+#define VA BIT(2)
+#define VB BIT(3)
+
+/*
+ * OMAP_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits
+ */
+#define TXROT(val) (val)
+#define TXROT_MASK TXROT(0x7)
+#define TXSEL BIT(3)
+#define TXSSZ(val) (val<<4)
+#define TXSSZ_MASK TXSSZ(0xf<<4)
+#define TXPAD(val) (val<<13)
+#define TXORD BIT(15)
+#define FSXDLY(val) (val<<16)
+
+#define ROTATE_24 0x6
+#define SLOTSIZE_32 0xf
+
+/*
+ * OMAP_MCASP_TXFMCTL_REG - Transmit Frame Control Register Bits
+ */
+#define FSXPOL BIT(0)
+#define AFSXE BIT(1)
+#define FSXDUR BIT(4)
+#define FSXMOD(val) (val<<7)
+
+/*
+ * OMAP_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits
+ */
+#define ACLKXDIV(val) (val)
+#define ACLKXE BIT(5)
+#define TX_ASYNC BIT(6)
+
+/*
+ * OMAP_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control
+ * Register Bits
+ */
+#define AHCLKXDIV(val) (val)
+#define AHCLKXE BIT(15)
+
+/*
+ * OMAP_MCASP_TXSTAT_REG - Transmit Status Register Bits
+ */
+#define TXSTAT_XUNDRN (0x1 << 0)
+#define TXSTAT_XSYNCERR (0x1 << 1)
+#define TXSTAT_XCKFAIL (0x1 << 2)
+#define TXSTAT_XDMSLOT (0x1 << 3)
+#define TXSTAT_XLAST (0x1 << 4)
+#define TXSTAT_XDATA (0x1 << 5)
+#define TXSTAT_XSTAFRM (0x1 << 6)
+#define TXSTAT_XDMAERR (0x1 << 7)
+#define TXSTAT_XERR (0x1 << 8)
+
+/*
+ * OMAP_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits
+ */
+#define MODE(val) (val)
+#define TXSTATE BIT(4)
+
+/*
+ * OMAP_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration
+ */
+#define TXTDMS(n) (1<<n)
+
+/*
+ * OMAP_MCASP_GBLCTL_REG - Global Control Register Bits
+ */
+#define TXCLKRST BIT(8) /* Transmitter Clock Divider Reset */
+#define TXHCLKRST BIT(9) /* Transmitter High Frequency Clock Divider*/
+#define TXSERCLR BIT(10) /* Transmit Serializer Clear */
+#define TXSMRST BIT(11) /* Transmitter State Machine Reset */
+#define TXFSRST BIT(12) /* Frame Sync Generator Reset */
+
+/*
+ * OMAP_MCASP_AMUTE_REG - Mute Control Register Bits
+ */
+#define MUTENA(val) (val)
+#define MUTEINPOL BIT(2)
+#define MUTEINENA BIT(3)
+#define MUTEIN BIT(4)
+#define MUTEX BIT(6)
+#define MUTEFSX BIT(8)
+#define MUTEBADCLKX BIT(10)
+#define MUTETXDMAERR BIT(12)
+
+/*
+ * OMAP_MCASP_TXEVTCTL_REG - Transmitter DMA Event Control Register bits
+ */
+#define TXDATADMADIS BIT(0)
+
+/*
+ * Stream DMA parameters
+ */
+static struct omap_pcm_dma_data omap_mcasp_dai_dma_params[] = {
+ {
+ .name = "Audio playback",
+ .dma_req = OMAP44XX_DMA_MCASP1_AXEVT,
+ .data_type = OMAP_DMA_DATA_TYPE_S16,
+ .sync_mode = OMAP_DMA_SYNC_ELEMENT,
+ .port_addr = OMAP44XX_MCASP_DAT_BASE + OMAP_MCASP_TXBUF0_REG,
+ },
+};
+
+static inline void mcasp_set_bits(void __iomem *reg, u32 val)
+{
+ __raw_writel(__raw_readl(reg) | val, reg);
+}
+
+static inline void mcasp_clr_bits(void __iomem *reg, u32 val)
+{
+ __raw_writel((__raw_readl(reg) & ~(val)), reg);
+}
+
+static inline void mcasp_mod_bits(void __iomem *reg, u32 val, u32 mask)
+{
+ __raw_writel((__raw_readl(reg) & ~mask) | val, reg);
+}
+
+static inline void mcasp_set_reg(void __iomem *reg, u32 val)
+{
+ __raw_writel(val, reg);
+}
+
+static inline u32 mcasp_get_reg(void __iomem *reg)
+{
+ return (unsigned int)__raw_readl(reg);
+}
+
+static inline void mcasp_set_ctl_reg(void __iomem *regs, u32 val)
+{
+ int i = 0;
+
+ mcasp_set_bits(regs, val);
+
+ /* programming GBLCTL needs to read back from GBLCTL and verfiy */
+ /* loop count is to avoid the lock-up */
+ for (i = 0; i < 1000; i++) {
+ if ((mcasp_get_reg(regs) & val) == val)
+ break;
+ }
+
+ if (i == 1000 && ((mcasp_get_reg(regs) & val) != val))
+ printk(KERN_ERR "GBLCTL write error\n");
+}
+
+static void mcasp_clk_on(struct omap_mcasp *mcasp)
+{
+ if (mcasp->clk_active)
+ return;
+ if (!omap_hwmod_enable_clocks(mcasp->oh))
+ mcasp->clk_active = 1;
+}
+
+static void mcasp_clk_off(struct omap_mcasp *mcasp)
+{
+ if (!mcasp->clk_active)
+ return;
+ omap_hwmod_disable_clocks(mcasp->oh);
+ mcasp->clk_active = 0;
+}
+
+static int mcasp_compute_clock_dividers(long fclk_rate, int tgt_sample_rate,
+ int *out_div_lo, int *out_div_hi)
+{
+ /* Given a particular functional clock rate and a target audio sample
+ * rate, determine the proper values for the ACLKXCTL and AHCLKXCTL, the
+ * dividers which produce the high frequency transmit master clock and
+ * the transmit clock.
+ */
+ long divisor;
+ int i;
+ BUG_ON(!out_div_lo);
+ BUG_ON(!out_div_hi);
+
+ /* Start by making sure the fclk is divisible by 128 (the number of
+ * clocks present in a single S/PDIF frame.
+ */
+ if (fclk_rate & 0x7F)
+ return -EINVAL;
+
+ fclk_rate >>= 7;
+
+ /* Next, make sure that our target Fs divides fClk/128 */
+ if (fclk_rate % tgt_sample_rate)
+ return -EINVAL;
+
+ divisor = fclk_rate / tgt_sample_rate;
+
+ /* At this point, divisor holds the product of the two divider values we
+ * need to use for ACLKXCTL and AHCLKXCTL. ACLKXCTL holds a 5 bit
+ * divider [1, 32], while AHCLKXCTL holds a 12 bit divider [1, 4096].
+ * We need to make sure that we can factor divisor into two integers
+ * which will fit into these divider registers. Find the largest 5-bit
+ * + 1 value which divides divisor and use that as our smaller divider.
+ * After removing this factor from divisor, if the result is <= 4096,
+ * then we have succeeded and will be able to produce the target sample
+ * rate.
+ */
+ for (i = 32; (i > 1) && (divisor % i); --i)
+ ; /* no body */
+
+ /* Make sure to subtract one, registers hold the value of the divider
+ * minus one (IOW, to divide by 5, the register gets programmed with the
+ * value 4. */
+ *out_div_lo = i - 1;
+ *out_div_hi = (divisor / i) - 1;
+
+ return (*out_div_hi <= 4096) ? 0 : -EINVAL;
+}
+
+static int mcasp_compute_playback_rates(long fclk_rate)
+{
+ static const int rate_table[][2] = {
+ { 5512, SNDRV_PCM_RATE_5512 },
+ { 8000, SNDRV_PCM_RATE_8000 },
+ { 11025, SNDRV_PCM_RATE_11025 },
+ { 16000, SNDRV_PCM_RATE_16000 },
+ { 22050, SNDRV_PCM_RATE_22050 },
+ { 32000, SNDRV_PCM_RATE_32000 },
+ { 44100, SNDRV_PCM_RATE_44100 },
+ { 48000, SNDRV_PCM_RATE_48000 },
+ { 64000, SNDRV_PCM_RATE_64000 },
+ { 88200, SNDRV_PCM_RATE_88200 },
+ { 96000, SNDRV_PCM_RATE_96000 },
+ { 176400, SNDRV_PCM_RATE_176400 },
+ { 192000, SNDRV_PCM_RATE_192000 },
+ };
+ int i, res;
+
+ if (!fclk_rate)
+ return 0;
+
+ res = 0;
+ for (i = 0; i < ARRAY_SIZE(rate_table); ++i) {
+ int lo, hi;
+
+ if (!mcasp_compute_clock_dividers(fclk_rate,
+ rate_table[i][0],
+ &lo,
+ &hi))
+ res |= rate_table[i][1];
+ }
+
+ return res;
+}
+
+static int mcasp_start_tx(struct omap_mcasp *mcasp)
+{
+ int i;
+ mcasp_set_ctl_reg(mcasp->base + OMAP_MCASP_GBLCTL_REG, TXHCLKRST);
+ mcasp_set_ctl_reg(mcasp->base + OMAP_MCASP_GBLCTL_REG, TXCLKRST);
+ mcasp_set_ctl_reg(mcasp->base + OMAP_MCASP_GBLCTL_REG, TXSERCLR);
+
+ /* Wait until the DMA has loaded the first sample into TXBUF before we
+ * let the TX state machine and frame sync generator out of reset. */
+ i = 0;
+ while (1) {
+ u32 reg = mcasp_get_reg(mcasp->base + OMAP_MCASP_TXSTAT_REG);
+ if (!(reg & TXSTAT_XDATA))
+ break;
+
+ if (++i > 1000) {
+ printk(KERN_ERR "Timeout waiting for DMA to load first"
+ " sample of audio.\n");
+ return -ETIMEDOUT;
+ }
+
+ udelay(1);
+ }
+
+ mcasp_set_ctl_reg(mcasp->base + OMAP_MCASP_GBLCTL_REG, TXSMRST);
+ mcasp_set_ctl_reg(mcasp->base + OMAP_MCASP_GBLCTL_REG, TXFSRST);
+ mcasp_clr_bits(mcasp->base + OMAP_MCASP_TXEVTCTL_REG, TXDATADMADIS);
+
+ return 0;
+}
+
+static int omap_mcasp_start(struct omap_mcasp *mcasp, int stream)
+{
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return mcasp_start_tx(mcasp);
+
+ return -EINVAL;
+}
+
+static void mcasp_stop_tx(struct omap_mcasp *mcasp)
+{
+ mcasp_set_reg(mcasp->base + OMAP_MCASP_GBLCTL_REG, 0);
+ mcasp_set_reg(mcasp->base + OMAP_MCASP_TXSTAT_REG,
+ OMAP_MCASP_TXSTAT_MASK);
+}
+
+static void omap_mcasp_stop(struct omap_mcasp *mcasp, int stream)
+{
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mcasp_stop_tx(mcasp);
+}
+
+static int omap_mcasp_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct platform_device *pdev;
+ struct omap_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+ mcasp_clk_on(mcasp);
+
+ pdev = to_platform_device(mcasp->dev);
+
+ if (!mcasp->active++)
+ pm_runtime_get_sync(&pdev->dev);
+
+ mcasp_set_reg(mcasp->base + OMAP_MCASP_SYSCONFIG_REG, 0x1);
+
+ return 0;
+}
+
+static void omap_mcasp_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct platform_device *pdev;
+ struct omap_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+ pdev = to_platform_device(mcasp->dev);
+
+ mcasp_set_reg(mcasp->base + OMAP_MCASP_SYSCONFIG_REG, 0x2);
+
+ if (!--mcasp->active)
+ pm_runtime_put_sync(&pdev->dev);
+
+ mcasp_clk_off(mcasp);
+}
+
+/* S/PDIF */
+static int omap_hw_dit_param(struct omap_mcasp *mcasp, unsigned int rate)
+{
+ u32 aclkxdiv, ahclkxdiv;
+ int res;
+
+ /* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */
+ mcasp_set_reg(mcasp->base + OMAP_MCASP_TXFMCTL_REG,
+ AFSXE | FSXMOD(0x180));
+
+ /* Set the TX clock controls : div = 1 and internal */
+ mcasp_set_reg(mcasp->base + OMAP_MCASP_ACLKXCTL_REG,
+ ACLKXE | TX_ASYNC);
+
+ /* Set the HS TX clock controls : div = 1 and internal */
+ mcasp_set_reg(mcasp->base + OMAP_MCASP_AHCLKXCTL_REG, AHCLKXE);
+
+ /* The SPDIF bit clock is derived from the McASP functional clock.
+ * The McASP has two programmable clock dividers (aclkxdiv and
+ * ahclkxdiv) that are configured via the registers MCASP_ACLKXCTL
+ * and MCASP_AHCLKXCTL. For SPDIF the bit clock frequency should be
+ * 128 * sample rate freq. The dividers are defined as part of
+ * platform data as they are dependent upon the functional clock
+ * setting. Lookup the appropriate dividers for the sampling
+ * frequency that we are playing.
+ */
+ res = mcasp_compute_clock_dividers(clk_get_rate(mcasp->fclk),
+ rate,
+ &aclkxdiv,
+ &ahclkxdiv);
+ if (res) {
+ dev_err(mcasp->dev,
+ "%s: No valid McASP config for sampling rate (%d)!\n",
+ __func__, rate);
+ return res;
+ }
+
+ mcasp_set_bits(mcasp->base + OMAP_MCASP_AHCLKXCTL_REG,
+ AHCLKXDIV(ahclkxdiv));
+ mcasp_set_bits(mcasp->base + OMAP_MCASP_ACLKXCTL_REG,
+ AHCLKXDIV(aclkxdiv));
+
+ /* Configure McASP formatter */
+ mcasp_mod_bits(mcasp->base + OMAP_MCASP_TXFMT_REG,
+ TXSSZ(SLOTSIZE_32), TXSSZ_MASK);
+ mcasp_mod_bits(mcasp->base + OMAP_MCASP_TXFMT_REG, TXROT(ROTATE_24),
+ TXROT_MASK);
+ mcasp_set_reg(mcasp->base + OMAP_MCASP_TXMASK_REG, 0xFFFF);
+
+ /* Set the TX tdm : for all the slots */
+ mcasp_set_reg(mcasp->base + OMAP_MCASP_TXTDM_REG, 0xFFFFFFFF);
+
+ /* configure the serializer for transmit mode operation */
+ mcasp_set_bits(mcasp->base + OMAP_MCASP_XRSRCTL0_REG, MODE(1));
+
+ /* All PINS as McASP */
+ mcasp_set_reg(mcasp->base + OMAP_MCASP_PFUNC_REG, 0);
+
+ mcasp_set_bits(mcasp->base + OMAP_MCASP_PDIR_REG, AXR0);
+
+ /* Enable the DIT */
+ mcasp_set_bits(mcasp->base + OMAP_MCASP_TXDITCTL_REG, DITEN);
+
+ mcasp_set_reg(mcasp->base + OMAP_MCASP_TXSTAT_REG, 0xFF);
+
+ return 0;
+}
+
+static int omap_mcasp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct omap_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+ int stream = substream->stream;
+
+ mcasp_stop_tx(mcasp);
+
+ if ((params_format(params)) != SNDRV_PCM_FORMAT_S16_LE) {
+ printk(KERN_WARNING "omap-mcasp: unsupported PCM format");
+ return -EINVAL;
+ }
+
+ if (omap_hw_dit_param(mcasp, params_rate(params)) < 0)
+ return -EPERM;
+
+ snd_soc_dai_set_dma_data(dai, substream,
+ &omap_mcasp_dai_dma_params[stream]);
+
+ return 0;
+}
+
+static int omap_mcasp_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *cpu_dai)
+{
+ struct omap_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = omap_mcasp_start(mcasp, substream->stream);
+ break;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ omap_mcasp_stop(mcasp, substream->stream);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static struct snd_soc_dai_ops omap_mcasp_dai_ops = {
+ .startup = omap_mcasp_startup,
+ .shutdown = omap_mcasp_shutdown,
+ .trigger = omap_mcasp_trigger,
+ .hw_params = omap_mcasp_hw_params,
+
+};
+
+static struct snd_soc_dai_driver omap_mcasp_dai[] = {
+ {
+ .name = "omap-mcasp-dai",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 384,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &omap_mcasp_dai_ops,
+ },
+};
+
+static __devinit int omap_mcasp_probe(struct platform_device *pdev)
+{
+ struct omap_mcasp *mcasp;
+ struct omap_hwmod *oh;
+ long fclk_rate;
+ int ret = 0;
+
+ oh = omap_hwmod_lookup("omap-mcasp-dai");
+ if (oh == NULL) {
+ dev_err(&pdev->dev, "no hwmod device found\n");
+ return -ENODEV;
+ }
+
+ mcasp = kzalloc(sizeof(struct omap_mcasp), GFP_KERNEL);
+ if (!mcasp)
+ return -ENOMEM;
+ mcasp->oh = oh;
+
+ mcasp->base = omap_hwmod_get_mpu_rt_va(oh);
+ if (!mcasp->base) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ mcasp->fclk = clk_get(&pdev->dev, "mcasp_fck");
+ if (!mcasp->fclk) {
+ ret = -ENODEV;
+ goto err;
+ }
+ mcasp_clk_on(mcasp);
+ fclk_rate = clk_get_rate(mcasp->fclk);
+
+ platform_set_drvdata(pdev, mcasp);
+ mcasp->dev = &pdev->dev;
+
+ omap_mcasp_dai[0].playback.rates =
+ mcasp_compute_playback_rates(fclk_rate);
+ if (!omap_mcasp_dai[0].playback.rates) {
+ dev_err(&pdev->dev, "no valid sample rates can be produce from"
+ " a %ld Hz fClk\n", fclk_rate);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = snd_soc_register_dai(&pdev->dev, omap_mcasp_dai);
+
+ if (ret < 0)
+ goto err;
+
+ pm_runtime_enable(&pdev->dev);
+ mcasp_clk_off(mcasp);
+
+ return 0;
+err:
+ if (mcasp && mcasp->fclk)
+ mcasp_clk_off(mcasp);
+ kfree(mcasp);
+
+ return ret;
+}
+
+static __devexit int omap_mcasp_remove(struct platform_device *pdev)
+{
+ struct omap_mcasp *mcasp = dev_get_drvdata(&pdev->dev);
+
+ snd_soc_unregister_dai(&pdev->dev);
+ mcasp_clk_off(mcasp);
+ clk_put(mcasp->fclk);
+
+ kfree(mcasp);
+
+ return 0;
+}
+
+static struct platform_driver omap_mcasp_driver = {
+ .probe = omap_mcasp_probe,
+ .remove = omap_mcasp_remove,
+ .driver = {
+ .name = "omap-mcasp-dai",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap_mcasp_init(void)
+{
+ return platform_driver_register(&omap_mcasp_driver);
+}
+module_init(omap_mcasp_init);
+
+static void __exit omap_mcasp_exit(void)
+{
+ platform_driver_unregister(&omap_mcasp_driver);
+}
+module_exit(omap_mcasp_exit);
+
+MODULE_AUTHOR("Jon Hunter <jon-hunter@ti.com>");
+MODULE_DESCRIPTION("TI OMAP McASP SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-mcasp.h b/sound/soc/omap/omap-mcasp.h
new file mode 100644
index 0000000..25b1aa1
--- /dev/null
+++ b/sound/soc/omap/omap-mcasp.h
@@ -0,0 +1,36 @@
+/*
+ * ALSA SoC McASP Audio Layer for TI OMAP processor
+ *
+ * MCASP related definitions
+ *
+ * Author: Jon Hunter <jon-hunter@ti.com>,
+ * Dan Milea <dan.milea@ti.com>,
+ *
+ * Based upon McASP driver written for TI DaVinci
+ *
+ * Copyright: (C) 2011 Texas Instruments
+ *
+ * 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 OMAP_MCASP_H
+#define OMAP_MCASP_H
+
+#include <linux/io.h>
+#include <plat/mcasp.h>
+
+#define OMAP44XX_MCASP_CFG_BASE 0x49028000
+#define OMAP44XX_MCASP_DAT_BASE 0x4902A000
+
+struct omap_mcasp {
+ struct device *dev;
+ void __iomem *base;
+ struct clk *fclk;
+ int clk_active;
+ int active;
+ struct omap_hwmod *oh;
+};
+
+#endif /* OMAP_MCASP_H */
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c
index c56f38a..ea2081f 100644
--- a/sound/soc/omap/omap-mcpdm.c
+++ b/sound/soc/omap/omap-mcpdm.c
@@ -64,7 +64,6 @@ struct omap_mcpdm {
unsigned long phys_base;
void __iomem *io_base;
int irq;
- struct delayed_work delayed_work;
struct mutex mutex;
struct omap_mcpdm_platform_data *pdata;
@@ -77,7 +76,6 @@ struct omap_mcpdm {
u32 dn_channels;
u32 up_channels;
int active;
- int powered;
int abe_mode;
/* DC offset */
@@ -364,27 +362,19 @@ static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
dev_dbg(dai->dev, "%s: active %d\n", __func__, dai->active);
- /* make sure we stop any pre-existing shutdown */
- cancel_delayed_work_sync(&mcpdm->delayed_work);
-
mutex_lock(&mcpdm->mutex);
+ /* nothing to do if already active */
+ if (mcpdm->active++)
+ goto out;
+
if (dai->id >= MCPDM_ABE_DAI_DL1)
mcpdm->abe_mode = 1;
else
mcpdm->abe_mode = 0;
- /* nothing to do if already active */
- if (mcpdm->active++ || mcpdm->powered)
- goto out;
-
pm_runtime_get_sync(mcpdm->dev);
- if (mcpdm->abe_mode)
- abe_dsp_pm_get();
-
- mcpdm->powered = 1;
-
/* Enable McPDM watch dog for ES above ES 1.0 to avoid saturation */
if (omap_rev() != OMAP4430_REV_ES1_0) {
ctrl = omap_mcpdm_read(mcpdm, MCPDM_CTRL);
@@ -399,60 +389,38 @@ out:
return err;
}
-/* work to delay McPDM shutdown */
-static void playback_work(struct work_struct *work)
+static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
- struct omap_mcpdm *mcpdm =
- container_of(work, struct omap_mcpdm, delayed_work.work);
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dai->dev, "%s: active %d\n", __func__, dai->active);
mutex_lock(&mcpdm->mutex);
- /*
- * still active or another stream started while
- * delayed work was waiting for execution
- */
- if (!mcpdm->powered || mcpdm->active)
+ if (--mcpdm->active)
goto out;
- /* ABE playback stop handled by delayed work */
if (mcpdm->abe_mode) {
if (omap_mcpdm_active(mcpdm)) {
omap_abe_port_disable(mcpdm->abe, mcpdm->dl_port);
omap_abe_port_disable(mcpdm->abe, mcpdm->ul_port);
udelay(250);
+ abe_remove_opp_req(mcpdm->dev);
omap_mcpdm_stop(mcpdm);
}
- omap_mcpdm_close(mcpdm);
- abe_dsp_shutdown();
- abe_dsp_pm_put();
} else {
omap_mcpdm_stop(mcpdm);
- omap_mcpdm_close(mcpdm);
}
+ omap_mcpdm_close(mcpdm);
+
pm_runtime_put_sync(mcpdm->dev);
- mcpdm->powered = 0;
out:
mutex_unlock(&mcpdm->mutex);
}
-static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
-
- dev_dbg(dai->dev, "%s: active %d\n", __func__, dai->active);
-
- mutex_lock(&mcpdm->mutex);
-
- if (!--mcpdm->active)
- schedule_delayed_work(&mcpdm->delayed_work,
- msecs_to_jiffies(1000)); /* TODO: pdata ? */
-
- mutex_unlock(&mcpdm->mutex);
-}
-
static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -519,6 +487,9 @@ static int omap_mcpdm_prepare(struct snd_pcm_substream *substream,
omap_abe_port_is_enabled(mcpdm->abe, mcpdm->dl_port))
goto out;
+ /* PDM tasks require ABE OPP 50 */
+ abe_add_opp_req(mcpdm->dev, ABE_OPP_50);
+
/* start ATC before McPDM IP */
omap_abe_port_enable(mcpdm->abe, mcpdm->dl_port);
omap_abe_port_enable(mcpdm->abe, mcpdm->ul_port);
@@ -728,8 +699,6 @@ static __devinit int asoc_mcpdm_probe(struct platform_device *pdev)
if (err < 0)
dev_err(mcpdm->dev,"failed to DL2 DC offset sysfs: %d\n", err);
- INIT_DELAYED_WORK(&mcpdm->delayed_work, playback_work);
-
#if defined(CONFIG_SND_OMAP_SOC_ABE_DSP) ||\
defined(CONFIG_SND_OMAP_SOC_ABE_DSP_MODULE)
@@ -776,8 +745,6 @@ static int __devexit asoc_mcpdm_remove(struct platform_device *pdev)
struct omap_mcpdm *mcpdm = platform_get_drvdata(pdev);
struct resource *res;
- flush_delayed_work_sync(&mcpdm->delayed_work);
-
snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(omap_mcpdm_dai));
device_remove_file(&pdev->dev, &dev_attr_dl1);
diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c
index ad2cec2..8baa898 100644
--- a/sound/soc/omap/sdp4430.c
+++ b/sound/soc/omap/sdp4430.c
@@ -377,6 +377,26 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Digital Mic1 Bias", NULL, "Digital Mic 2"},
};
+static int sdp4430_mcpdm_twl6040_pre(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct twl6040 *twl6040 = codec->control_data;
+
+ /* TWL6040 supplies McPDM PAD_CLKS */
+ return twl6040_enable(twl6040);
+}
+
+static void sdp4430_mcpdm_twl6040_post(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct twl6040 *twl6040 = codec->control_data;
+
+ /* TWL6040 supplies McPDM PAD_CLKS */
+ twl6040_disable(twl6040);
+}
+
static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
@@ -686,6 +706,8 @@ static struct snd_soc_dai_link sdp4430_dai[] = {
.codec_dai_name = "twl6040-dl1",
.codec_name = "twl6040-codec",
+ .pre = sdp4430_mcpdm_twl6040_pre,
+ .post = sdp4430_mcpdm_twl6040_post,
.ops = &sdp4430_mcpdm_ops,
.ignore_suspend = 1,
},
@@ -724,6 +746,8 @@ static struct snd_soc_dai_link sdp4430_dai[] = {
.no_pcm = 1, /* don't create ALSA pcm for this */
.init = sdp4430_twl6040_init,
+ .pre = sdp4430_mcpdm_twl6040_pre,
+ .post = sdp4430_mcpdm_twl6040_post,
.ops = &sdp4430_mcpdm_ops,
.be_id = OMAP_ABE_DAI_PDM_DL1,
},
@@ -757,6 +781,8 @@ static struct snd_soc_dai_link sdp4430_dai[] = {
.no_pcm = 1, /* don't create ALSA pcm for this */
.init = sdp4430_twl6040_dl2_init,
+ .pre = sdp4430_mcpdm_twl6040_pre,
+ .post = sdp4430_mcpdm_twl6040_post,
.ops = &sdp4430_mcpdm_ops,
.be_id = OMAP_ABE_DAI_PDM_DL2,
},
@@ -773,6 +799,8 @@ static struct snd_soc_dai_link sdp4430_dai[] = {
.codec_name = "twl6040-codec",
.no_pcm = 1, /* don't create ALSA pcm for this */
+ .pre = sdp4430_mcpdm_twl6040_pre,
+ .post = sdp4430_mcpdm_twl6040_post,
.ops = &sdp4430_mcpdm_ops,
.be_id = OMAP_ABE_DAI_PDM_VIB,
},
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 79a4135..b1398ee 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -561,13 +561,22 @@ int soc_pcm_open(struct snd_pcm_substream *substream)
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->pre) {
+ ret = rtd->dai_link->pre(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't setup DAI link %s\n",
+ rtd->dai_link->name);
+ goto out;
+ }
+ }
+
/* startup the audio subsystem */
if (cpu_dai->driver->ops->startup) {
ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open interface %s\n",
cpu_dai->name);
- goto out;
+ goto cpu_err;
}
}
@@ -712,6 +721,9 @@ codec_dai_err:
platform_err:
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
+cpu_err:
+ if (rtd->dai_link->post)
+ rtd->dai_link->post(substream);
out:
mutex_unlock(&rtd->pcm_mutex);
return ret;
@@ -790,6 +802,10 @@ int soc_pcm_close(struct snd_pcm_substream *substream)
if (platform->driver->ops && platform->driver->ops->close)
platform->driver->ops->close(substream);
+
+ if (rtd->dai_link->post)
+ rtd->dai_link->post(substream);
+
cpu_dai->runtime = NULL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 7a58b48..a3ac2cb 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -1498,7 +1498,8 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
trace_snd_soc_dapm_start(card);
list_for_each_entry(d, &card->dapm_list, list)
- if (d->n_widgets || d->codec == NULL)
+ if (d->n_widgets || d->codec == NULL ||
+ strstr(d->codec->name, "null-codec"))
d->dev_power = 0;
/* Check which widgets we need to power and store them in
@@ -2862,6 +2863,36 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
dapm->stream_event(dapm);
}
+static void soc_dapm_platform_stream_event(struct snd_soc_platform *platform,
+ const char *stream, int event)
+{
+ soc_dapm_stream_event(&platform->dapm, stream, event);
+}
+
+static void soc_dapm_codec_stream_event(struct snd_soc_codec *codec,
+ const char *stream, int event)
+{
+ soc_dapm_stream_event(&codec->dapm, stream, event);
+}
+
+void snd_soc_dapm_platform_stream_event(struct snd_soc_platform *platform,
+ const char *stream, int event)
+{
+ mutex_lock(&platform->card->dapm_mutex);
+ soc_dapm_platform_stream_event(platform, stream, event);
+ mutex_unlock(&platform->card->dapm_mutex);
+}
+EXPORT_SYMBOL(snd_soc_dapm_platform_stream_event);
+
+void snd_soc_dapm_codec_stream_event(struct snd_soc_codec *codec,
+ const char *stream, int event)
+{
+ mutex_lock(&codec->card->dapm_mutex);
+ soc_dapm_codec_stream_event(codec, stream, event);
+ mutex_unlock(&codec->card->dapm_mutex);
+}
+EXPORT_SYMBOL(snd_soc_dapm_codec_stream_event);
+
/**
* snd_soc_dapm_stream_event - send a stream event to the dapm core
* @rtd: PCM runtime data
@@ -2881,8 +2912,8 @@ int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd,
mutex_lock(&rtd->card->dapm_mutex);
- soc_dapm_stream_event(&rtd->platform->dapm, stream, event);
- soc_dapm_stream_event(&rtd->codec->dapm, stream, event);
+ soc_dapm_platform_stream_event(rtd->platform, stream, event);
+ soc_dapm_codec_stream_event(rtd->codec, stream, event);
mutex_unlock(&rtd->card->dapm_mutex);
return 0;
diff --git a/sound/soc/soc-dsp.c b/sound/soc/soc-dsp.c
index 73d103c..9620fc4 100644
--- a/sound/soc/soc-dsp.c
+++ b/sound/soc/soc-dsp.c
@@ -37,6 +37,22 @@ int soc_pcm_prepare(struct snd_pcm_substream *);
int soc_pcm_trigger(struct snd_pcm_substream *, int);
int soc_pcm_bespoke_trigger(struct snd_pcm_substream *, int);
+/* count the number of FE clients in a particular state */
+int soc_dsp_fe_state_count(struct snd_soc_pcm_runtime *be, int stream,
+ enum snd_soc_dsp_state state)
+{
+ struct snd_soc_dsp_params *dsp_params;
+ int count = 0;
+
+ list_for_each_entry(dsp_params, &be->dsp[stream].fe_clients, list_fe) {
+ if (dsp_params->fe->dsp[stream].state == state)
+ count++;
+ }
+
+ return count;
+}
+EXPORT_SYMBOL(soc_dsp_fe_state_count);
+
static inline int be_connect(struct snd_soc_pcm_runtime *fe,
struct snd_soc_pcm_runtime *be, int stream)
{
@@ -71,15 +87,43 @@ static inline int be_connect(struct snd_soc_pcm_runtime *fe,
return 1;
}
+static inline void be_reparent(struct snd_soc_pcm_runtime *fe,
+ struct snd_soc_pcm_runtime *be, int stream)
+{
+ struct snd_soc_dsp_params *dsp_params;
+ struct snd_pcm_substream *fe_substream, *be_substream;
+
+ /* reparent if BE is connected to other FEs */
+ if (!be->dsp[stream].users)
+ return;
+
+ be_substream = snd_soc_dsp_get_substream(be, stream);
+
+ list_for_each_entry(dsp_params, &be->dsp[stream].fe_clients, list_fe) {
+ if (dsp_params->fe != fe) {
+ fe_substream = snd_soc_dsp_get_substream(dsp_params->fe,
+ stream);
+ be_substream->runtime = fe_substream->runtime;
+ break;
+ }
+ }
+}
+
static inline void be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dsp_params *dsp_params, *d;
list_for_each_entry_safe(dsp_params, d, &fe->dsp[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dsp_params->be;
+
if (dsp_params->state == SND_SOC_DSP_LINK_STATE_FREE) {
dev_dbg(&fe->dev, " freed DSP %s path %s %s %s\n",
- stream ? "capture" : "playback", fe->dai_link->name,
- stream ? "<-" : "->", dsp_params->be->dai_link->name);
+ stream ? "capture" : "playback",
+ fe->dai_link->name, stream ? "<-" : "->",
+ be->dai_link->name);
+
+ /* BEs still alive need new FE */
+ be_reparent(fe, be, stream);
#ifdef CONFIG_DEBUG_FS
debugfs_remove(dsp_params->debugfs_state);
@@ -287,32 +331,6 @@ out:
}
/*
- * Update the state of all BE's with state old to state new.
- */
-static void be_state_update(struct snd_soc_pcm_runtime *fe, int stream,
- enum snd_soc_dsp_link_state old, enum snd_soc_dsp_link_state new)
-{
- struct snd_soc_dsp_params *dsp_params;
-
- list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) {
- if (dsp_params->state == old)
- dsp_params->state = new;
- }
-}
-
-/*
- * Update the state of all BE's to new regardless of current state.
- */
-static void fe_state_update(struct snd_soc_pcm_runtime *fe, int stream,
- enum snd_soc_dsp_link_state new)
-{
- struct snd_soc_dsp_params *dsp_params;
-
- list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be)
- dsp_params->state = new;
-}
-
-/*
* Clear the runtime pending state of all BE's.
*/
static void fe_clear_pending(struct snd_soc_pcm_runtime *fe, int stream)
@@ -332,17 +350,20 @@ static void soc_dsp_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe, int st
/* disable any enabled and non active backends */
list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dsp_params->be;
struct snd_pcm_substream *be_substream =
- snd_soc_dsp_get_substream(dsp_params->be, stream);
+ snd_soc_dsp_get_substream(be, stream);
- if (--dsp_params->be->dsp[stream].users != 0)
+ if (--be->dsp[stream].users != 0)
continue;
- if (dsp_params->state != SND_SOC_DSP_LINK_STATE_NEW)
+ if (be->dsp[stream].state != SND_SOC_DSP_STATE_OPEN)
continue;
soc_pcm_close(be_substream);
be_substream->runtime = NULL;
+
+ be->dsp[stream].state = SND_SOC_DSP_STATE_CLOSE;
}
}
@@ -355,59 +376,57 @@ static int soc_dsp_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
/* only startup BE DAIs that are either sinks or sources to this FE DAI */
list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dsp_params->be;
struct snd_pcm_substream *be_substream =
- snd_soc_dsp_get_substream(dsp_params->be, stream);
+ snd_soc_dsp_get_substream(be, stream);
/* is this op for this BE ? */
- if (!snd_soc_dsp_is_op_for_be(fe, dsp_params->be, stream))
+ if (!snd_soc_dsp_is_op_for_be(fe, be, stream))
continue;
/* first time the dsp_params is open ? */
- if (dsp_params->be->dsp[stream].users++ != 0)
+ if (be->dsp[stream].users++ != 0)
continue;
- /* only open and ref count new links */
- if (dsp_params->state != SND_SOC_DSP_LINK_STATE_NEW)
+ if ((be->dsp[stream].state != SND_SOC_DSP_STATE_NEW) &&
+ (be->dsp[stream].state != SND_SOC_DSP_STATE_CLOSE))
continue;
- dev_dbg(&dsp_params->be->dev, "dsp: open BE %s\n",
- dsp_params->be->dai_link->name);
+ dev_dbg(&be->dev, "dsp: open BE %s\n", be->dai_link->name);
- be_substream->runtime = dsp_params->be->dsp[stream].runtime;
+ be_substream->runtime = be->dsp[stream].runtime;
err = soc_pcm_open(be_substream);
if (err < 0)
goto unwind;
+
+ be->dsp[stream].state = SND_SOC_DSP_STATE_OPEN;
count++;
}
- /* update BE state */
- be_state_update(fe, stream,
- SND_SOC_DSP_LINK_STATE_NEW, SND_SOC_DSP_LINK_STATE_HW_PARAMS);
return count;
unwind:
/* disable any enabled and non active backends */
list_for_each_entry_continue_reverse(dsp_params, &fe->dsp[stream].be_clients, list_be) {
-
+ struct snd_soc_pcm_runtime *be = dsp_params->be;
struct snd_pcm_substream *be_substream =
- snd_soc_dsp_get_substream(dsp_params->be, stream);
+ snd_soc_dsp_get_substream(be, stream);
- if (!snd_soc_dsp_is_op_for_be(fe, dsp_params->be, stream))
+ if (!snd_soc_dsp_is_op_for_be(fe, be, stream))
continue;
- if (--dsp_params->be->dsp[stream].users != 0)
+ if (--be->dsp[stream].users != 0)
continue;
- if (dsp_params->state != SND_SOC_DSP_LINK_STATE_NEW)
+ if (be->dsp[stream].state != SND_SOC_DSP_STATE_OPEN)
continue;
soc_pcm_close(be_substream);
be_substream->runtime = NULL;
+
+ be->dsp[stream].state = SND_SOC_DSP_STATE_CLOSE;
}
- /* update BE state for disconnect */
- be_state_update(fe, stream,
- SND_SOC_DSP_LINK_STATE_NEW, SND_SOC_DSP_LINK_STATE_FREE);
return err;
}
@@ -459,6 +478,8 @@ static int soc_dsp_fe_dai_startup(struct snd_pcm_substream *fe_substream)
goto unwind;
}
+ fe->dsp[stream].state = SND_SOC_DSP_STATE_OPEN;
+
soc_dsp_set_dynamic_runtime(fe_substream);
snd_pcm_limit_hw_rates(runtime);
@@ -481,24 +502,28 @@ static int soc_dsp_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
/* only shutdown backends that are either sinks or sources to this frontend DAI */
list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dsp_params->be;
struct snd_pcm_substream *be_substream =
- snd_soc_dsp_get_substream(dsp_params->be, stream);
+ snd_soc_dsp_get_substream(be, stream);
/* is this op for this BE ? */
- if (!snd_soc_dsp_is_op_for_be(fe, dsp_params->be, stream))
+ if (!snd_soc_dsp_is_op_for_be(fe, be, stream))
continue;
- if (--dsp_params->be->dsp[stream].users != 0)
+ if (--be->dsp[stream].users != 0)
continue;
- if (dsp_params->state != SND_SOC_DSP_LINK_STATE_FREE)
+ if ((be->dsp[stream].state != SND_SOC_DSP_STATE_HW_FREE) &&
+ (be->dsp[stream].state != SND_SOC_DSP_STATE_OPEN))
continue;
- dev_dbg(&dsp_params->be->dev, "dsp: close BE %s\n",
+ dev_dbg(&be->dev, "dsp: close BE %s\n",
dsp_params->fe->dai_link->name);
soc_pcm_close(be_substream);
be_substream->runtime = NULL;
+
+ be->dsp[stream].state = SND_SOC_DSP_STATE_CLOSE;
}
return 0;
}
@@ -532,6 +557,7 @@ static int soc_dsp_fe_dai_shutdown(struct snd_pcm_substream *substream)
fe->cpu_dai->driver->capture.stream_name,
SND_SOC_DAPM_STREAM_STOP);
+ fe->dsp[stream].state = SND_SOC_DSP_STATE_CLOSE;
fe->dsp[stream].runtime_update = runtime_update;
mutex_unlock(&fe->card->dsp_mutex);
@@ -545,21 +571,23 @@ static int soc_dsp_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dsp_params->be;
struct snd_pcm_substream *be_substream =
- snd_soc_dsp_get_substream(dsp_params->be, stream);
+ snd_soc_dsp_get_substream(be, stream);
/* is this op for this BE ? */
- if (!snd_soc_dsp_is_op_for_be(fe, dsp_params->be, stream))
+ if (!snd_soc_dsp_is_op_for_be(fe, be, stream))
continue;
- if (dsp_params->state != SND_SOC_DSP_LINK_STATE_HW_PARAMS)
+ /* first time the dsp_params is open ? */
+ if (be->dsp[stream].users != 1)
continue;
- /* first time the dsp_params is open ? */
- if (dsp_params->be->dsp[stream].users != 1)
+ if ((be->dsp[stream].state != SND_SOC_DSP_STATE_OPEN) &&
+ (be->dsp[stream].state != SND_SOC_DSP_STATE_HW_FREE))
continue;
- dev_dbg(&dsp_params->be->dev, "dsp: hw_params BE %s\n",
+ dev_dbg(&be->dev, "dsp: hw_params BE %s\n",
dsp_params->fe->dai_link->name);
/* copy params for each dsp_params */
@@ -567,12 +595,13 @@ static int soc_dsp_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
sizeof(struct snd_pcm_hw_params));
/* perform any hw_params fixups */
- if (dsp_params->be->dai_link->be_hw_params_fixup) {
- ret = dsp_params->be->dai_link->be_hw_params_fixup(dsp_params->be,
+ if (be->dai_link->be_hw_params_fixup) {
+ ret = be->dai_link->be_hw_params_fixup(be,
&dsp_params->params);
if (ret < 0) {
- dev_err(&dsp_params->be->dev,
- "dsp: hw_params BE fixup failed %d\n", ret);
+ dev_err(&be->dev,
+ "dsp: hw_params BE fixup failed %d\n",
+ ret);
return ret;
}
}
@@ -582,6 +611,8 @@ static int soc_dsp_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
dev_err(&dsp_params->be->dev, "dsp: hw_params BE failed %d\n", ret);
return ret;
}
+
+ be->dsp[stream].state = SND_SOC_DSP_STATE_HW_PARAMS;
}
return 0;
}
@@ -610,6 +641,8 @@ int soc_dsp_fe_dai_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
dev_err(&fe->dev,"dsp: hw_params FE failed %d\n", ret);
+ fe->dsp[stream].state = SND_SOC_DSP_STATE_HW_PARAMS;
+
out:
fe->dsp[stream].runtime_update = runtime_update;
mutex_unlock(&fe->card->dsp_mutex);
@@ -638,52 +671,45 @@ int soc_dsp_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd)
list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dsp_params->be;
struct snd_pcm_substream *be_substream =
- snd_soc_dsp_get_substream(dsp_params->be, stream);
+ snd_soc_dsp_get_substream(be, stream);
/* is this op for this BE ? */
- if (!snd_soc_dsp_is_op_for_be(fe, dsp_params->be, stream))
+ if (!snd_soc_dsp_is_op_for_be(fe, be, stream))
continue;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- /* only start BEs that are not triggered */
- if (dsp_params->state == SND_SOC_DSP_LINK_STATE_PREPARE) {
- ret = dsp_do_trigger(dsp_params, be_substream, cmd);
- if (ret == 0)
- dsp_params->state = SND_SOC_DSP_LINK_STATE_START;
- }
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- /* only stop BEs that are being shutdown */
- if (dsp_params->state == SND_SOC_DSP_LINK_STATE_FREE &&
- dsp_params->be->dsp[stream].users == 1)
- ret = dsp_do_trigger(dsp_params, be_substream, cmd);
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_RESUME:
- /* suspend and resume all BEs */
- ret = dsp_do_trigger(dsp_params, be_substream, cmd);
- break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- /* only release Paused BEs */
- if (dsp_params->state == SND_SOC_DSP_LINK_STATE_PAUSED) {
- ret = dsp_do_trigger(dsp_params, be_substream, cmd);
- if (ret == 0)
- dsp_params->state = SND_SOC_DSP_LINK_STATE_START;
- }
+ if ((be->dsp[stream].state != SND_SOC_DSP_STATE_PREPARE) &&
+ (be->dsp[stream].state != SND_SOC_DSP_STATE_STOP))
+ continue;
+
+ ret = dsp_do_trigger(dsp_params, be_substream, cmd);
+ if (ret)
+ return ret;
+
+ be->dsp[stream].state = SND_SOC_DSP_STATE_START;
break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- /* only pause active BEs */
- if (dsp_params->state == SND_SOC_DSP_LINK_STATE_START) {
- ret = dsp_do_trigger(dsp_params, be_substream, cmd);
- if (ret == 0)
- dsp_params->state = SND_SOC_DSP_LINK_STATE_PAUSED;
- }
+ if (be->dsp[stream].state != SND_SOC_DSP_STATE_START)
+ continue;
+
+ if (soc_dsp_fe_state_count(be, stream,
+ SND_SOC_DSP_STATE_START) > 1)
+ continue;
+
+ ret = dsp_do_trigger(dsp_params, be_substream, cmd);
+ if (ret)
+ return ret;
+
+ be->dsp[stream].state = SND_SOC_DSP_STATE_STOP;
break;
}
- if (ret < 0)
- return ret;
}
return ret;
@@ -744,6 +770,19 @@ int soc_dsp_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
dev_err(&fe->dev, "dsp: invalid trigger cmd %d for %s\n", cmd,
fe->dai_link->name);
ret = -EINVAL;
+ goto out;
+ }
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ fe->dsp[stream].state = SND_SOC_DSP_STATE_START;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ fe->dsp[stream].state = SND_SOC_DSP_STATE_STOP;
break;
}
@@ -759,31 +798,29 @@ static int soc_dsp_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dsp_params->be;
struct snd_pcm_substream *be_substream =
- snd_soc_dsp_get_substream(dsp_params->be, stream);
+ snd_soc_dsp_get_substream(be, stream);
/* is this op for this BE ? */
- if (!snd_soc_dsp_is_op_for_be(fe, dsp_params->be, stream))
+ if (!snd_soc_dsp_is_op_for_be(fe, be, stream))
continue;
- /* only prepare ACTIVE or READY BE's */
- if (dsp_params->state == SND_SOC_DSP_LINK_STATE_NEW ||
- dsp_params->state == SND_SOC_DSP_LINK_STATE_FREE)
+ if ((be->dsp[stream].state != SND_SOC_DSP_STATE_HW_PARAMS) &&
+ (be->dsp[stream].state != SND_SOC_DSP_STATE_STOP))
continue;
- dev_dbg(&dsp_params->be->dev, "dsp: prepare BE %s\n",
+ dev_dbg(&be->dev, "dsp: prepare BE %s\n",
dsp_params->fe->dai_link->name);
ret = soc_pcm_prepare(be_substream);
if (ret < 0) {
- dev_err(&dsp_params->be->dev,"dsp: backend prepare failed %d\n",
- ret);
+ dev_err(&be->dev, "dsp: backend prepare failed %d\n",
+ ret);
break;
}
- /* mark the BE as active */
- be_state_update(fe, stream, SND_SOC_DSP_LINK_STATE_HW_PARAMS,
- SND_SOC_DSP_LINK_STATE_PREPARE);
+ be->dsp[stream].state = SND_SOC_DSP_STATE_PREPARE;
}
return ret;
}
@@ -812,9 +849,6 @@ int soc_dsp_fe_dai_prepare(struct snd_pcm_substream *substream)
if (ret < 0)
goto out;
- /* mark the BE as active */
- fe_state_update(fe, stream, SND_SOC_DSP_LINK_STATE_PREPARE);
-
/* call prepare on the frontend */
ret = soc_pcm_prepare(substream);
if (ret < 0) {
@@ -832,6 +866,8 @@ int soc_dsp_fe_dai_prepare(struct snd_pcm_substream *substream)
fe->cpu_dai->driver->capture.stream_name,
SNDRV_PCM_TRIGGER_START);
+ fe->dsp[stream].state = SND_SOC_DSP_STATE_PREPARE;
+
out:
fe->dsp[stream].runtime_update = runtime_update;
mutex_unlock(&fe->card->dsp_mutex);
@@ -846,24 +882,29 @@ static int soc_dsp_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
* to this frontend DAI */
list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dsp_params->be;
struct snd_pcm_substream *be_substream =
- snd_soc_dsp_get_substream(dsp_params->be, stream);
+ snd_soc_dsp_get_substream(be, stream);
/* is this op for this BE ? */
- if (!snd_soc_dsp_is_op_for_be(fe, dsp_params->be, stream))
+ if (!snd_soc_dsp_is_op_for_be(fe, be, stream))
continue;
- if (dsp_params->state != SND_SOC_DSP_LINK_STATE_FREE)
+ /* only free hw when no longer used */
+ if (be->dsp[stream].users != 1)
continue;
- /* only free hw when no longer used */
- if (dsp_params->be->dsp[stream].users != 1)
+ if ((be->dsp[stream].state != SND_SOC_DSP_STATE_HW_PARAMS) &&
+ (be->dsp[stream].state != SND_SOC_DSP_STATE_PREPARE) &&
+ (be->dsp[stream].state != SND_SOC_DSP_STATE_STOP))
continue;
- dev_dbg(&dsp_params->be->dev, "dsp: hw_free BE %s\n",
+ dev_dbg(&be->dev, "dsp: hw_free BE %s\n",
dsp_params->fe->dai_link->name);
soc_pcm_hw_free(be_substream);
+
+ be->dsp[stream].state = SND_SOC_DSP_STATE_HW_FREE;
}
return 0;
@@ -879,8 +920,6 @@ int soc_dsp_fe_dai_hw_free(struct snd_pcm_substream *substream)
runtime_update = fe->dsp[stream].runtime_update;
fe->dsp[stream].runtime_update = SND_SOC_DSP_UPDATE_FE;
- fe_state_update(fe, stream, SND_SOC_DSP_LINK_STATE_FREE);
-
dev_dbg(&fe->dev, "dsp: hw_free FE %s\n", fe->dai_link->name);
/* call hw_free on the frontend */
@@ -892,6 +931,7 @@ int soc_dsp_fe_dai_hw_free(struct snd_pcm_substream *substream)
* to this frontend DAI */
ret = soc_dsp_be_dai_hw_free(fe, stream);
+ fe->dsp[stream].state = SND_SOC_DSP_STATE_HW_FREE;
fe->dsp[stream].runtime_update = runtime_update;
mutex_unlock(&fe->card->dsp_mutex);
@@ -924,7 +964,6 @@ static int dsp_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dsp_link *dsp_link = fe->dai_link->dsp_link;
struct snd_pcm_substream *substream = snd_soc_dsp_get_substream(fe, stream);
- struct snd_soc_dsp_params *dsp_params;
int ret;
dev_dbg(&fe->dev, "runtime %s close on FE %s\n",
@@ -941,16 +980,12 @@ static int dsp_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
return ret;
}
} else {
+ dev_dbg(&fe->dev, "dsp: trigger FE %s cmd stop\n",
+ fe->dai_link->name);
- list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) {
-
- dev_dbg(&fe->dev, "dsp: trigger FE %s cmd stop\n",
- fe->dai_link->name);
-
- ret = soc_dsp_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP);
- if (ret < 0)
- return ret;
- }
+ ret = soc_dsp_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP);
+ if (ret < 0)
+ return ret;
}
ret = soc_dsp_be_dai_hw_free(fe, stream);
@@ -974,44 +1009,10 @@ static int dsp_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
return 0;
}
-/* check for running FEs */
-static int dsp_get_be_trigger_cmd(struct snd_soc_pcm_runtime *fe, int stream)
-{
- struct snd_soc_dsp_params *dsp_be_params, *dsp_fe_params;
-
- /* get the FEs for each BE */
- list_for_each_entry(dsp_be_params, &fe->dsp[stream].be_clients, list_be) {
- struct snd_soc_pcm_runtime *be = dsp_be_params->be;
-
- /* get the FEs for this BE */
- list_for_each_entry(dsp_fe_params, &be->dsp[stream].fe_clients, list_fe) {
-
- if (dsp_fe_params->state == SND_SOC_DSP_LINK_STATE_START)
- return SND_SOC_DSP_LINK_STATE_START;
- }
- }
- return SND_SOC_DSP_LINK_STATE_PAUSED;
-}
-
-/* check for running BEs */
-static int dsp_get_fe_trigger_cmd(struct snd_soc_pcm_runtime *fe, int stream)
-{
- struct snd_soc_dsp_params *dsp_be_params;
-
- /* get the FEs for each BE */
- list_for_each_entry(dsp_be_params, &fe->dsp[stream].be_clients, list_be) {
-
- if (dsp_be_params->state == SND_SOC_DSP_LINK_STATE_START)
- return SND_SOC_DSP_LINK_STATE_START;
- }
- return SND_SOC_DSP_LINK_STATE_PAUSED;
-}
-
static int dsp_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dsp_link *dsp_link = fe->dai_link->dsp_link;
struct snd_pcm_substream *substream = snd_soc_dsp_get_substream(fe, stream);
- struct snd_soc_dsp_params *dsp_params;
int ret, cmd;
dev_dbg(&fe->dev, "runtime %s open on FE %s\n",
@@ -1039,11 +1040,13 @@ static int dsp_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
fe->cpu_dai->driver->capture.stream_name,
SNDRV_PCM_TRIGGER_START);
- if (dsp_link->trigger[stream] == SND_SOC_DSP_TRIGGER_BESPOKE) {
-
- /* there is no point in triggering START iff all BEs are PAUSED */
- cmd = dsp_get_fe_trigger_cmd(fe, stream);
+ /* determine trigger command */
+ if (fe->dsp[stream].state == SND_SOC_DSP_STATE_START)
+ cmd = SNDRV_PCM_TRIGGER_START;
+ else
+ cmd = SNDRV_PCM_TRIGGER_STOP;
+ if (dsp_link->trigger[stream] == SND_SOC_DSP_TRIGGER_BESPOKE) {
/* call trigger on the frontend - FE takes care of all BE triggers */
dev_dbg(&fe->dev, "dsp: bespoke trigger FE %s cmd start\n",
fe->dai_link->name);
@@ -1053,42 +1056,13 @@ static int dsp_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
dev_err(&fe->dev,"dsp: trigger FE failed %d\n", ret);
return ret;
}
-
- /* successful trigger so update BE trigger status */
- list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) {
-
- /* is this op for this BE ? */
- if (!snd_soc_dsp_is_op_for_be(fe, dsp_params->be,
- stream))
- continue;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- case SNDRV_PCM_TRIGGER_RESUME:
- dsp_params->state = SND_SOC_DSP_LINK_STATE_START;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- dsp_params->state = SND_SOC_DSP_LINK_STATE_PAUSED;
- break;
- }
- }
} else {
+ dev_dbg(&fe->dev, "dsp: trigger FE %s cmd start\n",
+ fe->dai_link->name);
- list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) {
-
- /* there is no point in triggering start iff all FEs are PAUSED */
- cmd = dsp_get_be_trigger_cmd(fe, stream);
-
- dev_dbg(&fe->dev, "dsp: trigger FE %s cmd start\n",
- fe->dai_link->name);
-
- ret = soc_dsp_be_dai_trigger(fe, stream, cmd);
- if (ret < 0)
- return ret;
- }
+ ret = soc_dsp_be_dai_trigger(fe, stream, cmd);
+ if (ret < 0)
+ return ret;
}
return 0;
@@ -1547,13 +1521,18 @@ int soc_dsp_fe_dai_open(struct snd_pcm_substream *fe_substream)
int soc_dsp_fe_dai_close(struct snd_pcm_substream *fe_substream)
{
struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
- int ret;
+ struct snd_soc_dsp_params *dsp_params;
+ int stream = fe_substream->stream, ret;
ret = soc_dsp_fe_dai_shutdown(fe_substream);
- be_disconnect(fe, fe_substream->stream);
+ /* mark FE's links ready to prune */
+ list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be)
+ dsp_params->state = SND_SOC_DSP_LINK_STATE_FREE;
+
+ be_disconnect(fe, stream);
- fe->dsp[fe_substream->stream].runtime = NULL;
+ fe->dsp[stream].runtime = NULL;
return ret;
}