diff options
author | Alejandro Gonzalez <alex.gonzalez@digi.com> | 2010-03-12 12:33:19 +0100 |
---|---|---|
committer | Alejandro Gonzalez <alex.gonzalez@digi.com> | 2010-03-17 13:19:57 +0100 |
commit | 6be9c3c5e558bb11a65a18e8e3eebea16059c115 (patch) | |
tree | c68bf417f008445c2301744f5ac0a2c0a1ac4290 | |
parent | 8224ca931a798b6b8ebcd3c5232e3ecbbecd8bec (diff) |
ccwmx51: Support for the WM8753 audio chip
Adding audio support for the WM8753 chip.
Signed-off-by: Alejandro Gonzalez <alex.gonzalez@digi.com>
-rw-r--r-- | arch/arm/mach-mx51/mx51_ccwmx51js.c | 37 | ||||
-rw-r--r-- | arch/arm/mach-mx51/mx51_ccwmx51js_gpio.c | 49 | ||||
-rw-r--r-- | sound/soc/codecs/wm8753.c | 1 | ||||
-rw-r--r-- | sound/soc/imx/Kconfig | 8 | ||||
-rw-r--r-- | sound/soc/imx/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/imx/imx-ccwmx51-wm8753.c | 293 | ||||
-rw-r--r-- | sound/soc/soc-core.c | 4 |
7 files changed, 376 insertions, 18 deletions
diff --git a/arch/arm/mach-mx51/mx51_ccwmx51js.c b/arch/arm/mach-mx51/mx51_ccwmx51js.c index f00f3c83215b..e758a92dd7e1 100644 --- a/arch/arm/mach-mx51/mx51_ccwmx51js.c +++ b/arch/arm/mach-mx51/mx51_ccwmx51js.c @@ -150,6 +150,21 @@ static struct platform_device mxc_nandv2_mtd_device = { }, }; +static struct mxc_audio_platform_data wm8753_data = { + .ssi_num = 1, + .src_port = 2, + .ext_port = 3, + .sysclk = 12000000, +}; + +static struct platform_device mxc_wm8753_device = { + .name = "imx-ccwmx51", + .dev = { + .release = mxc_nop_release, + .platform_data = &wm8753_data, + }, +}; + static void ccwmx51_init_nand_mtd(void) { (void)platform_device_register(&mxc_nandv2_mtd_device); @@ -557,16 +572,29 @@ static void mxc_power_off(void) } static struct i2c_board_info ccwmx51_i2c_devices[] __initdata = { +#if defined(CONFIG_INPUT_MMA7455L) || defined(CONFIG_INPUT_MMA7455L_MODULE) { I2C_BOARD_INFO("mma7455l", 0x1d), .irq = IOMUX_TO_IRQ(MX51_PIN_GPIO1_7), }, +#endif +#if defined(CONFIG_SND_SOC_IMX_CCWMX51_WM8753) || defined(CONFIG_SND_SOC_IMX_CCWMX51_WM8753_MODULE) + { + I2C_BOARD_INFO("wm8753", 0x1A), + }, +#endif }; -int __init ccwmx51_init_mma7455l(void) +int __init ccwmx51_init_i2c2(void) { return i2c_register_board_info(1, ccwmx51_i2c_devices , ARRAY_SIZE(ccwmx51_i2c_devices) ); } + +static void ccwmx51_initwm8753(void) +{ + platform_device_register(&mxc_wm8753_device); +} + /*! * Board specific initialization. */ @@ -582,10 +610,11 @@ static void __init mxc_board_init(void) ccwmx51_init_nand_mtd(); mxc_init_fec(); ccwmx51_init_ext_eth_mac(); -#if defined(CONFIG_INPUT_MMA7455L) || defined(CONFIG_INPUT_MMA7455L_MODULE) - ccwmx51_init_mma7455l(); -#endif + ccwmx51_init_i2c2(); ccwmx51_init_mc13892(); +#if defined(CONFIG_SND_SOC_IMX_CCWMX51_WM8753) || defined(CONFIG_SND_SOC_IMX_CCWMX51_WM8753_MODULE) + ccwmx51_initwm8753(); +#endif pm_power_off = mxc_power_off; } diff --git a/arch/arm/mach-mx51/mx51_ccwmx51js_gpio.c b/arch/arm/mach-mx51/mx51_ccwmx51js_gpio.c index 86a15b3d3bad..8e547fa3a524 100644 --- a/arch/arm/mach-mx51/mx51_ccwmx51js_gpio.c +++ b/arch/arm/mach-mx51/mx51_ccwmx51js_gpio.c @@ -316,9 +316,7 @@ static struct mxc_iomux_pin_cfg __initdata ccwmx51_iomux_video1_pins[] = { #if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE) -static struct mxc_iomux_pin_cfg __initdata ccwmx51_iomux_mma7455l_pins[] = { - -// AG - Could use gpio_i2c_active() which is called from mxci2c_probe() and mxci2c_resume() +static struct mxc_iomux_pin_cfg __initdata ccwmx51_iomux_i2c_pins[] = { #ifdef CONFIG_I2C_MXC_SELECT1 { @@ -372,6 +370,7 @@ static struct mxc_iomux_pin_cfg __initdata ccwmx51_iomux_devices_pins[] = { (PAD_CTL_SRE_SLOW | PAD_CTL_DRV_MEDIUM | PAD_CTL_100K_PU | PAD_CTL_HYS_ENABLE | PAD_CTL_DRV_VOT_HIGH), }, +#if defined(CONFIG_INPUT_MMA7455L) || defined(CONFIG_INPUT_MMA7455L_MODULE) { /* MMA7455L interrupt line */ MX51_PIN_GPIO1_6, IOMUX_CONFIG_GPIO, }, @@ -381,9 +380,33 @@ static struct mxc_iomux_pin_cfg __initdata ccwmx51_iomux_devices_pins[] = { PAD_CTL_100K_PU | PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST), }, +#endif +#if defined(CONFIG_SND_SOC_WM8753) || defined(CONFIG_SND_SOC_WM8753_MODULE) + { /* AUD3_BB_CK */ + MX51_PIN_AUD3_BB_CK, IOMUX_CONFIG_ALT0 , + (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU | + PAD_CTL_HYS_NONE | PAD_CTL_ODE_OPENDRAIN_NONE | PAD_CTL_PKE_ENABLE | + PAD_CTL_PUE_KEEPER ), + }, + { /* AUD3_BB_FS */ + MX51_PIN_AUD3_BB_FS, IOMUX_CONFIG_ALT0 , + (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_HYS_NONE | + PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE), + }, + { /* AUD3_BB_RXD */ + MX51_PIN_AUD3_BB_RXD, IOMUX_CONFIG_ALT0 , + (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_HYS_NONE | + PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE), + }, + { /* AUD3_BB_TXD */ + MX51_PIN_AUD3_BB_TXD, IOMUX_CONFIG_ALT0 , + (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU | + PAD_CTL_HYS_NONE | PAD_CTL_ODE_OPENDRAIN_NONE | PAD_CTL_PKE_ENABLE | + PAD_CTL_PUE_KEEPER ), + }, +#endif }; - #if defined(CONFIG_SPI_MXC) || defined(CONFIG_SPI_MXC_MODULE) static struct mxc_iomux_pin_cfg __initdata ccwmx51_cspi_pins[] = { @@ -572,15 +595,15 @@ void __init ccwmx51_io_init(void) #endif #if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE) - for (i = 0; i < ARRAY_SIZE(ccwmx51_iomux_mma7455l_pins); i++) { - mxc_request_iomux(ccwmx51_iomux_mma7455l_pins[i].pin, - ccwmx51_iomux_mma7455l_pins[i].mux_mode); - if (ccwmx51_iomux_mma7455l_pins[i].pad_cfg) - mxc_iomux_set_pad(ccwmx51_iomux_mma7455l_pins[i].pin, - ccwmx51_iomux_mma7455l_pins[i].pad_cfg); - if (ccwmx51_iomux_mma7455l_pins[i].in_select) - mxc_iomux_set_input(ccwmx51_iomux_mma7455l_pins[i].in_select, - ccwmx51_iomux_mma7455l_pins[i].in_mode); + for (i = 0; i < ARRAY_SIZE(ccwmx51_iomux_i2c_pins); i++) { + mxc_request_iomux(ccwmx51_iomux_i2c_pins[i].pin, + ccwmx51_iomux_i2c_pins[i].mux_mode); + if (ccwmx51_iomux_i2c_pins[i].pad_cfg) + mxc_iomux_set_pad(ccwmx51_iomux_i2c_pins[i].pin, + ccwmx51_iomux_i2c_pins[i].pad_cfg); + if (ccwmx51_iomux_i2c_pins[i].in_select) + mxc_iomux_set_input(ccwmx51_iomux_i2c_pins[i].in_select, + ccwmx51_iomux_i2c_pins[i].in_mode); } #endif diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 49c4b2898aff..921932ab2d3a 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1848,6 +1848,7 @@ static struct spi_driver wm8753_spi_driver = { static int __init wm8753_modinit(void) { int ret; + #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) ret = i2c_add_driver(&wm8753_i2c_driver); if (ret != 0) diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index c642782e1721..43b6d8e3b10c 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig @@ -56,6 +56,14 @@ config SND_SOC_IMX_3STACK_WM8580 Say Y if you want to add support for Soc audio on IMX 3STACK with the WM8580 +config SND_SOC_IMX_CCWMX51_WM8753 + tristate "SoC Audio support for IMX - WM8753" + select SND_MXC_SOC_SSI + select SND_SOC_WM8753 + help + Say Y if you want to add support for Soc audio on IMX CCWMX51 + with the WM8753 + config SND_SOC_IMX_3STACK_AK5702 tristate "SoC Audio support for IMX - AK5702" select SND_MXC_SOC_ESAI diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile index d8cd279eb62b..284be7777de7 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile @@ -18,6 +18,8 @@ snd-soc-imx-3stack-ak4647-objs := imx-3stack-ak4647.o obj-$(CONFIG_SND_SOC_IMX_3STACK_AK4647) += snd-soc-imx-3stack-ak4647.o snd-soc-imx-3stack-wm8580-objs := imx-3stack-wm8580.o obj-$(CONFIG_SND_SOC_IMX_3STACK_WM8580) += snd-soc-imx-3stack-wm8580.o +snd-soc-imx-ccwmx51-wm8753-objs := imx-ccwmx51-wm8753.o +obj-$(CONFIG_SND_SOC_IMX_CCWMX51_WM8753) += snd-soc-imx-ccwmx51-wm8753.o snd-soc-imx-3stack-ak5702-objs := imx-3stack-ak5702.o obj-$(CONFIG_SND_SOC_IMX_3STACK_AK5702) += snd-soc-imx-3stack-ak5702.o snd-soc-imx-3stack-bt-objs := imx-3stack-bt.o diff --git a/sound/soc/imx/imx-ccwmx51-wm8753.c b/sound/soc/imx/imx-ccwmx51-wm8753.c new file mode 100644 index 000000000000..ecbc9cc126a0 --- /dev/null +++ b/sound/soc/imx/imx-ccwmx51-wm8753.c @@ -0,0 +1,293 @@ +/* + * imx-ccwmx51-wm8753.c -- i.MX CCWMX51 Driver for Freescale WM8753 Codec + * + * Copyright 2010 Digi International, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Adapted from sound/soc/imx/imx-3stack-sgtl5000.c. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/bitops.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/irq.h> +#include <linux/io.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 <mach/dma.h> +#include <mach/spba.h> +#include <mach/clock.h> + +#include "../codecs/wm8753.h" +#include "imx-ssi.h" +#include "imx-pcm.h" + +/* SSI BCLK and LRC master */ +#define WM8753_SSI_MASTER 1 + +struct imx_ccwmx51_priv { + int sysclk; + struct platform_device *pdev; +}; + +static struct imx_ccwmx51_priv card_priv; + +static int imx_ccwmx51_audio_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai_link *machine = rtd->dai; + struct snd_soc_dai *cpu_dai = machine->cpu_dai; + struct snd_soc_dai *codec_dai = machine->codec_dai; + struct imx_ccwmx51_priv *priv = &card_priv; + struct imx_ssi *ssi_mode = (struct imx_ssi *)cpu_dai->private_data; + int ret = 0; + + unsigned int channels = params_channels(params); + u32 dai_format; + + snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, priv->sysclk, 0); + +#if WM8753_SSI_MASTER + dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; +#else + dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; +#endif + + ssi_mode->sync_mode = 1; + if (channels == 1) + ssi_mode->network_mode = 0; + else + ssi_mode->network_mode = 1; + + /* set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, dai_format); + if (ret < 0) + return ret; + + /* set i.MX active slot mask */ + snd_soc_dai_set_tdm_slot(cpu_dai, + channels == 1 ? 0xfffffffe : 0xfffffffc, 2); + + /* set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); + if (ret < 0) + return ret; + + /* set the SSI system clock as input (unused) */ + snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_IN); + + return 0; +} + +static int imx_ccwmx51_startup(struct snd_pcm_substream *substream) +{ + return 0; +} + +static void imx_ccwmx51_shutdown(struct snd_pcm_substream *substream) +{ + +} + +/* + * imx_ccwmx51 WM8753 audio DAI operations. + */ +static struct snd_soc_ops imx_ccwmx51_ops = { + .startup = imx_ccwmx51_startup, + .shutdown = imx_ccwmx51_shutdown, + .hw_params = imx_ccwmx51_audio_hw_params, +}; + +static void imx_ccwmx51_init_dam(int ssi_port, int dai_port) +{ + unsigned int ssi_ptcr = 0; + unsigned int dai_ptcr = 0; + unsigned int ssi_pdcr = 0; + unsigned int dai_pdcr = 0; + /* iMX51 uses SSI1 or SSI2 via AUDMUX port dai_port for audio */ + + /* reset port ssi_port & dai_port */ + __raw_writel(0, DAM_PTCR(ssi_port)); + __raw_writel(0, DAM_PTCR(dai_port)); + __raw_writel(0, DAM_PDCR(ssi_port)); + __raw_writel(0, DAM_PDCR(dai_port)); + + /* set to synchronous */ + ssi_ptcr |= AUDMUX_PTCR_SYN; + dai_ptcr |= AUDMUX_PTCR_SYN; + +#if WM8753_SSI_MASTER + /* set Rx sources ssi_port <--> dai_port */ + ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port); + dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port); + + /* set Tx frame direction and source dai_port--> ssi_port output */ + ssi_ptcr |= AUDMUX_PTCR_TFSDIR; + ssi_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, dai_port); + + /* set Tx Clock direction and source dai_port--> ssi_port output */ + ssi_ptcr |= AUDMUX_PTCR_TCLKDIR; + ssi_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, dai_port); +#else + /* set Rx sources ssi_port <--> dai_port */ + ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port); + dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port); + + /* set Tx frame direction and source ssi_port --> dai_port output */ + dai_ptcr |= AUDMUX_PTCR_TFSDIR; + dai_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, ssi_port); + + /* set Tx Clock direction and source ssi_port--> dai_port output */ + dai_ptcr |= AUDMUX_PTCR_TCLKDIR; + dai_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, ssi_port); +#endif + + __raw_writel(ssi_ptcr, DAM_PTCR(ssi_port)); + __raw_writel(dai_ptcr, DAM_PTCR(dai_port)); + __raw_writel(ssi_pdcr, DAM_PDCR(ssi_port)); + __raw_writel(dai_pdcr, DAM_PDCR(dai_port)); +} + +static int imx_ccwmx51_wm8753_init(struct snd_soc_codec *codec) +{ + return 0; +} + +/* imx_ccwmx51 digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link imx_ccwmx51_dai = { + .name = "WM8753", + .stream_name = "WM8753", + .codec_dai = (struct snd_soc_dai *) &wm8753_dai, + .init = imx_ccwmx51_wm8753_init, + .ops = &imx_ccwmx51_ops, +}; + +static int imx_ccwmx51_card_remove(struct platform_device *pdev) +{ + struct imx_ccwmx51_priv *priv = &card_priv; + struct mxc_audio_platform_data *plat; + + if (priv->pdev) { + plat = priv->pdev->dev.platform_data; + if (plat->finit) + plat->finit(); + } + + return 0; +} + +static struct snd_soc_card snd_soc_card_imx_ccwmx51 = { + .name = "imx-ccwmx51", + .platform = &imx_soc_platform, + .dai_link = &imx_ccwmx51_dai, + .num_links = 1, + .remove = imx_ccwmx51_card_remove, +}; + +static struct snd_soc_device imx_ccwmx51_snd_devdata = { + .card = &snd_soc_card_imx_ccwmx51, + .codec_dev = &soc_codec_dev_wm8753, +}; + +static int __devinit imx_ccwmx51_wm8753_probe(struct platform_device *pdev) +{ + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + struct imx_ccwmx51_priv *priv = &card_priv; + struct snd_soc_dai *wm8753_cpu_dai; + int ret = 0; + + priv->sysclk = plat->sysclk; + priv->pdev = pdev; + + imx_ccwmx51_init_dam(plat->src_port, plat->ext_port); + + if (plat->src_port == 2) + wm8753_cpu_dai = &imx_ssi_dai[2]; + else + wm8753_cpu_dai = &imx_ssi_dai[0]; + + imx_ccwmx51_dai.cpu_dai = wm8753_cpu_dai; + + ret = -EINVAL; + if (plat->init && plat->init()) + goto err_plat_init; + + return 0; + +err_plat_init: + return ret; +} + +static int imx_ccwmx51_wm8753_remove(struct platform_device *pdev) +{ + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + + if (plat->finit) + plat->finit(); + + return 0; +} + +static struct platform_driver imx_ccwmx51_wm8753_audio_driver = { + .probe = imx_ccwmx51_wm8753_probe, + .remove = imx_ccwmx51_wm8753_remove, + .driver = { + .name = "imx-ccwmx51", + }, +}; + +static struct platform_device *imx_ccwmx51_snd_device; + +static int __init imx_ccwmx51_init(void) +{ + int ret; + + ret = platform_driver_register(&imx_ccwmx51_wm8753_audio_driver); + if (ret) + return -ENOMEM; + + imx_ccwmx51_snd_device = platform_device_alloc("soc-audio", 2); + if (!imx_ccwmx51_snd_device) + return -ENOMEM; + + platform_set_drvdata(imx_ccwmx51_snd_device, &imx_ccwmx51_snd_devdata); + imx_ccwmx51_snd_devdata.dev = &imx_ccwmx51_snd_device->dev; + ret = platform_device_add(imx_ccwmx51_snd_device); + + if (ret) + platform_device_put(imx_ccwmx51_snd_device); + + return ret; +} + +static void __exit imx_ccwmx51_exit(void) +{ + platform_driver_unregister(&imx_ccwmx51_wm8753_audio_driver); + platform_device_unregister(imx_ccwmx51_snd_device); +} + +module_init(imx_ccwmx51_init); +module_exit(imx_ccwmx51_exit); + +MODULE_AUTHOR("Digi International, Inc."); +MODULE_DESCRIPTION("WM8753 Driver for i.MX CCWMX51"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1d70829464ef..d3de8902ed8e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -125,7 +125,7 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream) if (codec_dai->symmetric_rates || cpu_dai->symmetric_rates || machine->symmetric_rates) { - dev_dbg(card->dev, "Symmetry forces %dHz rate\n", + dev_dbg(card->dev, "Symmetry forces %dHz rate\n", machine->rate); ret = snd_pcm_hw_constraint_minmax(substream->runtime, @@ -2245,8 +2245,10 @@ int snd_soc_register_dai(struct snd_soc_dai *dai) return -EINVAL; /* The device should become mandatory over time */ +#ifdef DEBUG if (!dai->dev) printk(KERN_WARNING "No device for DAI %s\n", dai->name); +#endif if (!dai->ops) dai->ops = &null_dai_ops; |