diff options
| -rw-r--r-- | sound/soc/imx/Kconfig | 8 | ||||
| -rw-r--r-- | sound/soc/imx/Makefile | 4 | ||||
| -rw-r--r-- | sound/soc/imx/mx27vis_wm8974.c | 317 |
3 files changed, 329 insertions, 0 deletions
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index 886dadd76bb2..565e32754087 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig | |||
| @@ -9,5 +9,13 @@ config SND_MX1_MX2_SOC | |||
| 9 | config SND_MXC_SOC_SSI | 9 | config SND_MXC_SOC_SSI |
| 10 | tristate | 10 | tristate |
| 11 | 11 | ||
| 12 | config SND_SOC_MX27VIS_WM8974 | ||
| 13 | tristate "SoC Audio support for MX27 - WM8974 Visstrim_sm10 board" | ||
| 14 | depends on SND_MX1_MX2_SOC && MACH_MX27 && MACH_IMX27_VISSTRIM_M10 | ||
| 15 | select SND_MXC_SOC_SSI | ||
| 16 | select SND_SOC_WM8974 | ||
| 17 | help | ||
| 18 | Say Y if you want to add support for SoC audio on Visstrim SM10 | ||
| 19 | board with WM8974. | ||
| 12 | 20 | ||
| 13 | 21 | ||
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile index 6552cb202bcc..c2ffd2c8df5a 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile | |||
| @@ -4,3 +4,7 @@ snd-soc-mxc-ssi-objs := mxc-ssi.o | |||
| 4 | 4 | ||
| 5 | obj-$(CONFIG_SND_MX1_MX2_SOC) += snd-soc-mx1_mx2.o | 5 | obj-$(CONFIG_SND_MX1_MX2_SOC) += snd-soc-mx1_mx2.o |
| 6 | obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-mxc-ssi.o | 6 | obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-mxc-ssi.o |
| 7 | |||
| 8 | # i.MX Machine Support | ||
| 9 | snd-soc-mx27vis-wm8974-objs := mx27vis_wm8974.o | ||
| 10 | obj-$(CONFIG_SND_SOC_MX27VIS_WM8974) += snd-soc-mx27vis-wm8974.o | ||
diff --git a/sound/soc/imx/mx27vis_wm8974.c b/sound/soc/imx/mx27vis_wm8974.c new file mode 100644 index 000000000000..e4dcb539108a --- /dev/null +++ b/sound/soc/imx/mx27vis_wm8974.c | |||
| @@ -0,0 +1,317 @@ | |||
| 1 | /* | ||
| 2 | * mx27vis_wm8974.c -- SoC audio for mx27vis | ||
| 3 | * | ||
| 4 | * Copyright 2009 Vista Silicon S.L. | ||
| 5 | * Author: Javier Martin | ||
| 6 | * javier.martin@vista-silicon.com | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License as published by the | ||
| 10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 11 | * option) any later version. | ||
| 12 | * | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/moduleparam.h> | ||
| 17 | #include <linux/device.h> | ||
| 18 | #include <linux/i2c.h> | ||
| 19 | #include <sound/core.h> | ||
| 20 | #include <sound/pcm.h> | ||
| 21 | #include <sound/soc.h> | ||
| 22 | #include <sound/soc-dapm.h> | ||
| 23 | |||
| 24 | |||
| 25 | #include "../codecs/wm8974.h" | ||
| 26 | #include "mx1_mx2-pcm.h" | ||
| 27 | #include "mxc-ssi.h" | ||
| 28 | #include <mach/gpio.h> | ||
| 29 | #include <mach/iomux.h> | ||
| 30 | |||
| 31 | #define IGNORED_ARG 0 | ||
| 32 | |||
| 33 | |||
| 34 | static struct snd_soc_card mx27vis; | ||
| 35 | |||
| 36 | /** | ||
| 37 | * This function connects SSI1 (HPCR1) as slave to | ||
| 38 | * SSI1 external signals (PPCR1) | ||
| 39 | * As slave, HPCR1 must set TFSDIR and TCLKDIR as inputs from | ||
| 40 | * port 4 | ||
| 41 | */ | ||
| 42 | void audmux_connect_1_4(void) | ||
| 43 | { | ||
| 44 | pr_debug("AUDMUX: normal operation mode\n"); | ||
| 45 | /* Reset HPCR1 and PPCR1 */ | ||
| 46 | |||
| 47 | DAM_HPCR1 = 0x00000000; | ||
| 48 | DAM_PPCR1 = 0x00000000; | ||
| 49 | |||
| 50 | /* set to synchronous */ | ||
| 51 | DAM_HPCR1 |= AUDMUX_HPCR_SYN; | ||
| 52 | DAM_PPCR1 |= AUDMUX_PPCR_SYN; | ||
| 53 | |||
| 54 | |||
| 55 | /* set Rx sources 1 <--> 4 */ | ||
| 56 | DAM_HPCR1 |= AUDMUX_HPCR_RXDSEL(3); /* port 4 */ | ||
| 57 | DAM_PPCR1 |= AUDMUX_PPCR_RXDSEL(0); /* port 1 */ | ||
| 58 | |||
| 59 | /* set Tx frame and Clock direction and source 4 --> 1 output */ | ||
| 60 | DAM_HPCR1 |= AUDMUX_HPCR_TFSDIR | AUDMUX_HPCR_TCLKDIR; | ||
| 61 | DAM_HPCR1 |= AUDMUX_HPCR_TFCSEL(3); /* TxDS and TxCclk from port 4 */ | ||
| 62 | |||
| 63 | return; | ||
| 64 | } | ||
| 65 | |||
| 66 | static int mx27vis_hifi_hw_params(struct snd_pcm_substream *substream, | ||
| 67 | struct snd_pcm_hw_params *params) | ||
| 68 | { | ||
| 69 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 70 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
| 71 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
| 72 | unsigned int pll_out = 0, bclk = 0, fmt = 0, mclk = 0; | ||
| 73 | int ret = 0; | ||
| 74 | |||
| 75 | /* | ||
| 76 | * The WM8974 is better at generating accurate audio clocks than the | ||
| 77 | * MX27 SSI controller, so we will use it as master when we can. | ||
| 78 | */ | ||
| 79 | switch (params_rate(params)) { | ||
| 80 | case 8000: | ||
| 81 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
| 82 | mclk = WM8974_MCLKDIV_12; | ||
| 83 | pll_out = 24576000; | ||
| 84 | break; | ||
| 85 | case 16000: | ||
| 86 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
| 87 | pll_out = 12288000; | ||
| 88 | break; | ||
| 89 | case 48000: | ||
| 90 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
| 91 | bclk = WM8974_BCLKDIV_4; | ||
| 92 | pll_out = 12288000; | ||
| 93 | break; | ||
| 94 | case 96000: | ||
| 95 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
| 96 | bclk = WM8974_BCLKDIV_2; | ||
| 97 | pll_out = 12288000; | ||
| 98 | break; | ||
| 99 | case 11025: | ||
| 100 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
| 101 | bclk = WM8974_BCLKDIV_16; | ||
| 102 | pll_out = 11289600; | ||
| 103 | break; | ||
| 104 | case 22050: | ||
| 105 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
| 106 | bclk = WM8974_BCLKDIV_8; | ||
| 107 | pll_out = 11289600; | ||
| 108 | break; | ||
| 109 | case 44100: | ||
| 110 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
| 111 | bclk = WM8974_BCLKDIV_4; | ||
| 112 | mclk = WM8974_MCLKDIV_2; | ||
| 113 | pll_out = 11289600; | ||
| 114 | break; | ||
| 115 | case 88200: | ||
| 116 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
| 117 | bclk = WM8974_BCLKDIV_2; | ||
| 118 | pll_out = 11289600; | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | |||
| 122 | /* set codec DAI configuration */ | ||
| 123 | ret = codec_dai->ops->set_fmt(codec_dai, | ||
| 124 | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | | ||
| 125 | SND_SOC_DAIFMT_SYNC | fmt); | ||
| 126 | if (ret < 0) { | ||
| 127 | printk(KERN_ERR "Error from codec DAI configuration\n"); | ||
| 128 | return ret; | ||
| 129 | } | ||
| 130 | |||
| 131 | /* set cpu DAI configuration */ | ||
| 132 | ret = cpu_dai->ops->set_fmt(cpu_dai, | ||
| 133 | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | ||
| 134 | SND_SOC_DAIFMT_SYNC | fmt); | ||
| 135 | if (ret < 0) { | ||
| 136 | printk(KERN_ERR "Error from cpu DAI configuration\n"); | ||
| 137 | return ret; | ||
| 138 | } | ||
| 139 | |||
| 140 | /* Put DC field of STCCR to 1 (not zero) */ | ||
| 141 | ret = cpu_dai->ops->set_tdm_slot(cpu_dai, 0, 2); | ||
| 142 | |||
| 143 | /* set the SSI system clock as input */ | ||
| 144 | ret = cpu_dai->ops->set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, | ||
| 145 | SND_SOC_CLOCK_IN); | ||
| 146 | if (ret < 0) { | ||
| 147 | printk(KERN_ERR "Error when setting system SSI clk\n"); | ||
| 148 | return ret; | ||
| 149 | } | ||
| 150 | |||
| 151 | /* set codec BCLK division for sample rate */ | ||
| 152 | ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_BCLKDIV, bclk); | ||
| 153 | if (ret < 0) { | ||
| 154 | printk(KERN_ERR "Error when setting BCLK division\n"); | ||
| 155 | return ret; | ||
| 156 | } | ||
| 157 | |||
| 158 | |||
| 159 | /* codec PLL input is 25 MHz */ | ||
| 160 | ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, | ||
| 161 | 25000000, pll_out); | ||
| 162 | if (ret < 0) { | ||
| 163 | printk(KERN_ERR "Error when setting PLL input\n"); | ||
| 164 | return ret; | ||
| 165 | } | ||
| 166 | |||
| 167 | /*set codec MCLK division for sample rate */ | ||
| 168 | ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_MCLKDIV, mclk); | ||
| 169 | if (ret < 0) { | ||
| 170 | printk(KERN_ERR "Error when setting MCLK division\n"); | ||
| 171 | return ret; | ||
| 172 | } | ||
| 173 | |||
| 174 | return 0; | ||
| 175 | } | ||
| 176 | |||
| 177 | static int mx27vis_hifi_hw_free(struct snd_pcm_substream *substream) | ||
| 178 | { | ||
| 179 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 180 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
| 181 | |||
| 182 | /* disable the PLL */ | ||
| 183 | return codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, 0, 0); | ||
| 184 | } | ||
| 185 | |||
| 186 | /* | ||
| 187 | * mx27vis WM8974 HiFi DAI opserations. | ||
| 188 | */ | ||
| 189 | static struct snd_soc_ops mx27vis_hifi_ops = { | ||
| 190 | .hw_params = mx27vis_hifi_hw_params, | ||
| 191 | .hw_free = mx27vis_hifi_hw_free, | ||
| 192 | }; | ||
| 193 | |||
| 194 | |||
| 195 | static int mx27vis_suspend(struct platform_device *pdev, pm_message_t state) | ||
| 196 | { | ||
| 197 | return 0; | ||
| 198 | } | ||
| 199 | |||
| 200 | static int mx27vis_resume(struct platform_device *pdev) | ||
| 201 | { | ||
| 202 | return 0; | ||
| 203 | } | ||
| 204 | |||
| 205 | static int mx27vis_probe(struct platform_device *pdev) | ||
| 206 | { | ||
| 207 | int ret = 0; | ||
| 208 | |||
| 209 | ret = get_ssi_clk(0, &pdev->dev); | ||
| 210 | |||
| 211 | if (ret < 0) { | ||
| 212 | printk(KERN_ERR "%s: cant get ssi clock\n", __func__); | ||
| 213 | return ret; | ||
| 214 | } | ||
| 215 | |||
| 216 | |||
| 217 | return 0; | ||
| 218 | } | ||
| 219 | |||
| 220 | static int mx27vis_remove(struct platform_device *pdev) | ||
| 221 | { | ||
| 222 | put_ssi_clk(0); | ||
| 223 | return 0; | ||
| 224 | } | ||
| 225 | |||
| 226 | static struct snd_soc_dai_link mx27vis_dai[] = { | ||
| 227 | { /* Hifi Playback*/ | ||
| 228 | .name = "WM8974", | ||
| 229 | .stream_name = "WM8974 HiFi", | ||
| 230 | .cpu_dai = &imx_ssi_pcm_dai[0], | ||
| 231 | .codec_dai = &wm8974_dai, | ||
| 232 | .ops = &mx27vis_hifi_ops, | ||
| 233 | }, | ||
| 234 | }; | ||
| 235 | |||
| 236 | static struct snd_soc_card mx27vis = { | ||
| 237 | .name = "mx27vis", | ||
| 238 | .platform = &mx1_mx2_soc_platform, | ||
| 239 | .probe = mx27vis_probe, | ||
| 240 | .remove = mx27vis_remove, | ||
| 241 | .suspend_pre = mx27vis_suspend, | ||
| 242 | .resume_post = mx27vis_resume, | ||
| 243 | .dai_link = mx27vis_dai, | ||
| 244 | .num_links = ARRAY_SIZE(mx27vis_dai), | ||
| 245 | }; | ||
| 246 | |||
| 247 | static struct snd_soc_device mx27vis_snd_devdata = { | ||
| 248 | .card = &mx27vis, | ||
| 249 | .codec_dev = &soc_codec_dev_wm8974, | ||
| 250 | }; | ||
| 251 | |||
| 252 | static struct platform_device *mx27vis_snd_device; | ||
| 253 | |||
| 254 | /* Temporal definition of board specific behaviour */ | ||
| 255 | void gpio_ssi_active(int ssi_num) | ||
| 256 | { | ||
| 257 | int ret = 0; | ||
| 258 | |||
| 259 | unsigned int ssi1_pins[] = { | ||
| 260 | PC20_PF_SSI1_FS, | ||
| 261 | PC21_PF_SSI1_RXD, | ||
| 262 | PC22_PF_SSI1_TXD, | ||
| 263 | PC23_PF_SSI1_CLK, | ||
| 264 | }; | ||
| 265 | unsigned int ssi2_pins[] = { | ||
| 266 | PC24_PF_SSI2_FS, | ||
| 267 | PC25_PF_SSI2_RXD, | ||
| 268 | PC26_PF_SSI2_TXD, | ||
| 269 | PC27_PF_SSI2_CLK, | ||
| 270 | }; | ||
| 271 | if (ssi_num == 0) | ||
| 272 | ret = mxc_gpio_setup_multiple_pins(ssi1_pins, | ||
| 273 | ARRAY_SIZE(ssi1_pins), "USB OTG"); | ||
| 274 | else | ||
| 275 | ret = mxc_gpio_setup_multiple_pins(ssi2_pins, | ||
| 276 | ARRAY_SIZE(ssi2_pins), "USB OTG"); | ||
| 277 | if (ret) | ||
| 278 | printk(KERN_ERR "Error requesting ssi %x pins\n", ssi_num); | ||
| 279 | } | ||
| 280 | |||
| 281 | |||
| 282 | static int __init mx27vis_init(void) | ||
| 283 | { | ||
| 284 | int ret; | ||
| 285 | |||
| 286 | mx27vis_snd_device = platform_device_alloc("soc-audio", -1); | ||
| 287 | if (!mx27vis_snd_device) | ||
| 288 | return -ENOMEM; | ||
| 289 | |||
| 290 | platform_set_drvdata(mx27vis_snd_device, &mx27vis_snd_devdata); | ||
| 291 | mx27vis_snd_devdata.dev = &mx27vis_snd_device->dev; | ||
| 292 | ret = platform_device_add(mx27vis_snd_device); | ||
| 293 | |||
| 294 | if (ret) { | ||
| 295 | printk(KERN_ERR "ASoC: Platform device allocation failed\n"); | ||
| 296 | platform_device_put(mx27vis_snd_device); | ||
| 297 | } | ||
| 298 | |||
| 299 | /* WM8974 uses SSI1 (HPCR1) via AUDMUX port 4 for audio (PPCR1) */ | ||
| 300 | gpio_ssi_active(0); | ||
| 301 | audmux_connect_1_4(); | ||
| 302 | |||
| 303 | return ret; | ||
| 304 | } | ||
| 305 | |||
| 306 | static void __exit mx27vis_exit(void) | ||
| 307 | { | ||
| 308 | /* We should call some "ssi_gpio_inactive()" properly */ | ||
| 309 | } | ||
| 310 | |||
| 311 | module_init(mx27vis_init); | ||
| 312 | module_exit(mx27vis_exit); | ||
| 313 | |||
| 314 | |||
| 315 | MODULE_AUTHOR("Javier Martin, javier.martin@vista-silicon.com"); | ||
| 316 | MODULE_DESCRIPTION("ALSA SoC WM8974 mx27vis"); | ||
| 317 | MODULE_LICENSE("GPL"); | ||
