From 8535e606799e4a538c13025fc31fda811549b88e Mon Sep 17 00:00:00 2001 From: Gary Zhang Date: Fri, 23 Mar 2012 16:50:45 +0800 Subject: [PATCH] ENGR00177581-3 WM8962: add wm8962 codec driver add wm8962 audio codec driver Signed-off-by: Gary Zhang --- sound/soc/codecs/wm8962.c | 2 +- sound/soc/imx/Kconfig | 8 + sound/soc/imx/Makefile | 4 +- sound/soc/imx/imx-wm8958.c | 5 - sound/soc/imx/imx-wm8962.c | 354 +++++++++++++++++++++++++++++++++++++ 5 files changed, 366 insertions(+), 7 deletions(-) create mode 100644 sound/soc/imx/imx-wm8962.c diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index c850e3d84ed0..1c91a590d3db 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3834,7 +3834,7 @@ static int wm8962_probe(struct snd_soc_codec *codec) WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, 0); - regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies); +/* regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies);*/ if (pdata) { /* Apply static configuration for GPIOs */ diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index 4f95bacebae6..35472e835cf9 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig @@ -69,6 +69,14 @@ config SND_SOC_IMX_WM8958 Say Y if you want to add support for SoC audio on an i.MX board with a WM8958 codec. +config SND_SOC_IMX_WM8962 + tristate "SoC Audio support for IMX boards with WM8958" + select SND_MXC_SOC_MX2 + select SND_SOC_WM8962 + help + Say Y if you want to add support for SoC audio on an i.MX board with + a WM8958 codec. + config SND_SOC_IMX_CS42888 tristate "SoC Audio support for i.MX boards with cs42888" depends on I2C && (MACH_MX6Q_ARM2 || MACH_MX6Q_SABREAUTO || MACH_MX53_ARD) diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile index 54cc5661446d..3df711671b15 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile @@ -14,17 +14,19 @@ snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o snd-soc-phycore-ac97-objs := phycore-ac97.o snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o +snd-soc-imx-wm8958-objs := imx-wm8958.o +snd-soc-imx-wm8962-objs := imx-wm8962.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o snd-soc-imx-cs42888-objs := imx-cs42888.o snd-soc-imx-spdif-objs := imx-spdif.o snd-soc-imx-hdmi-objs := imx-hdmi.o imx-hdmi-dai.o imx-hdmi-dma.o -snd-soc-imx-wm8958-objs := imx-wm8958.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o obj-$(CONFIG_SND_SOC_IMX_WM8958) += snd-soc-imx-wm8958.o +obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o obj-$(CONFIG_SND_SOC_IMX_CS42888) += snd-soc-imx-cs42888.o obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o diff --git a/sound/soc/imx/imx-wm8958.c b/sound/soc/imx/imx-wm8958.c index 64ff33e5e418..dc7a1f59b37c 100755 --- a/sound/soc/imx/imx-wm8958.c +++ b/sound/soc/imx/imx-wm8958.c @@ -105,11 +105,6 @@ static int imx_hifi_hw_params(struct snd_pcm_substream *substream, int ret = 0; unsigned int pll_out; u32 dai_format; - /* only need to do this once as capture and playback are sync */ - - if (priv->dai_hifi) - return 0; - priv->dai_hifi = 1; dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM; diff --git a/sound/soc/imx/imx-wm8962.c b/sound/soc/imx/imx-wm8962.c new file mode 100644 index 000000000000..3154cbcd7dfc --- /dev/null +++ b/sound/soc/imx/imx-wm8962.c @@ -0,0 +1,354 @@ +/* + * imx-wm8962.c + * + * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "imx-ssi.h" +#include "../codecs/wm8962.h" + +struct imx_priv { + int sysclk; /*mclk from the outside*/ + int codec_sysclk; + int dai_hifi; + struct platform_device *pdev; +}; + +static struct imx_priv card_priv; +static struct snd_soc_card snd_soc_card_imx; +struct clk *wm8962_mclk; +static struct snd_soc_jack hs_jack; + +/* Headphones jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +/* Headphones jack detection gpios */ +static struct snd_soc_jack_gpio hs_jack_gpios[] = { + [0] = { + /* gpio is set on per-platform basis */ + .name = "hp-gpio", + .report = SND_JACK_HEADPHONE, + .debounce_time = 200, + }, +}; + +static int imx_hifi_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + if (!codec_dai->active) + clk_enable(wm8962_mclk); + + return 0; +} + +static void imx_hifi_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + if (!codec_dai->active) + clk_disable(wm8962_mclk); + + return; +} + +static int imx_hifi_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 *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct imx_priv *priv = &card_priv; + unsigned int channels = params_channels(params); + unsigned int sample_rate = 44100; + int ret = 0; + u32 dai_format; + + dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + + /* 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, + channels == 1 ? 0xfffffffe : 0xfffffffc, + 2, 32); + + /* set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); + if (ret < 0) + return ret; + + sample_rate = params_rate(params); + + ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL_INT, + WM8962_FLL_MCLK, priv->sysclk, + sample_rate * 768); + if (ret < 0) + pr_err("Failed to start FLL: %d\n", ret); + + ret = snd_soc_dai_set_sysclk(codec_dai, + WM8962_SYSCLK_FLL, + sample_rate * 768, + SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err("Failed to set SYSCLK: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Main Speaker"), + SOC_DAPM_PIN_SWITCH("DMIC"), +}; + +/* imx card dapm widgets */ +static const struct snd_soc_dapm_widget imx_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + + SND_SOC_DAPM_MIC("AMIC", NULL), + + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +/* imx machine connections to the codec pins */ +static const struct snd_soc_dapm_route audio_map[] = { + { "Headphone Jack", NULL, "HPOUTL" }, + { "Headphone Jack", NULL, "HPOUTR" }, + + { "Ext Spk", NULL, "SPKOUTL" }, + { "Ext Spk", NULL, "SPKOUTR" }, + + { "MICBIAS", NULL, "AMIC" }, + { "IN3R", NULL, "MICBIAS" }, + +}; + +static int imx_wm8962_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + int ret; + +/* Add imx specific widgets */ + snd_soc_dapm_new_controls(&codec->dapm, imx_dapm_widgets, + ARRAY_SIZE(imx_dapm_widgets)); + + /* Set up imx specific audio path audio_map */ + snd_soc_dapm_add_routes(&codec->dapm, audio_map, ARRAY_SIZE(audio_map)); + + snd_soc_dapm_enable_pin(&codec->dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(&codec->dapm, "AMIC"); + + snd_soc_dapm_sync(&codec->dapm); + + if (hs_jack_gpios[0].gpio != -1) { + /* Jack detection API stuff */ + ret = snd_soc_jack_new(codec, "Headphone Jack", + SND_JACK_HEADPHONE, &hs_jack); + if (ret) + return ret; + + ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), + hs_jack_pins); + if (ret) { + printk(KERN_ERR "failed to call snd_soc_jack_add_pins\n"); + return ret; + } + + ret = snd_soc_jack_add_gpios(&hs_jack, + ARRAY_SIZE(hs_jack_gpios), hs_jack_gpios); + if (ret) + printk(KERN_WARNING "failed to call snd_soc_jack_add_gpios\n"); + } + + return 0; +} + +static struct snd_soc_ops imx_hifi_ops = { + .startup = imx_hifi_startup, + .shutdown = imx_hifi_shutdown, + .hw_params = imx_hifi_hw_params, +}; + +static struct snd_soc_dai_link imx_dai[] = { + { + .name = "HiFi", + .stream_name = "HiFi", + .codec_dai_name = "wm8962", + .codec_name = "wm8962.0-001a", + .cpu_dai_name = "imx-ssi.1", + .platform_name = "imx-pcm-audio.1", + .init = imx_wm8962_init, + .ops = &imx_hifi_ops, + }, +}; + +static struct snd_soc_card snd_soc_card_imx = { + .name = "wm8962-audio", + .dai_link = imx_dai, + .num_links = ARRAY_SIZE(imx_dai), +}; + +static int imx_audmux_config(int slave, int master) +{ + unsigned int ptcr, pdcr; + slave = slave - 1; + master = master - 1; + + ptcr = MXC_AUDMUX_V2_PTCR_SYN | + MXC_AUDMUX_V2_PTCR_TFSDIR | + MXC_AUDMUX_V2_PTCR_TFSEL(master) | + MXC_AUDMUX_V2_PTCR_TCLKDIR | + MXC_AUDMUX_V2_PTCR_TCSEL(master); + pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(master); + mxc_audmux_v2_configure_port(slave, ptcr, pdcr); + + ptcr = MXC_AUDMUX_V2_PTCR_SYN; + pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(slave); + mxc_audmux_v2_configure_port(master, ptcr, pdcr); + + return 0; +} + +/* + * This function will register the snd_soc_pcm_link drivers. + */ +static int __devinit imx_wm8962_probe(struct platform_device *pdev) +{ + + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + struct imx_priv *priv = &card_priv; + int ret = 0; + + wm8962_mclk = clk_get(NULL, "clko_clk"); + if (IS_ERR(wm8962_mclk)) { + printk(KERN_ERR "can't get CLKO clock.\n"); + return PTR_ERR(wm8962_mclk); + } + + priv->pdev = pdev; + + imx_audmux_config(plat->src_port, plat->ext_port); + + if (plat->init && plat->init()) { + ret = -EINVAL; + return ret; + } + + priv->sysclk = plat->sysclk; + hs_jack_gpios[0].gpio = plat->hp_gpio; + hs_jack_gpios[0].invert = plat->hp_active_low; + + return ret; +} + +static int __devexit imx_wm8962_remove(struct platform_device *pdev) +{ + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + + if (plat->finit) + plat->finit(); + + clk_disable(wm8962_mclk); + clk_put(wm8962_mclk); + + return 0; +} + +static struct platform_driver imx_wm8962_driver = { + .probe = imx_wm8962_probe, + .remove = imx_wm8962_remove, + .driver = { + .name = "imx-wm8962", + .owner = THIS_MODULE, + }, +}; + +static struct platform_device *imx_snd_device; + +static int __init imx_asoc_init(void) +{ + int ret; + + ret = platform_driver_register(&imx_wm8962_driver); + if (ret < 0) + goto exit; + + imx_snd_device = platform_device_alloc("soc-audio", 5); + if (!imx_snd_device) + goto err_device_alloc; + + platform_set_drvdata(imx_snd_device, &snd_soc_card_imx); + + ret = platform_device_add(imx_snd_device); + + if (0 == ret) + goto exit; + + platform_device_put(imx_snd_device); + +err_device_alloc: + platform_driver_unregister(&imx_wm8962_driver); +exit: + return ret; +} + +static void __exit imx_asoc_exit(void) +{ + platform_driver_unregister(&imx_wm8962_driver); + platform_device_unregister(imx_snd_device); +} + +module_init(imx_asoc_init); +module_exit(imx_asoc_exit); + +/* Module information */ +MODULE_DESCRIPTION("ALSA SoC imx wm8962"); +MODULE_LICENSE("GPL"); -- 2.39.2