[alsa-devel] ASoC updates for 2.6.31
The following changes since commit 8a1f936acdfd53cb0a981f3f80483863dcd84fa9: Peter Ujfalusi (1): ASoC: TWL4030: Add 4 channel TDM support
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git for-2.6.31
Daniel Mack (3): ASoC: add SOC_DOUBLE_EXT macro ASoC: cs4270: fix Master Capture Switch polarity ASoC: cs4270: add Master Playback Switch
Jon Smirl (3): ASoC: Set the MPC5200 i2s driver to BROKEN status. ASoC: Basic split of mpc5200 DMA code out from mpc5200_psc_i2s ASoC: Rename the PSC functions to DMA
Jonathan Cameron (1): ASoC WM8940 Driver
Joonyoung Shim (1): ASoC: TWL4030: Fix gain control for earpiece amplifier
Mark Brown (10): ASoC: Fix S3C64xx IIS device registration and support both ports ASoC: S3C2412: Failing to get the I2S clock is an error ASoC: Enforce symmetric rates for S3C64xx I2S interface ASoC: Include WM8350 register definitions in CODEC header ASoC: s3c-i2s-v2 diagnostic improvements ASoC: Use our registration function for S3C64xx ASoC: Fix logic in WM8350 master clocking check Merge branch 'for-2.6.30' into for-2.6.31 ASoC: Staticise TLV values in WM8940 Merge branch 'for-2.6.30' into for-2.6.31
Peter Ujfalusi (1): ASoC: Beagle: Add support for 4 channel
include/sound/soc.h | 8 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs4270.c | 44 ++- sound/soc/codecs/twl4030.c | 8 +- sound/soc/codecs/wm8350.c | 2 +- sound/soc/codecs/wm8350.h | 1 + sound/soc/codecs/wm8940.c | 955 +++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm8940.h | 104 +++++ sound/soc/fsl/Kconfig | 6 +- sound/soc/fsl/Makefile | 2 + sound/soc/fsl/mpc5200_dma.c | 457 +++++++++++++++++++ sound/soc/fsl/mpc5200_dma.h | 81 ++++ sound/soc/fsl/mpc5200_psc_i2s.c | 644 ++++----------------------- sound/soc/omap/omap3beagle.c | 26 +- sound/soc/s3c24xx/s3c2412-i2s.c | 2 +- sound/soc/s3c24xx/s3c64xx-i2s.c | 148 ++++-- sound/soc/s3c24xx/s3c64xx-i2s.h | 2 +- 18 files changed, 1867 insertions(+), 629 deletions(-) create mode 100644 sound/soc/codecs/wm8940.c create mode 100644 sound/soc/codecs/wm8940.h create mode 100644 sound/soc/fsl/mpc5200_dma.c create mode 100644 sound/soc/fsl/mpc5200_dma.h
From: Peter Ujfalusi peter.ujfalusi@nokia.com
This patch adds support for the four channel TDM mode on Beagle board.
Depending on the channel count, the interface needs to be configured differently (I2S for stereo DSP_A for four channels)
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/omap/omap3beagle.c | 26 ++++++++++++++++++-------- 1 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c index 6aa428e..b0cff9f 100644 --- a/sound/soc/omap/omap3beagle.c +++ b/sound/soc/omap/omap3beagle.c @@ -41,23 +41,33 @@ static int omap3beagle_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + unsigned int fmt; int ret;
+ switch (params_channels(params)) { + case 2: /* Stereo I2S mode */ + fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + break; + case 4: /* Four channel TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM; + break; + default: + return -EINVAL; + } + /* Set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); + ret = snd_soc_dai_set_fmt(codec_dai, fmt); if (ret < 0) { printk(KERN_ERR "can't set codec DAI configuration\n"); return ret; }
/* Set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); if (ret < 0) { printk(KERN_ERR "can't set cpu DAI configuration\n"); return ret;
From: Daniel Mack daniel@caiaq.de
Add a macro for double controls with special callback functions.
Signed-off-by: Daniel Mack daniel@caiaq.de Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- include/sound/soc.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/include/sound/soc.h b/include/sound/soc.h index b1f2f88..6ab80bf 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -118,6 +118,14 @@ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) } +#define SOC_DOUBLE_EXT(xname, xreg, shift_left, shift_right, xmax, xinvert,\ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .shift = shift_left, .rshift = shift_right, \ + .max = xmax, .invert = xinvert} } #define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
The S3C64xx IIS code had a number of problems with device registration. The hardware has two IIS ports of which the driver supported only one at once via a single exported DAI, attempting to identify the DAI to use based on the dev->id of the ASoC platform device. As well as limiting the driver to only supporting one IIS port at once this also meant that the ID of the soc-audio device (or in future the card device) had to match the IIS ID.
Fix both problems by converting the driver to register the DAIs based on probing of platform devices registered by the arch/arm code, using those platform devices to interact with the clock API.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/s3c24xx/s3c64xx-i2s.c | 146 ++++++++++++++++++++++++++------------- sound/soc/s3c24xx/s3c64xx-i2s.h | 2 +- 2 files changed, 98 insertions(+), 50 deletions(-)
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c index 33c5de7..a84c4be 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.c +++ b/sound/soc/s3c24xx/s3c64xx-i2s.c @@ -120,36 +120,8 @@ EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clockrate); static int s3c64xx_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai) { - struct device *dev = &pdev->dev; - struct s3c_i2sv2_info *i2s; - int ret; - - dev_dbg(dev, "%s: probing dai %d\n", __func__, pdev->id); - - if (pdev->id < 0 || pdev->id > ARRAY_SIZE(s3c64xx_i2s)) { - dev_err(dev, "id %d out of range\n", pdev->id); - return -EINVAL; - } - - i2s = &s3c64xx_i2s[pdev->id]; - - ret = s3c_i2sv2_probe(pdev, dai, i2s, - pdev->id ? S3C64XX_PA_IIS1 : S3C64XX_PA_IIS0); - if (ret) - return ret; - - i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id]; - i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id]; - - i2s->iis_cclk = clk_get(dev, "audio-bus"); - if (IS_ERR(i2s->iis_cclk)) { - dev_err(dev, "failed to get audio-bus"); - iounmap(i2s->regs); - return -ENODEV; - } - /* configure GPIO for i2s port */ - switch (pdev->id) { + switch (dai->id) { case 0: s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_I2S0_CLK); s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_I2S0_CDCLK); @@ -181,35 +153,114 @@ static struct snd_soc_dai_ops s3c64xx_i2s_dai_ops = { .set_sysclk = s3c64xx_i2s_set_sysclk, };
-struct snd_soc_dai s3c64xx_i2s_dai = { - .name = "s3c64xx-i2s", - .id = 0, - .probe = s3c64xx_i2s_probe, - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = S3C64XX_I2S_RATES, - .formats = S3C64XX_I2S_FMTS, +struct snd_soc_dai s3c64xx_i2s_dai[] = { + { + .name = "s3c64xx-i2s", + .id = 0, + .probe = s3c64xx_i2s_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = S3C64XX_I2S_RATES, + .formats = S3C64XX_I2S_FMTS, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = S3C64XX_I2S_RATES, + .formats = S3C64XX_I2S_FMTS, + }, + .ops = &s3c64xx_i2s_dai_ops, }, - .capture = { - .channels_min = 2, - .channels_max = 2, - .rates = S3C64XX_I2S_RATES, - .formats = S3C64XX_I2S_FMTS, + { + .name = "s3c64xx-i2s", + .id = 1, + .probe = s3c64xx_i2s_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = S3C64XX_I2S_RATES, + .formats = S3C64XX_I2S_FMTS, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = S3C64XX_I2S_RATES, + .formats = S3C64XX_I2S_FMTS, + }, + .ops = &s3c64xx_i2s_dai_ops, }, - .ops = &s3c64xx_i2s_dai_ops, }; EXPORT_SYMBOL_GPL(s3c64xx_i2s_dai);
+static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev) +{ + struct s3c_i2sv2_info *i2s; + struct snd_soc_dai *dai; + int ret; + + if (pdev->id >= ARRAY_SIZE(s3c64xx_i2s)) { + dev_err(&pdev->dev, "id %d out of range\n", pdev->id); + return -EINVAL; + } + + i2s = &s3c64xx_i2s[pdev->id]; + dai = &s3c64xx_i2s_dai[pdev->id]; + dai->dev = &pdev->dev; + + i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id]; + i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id]; + + i2s->iis_cclk = clk_get(&pdev->dev, "audio-bus"); + if (IS_ERR(i2s->iis_cclk)) { + dev_err(&pdev->dev, "failed to get audio-bus"); + ret = PTR_ERR(i2s->iis_cclk); + goto err; + } + + ret = s3c_i2sv2_probe(pdev, dai, i2s, + dai->id ? S3C64XX_PA_IIS1 : S3C64XX_PA_IIS0); + if (ret) + goto err_clk; + + ret = snd_soc_register_dai(dai); + if (ret != 0) + goto err_i2sv2; + + return 0; + +err_i2sv2: + /* Not implemented for I2Sv2 core yet */ +err_clk: + clk_put(i2s->iis_cclk); +err: + return ret; +} + +static __devexit int s3c64xx_iis_dev_remove(struct platform_device *pdev) +{ + dev_err(&pdev->dev, "Device removal not yet supported\n"); + return 0; +} + +static struct platform_driver s3c64xx_iis_driver = { + .probe = s3c64xx_iis_dev_probe, + .remove = s3c64xx_iis_dev_remove, + .driver = { + .name = "s3c64xx-iis", + .owner = THIS_MODULE, + }, +}; + static int __init s3c64xx_i2s_init(void) { - return s3c_i2sv2_register_dai(&s3c64xx_i2s_dai); + return platform_driver_register(&s3c64xx_iis_driver); } module_init(s3c64xx_i2s_init);
static void __exit s3c64xx_i2s_exit(void) { - snd_soc_unregister_dai(&s3c64xx_i2s_dai); + platform_driver_unregister(&s3c64xx_iis_driver); } module_exit(s3c64xx_i2s_exit);
@@ -217,6 +268,3 @@ module_exit(s3c64xx_i2s_exit); MODULE_AUTHOR("Ben Dooks, ben@simtec.co.uk"); MODULE_DESCRIPTION("S3C64XX I2S SoC Interface"); MODULE_LICENSE("GPL"); - - - diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.h b/sound/soc/s3c24xx/s3c64xx-i2s.h index b7ffe3c..597822a 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.h +++ b/sound/soc/s3c24xx/s3c64xx-i2s.h @@ -24,7 +24,7 @@ #define S3C64XX_CLKSRC_PCLK (0) #define S3C64XX_CLKSRC_MUX (1)
-extern struct snd_soc_dai s3c64xx_i2s_dai; +extern struct snd_soc_dai s3c64xx_i2s_dai[];
extern unsigned long s3c64xx_i2s_get_clockrate(struct snd_soc_dai *cpu_dai);
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/s3c24xx/s3c2412-i2s.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c index b7e0b3f..168a088 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.c +++ b/sound/soc/s3c24xx/s3c2412-i2s.c @@ -120,7 +120,7 @@ static int s3c2412_i2s_probe(struct platform_device *pdev,
s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk"); if (s3c2412_i2s.iis_cclk == NULL) { - pr_debug("failed to get i2sclk clock\n"); + pr_err("failed to get i2sclk clock\n"); iounmap(s3c2412_i2s.regs); return -ENODEV; }
There is only one LRCLK pin on each interface.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/s3c24xx/s3c64xx-i2s.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c index a84c4be..c335248 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.c +++ b/sound/soc/s3c24xx/s3c64xx-i2s.c @@ -171,6 +171,7 @@ struct snd_soc_dai s3c64xx_i2s_dai[] = { .formats = S3C64XX_I2S_FMTS, }, .ops = &s3c64xx_i2s_dai_ops, + .symmetric_rates = 1, }, { .name = "s3c64xx-i2s", @@ -189,6 +190,7 @@ struct snd_soc_dai s3c64xx_i2s_dai[] = { .formats = S3C64XX_I2S_FMTS, }, .ops = &s3c64xx_i2s_dai_ops, + .symmetric_rates = 1, }, }; EXPORT_SYMBOL_GPL(s3c64xx_i2s_dai);
It's expected behaviour for the CODEC header to provide them but the WM8350 doesn't due to having all the registers together under drivers/mfd.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8350.h | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/wm8350.h b/sound/soc/codecs/wm8350.h index d11bd92..d088eb4 100644 --- a/sound/soc/codecs/wm8350.h +++ b/sound/soc/codecs/wm8350.h @@ -13,6 +13,7 @@ #define _WM8350_H
#include <sound/soc.h> +#include <linux/mfd/wm8350/audio.h>
extern struct snd_soc_dai wm8350_dai; extern struct snd_soc_codec_device soc_codec_dev_wm8350;
Say what invalid values we're seeing when we see an invalid value and ensure that errors are displayed by default.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/s3c24xx/s3c-i2s-v2.c | 18 ++++++++++++------ 1 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c index ab680aa..aeea49c 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.c +++ b/sound/soc/s3c24xx/s3c-i2s-v2.c @@ -105,7 +105,9 @@ void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on) break;
default: - dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n"); + dev_err(i2s->dev, "TXEN: Invalid MODE %x in IISMOD\n", + mod & S3C2412_IISMOD_MODE_MASK); + break; }
writel(con, regs + S3C2412_IISCON); @@ -132,7 +134,9 @@ void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on) break;
default: - dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n"); + dev_err(i2s->dev, "TXDIS: Invalid MODE %xin IISMOD\n", + mod & S3C2412_IISMOD_MODE_MASK); + break; }
writel(mod, regs + S3C2412_IISMOD); @@ -175,7 +179,8 @@ void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on) break;
default: - dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); + dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n", + mod & S3C2412_IISMOD_MODE_MASK); }
writel(mod, regs + S3C2412_IISMOD); @@ -199,7 +204,8 @@ void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on) break;
default: - dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); + dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n", + mod & S3C2412_IISMOD_MODE_MASK); }
writel(con, regs + S3C2412_IISCON); @@ -281,7 +287,7 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, iismod |= IISMOD_MASTER; break; default: - pr_debug("unknwon master/slave format\n"); + pr_err("unknwon master/slave format\n"); return -EINVAL; }
@@ -298,7 +304,7 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, iismod |= S3C2412_IISMOD_SDF_IIS; break; default: - pr_debug("Unknown data format\n"); + pr_err("Unknown data format\n"); return -EINVAL; }
Make sure we get the DAI operations initialised.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/s3c24xx/s3c-i2s-v2.c | 18 ++++++------------ sound/soc/s3c24xx/s3c64xx-i2s.c | 2 +- 2 files changed, 7 insertions(+), 13 deletions(-)
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c index aeea49c..ab680aa 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.c +++ b/sound/soc/s3c24xx/s3c-i2s-v2.c @@ -105,9 +105,7 @@ void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on) break;
default: - dev_err(i2s->dev, "TXEN: Invalid MODE %x in IISMOD\n", - mod & S3C2412_IISMOD_MODE_MASK); - break; + dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n"); }
writel(con, regs + S3C2412_IISCON); @@ -134,9 +132,7 @@ void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on) break;
default: - dev_err(i2s->dev, "TXDIS: Invalid MODE %xin IISMOD\n", - mod & S3C2412_IISMOD_MODE_MASK); - break; + dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n"); }
writel(mod, regs + S3C2412_IISMOD); @@ -179,8 +175,7 @@ void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on) break;
default: - dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n", - mod & S3C2412_IISMOD_MODE_MASK); + dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); }
writel(mod, regs + S3C2412_IISMOD); @@ -204,8 +199,7 @@ void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on) break;
default: - dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n", - mod & S3C2412_IISMOD_MODE_MASK); + dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); }
writel(con, regs + S3C2412_IISCON); @@ -287,7 +281,7 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, iismod |= IISMOD_MASTER; break; default: - pr_err("unknwon master/slave format\n"); + pr_debug("unknwon master/slave format\n"); return -EINVAL; }
@@ -304,7 +298,7 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, iismod |= S3C2412_IISMOD_SDF_IIS; break; default: - pr_err("Unknown data format\n"); + pr_debug("Unknown data format\n"); return -EINVAL; }
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c index c335248..1345fbd 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.c +++ b/sound/soc/s3c24xx/s3c64xx-i2s.c @@ -225,7 +225,7 @@ static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev) if (ret) goto err_clk;
- ret = snd_soc_register_dai(dai); + ret = s3c_i2sv2_register_dai(dai); if (ret != 0) goto err_i2sv2;
From: Jonathan Cameron jic23@cam.ac.uk
Signed-off-by: Jonathan Cameron jic23@cam.ac.uk Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/wm8940.c | 955 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm8940.h | 104 +++++ 4 files changed, 1065 insertions(+), 0 deletions(-) create mode 100644 sound/soc/codecs/wm8940.c create mode 100644 sound/soc/codecs/wm8940.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 121d63f..1c19ad5 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -35,6 +35,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8900 if I2C select SND_SOC_WM8903 if I2C + select SND_SOC_WM8940 if I2C select SND_SOC_WM8960 if I2C select SND_SOC_WM8971 if I2C select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI @@ -140,6 +141,9 @@ config SND_SOC_WM8900 config SND_SOC_WM8903 tristate
+config SND_SOC_WM8940 + tristate + config SND_SOC_WM8960 tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 8116968..3d31b6b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -23,6 +23,7 @@ snd-soc-wm8750-objs := wm8750.o snd-soc-wm8753-objs := wm8753.o snd-soc-wm8900-objs := wm8900.o snd-soc-wm8903-objs := wm8903.o +snd-soc-wm8940-objs := wm8940.o snd-soc-wm8960-objs := wm8960.o snd-soc-wm8971-objs := wm8971.o snd-soc-wm8988-objs := wm8988.o @@ -57,6 +58,7 @@ obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o +obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c new file mode 100644 index 0000000..26987dc --- /dev/null +++ b/sound/soc/codecs/wm8940.c @@ -0,0 +1,955 @@ +/* + * wm8940.c -- WM8940 ALSA Soc Audio driver + * + * Author: Jonathan Cameron jic23@cam.ac.uk + * + * Based on wm8510.c + * Copyright 2006 Wolfson Microelectronics PLC. + * Author: Liam Girdwood lrg@slimlogic.co.uk + * + * 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. + * + * Not currently handled: + * Notch filter control + * AUXMode (inverting vs mixer) + * No means to obtain current gain if alc enabled. + * No use made of gpio + * Fast VMID discharge for power down + * Soft Start + * DLR and ALR Swaps not enabled + * Digital Sidetone not supported + */ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "wm8940.h" + +struct wm8940_priv { + unsigned int sysclk; + u16 reg_cache[WM8940_CACHEREGNUM]; + struct snd_soc_codec codec; +}; + +static u16 wm8940_reg_defaults[] = { + 0x8940, /* Soft Reset */ + 0x0000, /* Power 1 */ + 0x0000, /* Power 2 */ + 0x0000, /* Power 3 */ + 0x0010, /* Interface Control */ + 0x0000, /* Companding Control */ + 0x0140, /* Clock Control */ + 0x0000, /* Additional Controls */ + 0x0000, /* GPIO Control */ + 0x0002, /* Auto Increment Control */ + 0x0000, /* DAC Control */ + 0x00FF, /* DAC Volume */ + 0, + 0, + 0x0100, /* ADC Control */ + 0x00FF, /* ADC Volume */ + 0x0000, /* Notch Filter 1 Control 1 */ + 0x0000, /* Notch Filter 1 Control 2 */ + 0x0000, /* Notch Filter 2 Control 1 */ + 0x0000, /* Notch Filter 2 Control 2 */ + 0x0000, /* Notch Filter 3 Control 1 */ + 0x0000, /* Notch Filter 3 Control 2 */ + 0x0000, /* Notch Filter 4 Control 1 */ + 0x0000, /* Notch Filter 4 Control 2 */ + 0x0032, /* DAC Limit Control 1 */ + 0x0000, /* DAC Limit Control 2 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0x0038, /* ALC Control 1 */ + 0x000B, /* ALC Control 2 */ + 0x0032, /* ALC Control 3 */ + 0x0000, /* Noise Gate */ + 0x0041, /* PLLN */ + 0x000C, /* PLLK1 */ + 0x0093, /* PLLK2 */ + 0x00E9, /* PLLK3 */ + 0, + 0, + 0x0030, /* ALC Control 4 */ + 0, + 0x0002, /* Input Control */ + 0x0050, /* PGA Gain */ + 0, + 0x0002, /* ADC Boost Control */ + 0, + 0x0002, /* Output Control */ + 0x0000, /* Speaker Mixer Control */ + 0, + 0, + 0, + 0x0079, /* Speaker Volume */ + 0, + 0x0000, /* Mono Mixer Control */ +}; + +static inline unsigned int wm8940_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + + if (reg >= ARRAY_SIZE(wm8940_reg_defaults)) + return -1; + + return cache[reg]; +} + +static inline int wm8940_write_reg_cache(struct snd_soc_codec *codec, + u16 reg, unsigned int value) +{ + u16 *cache = codec->reg_cache; + + if (reg >= ARRAY_SIZE(wm8940_reg_defaults)) + return -1; + + cache[reg] = value; + + return 0; +} + +static int wm8940_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + int ret; + u8 data[3] = { reg, + (value & 0xff00) >> 8, + (value & 0x00ff) + }; + + wm8940_write_reg_cache(codec, reg, value); + + ret = codec->hw_write(codec->control_data, data, 3); + + if (ret < 0) + return ret; + else if (ret != 3) + return -EIO; + return 0; +} + +static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" }; +static const struct soc_enum wm8940_adc_companding_enum += SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 1, 4, wm8940_companding); +static const struct soc_enum wm8940_dac_companding_enum += SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 3, 4, wm8940_companding); + +static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"}; +static const struct soc_enum wm8940_alc_mode_enum += SOC_ENUM_SINGLE(WM8940_ALC3, 8, 2, wm8940_alc_mode_text); + +static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"}; +static const struct soc_enum wm8940_mic_bias_level_enum += SOC_ENUM_SINGLE(WM8940_INPUTCTL, 8, 2, wm8940_mic_bias_level_text); + +static const char *wm8940_filter_mode_text[] = {"Audio", "Application"}; +static const struct soc_enum wm8940_filter_mode_enum += SOC_ENUM_SINGLE(WM8940_ADC, 7, 2, wm8940_filter_mode_text); + +DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1); +DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0); +DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0); +DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0); +DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0); +DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0); +DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0); +DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0); +DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1); +DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0); + +static const struct snd_kcontrol_new wm8940_snd_controls[] = { + SOC_SINGLE("Digital Loopback Switch", WM8940_COMPANDINGCTL, + 6, 1, 0), + SOC_ENUM("DAC Companding", wm8940_dac_companding_enum), + SOC_ENUM("ADC Companding", wm8940_adc_companding_enum), + + SOC_ENUM("ALC Mode", wm8940_alc_mode_enum), + SOC_SINGLE("ALC Switch", WM8940_ALC1, 8, 1, 0), + SOC_SINGLE_TLV("ALC Capture Max Gain", WM8940_ALC1, + 3, 7, 1, wm8940_alc_max_tlv), + SOC_SINGLE_TLV("ALC Capture Min Gain", WM8940_ALC1, + 0, 7, 0, wm8940_alc_min_tlv), + SOC_SINGLE_TLV("ALC Capture Target", WM8940_ALC2, + 0, 14, 0, wm8940_alc_tar_tlv), + SOC_SINGLE("ALC Capture Hold", WM8940_ALC2, 4, 10, 0), + SOC_SINGLE("ALC Capture Decay", WM8940_ALC3, 4, 10, 0), + SOC_SINGLE("ALC Capture Attach", WM8940_ALC3, 0, 10, 0), + SOC_SINGLE("ALC ZC Switch", WM8940_ALC4, 1, 1, 0), + SOC_SINGLE("ALC Capture Noise Gate Switch", WM8940_NOISEGATE, + 3, 1, 0), + SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8940_NOISEGATE, + 0, 7, 0), + + SOC_SINGLE("DAC Playback Limiter Switch", WM8940_DACLIM1, 8, 1, 0), + SOC_SINGLE("DAC Playback Limiter Attack", WM8940_DACLIM1, 0, 9, 0), + SOC_SINGLE("DAC Playback Limiter Decay", WM8940_DACLIM1, 4, 11, 0), + SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8940_DACLIM2, + 4, 9, 1, wm8940_lim_thresh_tlv), + SOC_SINGLE_TLV("DAC Playback Limiter Boost", WM8940_DACLIM2, + 0, 12, 0, wm8940_lim_boost_tlv), + + SOC_SINGLE("Capture PGA ZC Switch", WM8940_PGAGAIN, 7, 1, 0), + SOC_SINGLE_TLV("Capture PGA Volume", WM8940_PGAGAIN, + 0, 63, 0, wm8940_pga_vol_tlv), + SOC_SINGLE_TLV("Digital Playback Volume", WM8940_DACVOL, + 0, 255, 0, wm8940_adc_tlv), + SOC_SINGLE_TLV("Digital Capture Volume", WM8940_ADCVOL, + 0, 255, 0, wm8940_adc_tlv), + SOC_ENUM("Mic Bias Level", wm8940_mic_bias_level_enum), + SOC_SINGLE_TLV("Capture Boost Volue", WM8940_ADCBOOST, + 8, 1, 0, wm8940_capture_boost_vol_tlv), + SOC_SINGLE_TLV("Speaker Playback Volume", WM8940_SPKVOL, + 0, 63, 0, wm8940_spk_vol_tlv), + SOC_SINGLE("Speaker Playback Switch", WM8940_SPKVOL, 6, 1, 1), + + SOC_SINGLE_TLV("Speaker Mixer Line Bypass Volume", WM8940_SPKVOL, + 8, 1, 1, wm8940_att_tlv), + SOC_SINGLE("Speaker Playback ZC Switch", WM8940_SPKVOL, 7, 1, 0), + + SOC_SINGLE("Mono Out Switch", WM8940_MONOMIX, 6, 1, 1), + SOC_SINGLE_TLV("Mono Mixer Line Bypass Volume", WM8940_MONOMIX, + 7, 1, 1, wm8940_att_tlv), + + SOC_SINGLE("High Pass Filter Switch", WM8940_ADC, 8, 1, 0), + SOC_ENUM("High Pass Filter Mode", wm8940_filter_mode_enum), + SOC_SINGLE("High Pass Filter Cut Off", WM8940_ADC, 4, 7, 0), + SOC_SINGLE("ADC Inversion Switch", WM8940_ADC, 0, 1, 0), + SOC_SINGLE("DAC Inversion Switch", WM8940_DAC, 0, 1, 0), + SOC_SINGLE("DAC Auto Mute Switch", WM8940_DAC, 2, 1, 0), + SOC_SINGLE("ZC Timeout Clock Switch", WM8940_ADDCNTRL, 0, 1, 0), +}; + +static const struct snd_kcontrol_new wm8940_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_SPKMIX, 1, 1, 0), + SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_SPKMIX, 5, 1, 0), + SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_SPKMIX, 0, 1, 0), +}; + +static const struct snd_kcontrol_new wm8940_mono_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_MONOMIX, 1, 1, 0), + SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_MONOMIX, 2, 1, 0), + SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_MONOMIX, 0, 1, 0), +}; + +DECLARE_TLV_DB_SCALE(wm8940_boost_vol_tlv, -1500, 300, 1); +static const struct snd_kcontrol_new wm8940_input_boost_controls[] = { + SOC_DAPM_SINGLE("Mic PGA Switch", WM8940_PGAGAIN, 6, 1, 1), + SOC_DAPM_SINGLE_TLV("Aux Volume", WM8940_ADCBOOST, + 0, 7, 0, wm8940_boost_vol_tlv), + SOC_DAPM_SINGLE_TLV("Mic Volume", WM8940_ADCBOOST, + 4, 7, 0, wm8940_boost_vol_tlv), +}; + +static const struct snd_kcontrol_new wm8940_micpga_controls[] = { + SOC_DAPM_SINGLE("AUX Switch", WM8940_INPUTCTL, 2, 1, 0), + SOC_DAPM_SINGLE("MICP Switch", WM8940_INPUTCTL, 0, 1, 0), + SOC_DAPM_SINGLE("MICN Switch", WM8940_INPUTCTL, 1, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Speaker Mixer", WM8940_POWER3, 2, 0, + &wm8940_speaker_mixer_controls[0], + ARRAY_SIZE(wm8940_speaker_mixer_controls)), + SND_SOC_DAPM_MIXER("Mono Mixer", WM8940_POWER3, 3, 0, + &wm8940_mono_mixer_controls[0], + ARRAY_SIZE(wm8940_mono_mixer_controls)), + SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8940_POWER3, 0, 0), + + SND_SOC_DAPM_PGA("SpkN Out", WM8940_POWER3, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("SpkP Out", WM8940_POWER3, 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono Out", WM8940_POWER3, 7, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("MONOOUT"), + SND_SOC_DAPM_OUTPUT("SPKOUTP"), + SND_SOC_DAPM_OUTPUT("SPKOUTN"), + + SND_SOC_DAPM_PGA("Aux Input", WM8940_POWER1, 6, 0, NULL, 0), + SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8940_POWER2, 0, 0), + SND_SOC_DAPM_MIXER("Mic PGA", WM8940_POWER2, 2, 0, + &wm8940_micpga_controls[0], + ARRAY_SIZE(wm8940_micpga_controls)), + SND_SOC_DAPM_MIXER("Boost Mixer", WM8940_POWER2, 4, 0, + &wm8940_input_boost_controls[0], + ARRAY_SIZE(wm8940_input_boost_controls)), + SND_SOC_DAPM_MICBIAS("Mic Bias", WM8940_POWER1, 4, 0), + + SND_SOC_DAPM_INPUT("MICN"), + SND_SOC_DAPM_INPUT("MICP"), + SND_SOC_DAPM_INPUT("AUX"), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Mono output mixer */ + {"Mono Mixer", "PCM Playback Switch", "DAC"}, + {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, + {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"}, + + /* Speaker output mixer */ + {"Speaker Mixer", "PCM Playback Switch", "DAC"}, + {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, + {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, + + /* Outputs */ + {"Mono Out", NULL, "Mono Mixer"}, + {"MONOOUT", NULL, "Mono Out"}, + {"SpkN Out", NULL, "Speaker Mixer"}, + {"SpkP Out", NULL, "Speaker Mixer"}, + {"SPKOUTN", NULL, "SpkN Out"}, + {"SPKOUTP", NULL, "SpkP Out"}, + + /* Microphone PGA */ + {"Mic PGA", "MICN Switch", "MICN"}, + {"Mic PGA", "MICP Switch", "MICP"}, + {"Mic PGA", "AUX Switch", "AUX"}, + + /* Boost Mixer */ + {"Boost Mixer", "Mic PGA Switch", "Mic PGA"}, + {"Boost Mixer", "Mic Volume", "MICP"}, + {"Boost Mixer", "Aux Volume", "Aux Input"}, + + {"ADC", NULL, "Boost Mixer"}, +}; + +static int wm8940_add_widgets(struct snd_soc_codec *codec) +{ + int ret; + + ret = snd_soc_dapm_new_controls(codec, wm8940_dapm_widgets, + ARRAY_SIZE(wm8940_dapm_widgets)); + if (ret) + goto error_ret; + ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + if (ret) + goto error_ret; + ret = snd_soc_dapm_new_widgets(codec); + +error_ret: + return ret; +} + +#define wm8940_reset(c) wm8940_write(c, WM8940_SOFTRESET, 0); + +static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface = wm8940_read_reg_cache(codec, WM8940_IFACE) & 0xFE67; + u16 clk = wm8940_read_reg_cache(codec, WM8940_CLOCK) & 0x1fe; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + clk |= 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + wm8940_write(codec, WM8940_CLOCK, clk); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= (2 << 3); + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= (1 << 3); + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= (3 << 3); + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= (3 << 3) | (1 << 7); + break; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= (1 << 7); + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= (1 << 8); + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= (1 << 8) | (1 << 7); + break; + } + + wm8940_write(codec, WM8940_IFACE, iface); + + return 0; +} + +static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + u16 iface = wm8940_read_reg_cache(codec, WM8940_IFACE) & 0xFD9F; + u16 addcntrl = wm8940_read_reg_cache(codec, WM8940_ADDCNTRL) & 0xFFF1; + u16 companding = wm8940_read_reg_cache(codec, + WM8940_COMPANDINGCTL) & 0xFFDF; + int ret; + + /* LoutR control */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE + && params_channels(params) == 2) + iface |= (1 << 9); + + switch (params_rate(params)) { + case SNDRV_PCM_RATE_8000: + addcntrl |= (0x5 << 1); + break; + case SNDRV_PCM_RATE_11025: + addcntrl |= (0x4 << 1); + break; + case SNDRV_PCM_RATE_16000: + addcntrl |= (0x3 << 1); + break; + case SNDRV_PCM_RATE_22050: + addcntrl |= (0x2 << 1); + break; + case SNDRV_PCM_RATE_32000: + addcntrl |= (0x1 << 1); + break; + case SNDRV_PCM_RATE_44100: + case SNDRV_PCM_RATE_48000: + break; + } + ret = wm8940_write(codec, WM8940_ADDCNTRL, addcntrl); + if (ret) + goto error_ret; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + companding = companding | (1 << 5); + break; + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= (1 << 5); + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= (2 << 5); + break; + case SNDRV_PCM_FORMAT_S32_LE: + iface |= (3 << 5); + break; + } + ret = wm8940_write(codec, WM8940_COMPANDINGCTL, companding); + if (ret) + goto error_ret; + ret = wm8940_write(codec, WM8940_IFACE, iface); + +error_ret: + return ret; +} + +static int wm8940_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u16 mute_reg = wm8940_read_reg_cache(codec, WM8940_DAC) & 0xffbf; + + if (mute) + mute_reg |= 0x40; + + return wm8940_write(codec, WM8940_DAC, mute_reg); +} + +static int wm8940_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u16 val; + u16 pwr_reg = wm8940_read_reg_cache(codec, WM8940_POWER1) & 0x1F0; + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + /* ensure bufioen and biasen */ + pwr_reg |= (1 << 2) | (1 << 3); + /* Enable thermal shutdown */ + val = wm8940_read_reg_cache(codec, WM8940_OUTPUTCTL); + ret = wm8940_write(codec, WM8940_OUTPUTCTL, val | 0x2); + if (ret) + break; + /* set vmid to 75k */ + ret = wm8940_write(codec, WM8940_POWER1, pwr_reg | 0x1); + break; + case SND_SOC_BIAS_PREPARE: + /* ensure bufioen and biasen */ + pwr_reg |= (1 << 2) | (1 << 3); + ret = wm8940_write(codec, WM8940_POWER1, pwr_reg | 0x1); + break; + case SND_SOC_BIAS_STANDBY: + /* ensure bufioen and biasen */ + pwr_reg |= (1 << 2) | (1 << 3); + /* set vmid to 300k for standby */ + ret = wm8940_write(codec, WM8940_POWER1, pwr_reg | 0x2); + break; + case SND_SOC_BIAS_OFF: + ret = wm8940_write(codec, WM8940_POWER1, pwr_reg); + break; + } + + return ret; +} + +struct pll_ { + unsigned int pre_scale:2; + unsigned int n:4; + unsigned int k; +}; + +static struct pll_ pll_div; + +/* The size in bits of the pll divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 24) * 10) +static void pll_factors(unsigned int target, unsigned int source) +{ + unsigned long long Kpart; + unsigned int K, Ndiv, Nmod; + /* The left shift ist to avoid accuracy loss when right shifting */ + Ndiv = target / source; + + if (Ndiv > 12) { + source <<= 1; + /* Multiply by 2 */ + pll_div.pre_scale = 0; + Ndiv = target / source; + } else if (Ndiv < 3) { + source >>= 2; + /* Divide by 4 */ + pll_div.pre_scale = 3; + Ndiv = target / source; + } else if (Ndiv < 6) { + source >>= 1; + /* divide by 2 */ + pll_div.pre_scale = 2; + Ndiv = target / source; + } else + pll_div.pre_scale = 1; + + if ((Ndiv < 6) || (Ndiv > 12)) + printk(KERN_WARNING + "WM8940 N value %d outwith recommended range!d\n", + Ndiv); + + pll_div.n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div.k = K; +} + +/* Untested at the moment */ +static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, + int pll_id, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 reg; + + /* Turn off PLL */ + reg = wm8940_read_reg_cache(codec, WM8940_POWER1); + wm8940_write(codec, WM8940_POWER1, reg & 0x1df); + + if (freq_in == 0 || freq_out == 0) { + /* Clock CODEC directly from MCLK */ + reg = wm8940_read_reg_cache(codec, WM8940_CLOCK); + wm8940_write(codec, WM8940_CLOCK, reg & 0x0ff); + /* Pll power down */ + wm8940_write(codec, WM8940_PLLN, (1 << 7)); + return 0; + } + + /* Pll is followed by a frequency divide by 4 */ + pll_factors(freq_out*4, freq_in); + if (pll_div.k) + wm8940_write(codec, WM8940_PLLN, + (pll_div.pre_scale << 4) | pll_div.n | (1 << 6)); + else /* No factional component */ + wm8940_write(codec, WM8940_PLLN, + (pll_div.pre_scale << 4) | pll_div.n); + wm8940_write(codec, WM8940_PLLK1, pll_div.k >> 18); + wm8940_write(codec, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff); + wm8940_write(codec, WM8940_PLLK3, pll_div.k & 0x1ff); + /* Enable the PLL */ + reg = wm8940_read_reg_cache(codec, WM8940_POWER1); + wm8940_write(codec, WM8940_POWER1, reg | 0x020); + + /* Run CODEC from PLL instead of MCLK */ + reg = wm8940_read_reg_cache(codec, WM8940_CLOCK); + wm8940_write(codec, WM8940_CLOCK, reg | 0x100); + + return 0; +} + +static int wm8940_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8940_priv *wm8940 = codec->private_data; + + switch (freq) { + case 11289600: + case 12000000: + case 12288000: + case 16934400: + case 18432000: + wm8940->sysclk = freq; + return 0; + } + return -EINVAL; +} + +static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 reg; + int ret = 0; + + switch (div_id) { + case WM8940_BCLKDIV: + reg = wm8940_read_reg_cache(codec, WM8940_CLOCK) & 0xFFEF3; + ret = wm8940_write(codec, WM8940_CLOCK, reg | (div << 2)); + break; + case WM8940_MCLKDIV: + reg = wm8940_read_reg_cache(codec, WM8940_CLOCK) & 0xFF1F; + ret = wm8940_write(codec, WM8940_CLOCK, reg | (div << 5)); + break; + case WM8940_OPCLKDIV: + reg = wm8940_read_reg_cache(codec, WM8940_ADDCNTRL) & 0xFFCF; + ret = wm8940_write(codec, WM8940_ADDCNTRL, reg | (div << 4)); + break; + } + return ret; +} + +#define WM8940_RATES SNDRV_PCM_RATE_8000_48000 + +#define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops wm8940_dai_ops = { + .hw_params = wm8940_i2s_hw_params, + .set_sysclk = wm8940_set_dai_sysclk, + .digital_mute = wm8940_mute, + .set_fmt = wm8940_set_dai_fmt, + .set_clkdiv = wm8940_set_dai_clkdiv, + .set_pll = wm8940_set_dai_pll, +}; + +struct snd_soc_dai wm8940_dai = { + .name = "WM8940", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8940_RATES, + .formats = WM8940_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8940_RATES, + .formats = WM8940_FORMATS, + }, + .ops = &wm8940_dai_ops, + .symmetric_rates = 1, +}; +EXPORT_SYMBOL_GPL(wm8940_dai); + +static int wm8940_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + return wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF); +} + +static int wm8940_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + int i; + int ret; + u8 data[3]; + u16 *cache = codec->reg_cache; + + /* Sync reg_cache with the hardware + * Could use auto incremented writes to speed this up + */ + for (i = 0; i < ARRAY_SIZE(wm8940_reg_defaults); i++) { + data[0] = i; + data[1] = (cache[i] & 0xFF00) >> 8; + data[2] = cache[i] & 0x00FF; + ret = codec->hw_write(codec->control_data, data, 3); + if (ret < 0) + goto error_ret; + else if (ret != 3) { + ret = -EIO; + goto error_ret; + } + } + ret = wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + if (ret) + goto error_ret; + ret = wm8940_set_bias_level(codec, codec->suspend_bias_level); + +error_ret: + return ret; +} + +static struct snd_soc_codec *wm8940_codec; + +static int wm8940_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + + int ret = 0; + + if (wm8940_codec == NULL) { + dev_err(&pdev->dev, "Codec device not registered\n"); + return -ENODEV; + } + + socdev->card->codec = wm8940_codec; + codec = wm8940_codec; + + mutex_init(&codec->mutex); + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + dev_err(codec->dev, "failed to create pcms: %d\n", ret); + goto pcm_err; + } + + ret = snd_soc_add_controls(codec, wm8940_snd_controls, + ARRAY_SIZE(wm8940_snd_controls)); + if (ret) + goto error_free_pcms; + ret = wm8940_add_widgets(codec); + if (ret) + goto error_free_pcms; + + ret = snd_soc_init_card(socdev); + if (ret < 0) { + dev_err(codec->dev, "failed to register card: %d\n", ret); + goto error_free_pcms; + } + + return ret; + +error_free_pcms: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + return ret; +} + +static int wm8940_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm8940 = { + .probe = wm8940_probe, + .remove = wm8940_remove, + .suspend = wm8940_suspend, + .resume = wm8940_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8940); + +static int wm8940_register(struct wm8940_priv *wm8940) +{ + struct wm8940_setup_data *pdata = wm8940->codec.dev->platform_data; + struct snd_soc_codec *codec = &wm8940->codec; + int ret; + u16 reg; + if (wm8940_codec) { + dev_err(codec->dev, "Another WM8940 is registered\n"); + return -EINVAL; + } + + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + codec->private_data = wm8940; + codec->name = "WM8940"; + codec->owner = THIS_MODULE; + codec->read = wm8940_read_reg_cache; + codec->write = wm8940_write; + codec->bias_level = SND_SOC_BIAS_OFF; + codec->set_bias_level = wm8940_set_bias_level; + codec->dai = &wm8940_dai; + codec->num_dai = 1; + codec->reg_cache_size = ARRAY_SIZE(wm8940_reg_defaults); + codec->reg_cache = &wm8940->reg_cache; + + memcpy(codec->reg_cache, wm8940_reg_defaults, + sizeof(wm8940_reg_defaults)); + + ret = wm8940_reset(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to issue reset\n"); + return ret; + } + + wm8940_dai.dev = codec->dev; + + wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + ret = wm8940_write(codec, WM8940_POWER1, 0x180); + if (ret < 0) + return ret; + + if (!pdata) + dev_warn(codec->dev, "No platform data supplied\n"); + else { + reg = wm8940_read_reg_cache(codec, WM8940_OUTPUTCTL); + ret = wm8940_write(codec, WM8940_OUTPUTCTL, reg | pdata->vroi); + if (ret < 0) + return ret; + } + + + wm8940_codec = codec; + + ret = snd_soc_register_codec(codec); + if (ret) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + return ret; + } + + ret = snd_soc_register_dai(&wm8940_dai); + if (ret) { + dev_err(codec->dev, "Failed to register DAI: %d\n", ret); + snd_soc_unregister_codec(codec); + return ret; + } + + return 0; +} + +static void wm8940_unregister(struct wm8940_priv *wm8940) +{ + wm8940_set_bias_level(&wm8940->codec, SND_SOC_BIAS_OFF); + snd_soc_unregister_dai(&wm8940_dai); + snd_soc_unregister_codec(&wm8940->codec); + kfree(wm8940); + wm8940_codec = NULL; +} + +static int wm8940_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm8940_priv *wm8940; + struct snd_soc_codec *codec; + + wm8940 = kzalloc(sizeof *wm8940, GFP_KERNEL); + if (wm8940 == NULL) + return -ENOMEM; + + codec = &wm8940->codec; + codec->hw_write = (hw_write_t)i2c_master_send; + i2c_set_clientdata(i2c, wm8940); + codec->control_data = i2c; + codec->dev = &i2c->dev; + + return wm8940_register(wm8940); +} + +static int wm8940_i2c_remove(struct i2c_client *client) +{ + struct wm8940_priv *wm8940 = i2c_get_clientdata(client); + + wm8940_unregister(wm8940); + + return 0; +} + +static const struct i2c_device_id wm8940_i2c_id[] = { + { "wm8940", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id); + +static struct i2c_driver wm8940_i2c_driver = { + .driver = { + .name = "WM8940 I2C Codec", + .owner = THIS_MODULE, + }, + .probe = wm8940_i2c_probe, + .remove = __devexit_p(wm8940_i2c_remove), + .id_table = wm8940_i2c_id, +}; + +static int __init wm8940_modinit(void) +{ + int ret; + + ret = i2c_add_driver(&wm8940_i2c_driver); + if (ret) + printk(KERN_ERR "Failed to register WM8940 I2C driver: %d\n", + ret); + return ret; +} +module_init(wm8940_modinit); + +static void __exit wm8940_exit(void) +{ + i2c_del_driver(&wm8940_i2c_driver); +} +module_exit(wm8940_exit); + +MODULE_DESCRIPTION("ASoC WM8940 driver"); +MODULE_AUTHOR("Jonathan Cameron"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8940.h b/sound/soc/codecs/wm8940.h new file mode 100644 index 0000000..8410eed --- /dev/null +++ b/sound/soc/codecs/wm8940.h @@ -0,0 +1,104 @@ +/* + * wm8940.h -- WM8940 Soc Audio driver + * + * 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 _WM8940_H +#define _WM8940_H + +struct wm8940_setup_data { + /* Vref to analogue output resistance */ +#define WM8940_VROI_1K 0 +#define WM8940_VROI_30K 1 + unsigned int vroi:1; +}; +extern struct snd_soc_dai wm8940_dai; +extern struct snd_soc_codec_device soc_codec_dev_wm8940; + +/* WM8940 register space */ +#define WM8940_SOFTRESET 0x00 +#define WM8940_POWER1 0x01 +#define WM8940_POWER2 0x02 +#define WM8940_POWER3 0x03 +#define WM8940_IFACE 0x04 +#define WM8940_COMPANDINGCTL 0x05 +#define WM8940_CLOCK 0x06 +#define WM8940_ADDCNTRL 0x07 +#define WM8940_GPIO 0x08 +#define WM8940_CTLINT 0x09 +#define WM8940_DAC 0x0A +#define WM8940_DACVOL 0x0B + +#define WM8940_ADC 0x0E +#define WM8940_ADCVOL 0x0F +#define WM8940_NOTCH1 0x10 +#define WM8940_NOTCH2 0x11 +#define WM8940_NOTCH3 0x12 +#define WM8940_NOTCH4 0x13 +#define WM8940_NOTCH5 0x14 +#define WM8940_NOTCH6 0x15 +#define WM8940_NOTCH7 0x16 +#define WM8940_NOTCH8 0x17 +#define WM8940_DACLIM1 0x18 +#define WM8940_DACLIM2 0x19 + +#define WM8940_ALC1 0x20 +#define WM8940_ALC2 0x21 +#define WM8940_ALC3 0x22 +#define WM8940_NOISEGATE 0x23 +#define WM8940_PLLN 0x24 +#define WM8940_PLLK1 0x25 +#define WM8940_PLLK2 0x26 +#define WM8940_PLLK3 0x27 + +#define WM8940_ALC4 0x2A + +#define WM8940_INPUTCTL 0x2C +#define WM8940_PGAGAIN 0x2D + +#define WM8940_ADCBOOST 0x2F + +#define WM8940_OUTPUTCTL 0x31 +#define WM8940_SPKMIX 0x32 + +#define WM8940_SPKVOL 0x36 + +#define WM8940_MONOMIX 0x38 + +#define WM8940_CACHEREGNUM 0x57 + + +/* Clock divider Id's */ +#define WM8940_BCLKDIV 0 +#define WM8940_MCLKDIV 1 +#define WM8940_OPCLKDIV 2 + +/* MCLK clock dividers */ +#define WM8940_MCLKDIV_1 0 +#define WM8940_MCLKDIV_1_5 1 +#define WM8940_MCLKDIV_2 2 +#define WM8940_MCLKDIV_3 3 +#define WM8940_MCLKDIV_4 4 +#define WM8940_MCLKDIV_6 5 +#define WM8940_MCLKDIV_8 6 +#define WM8940_MCLKDIV_12 7 + +/* BCLK clock dividers */ +#define WM8940_BCLKDIV_1 0 +#define WM8940_BCLKDIV_2 1 +#define WM8940_BCLKDIV_4 2 +#define WM8940_BCLKDIV_8 3 +#define WM8940_BCLKDIV_16 4 +#define WM8940_BCLKDIV_32 5 + +/* PLL Out Dividers */ +#define WM8940_OPCLKDIV_1 0 +#define WM8940_OPCLKDIV_2 1 +#define WM8940_OPCLKDIV_3 2 +#define WM8940_OPCLKDIV_4 3 + +#endif /* _WM8940_H */ +
From: Daniel Mack daniel@caiaq.de
The control modifies the MUTE register, hence the polarity must be inverted.
Signed-off-by: Daniel Mack daniel@caiaq.de Acked-By: Timur Tabi timur@freescale.com Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/cs4270.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 7fa09a3..3c34fe6 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -486,7 +486,7 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = { SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0), SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1), SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0), - SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 0) + SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1) };
/*
From: Daniel Mack daniel@caiaq.de
This adds a new control named 'Master Playback Switch' for cs4270 codecs. It is implemented using the new SOC_DOUBLE_EXT macro to catch the put function and store the information about manually set mute controls from userspace. When a manual mute is set, we don't want the soc core to un-mute the outputs.
Renamed cs4270_mute() to cs4270_dai_mute() to avoid confusion.
Signed-off-by: Daniel Mack daniel@caiaq.de Acked-by: Timur Tabi timur@freescale.com Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/cs4270.c | 44 +++++++++++++++++++++++++++++++++++++++----- 1 files changed, 39 insertions(+), 5 deletions(-)
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 3c34fe6..ece6ed6 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -109,6 +109,7 @@ struct cs4270_private { unsigned int mclk; /* Input frequency of the MCLK pin */ unsigned int mode; /* The mode (I2S or left-justified) */ unsigned int slave_mode; + unsigned int manual_mute; };
/** @@ -453,7 +454,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream, }
/** - * cs4270_mute - enable/disable the CS4270 external mute + * cs4270_dai_mute - enable/disable the CS4270 external mute * @dai: the SOC DAI * @mute: 0 = disable mute, 1 = enable mute * @@ -462,21 +463,52 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream, * board does not have the MUTEA or MUTEB pins connected to such circuitry, * then this function will do nothing. */ -static int cs4270_mute(struct snd_soc_dai *dai, int mute) +static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; + struct cs4270_private *cs4270 = codec->private_data; int reg6;
reg6 = snd_soc_read(codec, CS4270_MUTE);
if (mute) reg6 |= CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B; - else + else { reg6 &= ~(CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B); + reg6 |= cs4270->manual_mute; + }
return snd_soc_write(codec, CS4270_MUTE, reg6); }
+/** + * cs4270_soc_put_mute - put callback for the 'Master Playback switch' + * alsa control. + * @kcontrol: mixer control + * @ucontrol: control element information + * + * This function basically passes the arguments on to the generic + * snd_soc_put_volsw() function and saves the mute information in + * our private data structure. This is because we want to prevent + * cs4270_dai_mute() neglecting the user's decision to manually + * mute the codec's output. + * + * Returns 0 for success. + */ +static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs4270_private *cs4270 = codec->private_data; + int left = !ucontrol->value.integer.value[0]; + int right = !ucontrol->value.integer.value[1]; + + cs4270->manual_mute = (left ? CS4270_MUTE_DAC_A : 0) | + (right ? CS4270_MUTE_DAC_B : 0); + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + /* A list of non-DAPM controls that the CS4270 supports */ static const struct snd_kcontrol_new cs4270_snd_controls[] = { SOC_DOUBLE_R("Master Playback Volume", @@ -486,7 +518,9 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = { SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0), SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1), SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0), - SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1) + SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1), + SOC_DOUBLE_EXT("Master Playback Switch", CS4270_MUTE, 0, 1, 1, 1, + snd_soc_get_volsw, cs4270_soc_put_mute), };
/* @@ -506,7 +540,7 @@ static struct snd_soc_dai_ops cs4270_dai_ops = { .hw_params = cs4270_hw_params, .set_sysclk = cs4270_set_dai_sysclk, .set_fmt = cs4270_set_dai_fmt, - .digital_mute = cs4270_mute, + .digital_mute = cs4270_dai_mute, };
struct snd_soc_dai cs4270_dai = {
We need to check only if the WM8350 is master and only when starting the stream so if either is not true then we can skip the check.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8350.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 3b1d099..0275321 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -968,7 +968,7 @@ static int wm8350_pcm_trigger(struct snd_pcm_substream *substream, * required for LRC in master mode. The DACs or ADCs need a * valid audio path i.e. pin -> ADC or DAC -> pin before * the LRC will be enabled in master mode. */ - if (!master && cmd != SNDRV_PCM_TRIGGER_START) + if (!master || cmd != SNDRV_PCM_TRIGGER_START) return 0;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
From: Jon Smirl jonsmirl@gmail.com
Signed-off-by: Jon Smirl jonsmirl@gmail.com Acked-by: Grant Likely grant.likely@secretlab.ca Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/fsl/Kconfig | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 9fc9082..e7dd79a 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -21,7 +21,7 @@ config SND_SOC_MPC8610_HPCD
config SND_SOC_MPC5200_I2S tristate "Freescale MPC5200 PSC in I2S mode driver" - depends on PPC_MPC52xx && PPC_BESTCOMM + depends on PPC_MPC52xx && PPC_BESTCOMM && BROKEN select SND_SOC_OF_SIMPLE select PPC_BESTCOMM_GEN_BD help
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8940.c | 22 +++++++++++----------- 1 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 26987dc..a66dacc 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -168,16 +168,16 @@ static const char *wm8940_filter_mode_text[] = {"Audio", "Application"}; static const struct soc_enum wm8940_filter_mode_enum = SOC_ENUM_SINGLE(WM8940_ADC, 7, 2, wm8940_filter_mode_text);
-DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1); -DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0); -DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0); -DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0); -DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0); -DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0); -DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0); -DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0); -DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1); -DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0); +static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1); +static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0); +static DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0); +static DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0); +static DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0); +static DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0); +static DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0); +static DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1); +static DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0);
static const struct snd_kcontrol_new wm8940_snd_controls[] = { SOC_SINGLE("Digital Loopback Switch", WM8940_COMPANDINGCTL, @@ -253,7 +253,7 @@ static const struct snd_kcontrol_new wm8940_mono_mixer_controls[] = { SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_MONOMIX, 0, 1, 0), };
-DECLARE_TLV_DB_SCALE(wm8940_boost_vol_tlv, -1500, 300, 1); +static DECLARE_TLV_DB_SCALE(wm8940_boost_vol_tlv, -1500, 300, 1); static const struct snd_kcontrol_new wm8940_input_boost_controls[] = { SOC_DAPM_SINGLE("Mic PGA Switch", WM8940_PGAGAIN, 6, 1, 1), SOC_DAPM_SINGLE_TLV("Aux Volume", WM8940_ADCBOOST,
From: Joonyoung Shim jy0922.shim@samsung.com
The gain control for earpiece amplifier uses 0dB ~ 12dB according to the TRM, but the present code is implemented to -6dB ~ 6dB.
Signed-off-by: Joonyoung Shim jy0922.shim@samsung.com Acked-by: Peter Ujfalusi peter.ujfalusi@nokia.com Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/twl4030.c | 8 +++++++- 1 files changed, 7 insertions(+), 1 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 921b205..df7c8c2 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -836,6 +836,12 @@ static DECLARE_TLV_DB_SCALE(analog_tlv, -2400, 200, 0); static DECLARE_TLV_DB_SCALE(output_tvl, -1200, 600, 1);
/* + * Gain control for earpiece amplifier + * 0 dB to 12 dB in 6 dB steps (mute instead of -6) + */ +static DECLARE_TLV_DB_SCALE(output_ear_tvl, -600, 600, 1); + +/* * Capture gain after the ADCs * from 0 dB to 31 dB in 1 dB steps */ @@ -900,7 +906,7 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { 4, 3, 0, output_tvl),
SOC_SINGLE_TLV_TWL4030("Earpiece Playback Volume", - TWL4030_REG_EAR_CTL, 4, 3, 0, output_tvl), + TWL4030_REG_EAR_CTL, 4, 3, 0, output_ear_tvl),
/* Common capture gain controls */ SOC_DOUBLE_R_TLV("TX1 Digital Capture Volume",
From: Jon Smirl jonsmirl@gmail.com
Basic split of mpc5200 DMA code out from i2s into a standalone file.
[Fixed trailing whitespace -- broonie.]
Signed-off-by: Jon Smirl jonsmirl@gmail.com Acked-by: Grant Likely grant.likely@secretlab.ca Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/fsl/Kconfig | 4 + sound/soc/fsl/Makefile | 2 + sound/soc/fsl/mpc5200_dma.c | 457 ++++++++++++++++++++++++++++++++++++ sound/soc/fsl/mpc5200_dma.h | 81 +++++++ sound/soc/fsl/mpc5200_psc_i2s.c | 484 +-------------------------------------- 5 files changed, 546 insertions(+), 482 deletions(-) create mode 100644 sound/soc/fsl/mpc5200_dma.c create mode 100644 sound/soc/fsl/mpc5200_dma.h
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index e7dd79a..d94745b 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,6 +1,9 @@ config SND_SOC_OF_SIMPLE tristate
+config SND_MPC52xx_DMA + tristate + # ASoC platform support for the Freescale MPC8610 SOC. This compiles drivers # for the SSI and the Elo DMA controller. You will still need to select # a platform driver and a codec driver. @@ -23,6 +26,7 @@ config SND_SOC_MPC5200_I2S tristate "Freescale MPC5200 PSC in I2S mode driver" depends on PPC_MPC52xx && PPC_BESTCOMM && BROKEN select SND_SOC_OF_SIMPLE + select SND_MPC52xx_DMA select PPC_BESTCOMM_GEN_BD help Say Y here to support the MPC5200 PSCs in I2S mode. diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index f85134c..7731ef2 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -10,5 +10,7 @@ snd-soc-fsl-ssi-objs := fsl_ssi.o snd-soc-fsl-dma-objs := fsl_dma.o obj-$(CONFIG_SND_SOC_MPC8610) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o
+# MPC5200 Platform Support +obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c new file mode 100644 index 0000000..cccaff4 --- /dev/null +++ b/sound/soc/fsl/mpc5200_dma.c @@ -0,0 +1,457 @@ +/* + * Freescale MPC5200 PSC DMA + * ALSA SoC Platform driver + * + * Copyright (C) 2008 Secret Lab Technologies Ltd. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/dma-mapping.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> +#include <sound/soc-of-simple.h> + +#include <sysdev/bestcomm/bestcomm.h> +#include <sysdev/bestcomm/gen_bd.h> +#include <asm/mpc52xx_psc.h> + +#include "mpc5200_dma.h" + +MODULE_AUTHOR("Grant Likely grant.likely@secretlab.ca"); +MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver"); +MODULE_LICENSE("GPL"); + +/* + * Interrupt handlers + */ +static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s) +{ + struct psc_i2s *psc_i2s = _psc_i2s; + struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs; + u16 isr; + + isr = in_be16(®s->mpc52xx_psc_isr); + + /* Playback underrun error */ + if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) + psc_i2s->stats.underrun_count++; + + /* Capture overrun error */ + if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) + psc_i2s->stats.overrun_count++; + + out_8(®s->command, 4 << 4); /* reset the error status */ + + return IRQ_HANDLED; +} + +/** + * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer + * @s: pointer to stream private data structure + * + * Enqueues another audio period buffer into the bestcomm queue. + * + * Note: The routine must only be called when there is space available in + * the queue. Otherwise the enqueue will fail and the audio ring buffer + * will get out of sync + */ +static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s) +{ + struct bcom_bd *bd; + + /* Prepare and enqueue the next buffer descriptor */ + bd = bcom_prepare_next_buffer(s->bcom_task); + bd->status = s->period_bytes; + bd->data[0] = s->period_next_pt; + bcom_submit_next_buffer(s->bcom_task, NULL); + + /* Update for next period */ + s->period_next_pt += s->period_bytes; + if (s->period_next_pt >= s->period_end) + s->period_next_pt = s->period_start; +} + +/* Bestcomm DMA irq handler */ +static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream) +{ + struct psc_i2s_stream *s = _psc_i2s_stream; + + /* For each finished period, dequeue the completed period buffer + * and enqueue a new one in it's place. */ + while (bcom_buffer_done(s->bcom_task)) { + bcom_retrieve_buffer(s->bcom_task, NULL, NULL); + s->period_current_pt += s->period_bytes; + if (s->period_current_pt >= s->period_end) + s->period_current_pt = s->period_start; + psc_i2s_bcom_enqueue_next_buffer(s); + bcom_enable(s->bcom_task); + } + + /* If the stream is active, then also inform the PCM middle layer + * of the period finished event. */ + if (s->active) + snd_pcm_period_elapsed(s->stream); + + return IRQ_HANDLED; +} + +/** + * psc_i2s_startup: create a new substream + * + * This is the first function called when a stream is opened. + * + * If this is the first stream open, then grab the IRQ and program most of + * the PSC registers. + */ +int psc_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + int rc; + + dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream); + + if (!psc_i2s->playback.active && + !psc_i2s->capture.active) { + /* Setup the IRQs */ + rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED, + "psc-i2s-status", psc_i2s); + rc |= request_irq(psc_i2s->capture.irq, + &psc_i2s_bcom_irq, IRQF_SHARED, + "psc-i2s-capture", &psc_i2s->capture); + rc |= request_irq(psc_i2s->playback.irq, + &psc_i2s_bcom_irq, IRQF_SHARED, + "psc-i2s-playback", &psc_i2s->playback); + if (rc) { + free_irq(psc_i2s->irq, psc_i2s); + free_irq(psc_i2s->capture.irq, + &psc_i2s->capture); + free_irq(psc_i2s->playback.irq, + &psc_i2s->playback); + return -ENODEV; + } + } + + return 0; +} + +int psc_i2s_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_pcm_set_runtime_buffer(substream, NULL); + return 0; +} + +/** + * psc_i2s_trigger: start and stop the DMA transfer. + * + * This function is called by ALSA to start, stop, pause, and resume the DMA + * transfer of data. + */ +int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct psc_i2s_stream *s; + struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs; + u16 imr; + u8 psc_cmd; + unsigned long flags; + + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + s = &psc_i2s->capture; + else + s = &psc_i2s->playback; + + dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)" + " stream_id=%i\n", + substream, cmd, substream->pstr->stream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + s->period_bytes = frames_to_bytes(runtime, + runtime->period_size); + s->period_start = virt_to_phys(runtime->dma_area); + s->period_end = s->period_start + + (s->period_bytes * runtime->periods); + s->period_next_pt = s->period_start; + s->period_current_pt = s->period_start; + s->active = 1; + + /* First; reset everything */ + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { + out_8(®s->command, MPC52xx_PSC_RST_RX); + out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); + } else { + out_8(®s->command, MPC52xx_PSC_RST_TX); + out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); + } + + /* Next, fill up the bestcomm bd queue and enable DMA. + * This will begin filling the PSC's fifo. */ + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + bcom_gen_bd_rx_reset(s->bcom_task); + else + bcom_gen_bd_tx_reset(s->bcom_task); + while (!bcom_queue_full(s->bcom_task)) + psc_i2s_bcom_enqueue_next_buffer(s); + bcom_enable(s->bcom_task); + + /* Due to errata in the i2s mode; need to line up enabling + * the transmitter with a transition on the frame sync + * line */ + + spin_lock_irqsave(&psc_i2s->lock, flags); + /* first make sure it is low */ + while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) + ; + /* then wait for the transition to high */ + while ((in_8(®s->ipcr_acr.ipcr) & 0x80) == 0) + ; + /* Finally, enable the PSC. + * Receiver must always be enabled; even when we only want + * transmit. (see 15.3.2.3 of MPC5200B User's Guide) */ + psc_cmd = MPC52xx_PSC_RX_ENABLE; + if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) + psc_cmd |= MPC52xx_PSC_TX_ENABLE; + out_8(®s->command, psc_cmd); + spin_unlock_irqrestore(&psc_i2s->lock, flags); + + break; + + case SNDRV_PCM_TRIGGER_STOP: + /* Turn off the PSC */ + s->active = 0; + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (!psc_i2s->playback.active) { + out_8(®s->command, 2 << 4); /* reset rx */ + out_8(®s->command, 3 << 4); /* reset tx */ + out_8(®s->command, 4 << 4); /* reset err */ + } + } else { + out_8(®s->command, 3 << 4); /* reset tx */ + out_8(®s->command, 4 << 4); /* reset err */ + if (!psc_i2s->capture.active) + out_8(®s->command, 2 << 4); /* reset rx */ + } + + bcom_disable(s->bcom_task); + while (!bcom_queue_empty(s->bcom_task)) + bcom_retrieve_buffer(s->bcom_task, NULL, NULL); + + break; + + default: + dev_dbg(psc_i2s->dev, "invalid command\n"); + return -EINVAL; + } + + /* Update interrupt enable settings */ + imr = 0; + if (psc_i2s->playback.active) + imr |= MPC52xx_PSC_IMR_TXEMP; + if (psc_i2s->capture.active) + imr |= MPC52xx_PSC_IMR_ORERR; + out_be16(®s->isr_imr.imr, imr); + + return 0; +} + +/** + * psc_i2s_shutdown: shutdown the data transfer on a stream + * + * Shutdown the PSC if there are no other substreams open. + */ +void psc_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + + dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream); + + /* + * If this is the last active substream, disable the PSC and release + * the IRQ. + */ + if (!psc_i2s->playback.active && + !psc_i2s->capture.active) { + + /* Disable all interrupts and reset the PSC */ + out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); + out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */ + out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */ + out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ + out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ + + /* Release irqs */ + free_irq(psc_i2s->irq, psc_i2s); + free_irq(psc_i2s->capture.irq, &psc_i2s->capture); + free_irq(psc_i2s->playback.irq, &psc_i2s->playback); + } +} + +/* --------------------------------------------------------------------- + * The PSC DMA 'ASoC platform' driver + * + * Can be referenced by an 'ASoC machine' driver + * This driver only deals with the audio bus; it doesn't have any + * interaction with the attached codec + */ + +static const struct snd_pcm_hardware psc_i2s_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .period_bytes_max = 1024 * 1024, + .period_bytes_min = 32, + .periods_min = 2, + .periods_max = 256, + .buffer_bytes_max = 2 * 1024 * 1024, + .fifo_size = 0, +}; + +static int psc_i2s_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + struct psc_i2s_stream *s; + + dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream); + + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + s = &psc_i2s->capture; + else + s = &psc_i2s->playback; + + snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware); + + s->stream = substream; + return 0; +} + +static int psc_i2s_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + struct psc_i2s_stream *s; + + dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream); + + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + s = &psc_i2s->capture; + else + s = &psc_i2s->playback; + + s->stream = NULL; + return 0; +} + +static snd_pcm_uframes_t +psc_i2s_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + struct psc_i2s_stream *s; + dma_addr_t count; + + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + s = &psc_i2s->capture; + else + s = &psc_i2s->playback; + + count = s->period_current_pt - s->period_start; + + return bytes_to_frames(substream->runtime, count); +} + +static struct snd_pcm_ops psc_i2s_pcm_ops = { + .open = psc_i2s_pcm_open, + .close = psc_i2s_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .pointer = psc_i2s_pcm_pointer, +}; + +static u64 psc_i2s_pcm_dmamask = 0xffffffff; +static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, + struct snd_pcm *pcm) +{ + struct snd_soc_pcm_runtime *rtd = pcm->private_data; + size_t size = psc_i2s_pcm_hardware.buffer_bytes_max; + int rc = 0; + + dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n", + card, dai, pcm); + + if (!card->dev->dma_mask) + card->dev->dma_mask = &psc_i2s_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = 0xffffffff; + + if (pcm->streams[0].substream) { + rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, + &pcm->streams[0].substream->dma_buffer); + if (rc) + goto playback_alloc_err; + } + + if (pcm->streams[1].substream) { + rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, + &pcm->streams[1].substream->dma_buffer); + if (rc) + goto capture_alloc_err; + } + + return 0; + + capture_alloc_err: + if (pcm->streams[0].substream) + snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); + playback_alloc_err: + dev_err(card->dev, "Cannot allocate buffer(s)\n"); + return -ENOMEM; +} + +static void psc_i2s_pcm_free(struct snd_pcm *pcm) +{ + struct snd_soc_pcm_runtime *rtd = pcm->private_data; + struct snd_pcm_substream *substream; + int stream; + + dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm); + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (substream) { + snd_dma_free_pages(&substream->dma_buffer); + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + } + } +} + +struct snd_soc_platform psc_i2s_pcm_soc_platform = { + .name = "mpc5200-psc-audio", + .pcm_ops = &psc_i2s_pcm_ops, + .pcm_new = &psc_i2s_pcm_new, + .pcm_free = &psc_i2s_pcm_free, +}; + diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h new file mode 100644 index 0000000..9a19e8a --- /dev/null +++ b/sound/soc/fsl/mpc5200_dma.h @@ -0,0 +1,81 @@ +/* + * Freescale MPC5200 Audio DMA driver + */ + +#ifndef __SOUND_SOC_FSL_MPC5200_DMA_H__ +#define __SOUND_SOC_FSL_MPC5200_DMA_H__ + +/** + * psc_i2s_stream - Data specific to a single stream (playback or capture) + * @active: flag indicating if the stream is active + * @psc_i2s: pointer back to parent psc_i2s data structure + * @bcom_task: bestcomm task structure + * @irq: irq number for bestcomm task + * @period_start: physical address of start of DMA region + * @period_end: physical address of end of DMA region + * @period_next_pt: physical address of next DMA buffer to enqueue + * @period_bytes: size of DMA period in bytes + */ +struct psc_i2s_stream { + int active; + struct psc_i2s *psc_i2s; + struct bcom_task *bcom_task; + int irq; + struct snd_pcm_substream *stream; + dma_addr_t period_start; + dma_addr_t period_end; + dma_addr_t period_next_pt; + dma_addr_t period_current_pt; + int period_bytes; +}; + +/** + * psc_i2s - Private driver data + * @name: short name for this device ("PSC0", "PSC1", etc) + * @psc_regs: pointer to the PSC's registers + * @fifo_regs: pointer to the PSC's FIFO registers + * @irq: IRQ of this PSC + * @dev: struct device pointer + * @dai: the CPU DAI for this device + * @sicr: Base value used in serial interface control register; mode is ORed + * with this value. + * @playback: Playback stream context data + * @capture: Capture stream context data + */ +struct psc_i2s { + char name[32]; + struct mpc52xx_psc __iomem *psc_regs; + struct mpc52xx_psc_fifo __iomem *fifo_regs; + unsigned int irq; + struct device *dev; + struct snd_soc_dai dai; + spinlock_t lock; + u32 sicr; + + /* per-stream data */ + struct psc_i2s_stream playback; + struct psc_i2s_stream capture; + + /* Statistics */ + struct { + int overrun_count; + int underrun_count; + } stats; +}; + + +int psc_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + +int psc_i2s_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + +void psc_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + +int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai); + +extern struct snd_soc_platform psc_i2s_pcm_soc_platform; + +#endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */ diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 3aa729d..8974b53 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -25,6 +25,8 @@ #include <sysdev/bestcomm/gen_bd.h> #include <asm/mpc52xx_psc.h>
+#include "mpc5200_dma.h" + MODULE_AUTHOR("Grant Likely grant.likely@secretlab.ca"); MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver"); MODULE_LICENSE("GPL"); @@ -47,179 +49,6 @@ MODULE_LICENSE("GPL"); SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \ SNDRV_PCM_FMTBIT_S32_BE)
-/** - * psc_i2s_stream - Data specific to a single stream (playback or capture) - * @active: flag indicating if the stream is active - * @psc_i2s: pointer back to parent psc_i2s data structure - * @bcom_task: bestcomm task structure - * @irq: irq number for bestcomm task - * @period_start: physical address of start of DMA region - * @period_end: physical address of end of DMA region - * @period_next_pt: physical address of next DMA buffer to enqueue - * @period_bytes: size of DMA period in bytes - */ -struct psc_i2s_stream { - int active; - struct psc_i2s *psc_i2s; - struct bcom_task *bcom_task; - int irq; - struct snd_pcm_substream *stream; - dma_addr_t period_start; - dma_addr_t period_end; - dma_addr_t period_next_pt; - dma_addr_t period_current_pt; - int period_bytes; -}; - -/** - * psc_i2s - Private driver data - * @name: short name for this device ("PSC0", "PSC1", etc) - * @psc_regs: pointer to the PSC's registers - * @fifo_regs: pointer to the PSC's FIFO registers - * @irq: IRQ of this PSC - * @dev: struct device pointer - * @dai: the CPU DAI for this device - * @sicr: Base value used in serial interface control register; mode is ORed - * with this value. - * @playback: Playback stream context data - * @capture: Capture stream context data - */ -struct psc_i2s { - char name[32]; - struct mpc52xx_psc __iomem *psc_regs; - struct mpc52xx_psc_fifo __iomem *fifo_regs; - unsigned int irq; - struct device *dev; - struct snd_soc_dai dai; - spinlock_t lock; - u32 sicr; - - /* per-stream data */ - struct psc_i2s_stream playback; - struct psc_i2s_stream capture; - - /* Statistics */ - struct { - int overrun_count; - int underrun_count; - } stats; -}; - -/* - * Interrupt handlers - */ -static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s) -{ - struct psc_i2s *psc_i2s = _psc_i2s; - struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs; - u16 isr; - - isr = in_be16(®s->mpc52xx_psc_isr); - - /* Playback underrun error */ - if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) - psc_i2s->stats.underrun_count++; - - /* Capture overrun error */ - if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) - psc_i2s->stats.overrun_count++; - - out_8(®s->command, 4 << 4); /* reset the error status */ - - return IRQ_HANDLED; -} - -/** - * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer - * @s: pointer to stream private data structure - * - * Enqueues another audio period buffer into the bestcomm queue. - * - * Note: The routine must only be called when there is space available in - * the queue. Otherwise the enqueue will fail and the audio ring buffer - * will get out of sync - */ -static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s) -{ - struct bcom_bd *bd; - - /* Prepare and enqueue the next buffer descriptor */ - bd = bcom_prepare_next_buffer(s->bcom_task); - bd->status = s->period_bytes; - bd->data[0] = s->period_next_pt; - bcom_submit_next_buffer(s->bcom_task, NULL); - - /* Update for next period */ - s->period_next_pt += s->period_bytes; - if (s->period_next_pt >= s->period_end) - s->period_next_pt = s->period_start; -} - -/* Bestcomm DMA irq handler */ -static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream) -{ - struct psc_i2s_stream *s = _psc_i2s_stream; - - /* For each finished period, dequeue the completed period buffer - * and enqueue a new one in it's place. */ - while (bcom_buffer_done(s->bcom_task)) { - bcom_retrieve_buffer(s->bcom_task, NULL, NULL); - s->period_current_pt += s->period_bytes; - if (s->period_current_pt >= s->period_end) - s->period_current_pt = s->period_start; - psc_i2s_bcom_enqueue_next_buffer(s); - bcom_enable(s->bcom_task); - } - - /* If the stream is active, then also inform the PCM middle layer - * of the period finished event. */ - if (s->active) - snd_pcm_period_elapsed(s->stream); - - return IRQ_HANDLED; -} - -/** - * psc_i2s_startup: create a new substream - * - * This is the first function called when a stream is opened. - * - * If this is the first stream open, then grab the IRQ and program most of - * the PSC registers. - */ -static int psc_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; - int rc; - - dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream); - - if (!psc_i2s->playback.active && - !psc_i2s->capture.active) { - /* Setup the IRQs */ - rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED, - "psc-i2s-status", psc_i2s); - rc |= request_irq(psc_i2s->capture.irq, - &psc_i2s_bcom_irq, IRQF_SHARED, - "psc-i2s-capture", &psc_i2s->capture); - rc |= request_irq(psc_i2s->playback.irq, - &psc_i2s_bcom_irq, IRQF_SHARED, - "psc-i2s-playback", &psc_i2s->playback); - if (rc) { - free_irq(psc_i2s->irq, psc_i2s); - free_irq(psc_i2s->capture.irq, - &psc_i2s->capture); - free_irq(psc_i2s->playback.irq, - &psc_i2s->playback); - return -ENODEV; - } - } - - return 0; -} - static int psc_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -258,164 +87,6 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, return 0; }
-static int psc_i2s_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - snd_pcm_set_runtime_buffer(substream, NULL); - return 0; -} - -/** - * psc_i2s_trigger: start and stop the DMA transfer. - * - * This function is called by ALSA to start, stop, pause, and resume the DMA - * transfer of data. - */ -static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - struct psc_i2s_stream *s; - struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs; - u16 imr; - u8 psc_cmd; - unsigned long flags; - - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_i2s->capture; - else - s = &psc_i2s->playback; - - dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)" - " stream_id=%i\n", - substream, cmd, substream->pstr->stream); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - s->period_bytes = frames_to_bytes(runtime, - runtime->period_size); - s->period_start = virt_to_phys(runtime->dma_area); - s->period_end = s->period_start + - (s->period_bytes * runtime->periods); - s->period_next_pt = s->period_start; - s->period_current_pt = s->period_start; - s->active = 1; - - /* First; reset everything */ - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { - out_8(®s->command, MPC52xx_PSC_RST_RX); - out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); - } else { - out_8(®s->command, MPC52xx_PSC_RST_TX); - out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); - } - - /* Next, fill up the bestcomm bd queue and enable DMA. - * This will begin filling the PSC's fifo. */ - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - bcom_gen_bd_rx_reset(s->bcom_task); - else - bcom_gen_bd_tx_reset(s->bcom_task); - while (!bcom_queue_full(s->bcom_task)) - psc_i2s_bcom_enqueue_next_buffer(s); - bcom_enable(s->bcom_task); - - /* Due to errata in the i2s mode; need to line up enabling - * the transmitter with a transition on the frame sync - * line */ - - spin_lock_irqsave(&psc_i2s->lock, flags); - /* first make sure it is low */ - while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) - ; - /* then wait for the transition to high */ - while ((in_8(®s->ipcr_acr.ipcr) & 0x80) == 0) - ; - /* Finally, enable the PSC. - * Receiver must always be enabled; even when we only want - * transmit. (see 15.3.2.3 of MPC5200B User's Guide) */ - psc_cmd = MPC52xx_PSC_RX_ENABLE; - if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) - psc_cmd |= MPC52xx_PSC_TX_ENABLE; - out_8(®s->command, psc_cmd); - spin_unlock_irqrestore(&psc_i2s->lock, flags); - - break; - - case SNDRV_PCM_TRIGGER_STOP: - /* Turn off the PSC */ - s->active = 0; - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { - if (!psc_i2s->playback.active) { - out_8(®s->command, 2 << 4); /* reset rx */ - out_8(®s->command, 3 << 4); /* reset tx */ - out_8(®s->command, 4 << 4); /* reset err */ - } - } else { - out_8(®s->command, 3 << 4); /* reset tx */ - out_8(®s->command, 4 << 4); /* reset err */ - if (!psc_i2s->capture.active) - out_8(®s->command, 2 << 4); /* reset rx */ - } - - bcom_disable(s->bcom_task); - while (!bcom_queue_empty(s->bcom_task)) - bcom_retrieve_buffer(s->bcom_task, NULL, NULL); - - break; - - default: - dev_dbg(psc_i2s->dev, "invalid command\n"); - return -EINVAL; - } - - /* Update interrupt enable settings */ - imr = 0; - if (psc_i2s->playback.active) - imr |= MPC52xx_PSC_IMR_TXEMP; - if (psc_i2s->capture.active) - imr |= MPC52xx_PSC_IMR_ORERR; - out_be16(®s->isr_imr.imr, imr); - - return 0; -} - -/** - * psc_i2s_shutdown: shutdown the data transfer on a stream - * - * Shutdown the PSC if there are no other substreams open. - */ -static void psc_i2s_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; - - dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream); - - /* - * If this is the last active substream, disable the PSC and release - * the IRQ. - */ - if (!psc_i2s->playback.active && - !psc_i2s->capture.active) { - - /* Disable all interrupts and reset the PSC */ - out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); - out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */ - out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */ - out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ - out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ - - /* Release irqs */ - free_irq(psc_i2s->irq, psc_i2s); - free_irq(psc_i2s->capture.irq, &psc_i2s->capture); - free_irq(psc_i2s->playback.irq, &psc_i2s->playback); - } -} - /** * psc_i2s_set_sysclk: set the clock frequency and direction * @@ -495,157 +166,6 @@ static struct snd_soc_dai psc_i2s_dai_template = { };
/* --------------------------------------------------------------------- - * The PSC I2S 'ASoC platform' driver - * - * Can be referenced by an 'ASoC machine' driver - * This driver only deals with the audio bus; it doesn't have any - * interaction with the attached codec - */ - -static const struct snd_pcm_hardware psc_i2s_pcm_hardware = { - .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, - .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | - SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, - .rate_min = 8000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .period_bytes_max = 1024 * 1024, - .period_bytes_min = 32, - .periods_min = 2, - .periods_max = 256, - .buffer_bytes_max = 2 * 1024 * 1024, - .fifo_size = 0, -}; - -static int psc_i2s_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; - struct psc_i2s_stream *s; - - dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream); - - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_i2s->capture; - else - s = &psc_i2s->playback; - - snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware); - - s->stream = substream; - return 0; -} - -static int psc_i2s_pcm_close(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; - struct psc_i2s_stream *s; - - dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream); - - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_i2s->capture; - else - s = &psc_i2s->playback; - - s->stream = NULL; - return 0; -} - -static snd_pcm_uframes_t -psc_i2s_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; - struct psc_i2s_stream *s; - dma_addr_t count; - - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_i2s->capture; - else - s = &psc_i2s->playback; - - count = s->period_current_pt - s->period_start; - - return bytes_to_frames(substream->runtime, count); -} - -static struct snd_pcm_ops psc_i2s_pcm_ops = { - .open = psc_i2s_pcm_open, - .close = psc_i2s_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .pointer = psc_i2s_pcm_pointer, -}; - -static u64 psc_i2s_pcm_dmamask = 0xffffffff; -static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) -{ - struct snd_soc_pcm_runtime *rtd = pcm->private_data; - size_t size = psc_i2s_pcm_hardware.buffer_bytes_max; - int rc = 0; - - dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n", - card, dai, pcm); - - if (!card->dev->dma_mask) - card->dev->dma_mask = &psc_i2s_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = 0xffffffff; - - if (pcm->streams[0].substream) { - rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, - &pcm->streams[0].substream->dma_buffer); - if (rc) - goto playback_alloc_err; - } - - if (pcm->streams[1].substream) { - rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, - &pcm->streams[1].substream->dma_buffer); - if (rc) - goto capture_alloc_err; - } - - return 0; - - capture_alloc_err: - if (pcm->streams[0].substream) - snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); - playback_alloc_err: - dev_err(card->dev, "Cannot allocate buffer(s)\n"); - return -ENOMEM; -} - -static void psc_i2s_pcm_free(struct snd_pcm *pcm) -{ - struct snd_soc_pcm_runtime *rtd = pcm->private_data; - struct snd_pcm_substream *substream; - int stream; - - dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm); - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (substream) { - snd_dma_free_pages(&substream->dma_buffer); - substream->dma_buffer.area = NULL; - substream->dma_buffer.addr = 0; - } - } -} - -struct snd_soc_platform psc_i2s_pcm_soc_platform = { - .name = "mpc5200-psc-audio", - .pcm_ops = &psc_i2s_pcm_ops, - .pcm_new = &psc_i2s_pcm_new, - .pcm_free = &psc_i2s_pcm_free, -}; - -/* --------------------------------------------------------------------- * Sysfs attributes for debugging */
At Tue, 28 Apr 2009 16:23:41 +0100, Mark Brown wrote:
From: Jon Smirl jonsmirl@gmail.com
Basic split of mpc5200 DMA code out from i2s into a standalone file.
[Fixed trailing whitespace -- broonie.]
Signed-off-by: Jon Smirl jonsmirl@gmail.com Acked-by: Grant Likely grant.likely@secretlab.ca Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com
This patch conflicts with the last patch I merged to add SNDRV_PCM_INFO_BATCH to some drivers including mpc5200. (It's in fix/pcm-info-batch branch and not merged to */asoc branch yet. Feel free to pull to your tree by yourself.) And, the jiffies check shall be anyway fixed in the upstream PCM core side, thus there is no big merit to hurry to merge the unfinished changes now.
So, could you back off mpc5200 changes and wait for a while until the issue regarding PCM jiffies-check is resolved?
Note that the info flag should contain SNDRV_PCM_INFO_BATCH unless the driver provides the precise position reporting in the pointer callback. The flag can be removed once after you implement that feature, of course.
thanks,
Takashi
On Tue, Apr 28, 2009 at 11:46 AM, Takashi Iwai tiwai@suse.de wrote:
At Tue, 28 Apr 2009 16:23:41 +0100, Mark Brown wrote:
From: Jon Smirl jonsmirl@gmail.com
Basic split of mpc5200 DMA code out from i2s into a standalone file.
[Fixed trailing whitespace -- broonie.]
Signed-off-by: Jon Smirl jonsmirl@gmail.com Acked-by: Grant Likely grant.likely@secretlab.ca Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com
This patch conflicts with the last patch I merged to add SNDRV_PCM_INFO_BATCH to some drivers including mpc5200. (It's in fix/pcm-info-batch branch and not merged to */asoc branch yet. Feel free to pull to your tree by yourself.) And, the jiffies check shall be anyway fixed in the upstream PCM core side, thus there is no big merit to hurry to merge the unfinished changes now.
So, could you back off mpc5200 changes and wait for a while until the issue regarding PCM jiffies-check is resolved?
This is why I was pushing so hard to get the patches that split the files in. Takashi is going to change the base mpc5200_i2c file and I am going to have to manually fix up up my 14 patches layered on top. Git handles file renames fine, but it doesn't handle the case of splitting a file very well. Once the patches that split the file are in the tree Git handles things ok.
Note that the info flag should contain SNDRV_PCM_INFO_BATCH unless the driver provides the precise position reporting in the pointer callback. The flag can be removed once after you implement that feature, of course.
thanks,
Takashi
At Tue, 28 Apr 2009 11:59:01 -0400, Jon Smirl wrote:
On Tue, Apr 28, 2009 at 11:46 AM, Takashi Iwai tiwai@suse.de wrote:
At Tue, 28 Apr 2009 16:23:41 +0100, Mark Brown wrote:
From: Jon Smirl jonsmirl@gmail.com
Basic split of mpc5200 DMA code out from i2s into a standalone file.
[Fixed trailing whitespace -- broonie.]
Signed-off-by: Jon Smirl jonsmirl@gmail.com Acked-by: Grant Likely grant.likely@secretlab.ca Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com
This patch conflicts with the last patch I merged to add SNDRV_PCM_INFO_BATCH to some drivers including mpc5200. (It's in fix/pcm-info-batch branch and not merged to */asoc branch yet. Feel free to pull to your tree by yourself.) And, the jiffies check shall be anyway fixed in the upstream PCM core side, thus there is no big merit to hurry to merge the unfinished changes now.
So, could you back off mpc5200 changes and wait for a while until the issue regarding PCM jiffies-check is resolved?
This is why I was pushing so hard to get the patches that split the files in.
And this is why I am against pulling now :)
Takashi
On Tue, Apr 28, 2009 at 12:51 PM, Takashi Iwai tiwai@suse.de wrote:
At Tue, 28 Apr 2009 11:59:01 -0400, Jon Smirl wrote:
On Tue, Apr 28, 2009 at 11:46 AM, Takashi Iwai tiwai@suse.de wrote:
At Tue, 28 Apr 2009 16:23:41 +0100, Mark Brown wrote:
From: Jon Smirl jonsmirl@gmail.com
Basic split of mpc5200 DMA code out from i2s into a standalone file.
[Fixed trailing whitespace -- broonie.]
Signed-off-by: Jon Smirl jonsmirl@gmail.com Acked-by: Grant Likely grant.likely@secretlab.ca Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com
This patch conflicts with the last patch I merged to add SNDRV_PCM_INFO_BATCH to some drivers including mpc5200. (It's in fix/pcm-info-batch branch and not merged to */asoc branch yet. Feel free to pull to your tree by yourself.) And, the jiffies check shall be anyway fixed in the upstream PCM core side, thus there is no big merit to hurry to merge the unfinished changes now.
So, could you back off mpc5200 changes and wait for a while until the issue regarding PCM jiffies-check is resolved?
This is why I was pushing so hard to get the patches that split the files in.
And this is why I am against pulling now :)
I wanted the split files added to 2.6.30 not 2.6.31. If they had gone into 2.6.30 you would have patched after the split and everything would have propagated without merge conflicts. In general, changes in git that cross a file split will cause manual manual merges in everything pending past the split.
Takashi
On Tue, Apr 28, 2009 at 12:55 PM, Jon Smirl jonsmirl@gmail.com wrote:
On Tue, Apr 28, 2009 at 12:51 PM, Takashi Iwai tiwai@suse.de wrote:
At Tue, 28 Apr 2009 11:59:01 -0400, Jon Smirl wrote:
On Tue, Apr 28, 2009 at 11:46 AM, Takashi Iwai tiwai@suse.de wrote:
At Tue, 28 Apr 2009 16:23:41 +0100, Mark Brown wrote:
From: Jon Smirl jonsmirl@gmail.com
Basic split of mpc5200 DMA code out from i2s into a standalone file.
[Fixed trailing whitespace -- broonie.]
Signed-off-by: Jon Smirl jonsmirl@gmail.com Acked-by: Grant Likely grant.likely@secretlab.ca Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com
This patch conflicts with the last patch I merged to add SNDRV_PCM_INFO_BATCH to some drivers including mpc5200. (It's in fix/pcm-info-batch branch and not merged to */asoc branch yet. Feel free to pull to your tree by yourself.) And, the jiffies check shall be anyway fixed in the upstream PCM core side, thus there is no big merit to hurry to merge the unfinished changes now.
So, could you back off mpc5200 changes and wait for a while until the issue regarding PCM jiffies-check is resolved?
This is why I was pushing so hard to get the patches that split the files in.
And this is why I am against pulling now :)
I wanted the split files added to 2.6.30 not 2.6.31. If they had gone into 2.6.30 you would have patched after the split and everything would have propagated without merge conflicts. In general, changes in git that cross a file split will cause manual manual merges in everything pending past the split.
BTW, pulling the split patches out of 2.6.31 is just going to move the merge mess onto my local patch stack. To eliminate the merge mess we need to get the split in and then start patching after the split.
Takashi
-- Jon Smirl jonsmirl@gmail.com
At Tue, 28 Apr 2009 13:14:09 -0400, Jon Smirl wrote:
On Tue, Apr 28, 2009 at 12:55 PM, Jon Smirl jonsmirl@gmail.com wrote:
On Tue, Apr 28, 2009 at 12:51 PM, Takashi Iwai tiwai@suse.de wrote:
At Tue, 28 Apr 2009 11:59:01 -0400, Jon Smirl wrote:
On Tue, Apr 28, 2009 at 11:46 AM, Takashi Iwai tiwai@suse.de wrote:
At Tue, 28 Apr 2009 16:23:41 +0100, Mark Brown wrote:
From: Jon Smirl jonsmirl@gmail.com
Basic split of mpc5200 DMA code out from i2s into a standalone file.
[Fixed trailing whitespace -- broonie.]
Signed-off-by: Jon Smirl jonsmirl@gmail.com Acked-by: Grant Likely grant.likely@secretlab.ca Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com
This patch conflicts with the last patch I merged to add SNDRV_PCM_INFO_BATCH to some drivers including mpc5200. (It's in fix/pcm-info-batch branch and not merged to */asoc branch yet. Feel free to pull to your tree by yourself.) And, the jiffies check shall be anyway fixed in the upstream PCM core side, thus there is no big merit to hurry to merge the unfinished changes now.
So, could you back off mpc5200 changes and wait for a while until the issue regarding PCM jiffies-check is resolved?
This is why I was pushing so hard to get the patches that split the files in.
And this is why I am against pulling now :)
I wanted the split files added to 2.6.30 not 2.6.31. If they had gone into 2.6.30 you would have patched after the split and everything would have propagated without merge conflicts. In general, changes in git that cross a file split will cause manual manual merges in everything pending past the split.
BTW, pulling the split patches out of 2.6.31 is just going to move the merge mess onto my local patch stack. To eliminate the merge mess we need to get the split in and then start patching after the split.
I agree with that.
Also, we need to reconsider which of you change is really needed since the current problem of the PCM will be solved in the PCM core side soon later without the change of the driver side.
Takashi
On Tue, Apr 28, 2009 at 09:37:34PM +0200, Takashi Iwai wrote:
Also, we need to reconsider which of you change is really needed since the current problem of the PCM will be solved in the PCM core side soon later without the change of the driver side.
AFAICT all the currently posted patches are needed since they're only doing the refactoring of the code required to support AC97. They're only related to the fixes in that John's board doesn't have I2S but since the DMA is shared the fixes that John develops while making AC97 work will also fix I2S.
On Tue, Apr 28, 2009 at 3:53 PM, Mark Brown broonie@sirena.org.uk wrote:
On Tue, Apr 28, 2009 at 09:37:34PM +0200, Takashi Iwai wrote:
Also, we need to reconsider which of you change is really needed since the current problem of the PCM will be solved in the PCM core side soon later without the change of the driver side.
AFAICT all the currently posted patches are needed since they're only doing the refactoring of the code required to support AC97. They're only related to the fixes in that John's board doesn't have I2S but since the DMA is shared the fixes that John develops while making AC97 work will also fix I2S.
I purposely sent in refactoring changes that made no functional changes to the code.
I have been caught in this mess before. This isn't a simple case of resolving conflicts. What happens is that git isn't smart enough to track changes across a refactor. That results in big conflicts covering most of the contents of the files involved. The conflicts in the refactor then cascade into all of the other patches.
If we put the refactor in front of the fixes git will get everything right.
Why are people going to complain about patches to a driver marked broken? You can't even compile it without editing the Kconfig.
On Tue, Apr 28, 2009 at 3:59 PM, Jon Smirl jonsmirl@gmail.com wrote:
On Tue, Apr 28, 2009 at 3:53 PM, Mark Brown broonie@sirena.org.uk wrote:
On Tue, Apr 28, 2009 at 09:37:34PM +0200, Takashi Iwai wrote:
Also, we need to reconsider which of you change is really needed since the current problem of the PCM will be solved in the PCM core side soon later without the change of the driver side.
AFAICT all the currently posted patches are needed since they're only doing the refactoring of the code required to support AC97. They're only related to the fixes in that John's board doesn't have I2S but since the DMA is shared the fixes that John develops while making AC97 work will also fix I2S.
I purposely sent in refactoring changes that made no functional changes to the code.
I have been caught in this mess before. This isn't a simple case of resolving conflicts. What happens is that git isn't smart enough to track changes across a refactor. That results in big conflicts covering most of the contents of the files involved. The conflicts in
Thinking about this for a couple minutes, what happens is the three way merge becomes a four or more way merge depending on how many new files were created in the the refactor. Git doesn't have an n-way merge tool so it just kicks the entire files out as conflicts.
the refactor then cascade into all of the other patches.
If we put the refactor in front of the fixes git will get everything right.
Why are people going to complain about patches to a driver marked broken? You can't even compile it without editing the Kconfig.
-- Jon Smirl jonsmirl@gmail.com
On Tue, Apr 28, 2009 at 03:59:52PM -0400, Jon Smirl wrote:
Why are people going to complain about patches to a driver marked broken? You can't even compile it without editing the Kconfig.
There is no problem with doing whatever to the driver for 2.6.31 but we do need to make sure we stay in sync with the other API work that's going on in 2.6.31.
For 2.6.30 we're outside the merge window so we need to send targetted fixes for bugs - if we sent your current patches upstream now the pushback would be that such large refactorings should be merged as part of the merge window and the bug fixes should be extracted from your refactoring and merged now. This is the kernel wide development policy, not an ALSA or ASoC specific one.
Ideally we wouldn't have to mark the driver broken but would instead be merging bug fixes to it into 2.6.30 - marking it as broken was an alternative to that.
For the merges the simplest thing would be to fix the current code before doing the refactoring, though from the sounds of it the final fixes required for 2.6.30 will be very small in the driver so I'd expect this can be resolved fairly simply. Changes made in the 2.6.30 branch get merged into the 2.6.31 branch very quickly so any divergence between the two branches shouldn't exist for long.
At Tue, 28 Apr 2009 15:59:52 -0400, Jon Smirl wrote:
On Tue, Apr 28, 2009 at 3:53 PM, Mark Brown broonie@sirena.org.uk wrote:
On Tue, Apr 28, 2009 at 09:37:34PM +0200, Takashi Iwai wrote:
Also, we need to reconsider which of you change is really needed since the current problem of the PCM will be solved in the PCM core side soon later without the change of the driver side.
AFAICT all the currently posted patches are needed since they're only doing the refactoring of the code required to support AC97. They're only related to the fixes in that John's board doesn't have I2S but since the DMA is shared the fixes that John develops while making AC97 work will also fix I2S.
I purposely sent in refactoring changes that made no functional changes to the code.
I have been caught in this mess before. This isn't a simple case of resolving conflicts.
Why do you believe that this case will be like that, too?
What happens is that git isn't smart enough to track changes across a refactor. That results in big conflicts covering most of the contents of the files involved. The conflicts in the refactor then cascade into all of the other patches.
If we put the refactor in front of the fixes git will get everything right.
Well, you can do it by yourself on your local tree, too. topgit or other tools can serve well for such use cases. Then either let Mark or me pull your tree, or straighten the patches to submit.
Why are people going to complain about patches to a driver marked broken? You can't even compile it without editing the Kconfig.
Because we broke it.
Takashi
At Tue, 28 Apr 2009 20:53:53 +0100, Mark Brown wrote:
On Tue, Apr 28, 2009 at 09:37:34PM +0200, Takashi Iwai wrote:
Also, we need to reconsider which of you change is really needed since the current problem of the PCM will be solved in the PCM core side soon later without the change of the driver side.
AFAICT all the currently posted patches are needed since they're only doing the refactoring of the code required to support AC97. They're only related to the fixes in that John's board doesn't have I2S but since the DMA is shared the fixes that John develops while making AC97 work will also fix I2S.
OK. I thought that he rewrote the pointer callback to return jiffies-based values and it could be superfluous. Other than that, it should be all fine.
Basically I don't mind to merge Jon's patch series now for 2.6.31 once after the current merge conflicts are resolved. There won't be many changes on 2.6.30 for that driver any more. At most, a patch will be just to remove BROKEN from kconfig again (hopefully in time).
Takashi
At Tue, 28 Apr 2009 12:55:16 -0400, Jon Smirl wrote:
On Tue, Apr 28, 2009 at 12:51 PM, Takashi Iwai tiwai@suse.de wrote:
At Tue, 28 Apr 2009 11:59:01 -0400, Jon Smirl wrote:
On Tue, Apr 28, 2009 at 11:46 AM, Takashi Iwai tiwai@suse.de wrote:
At Tue, 28 Apr 2009 16:23:41 +0100, Mark Brown wrote:
From: Jon Smirl jonsmirl@gmail.com
Basic split of mpc5200 DMA code out from i2s into a standalone file.
[Fixed trailing whitespace -- broonie.]
Signed-off-by: Jon Smirl jonsmirl@gmail.com Acked-by: Grant Likely grant.likely@secretlab.ca Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com
This patch conflicts with the last patch I merged to add SNDRV_PCM_INFO_BATCH to some drivers including mpc5200. (It's in fix/pcm-info-batch branch and not merged to */asoc branch yet. Feel free to pull to your tree by yourself.) And, the jiffies check shall be anyway fixed in the upstream PCM core side, thus there is no big merit to hurry to merge the unfinished changes now.
So, could you back off mpc5200 changes and wait for a while until the issue regarding PCM jiffies-check is resolved?
This is why I was pushing so hard to get the patches that split the files in.
And this is why I am against pulling now :)
I wanted the split files added to 2.6.30 not 2.6.31. If they had gone into 2.6.30 you would have patched after the split and everything would have propagated without merge conflicts. In general, changes in git that cross a file split will cause manual manual merges in everything pending past the split.
Not really so. The changes done in parallel repositories may conflict always. For example, this case would cause the conflict. The difference is only who resolves the conflict...
In addition, this split is certainly not appropriate for 2.6.30 since it itself has nothing to do with the fix.
Takashi
On Tue, Apr 28, 2009 at 12:55:16PM -0400, Jon Smirl wrote:
I wanted the split files added to 2.6.30 not 2.6.31. If they had gone into 2.6.30 you would have patched after the split and everything would have propagated without merge conflicts. In general, changes in git that cross a file split will cause manual manual merges in everything pending past the split.
It wouldn't have helped with the merge issues since bug fixes get merged up into the 2.6.31 branch very fast. In any case, we're in the -rc part of the development cycle - we should be merging targetted bug fixes only. Eyebrows would be raised by a diff of that size going in.
On Tue, Apr 28, 2009 at 05:46:11PM +0200, Takashi Iwai wrote:
And, the jiffies check shall be anyway fixed in the upstream PCM core side, thus there is no big merit to hurry to merge the unfinished changes now.
The refactoring is a largely orthogonal issue to the jiffies check - the refactoring is needed to split the DMA code out so that it can be shared with the AC97 driver John is writing.
So, could you back off mpc5200 changes and wait for a while until the issue regarding PCM jiffies-check is resolved?
Done:
The following changes since commit 8a1f936acdfd53cb0a981f3f80483863dcd84fa9: Peter Ujfalusi (1): ASoC: TWL4030: Add 4 channel TDM support
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git for-2.6.31
Daniel Mack (3): ASoC: add SOC_DOUBLE_EXT macro ASoC: cs4270: fix Master Capture Switch polarity ASoC: cs4270: add Master Playback Switch
Jon Smirl (1): ASoC: Set the MPC5200 i2s driver to BROKEN status.
Jonathan Cameron (1): ASoC WM8940 Driver
Joonyoung Shim (1): ASoC: TWL4030: Fix gain control for earpiece amplifier
Mark Brown (10): ASoC: Fix S3C64xx IIS device registration and support both ports ASoC: S3C2412: Failing to get the I2S clock is an error ASoC: Enforce symmetric rates for S3C64xx I2S interface ASoC: Include WM8350 register definitions in CODEC header ASoC: s3c-i2s-v2 diagnostic improvements ASoC: Use our registration function for S3C64xx ASoC: Fix logic in WM8350 master clocking check Merge branch 'for-2.6.30' into for-2.6.31 ASoC: Staticise TLV values in WM8940 Merge branch 'for-2.6.30' into for-2.6.31
Peter Ujfalusi (1): ASoC: Beagle: Add support for 4 channel
include/sound/soc.h | 8 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs4270.c | 44 ++- sound/soc/codecs/twl4030.c | 8 +- sound/soc/codecs/wm8350.c | 2 +- sound/soc/codecs/wm8350.h | 1 + sound/soc/codecs/wm8940.c | 955 +++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm8940.h | 104 +++++ sound/soc/fsl/Kconfig | 2 +- sound/soc/omap/omap3beagle.c | 26 +- sound/soc/s3c24xx/s3c2412-i2s.c | 2 +- sound/soc/s3c24xx/s3c64xx-i2s.c | 148 ++++-- sound/soc/s3c24xx/s3c64xx-i2s.h | 2 +- 14 files changed, 1241 insertions(+), 67 deletions(-) create mode 100644 sound/soc/codecs/wm8940.c create mode 100644 sound/soc/codecs/wm8940.h
On Tue, Apr 28, 2009 at 3:25 PM, Mark Brown broonie@sirena.org.uk wrote:
On Tue, Apr 28, 2009 at 05:46:11PM +0200, Takashi Iwai wrote:
And, the jiffies check shall be anyway fixed in the upstream PCM core side, thus there is no big merit to hurry to merge the unfinished changes now.
The refactoring is a largely orthogonal issue to the jiffies check - the refactoring is needed to split the DMA code out so that it can be shared with the AC97 driver John is writing.
So, could you back off mpc5200 changes and wait for a while until the issue regarding PCM jiffies-check is resolved?
Done:
Now I get to deal with the merge mess in my patch stack. All this did was move the problem.
The following changes since commit 8a1f936acdfd53cb0a981f3f80483863dcd84fa9: Peter Ujfalusi (1): ASoC: TWL4030: Add 4 channel TDM support
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git for-2.6.31
Daniel Mack (3): ASoC: add SOC_DOUBLE_EXT macro ASoC: cs4270: fix Master Capture Switch polarity ASoC: cs4270: add Master Playback Switch
Jon Smirl (1): ASoC: Set the MPC5200 i2s driver to BROKEN status.
Jonathan Cameron (1): ASoC WM8940 Driver
Joonyoung Shim (1): ASoC: TWL4030: Fix gain control for earpiece amplifier
Mark Brown (10): ASoC: Fix S3C64xx IIS device registration and support both ports ASoC: S3C2412: Failing to get the I2S clock is an error ASoC: Enforce symmetric rates for S3C64xx I2S interface ASoC: Include WM8350 register definitions in CODEC header ASoC: s3c-i2s-v2 diagnostic improvements ASoC: Use our registration function for S3C64xx ASoC: Fix logic in WM8350 master clocking check Merge branch 'for-2.6.30' into for-2.6.31 ASoC: Staticise TLV values in WM8940 Merge branch 'for-2.6.30' into for-2.6.31
Peter Ujfalusi (1): ASoC: Beagle: Add support for 4 channel
include/sound/soc.h | 8 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs4270.c | 44 ++- sound/soc/codecs/twl4030.c | 8 +- sound/soc/codecs/wm8350.c | 2 +- sound/soc/codecs/wm8350.h | 1 + sound/soc/codecs/wm8940.c | 955 +++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm8940.h | 104 +++++ sound/soc/fsl/Kconfig | 2 +- sound/soc/omap/omap3beagle.c | 26 +- sound/soc/s3c24xx/s3c2412-i2s.c | 2 +- sound/soc/s3c24xx/s3c64xx-i2s.c | 148 ++++-- sound/soc/s3c24xx/s3c64xx-i2s.h | 2 +- 14 files changed, 1241 insertions(+), 67 deletions(-) create mode 100644 sound/soc/codecs/wm8940.c create mode 100644 sound/soc/codecs/wm8940.h _______________________________________________ Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
On Tue, Apr 28, 2009 at 03:32:18PM -0400, Jon Smirl wrote:
Now I get to deal with the merge mess in my patch stack. All this did was move the problem.
Please send patches against:
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git mpc5200
At Tue, 28 Apr 2009 20:25:45 +0100, Mark Brown wrote:
On Tue, Apr 28, 2009 at 05:46:11PM +0200, Takashi Iwai wrote:
And, the jiffies check shall be anyway fixed in the upstream PCM core side, thus there is no big merit to hurry to merge the unfinished changes now.
The refactoring is a largely orthogonal issue to the jiffies check - the refactoring is needed to split the DMA code out so that it can be shared with the AC97 driver John is writing.
So, could you back off mpc5200 changes and wait for a while until the issue regarding PCM jiffies-check is resolved?
Done:
Thanks, pulled now.
Takashi
The following changes since commit 8a1f936acdfd53cb0a981f3f80483863dcd84fa9: Peter Ujfalusi (1): ASoC: TWL4030: Add 4 channel TDM support
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git for-2.6.31
Daniel Mack (3): ASoC: add SOC_DOUBLE_EXT macro ASoC: cs4270: fix Master Capture Switch polarity ASoC: cs4270: add Master Playback Switch
Jon Smirl (1): ASoC: Set the MPC5200 i2s driver to BROKEN status.
Jonathan Cameron (1): ASoC WM8940 Driver
Joonyoung Shim (1): ASoC: TWL4030: Fix gain control for earpiece amplifier
Mark Brown (10): ASoC: Fix S3C64xx IIS device registration and support both ports ASoC: S3C2412: Failing to get the I2S clock is an error ASoC: Enforce symmetric rates for S3C64xx I2S interface ASoC: Include WM8350 register definitions in CODEC header ASoC: s3c-i2s-v2 diagnostic improvements ASoC: Use our registration function for S3C64xx ASoC: Fix logic in WM8350 master clocking check Merge branch 'for-2.6.30' into for-2.6.31 ASoC: Staticise TLV values in WM8940 Merge branch 'for-2.6.30' into for-2.6.31
Peter Ujfalusi (1): ASoC: Beagle: Add support for 4 channel
include/sound/soc.h | 8 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs4270.c | 44 ++- sound/soc/codecs/twl4030.c | 8 +- sound/soc/codecs/wm8350.c | 2 +- sound/soc/codecs/wm8350.h | 1 + sound/soc/codecs/wm8940.c | 955 +++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm8940.h | 104 +++++ sound/soc/fsl/Kconfig | 2 +- sound/soc/omap/omap3beagle.c | 26 +- sound/soc/s3c24xx/s3c2412-i2s.c | 2 +- sound/soc/s3c24xx/s3c64xx-i2s.c | 148 ++++-- sound/soc/s3c24xx/s3c64xx-i2s.h | 2 +- 14 files changed, 1241 insertions(+), 67 deletions(-) create mode 100644 sound/soc/codecs/wm8940.c create mode 100644 sound/soc/codecs/wm8940.h
From: Jon Smirl jonsmirl@gmail.com
Rename the functions in the mpc5200 DMA file from i2s to dma to reflect the file they are in.
Signed-off-by: Jon Smirl jonsmirl@gmail.com Grant Likely grant.likely@secretlab.ca Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/fsl/mpc5200_dma.c | 194 +++++++++++++++++++------------------- sound/soc/fsl/mpc5200_dma.h | 26 +++--- sound/soc/fsl/mpc5200_psc_i2s.c | 160 ++++++++++++++++---------------- 3 files changed, 190 insertions(+), 190 deletions(-)
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index cccaff4..c82ef75 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -34,21 +34,21 @@ MODULE_LICENSE("GPL"); /* * Interrupt handlers */ -static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s) +static irqreturn_t psc_dma_status_irq(int irq, void *_psc_dma) { - struct psc_i2s *psc_i2s = _psc_i2s; - struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs; + struct psc_dma *psc_dma = _psc_dma; + struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; u16 isr;
isr = in_be16(®s->mpc52xx_psc_isr);
/* Playback underrun error */ - if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) - psc_i2s->stats.underrun_count++; + if (psc_dma->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) + psc_dma->stats.underrun_count++;
/* Capture overrun error */ - if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) - psc_i2s->stats.overrun_count++; + if (psc_dma->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) + psc_dma->stats.overrun_count++;
out_8(®s->command, 4 << 4); /* reset the error status */
@@ -56,7 +56,7 @@ static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s) }
/** - * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer + * psc_dma_bcom_enqueue_next_buffer - Enqueue another audio buffer * @s: pointer to stream private data structure * * Enqueues another audio period buffer into the bestcomm queue. @@ -65,7 +65,7 @@ static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s) * the queue. Otherwise the enqueue will fail and the audio ring buffer * will get out of sync */ -static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s) +static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s) { struct bcom_bd *bd;
@@ -82,9 +82,9 @@ static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s) }
/* Bestcomm DMA irq handler */ -static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream) +static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) { - struct psc_i2s_stream *s = _psc_i2s_stream; + struct psc_dma_stream *s = _psc_dma_stream;
/* For each finished period, dequeue the completed period buffer * and enqueue a new one in it's place. */ @@ -93,7 +93,7 @@ static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream) s->period_current_pt += s->period_bytes; if (s->period_current_pt >= s->period_end) s->period_current_pt = s->period_start; - psc_i2s_bcom_enqueue_next_buffer(s); + psc_dma_bcom_enqueue_next_buffer(s); bcom_enable(s->bcom_task); }
@@ -106,39 +106,39 @@ static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream) }
/** - * psc_i2s_startup: create a new substream + * psc_dma_startup: create a new substream * * This is the first function called when a stream is opened. * * If this is the first stream open, then grab the IRQ and program most of * the PSC registers. */ -int psc_i2s_startup(struct snd_pcm_substream *substream, +int psc_dma_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; int rc;
- dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream); + dev_dbg(psc_dma->dev, "psc_dma_startup(substream=%p)\n", substream);
- if (!psc_i2s->playback.active && - !psc_i2s->capture.active) { + if (!psc_dma->playback.active && + !psc_dma->capture.active) { /* Setup the IRQs */ - rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED, - "psc-i2s-status", psc_i2s); - rc |= request_irq(psc_i2s->capture.irq, - &psc_i2s_bcom_irq, IRQF_SHARED, - "psc-i2s-capture", &psc_i2s->capture); - rc |= request_irq(psc_i2s->playback.irq, - &psc_i2s_bcom_irq, IRQF_SHARED, - "psc-i2s-playback", &psc_i2s->playback); + rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, + "psc-dma-status", psc_dma); + rc |= request_irq(psc_dma->capture.irq, + &psc_dma_bcom_irq, IRQF_SHARED, + "psc-dma-capture", &psc_dma->capture); + rc |= request_irq(psc_dma->playback.irq, + &psc_dma_bcom_irq, IRQF_SHARED, + "psc-dma-playback", &psc_dma->playback); if (rc) { - free_irq(psc_i2s->irq, psc_i2s); - free_irq(psc_i2s->capture.irq, - &psc_i2s->capture); - free_irq(psc_i2s->playback.irq, - &psc_i2s->playback); + free_irq(psc_dma->irq, psc_dma); + free_irq(psc_dma->capture.irq, + &psc_dma->capture); + free_irq(psc_dma->playback.irq, + &psc_dma->playback); return -ENODEV; } } @@ -146,7 +146,7 @@ int psc_i2s_startup(struct snd_pcm_substream *substream, return 0; }
-int psc_i2s_hw_free(struct snd_pcm_substream *substream, +int psc_dma_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { snd_pcm_set_runtime_buffer(substream, NULL); @@ -154,29 +154,29 @@ int psc_i2s_hw_free(struct snd_pcm_substream *substream, }
/** - * psc_i2s_trigger: start and stop the DMA transfer. + * psc_dma_trigger: start and stop the DMA transfer. * * This function is called by ALSA to start, stop, pause, and resume the DMA * transfer of data. */ -int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, +int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct psc_i2s_stream *s; - struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs; + struct psc_dma_stream *s; + struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; u16 imr; u8 psc_cmd; unsigned long flags;
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_i2s->capture; + s = &psc_dma->capture; else - s = &psc_i2s->playback; + s = &psc_dma->playback;
- dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)" + dev_dbg(psc_dma->dev, "psc_dma_trigger(substream=%p, cmd=%i)" " stream_id=%i\n", substream, cmd, substream->pstr->stream);
@@ -207,14 +207,14 @@ int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, else bcom_gen_bd_tx_reset(s->bcom_task); while (!bcom_queue_full(s->bcom_task)) - psc_i2s_bcom_enqueue_next_buffer(s); + psc_dma_bcom_enqueue_next_buffer(s); bcom_enable(s->bcom_task);
- /* Due to errata in the i2s mode; need to line up enabling + /* Due to errata in the dma mode; need to line up enabling * the transmitter with a transition on the frame sync * line */
- spin_lock_irqsave(&psc_i2s->lock, flags); + spin_lock_irqsave(&psc_dma->lock, flags); /* first make sure it is low */ while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) ; @@ -228,7 +228,7 @@ int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) psc_cmd |= MPC52xx_PSC_TX_ENABLE; out_8(®s->command, psc_cmd); - spin_unlock_irqrestore(&psc_i2s->lock, flags); + spin_unlock_irqrestore(&psc_dma->lock, flags);
break;
@@ -236,7 +236,7 @@ int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, /* Turn off the PSC */ s->active = 0; if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { - if (!psc_i2s->playback.active) { + if (!psc_dma->playback.active) { out_8(®s->command, 2 << 4); /* reset rx */ out_8(®s->command, 3 << 4); /* reset tx */ out_8(®s->command, 4 << 4); /* reset err */ @@ -244,7 +244,7 @@ int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, } else { out_8(®s->command, 3 << 4); /* reset tx */ out_8(®s->command, 4 << 4); /* reset err */ - if (!psc_i2s->capture.active) + if (!psc_dma->capture.active) out_8(®s->command, 2 << 4); /* reset rx */ }
@@ -255,15 +255,15 @@ int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, break;
default: - dev_dbg(psc_i2s->dev, "invalid command\n"); + dev_dbg(psc_dma->dev, "invalid command\n"); return -EINVAL; }
/* Update interrupt enable settings */ imr = 0; - if (psc_i2s->playback.active) + if (psc_dma->playback.active) imr |= MPC52xx_PSC_IMR_TXEMP; - if (psc_i2s->capture.active) + if (psc_dma->capture.active) imr |= MPC52xx_PSC_IMR_ORERR; out_be16(®s->isr_imr.imr, imr);
@@ -271,36 +271,36 @@ int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, }
/** - * psc_i2s_shutdown: shutdown the data transfer on a stream + * psc_dma_shutdown: shutdown the data transfer on a stream * * Shutdown the PSC if there are no other substreams open. */ -void psc_i2s_shutdown(struct snd_pcm_substream *substream, +void psc_dma_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
- dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream); + dev_dbg(psc_dma->dev, "psc_dma_shutdown(substream=%p)\n", substream);
/* * If this is the last active substream, disable the PSC and release * the IRQ. */ - if (!psc_i2s->playback.active && - !psc_i2s->capture.active) { + if (!psc_dma->playback.active && + !psc_dma->capture.active) {
/* Disable all interrupts and reset the PSC */ - out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); - out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */ - out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */ - out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ - out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ + out_be16(&psc_dma->psc_regs->isr_imr.imr, 0); + out_8(&psc_dma->psc_regs->command, 3 << 4); /* reset tx */ + out_8(&psc_dma->psc_regs->command, 2 << 4); /* reset rx */ + out_8(&psc_dma->psc_regs->command, 1 << 4); /* reset mode */ + out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */
/* Release irqs */ - free_irq(psc_i2s->irq, psc_i2s); - free_irq(psc_i2s->capture.irq, &psc_i2s->capture); - free_irq(psc_i2s->playback.irq, &psc_i2s->playback); + free_irq(psc_dma->irq, psc_dma); + free_irq(psc_dma->capture.irq, &psc_dma->capture); + free_irq(psc_dma->playback.irq, &psc_dma->playback); } }
@@ -312,7 +312,7 @@ void psc_i2s_shutdown(struct snd_pcm_substream *substream, * interaction with the attached codec */
-static const struct snd_pcm_hardware psc_i2s_pcm_hardware = { +static const struct snd_pcm_hardware psc_dma_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | @@ -329,80 +329,80 @@ static const struct snd_pcm_hardware psc_i2s_pcm_hardware = { .fifo_size = 0, };
-static int psc_i2s_pcm_open(struct snd_pcm_substream *substream) +static int psc_dma_pcm_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; - struct psc_i2s_stream *s; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma_stream *s;
- dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream); + dev_dbg(psc_dma->dev, "psc_dma_pcm_open(substream=%p)\n", substream);
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_i2s->capture; + s = &psc_dma->capture; else - s = &psc_i2s->playback; + s = &psc_dma->playback;
- snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware); + snd_soc_set_runtime_hwparams(substream, &psc_dma_pcm_hardware);
s->stream = substream; return 0; }
-static int psc_i2s_pcm_close(struct snd_pcm_substream *substream) +static int psc_dma_pcm_close(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; - struct psc_i2s_stream *s; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma_stream *s;
- dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream); + dev_dbg(psc_dma->dev, "psc_dma_pcm_close(substream=%p)\n", substream);
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_i2s->capture; + s = &psc_dma->capture; else - s = &psc_i2s->playback; + s = &psc_dma->playback;
s->stream = NULL; return 0; }
static snd_pcm_uframes_t -psc_i2s_pcm_pointer(struct snd_pcm_substream *substream) +psc_dma_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; - struct psc_i2s_stream *s; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma_stream *s; dma_addr_t count;
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_i2s->capture; + s = &psc_dma->capture; else - s = &psc_i2s->playback; + s = &psc_dma->playback;
count = s->period_current_pt - s->period_start;
return bytes_to_frames(substream->runtime, count); }
-static struct snd_pcm_ops psc_i2s_pcm_ops = { - .open = psc_i2s_pcm_open, - .close = psc_i2s_pcm_close, +static struct snd_pcm_ops psc_dma_pcm_ops = { + .open = psc_dma_pcm_open, + .close = psc_dma_pcm_close, .ioctl = snd_pcm_lib_ioctl, - .pointer = psc_i2s_pcm_pointer, + .pointer = psc_dma_pcm_pointer, };
-static u64 psc_i2s_pcm_dmamask = 0xffffffff; -static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, +static u64 psc_dma_pcm_dmamask = 0xffffffff; +static int psc_dma_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm) { struct snd_soc_pcm_runtime *rtd = pcm->private_data; - size_t size = psc_i2s_pcm_hardware.buffer_bytes_max; + size_t size = psc_dma_pcm_hardware.buffer_bytes_max; int rc = 0;
- dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n", + dev_dbg(rtd->socdev->dev, "psc_dma_pcm_new(card=%p, dai=%p, pcm=%p)\n", card, dai, pcm);
if (!card->dev->dma_mask) - card->dev->dma_mask = &psc_i2s_pcm_dmamask; + card->dev->dma_mask = &psc_dma_pcm_dmamask; if (!card->dev->coherent_dma_mask) card->dev->coherent_dma_mask = 0xffffffff;
@@ -430,13 +430,13 @@ static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, return -ENOMEM; }
-static void psc_i2s_pcm_free(struct snd_pcm *pcm) +static void psc_dma_pcm_free(struct snd_pcm *pcm) { struct snd_soc_pcm_runtime *rtd = pcm->private_data; struct snd_pcm_substream *substream; int stream;
- dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm); + dev_dbg(rtd->socdev->dev, "psc_dma_pcm_free(pcm=%p)\n", pcm);
for (stream = 0; stream < 2; stream++) { substream = pcm->streams[stream].substream; @@ -448,10 +448,10 @@ static void psc_i2s_pcm_free(struct snd_pcm *pcm) } }
-struct snd_soc_platform psc_i2s_pcm_soc_platform = { +struct snd_soc_platform psc_dma_pcm_soc_platform = { .name = "mpc5200-psc-audio", - .pcm_ops = &psc_i2s_pcm_ops, - .pcm_new = &psc_i2s_pcm_new, - .pcm_free = &psc_i2s_pcm_free, + .pcm_ops = &psc_dma_pcm_ops, + .pcm_new = &psc_dma_pcm_new, + .pcm_free = &psc_dma_pcm_free, };
diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h index 9a19e8a..a33232c 100644 --- a/sound/soc/fsl/mpc5200_dma.h +++ b/sound/soc/fsl/mpc5200_dma.h @@ -6,9 +6,9 @@ #define __SOUND_SOC_FSL_MPC5200_DMA_H__
/** - * psc_i2s_stream - Data specific to a single stream (playback or capture) + * psc_dma_stream - Data specific to a single stream (playback or capture) * @active: flag indicating if the stream is active - * @psc_i2s: pointer back to parent psc_i2s data structure + * @psc_dma: pointer back to parent psc_dma data structure * @bcom_task: bestcomm task structure * @irq: irq number for bestcomm task * @period_start: physical address of start of DMA region @@ -16,9 +16,9 @@ * @period_next_pt: physical address of next DMA buffer to enqueue * @period_bytes: size of DMA period in bytes */ -struct psc_i2s_stream { +struct psc_dma_stream { int active; - struct psc_i2s *psc_i2s; + struct psc_dma *psc_dma; struct bcom_task *bcom_task; int irq; struct snd_pcm_substream *stream; @@ -30,7 +30,7 @@ struct psc_i2s_stream { };
/** - * psc_i2s - Private driver data + * psc_dma - Private driver data * @name: short name for this device ("PSC0", "PSC1", etc) * @psc_regs: pointer to the PSC's registers * @fifo_regs: pointer to the PSC's FIFO registers @@ -42,7 +42,7 @@ struct psc_i2s_stream { * @playback: Playback stream context data * @capture: Capture stream context data */ -struct psc_i2s { +struct psc_dma { char name[32]; struct mpc52xx_psc __iomem *psc_regs; struct mpc52xx_psc_fifo __iomem *fifo_regs; @@ -53,8 +53,8 @@ struct psc_i2s { u32 sicr;
/* per-stream data */ - struct psc_i2s_stream playback; - struct psc_i2s_stream capture; + struct psc_dma_stream playback; + struct psc_dma_stream capture;
/* Statistics */ struct { @@ -64,18 +64,18 @@ struct psc_i2s { };
-int psc_i2s_startup(struct snd_pcm_substream *substream, +int psc_dma_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai);
-int psc_i2s_hw_free(struct snd_pcm_substream *substream, +int psc_dma_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai);
-void psc_i2s_shutdown(struct snd_pcm_substream *substream, +void psc_dma_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai);
-int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, +int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai);
-extern struct snd_soc_platform psc_i2s_pcm_soc_platform; +extern struct snd_soc_platform psc_dma_pcm_soc_platform;
#endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */ diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 8974b53..12a7917 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -54,10 +54,10 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; u32 mode;
- dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i" + dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" " periods=%i buffer_size=%i buffer_bytes=%i\n", __func__, substream, params_period_size(params), params_period_bytes(params), params_periods(params), @@ -77,10 +77,10 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, mode = MPC52xx_PSC_SICR_SIM_CODEC_32; break; default: - dev_dbg(psc_i2s->dev, "invalid format\n"); + dev_dbg(psc_dma->dev, "invalid format\n"); return -EINVAL; } - out_be32(&psc_i2s->psc_regs->sicr, psc_i2s->sicr | mode); + out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr | mode);
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
@@ -104,8 +104,8 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { - struct psc_i2s *psc_i2s = cpu_dai->private_data; - dev_dbg(psc_i2s->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n", + struct psc_dma *psc_dma = cpu_dai->private_data; + dev_dbg(psc_dma->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n", cpu_dai, dir); return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; } @@ -123,8 +123,8 @@ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, */ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) { - struct psc_i2s *psc_i2s = cpu_dai->private_data; - dev_dbg(psc_i2s->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n", + struct psc_dma *psc_dma = cpu_dai->private_data; + dev_dbg(psc_dma->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n", cpu_dai, format); return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; } @@ -140,11 +140,11 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) * psc_i2s_dai_template: template CPU Digital Audio Interface */ static struct snd_soc_dai_ops psc_i2s_dai_ops = { - .startup = psc_i2s_startup, + .startup = psc_dma_startup, .hw_params = psc_i2s_hw_params, - .hw_free = psc_i2s_hw_free, - .shutdown = psc_i2s_shutdown, - .trigger = psc_i2s_trigger, + .hw_free = psc_dma_hw_free, + .shutdown = psc_dma_shutdown, + .trigger = psc_dma_trigger, .set_sysclk = psc_i2s_set_sysclk, .set_fmt = psc_i2s_set_fmt, }; @@ -172,24 +172,24 @@ static struct snd_soc_dai psc_i2s_dai_template = { static ssize_t psc_i2s_status_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct psc_i2s *psc_i2s = dev_get_drvdata(dev); + struct psc_dma *psc_dma = dev_get_drvdata(dev);
return sprintf(buf, "status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x " "tfnum=%i tfstat=0x%.4x\n", - in_be16(&psc_i2s->psc_regs->sr_csr.status), - in_be32(&psc_i2s->psc_regs->sicr), - in_be16(&psc_i2s->fifo_regs->rfnum) & 0x1ff, - in_be16(&psc_i2s->fifo_regs->rfstat), - in_be16(&psc_i2s->fifo_regs->tfnum) & 0x1ff, - in_be16(&psc_i2s->fifo_regs->tfstat)); + in_be16(&psc_dma->psc_regs->sr_csr.status), + in_be32(&psc_dma->psc_regs->sicr), + in_be16(&psc_dma->fifo_regs->rfnum) & 0x1ff, + in_be16(&psc_dma->fifo_regs->rfstat), + in_be16(&psc_dma->fifo_regs->tfnum) & 0x1ff, + in_be16(&psc_dma->fifo_regs->tfstat)); }
-static int *psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s, const char *name) +static int *psc_i2s_get_stat_attr(struct psc_dma *psc_dma, const char *name) { if (strcmp(name, "playback_underrun") == 0) - return &psc_i2s->stats.underrun_count; + return &psc_dma->stats.underrun_count; if (strcmp(name, "capture_overrun") == 0) - return &psc_i2s->stats.overrun_count; + return &psc_dma->stats.overrun_count;
return NULL; } @@ -197,10 +197,10 @@ static int *psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s, const char *name) static ssize_t psc_i2s_stat_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct psc_i2s *psc_i2s = dev_get_drvdata(dev); + struct psc_dma *psc_dma = dev_get_drvdata(dev); int *attrib;
- attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name); + attrib = psc_i2s_get_stat_attr(psc_dma, attr->attr.name); if (!attrib) return 0;
@@ -212,10 +212,10 @@ static ssize_t psc_i2s_stat_store(struct device *dev, const char *buf, size_t count) { - struct psc_i2s *psc_i2s = dev_get_drvdata(dev); + struct psc_dma *psc_dma = dev_get_drvdata(dev); int *attrib;
- attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name); + attrib = psc_i2s_get_stat_attr(psc_dma, attr->attr.name); if (!attrib) return 0;
@@ -238,7 +238,7 @@ static int __devinit psc_i2s_of_probe(struct of_device *op, const struct of_device_id *match) { phys_addr_t fifo; - struct psc_i2s *psc_i2s; + struct psc_dma *psc_dma; struct resource res; int size, psc_id, irq, rc; const __be32 *prop; @@ -265,56 +265,56 @@ static int __devinit psc_i2s_of_probe(struct of_device *op, }
/* Allocate and initialize the driver private data */ - psc_i2s = kzalloc(sizeof *psc_i2s, GFP_KERNEL); - if (!psc_i2s) { + psc_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL); + if (!psc_dma) { iounmap(regs); return -ENOMEM; } - spin_lock_init(&psc_i2s->lock); - psc_i2s->irq = irq; - psc_i2s->psc_regs = regs; - psc_i2s->fifo_regs = regs + sizeof *psc_i2s->psc_regs; - psc_i2s->dev = &op->dev; - psc_i2s->playback.psc_i2s = psc_i2s; - psc_i2s->capture.psc_i2s = psc_i2s; - snprintf(psc_i2s->name, sizeof psc_i2s->name, "PSC%u", psc_id+1); + spin_lock_init(&psc_dma->lock); + psc_dma->irq = irq; + psc_dma->psc_regs = regs; + psc_dma->fifo_regs = regs + sizeof *psc_dma->psc_regs; + psc_dma->dev = &op->dev; + psc_dma->playback.psc_dma = psc_dma; + psc_dma->capture.psc_dma = psc_dma; + snprintf(psc_dma->name, sizeof psc_dma->name, "PSC%u", psc_id+1);
/* Fill out the CPU DAI structure */ - memcpy(&psc_i2s->dai, &psc_i2s_dai_template, sizeof psc_i2s->dai); - psc_i2s->dai.private_data = psc_i2s; - psc_i2s->dai.name = psc_i2s->name; - psc_i2s->dai.id = psc_id; + memcpy(&psc_dma->dai, &psc_i2s_dai_template, sizeof psc_dma->dai); + psc_dma->dai.private_data = psc_dma; + psc_dma->dai.name = psc_dma->name; + psc_dma->dai.id = psc_id;
/* Find the address of the fifo data registers and setup the * DMA tasks */ fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); - psc_i2s->capture.bcom_task = + psc_dma->capture.bcom_task = bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512); - psc_i2s->playback.bcom_task = + psc_dma->playback.bcom_task = bcom_psc_gen_bd_tx_init(psc_id, 10, fifo); - if (!psc_i2s->capture.bcom_task || - !psc_i2s->playback.bcom_task) { + if (!psc_dma->capture.bcom_task || + !psc_dma->playback.bcom_task) { dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); iounmap(regs); - kfree(psc_i2s); + kfree(psc_dma); return -ENODEV; }
/* Disable all interrupts and reset the PSC */ - out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); - out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset transmitter */ - out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset receiver */ - out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ - out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ + out_be16(&psc_dma->psc_regs->isr_imr.imr, 0); + out_8(&psc_dma->psc_regs->command, 3 << 4); /* reset transmitter */ + out_8(&psc_dma->psc_regs->command, 2 << 4); /* reset receiver */ + out_8(&psc_dma->psc_regs->command, 1 << 4); /* reset mode */ + out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */
/* Configure the serial interface mode; defaulting to CODEC8 mode */ - psc_i2s->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S | + psc_dma->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S | MPC52xx_PSC_SICR_CLKPOL; if (of_get_property(op->node, "fsl,cellslave", NULL)) - psc_i2s->sicr |= MPC52xx_PSC_SICR_CELLSLAVE | + psc_dma->sicr |= MPC52xx_PSC_SICR_CELLSLAVE | MPC52xx_PSC_SICR_GENCLK; - out_be32(&psc_i2s->psc_regs->sicr, - psc_i2s->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8); + out_be32(&psc_dma->psc_regs->sicr, + psc_dma->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8);
/* Check for the codec handle. If it is not present then we * are done */ @@ -325,54 +325,54 @@ static int __devinit psc_i2s_of_probe(struct of_device *op, * First write: RxRdy (FIFO Alarm) generates rx FIFO irq * Second write: register Normal mode for non loopback */ - out_8(&psc_i2s->psc_regs->mode, 0); - out_8(&psc_i2s->psc_regs->mode, 0); + out_8(&psc_dma->psc_regs->mode, 0); + out_8(&psc_dma->psc_regs->mode, 0);
/* Set the TX and RX fifo alarm thresholds */ - out_be16(&psc_i2s->fifo_regs->rfalarm, 0x100); - out_8(&psc_i2s->fifo_regs->rfcntl, 0x4); - out_be16(&psc_i2s->fifo_regs->tfalarm, 0x100); - out_8(&psc_i2s->fifo_regs->tfcntl, 0x7); + out_be16(&psc_dma->fifo_regs->rfalarm, 0x100); + out_8(&psc_dma->fifo_regs->rfcntl, 0x4); + out_be16(&psc_dma->fifo_regs->tfalarm, 0x100); + out_8(&psc_dma->fifo_regs->tfcntl, 0x7);
/* Lookup the IRQ numbers */ - psc_i2s->playback.irq = - bcom_get_task_irq(psc_i2s->playback.bcom_task); - psc_i2s->capture.irq = - bcom_get_task_irq(psc_i2s->capture.bcom_task); + psc_dma->playback.irq = + bcom_get_task_irq(psc_dma->playback.bcom_task); + psc_dma->capture.irq = + bcom_get_task_irq(psc_dma->capture.bcom_task);
/* Save what we've done so it can be found again later */ - dev_set_drvdata(&op->dev, psc_i2s); + dev_set_drvdata(&op->dev, psc_dma);
/* Register the SYSFS files */ - rc = device_create_file(psc_i2s->dev, &dev_attr_status); - rc |= device_create_file(psc_i2s->dev, &dev_attr_capture_overrun); - rc |= device_create_file(psc_i2s->dev, &dev_attr_playback_underrun); + rc = device_create_file(psc_dma->dev, &dev_attr_status); + rc |= device_create_file(psc_dma->dev, &dev_attr_capture_overrun); + rc |= device_create_file(psc_dma->dev, &dev_attr_playback_underrun); if (rc) - dev_info(psc_i2s->dev, "error creating sysfs files\n"); + dev_info(psc_dma->dev, "error creating sysfs files\n");
- snd_soc_register_platform(&psc_i2s_pcm_soc_platform); + snd_soc_register_platform(&psc_dma_pcm_soc_platform);
/* Tell the ASoC OF helpers about it */ - of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node, - &psc_i2s->dai); + of_snd_soc_register_platform(&psc_dma_pcm_soc_platform, op->node, + &psc_dma->dai);
return 0; }
static int __devexit psc_i2s_of_remove(struct of_device *op) { - struct psc_i2s *psc_i2s = dev_get_drvdata(&op->dev); + struct psc_dma *psc_dma = dev_get_drvdata(&op->dev);
dev_dbg(&op->dev, "psc_i2s_remove()\n");
- snd_soc_unregister_platform(&psc_i2s_pcm_soc_platform); + snd_soc_unregister_platform(&psc_dma_pcm_soc_platform);
- bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task); - bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task); + bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); + bcom_gen_bd_tx_release(psc_dma->playback.bcom_task);
- iounmap(psc_i2s->psc_regs); - iounmap(psc_i2s->fifo_regs); - kfree(psc_i2s); + iounmap(psc_dma->psc_regs); + iounmap(psc_dma->fifo_regs); + kfree(psc_dma); dev_set_drvdata(&op->dev, NULL);
return 0;
participants (4)
-
Jon Smirl
-
Mark Brown
-
Mark Brown
-
Takashi Iwai