From 49c0b7bee377a056fe747d8a909895a6d3d18c6c Mon Sep 17 00:00:00 2001 From: Gary Zhang Date: Tue, 28 Feb 2012 14:51:40 +0800 Subject: [PATCH] ENGR00175219-3 wm8958: add audio codec support add wm8958 audio codec support Signed-off-by: Gary Zhang --- drivers/mfd/wm8994-core.c | 21 ++- sound/soc/codecs/wm8994.c | 7 +- sound/soc/imx/Kconfig | 9 + sound/soc/imx/Makefile | 2 + sound/soc/imx/imx-wm8958.c | 345 +++++++++++++++++++++++++++++++++++++ 5 files changed, 378 insertions(+), 6 deletions(-) create mode 100755 sound/soc/imx/imx-wm8958.c diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index e198d40292e7..17dcc13ab67a 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -254,7 +254,7 @@ static const char *wm8994_main_supplies[] = { }; static const char *wm8958_main_supplies[] = { - "DBVDD1", +/* "DBVDD1", "DBVDD2", "DBVDD3", "DCVDD", @@ -262,7 +262,7 @@ static const char *wm8958_main_supplies[] = { "AVDD2", "CPVDD", "SPKVDD1", - "SPKVDD2", + "SPKVDD2",*/ }; #ifdef CONFIG_PM @@ -585,6 +585,21 @@ static int wm8994_i2c_read_device(struct wm8994 *wm8994, unsigned short reg, static int wm8994_i2c_write_device(struct wm8994 *wm8994, unsigned short reg, int bytes, const void *src) { + struct i2c_client *i2c = wm8994->control_data; + unsigned char msg[bytes + 2]; + int ret; + + reg = cpu_to_be16(reg); + memcpy(&msg[0], ®, 2); + memcpy(&msg[2], src, bytes); + + ret = i2c_master_send(i2c, msg, bytes + 2); + if (ret < 0) + return ret; + if (ret < bytes + 2) + return -EIO; + +#if 0 struct i2c_client *i2c = wm8994->control_data; struct i2c_msg xfer[2]; int ret; @@ -606,7 +621,7 @@ static int wm8994_i2c_write_device(struct wm8994 *wm8994, unsigned short reg, return ret; if (ret != 2) return -EIO; - +#endif return 0; } diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 219491237875..9e5bb4b79a2b 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -55,7 +55,7 @@ static int wm8994_retune_mobile_base[] = { static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg) { - struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); +typedef struct wm8994_priv *wm8994; struct wm8994 *control = codec->control_data; switch (reg) { @@ -1968,6 +1968,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) default: return -EINVAL; } + ms |= WM8994_AIF1_LRCLK_FRC; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: @@ -2363,7 +2364,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .id = 1, .playback = { .stream_name = "AIF1 Playback", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, @@ -2371,7 +2372,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .capture = { .stream_name = "AIF1 Capture", .channels_min = 1, - .channels_max = 2, + .channels_max = 1, .rates = WM8994_RATES, .formats = WM8994_FORMATS, }, diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index 89eecb92f929..4f95bacebae6 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig @@ -60,6 +60,15 @@ config SND_SOC_IMX_SGTL5000 Say Y if you want to add support for SoC audio on an i.MX board with a sgtl5000 codec. +config SND_SOC_IMX_WM8958 + tristate "SoC Audio support for IMX boards with WM8958" + depends on MFD_WM8994 + select SND_MXC_SOC_MX2 + select SND_SOC_WM8994 + 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 07153b90d13e..c1abab092adc 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile @@ -18,6 +18,7 @@ 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 @@ -27,3 +28,4 @@ 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 obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o +obj-$(CONFIG_SND_SOC_IMX_WM8958) += snd-soc-imx-wm8958.o \ No newline at end of file diff --git a/sound/soc/imx/imx-wm8958.c b/sound/soc/imx/imx-wm8958.c new file mode 100755 index 000000000000..aadc51822dc0 --- /dev/null +++ b/sound/soc/imx/imx-wm8958.c @@ -0,0 +1,345 @@ +/* + * imx-wm8958.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 "imx-ssi.h" +#include "../codecs/wm8994.h" +#include + +struct imx_priv { + int sysclk; /*mclk from the outside*/ + int codec_sysclk; + int dai_hifi; + struct platform_device *pdev; + struct wm8994 *wm8958; +}; +static struct imx_priv card_priv; +static struct snd_soc_card snd_soc_card_imx; + +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) +{ + return 0; +} + +static void imx_hifi_shutdown(struct snd_pcm_substream *substream) +{ + 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); + 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; + + /* 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; + + if (params_rate(params) == 8000 || params_rate(params) == 11025) + pll_out = params_rate(params) * 512; + else + pll_out = params_rate(params) * 256; + + ret = + snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, + priv->sysclk, pll_out); + if (ret < 0) + return ret; + + ret = + snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, pll_out, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + return 0; +} + +/* imx card dapm widgets */ +static const struct snd_soc_dapm_widget imx_dapm_widgets[] = { + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Main Mic", NULL), + SND_SOC_DAPM_HP("Headset Phone", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +/* imx machine connections to the codec pins */ +static const struct snd_soc_dapm_route audio_map[] = { + /* ----input ------------------- */ + /* Mic Jack --> MIC_IN (with automatic bias) */ + {"MICBIAS2", NULL, "Headset Mic"}, + {"IN1LP", NULL, "MICBIAS2"}, + {"IN1LN", NULL, "Headset Mic"}, + + {"MICBIAS1", NULL, "Main Mic"}, + {"IN1RP", NULL, "MICBIAS1"}, + {"IN1RN", NULL, "Main Mic"}, + + /* ----output------------------- */ + /* HP_OUT --> Headphone Jack */ + {"Headset Phone", NULL, "HPOUT1L"}, + {"Headset Phone", NULL, "HPOUT1R"}, + + /* LINE_OUT --> Ext Speaker */ + {"Ext Spk", NULL, "SPKOUTLP"}, + {"Ext Spk", NULL, "SPKOUTLN"}, + {"Ext Spk", NULL, "SPKOUTRP"}, + {"Ext Spk", NULL, "SPKOUTRN"}, + +}; + +static int imx_wm8958_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, "Headset Phone"); + + 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 = "wm8994-aif1", + .codec_name = "wm8994-codec", + .cpu_dai_name = "imx-ssi.1", + .platform_name = "imx-pcm-audio.1", + .init = imx_wm8958_init, + .ops = &imx_hifi_ops, + }, +}; + +static struct snd_soc_card snd_soc_card_imx = { + .name = "wm8958-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_wm8958_probe(struct platform_device *pdev) +{ + + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + struct imx_priv *priv = &card_priv; + struct wm8994 *wm8958 = plat->priv; + int ret = 0; + + priv->pdev = pdev; + priv->wm8958 = wm8958; + + 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_wm8958_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_wm8958_driver = { + .probe = imx_wm8958_probe, + .remove = imx_wm8958_remove, + .driver = { + .name = "imx-wm8958", + .owner = THIS_MODULE, + }, +}; + +static struct platform_device *imx_snd_device; + +static int __init imx_asoc_init(void) +{ + int ret; + + ret = platform_driver_register(&imx_wm8958_driver); + if (ret < 0) + goto exit; + + imx_snd_device = platform_device_alloc("soc-audio", -1); + 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_wm8958_driver); +exit: + return ret; +} + +static void __exit imx_asoc_exit(void) +{ + platform_driver_unregister(&imx_wm8958_driver); + platform_device_unregister(imx_snd_device); +} + +module_init(imx_asoc_init); +module_exit(imx_asoc_exit); + +/* Module information */ +MODULE_DESCRIPTION("ALSA SoC imx wm8958"); +MODULE_LICENSE("GPL"); -- 2.39.5