1 /* 2 * pxa2xx-i2s.c -- ALSA Soc Audio Layer 3 * 4 * Copyright 2005 Wolfson Microelectronics PLC. 5 * Author: Liam Girdwood 6 * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.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 * Revision history 14 * 12th Aug 2005 Initial version. 15 */ 16 17 #include <linux/init.h> 18 #include <linux/module.h> 19 #include <linux/device.h> 20 #include <linux/delay.h> 21 #include <sound/driver.h> 22 #include <sound/core.h> 23 #include <sound/pcm.h> 24 #include <sound/initval.h> 25 #include <sound/soc.h> 26 27 #include <asm/hardware.h> 28 #include <asm/arch/pxa-regs.h> 29 #include <asm/arch/audio.h> 30 31 #include "pxa2xx-pcm.h" 32 33 /* used to disable sysclk if external crystal is used */ 34 static int extclk; 35 module_param(extclk, int, 0); 36 MODULE_PARM_DESC(extclk, "set to 1 to disable pxa2xx i2s sysclk"); 37 38 struct pxa_i2s_port { 39 u32 sadiv; 40 u32 sacr0; 41 u32 sacr1; 42 u32 saimr; 43 int master; 44 }; 45 static struct pxa_i2s_port pxa_i2s; 46 47 #define PXA_I2S_DAIFMT \ 48 (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF) 49 50 #define PXA_I2S_DIR \ 51 (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) 52 53 #define PXA_I2S_RATES \ 54 (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ 55 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ 56 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) 57 58 /* priv is divider */ 59 static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = { 60 /* pxa2xx I2S frame and clock master modes */ 61 { 62 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, 63 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, 64 .pcmrate = SNDRV_PCM_RATE_8000, 65 .pcmdir = PXA_I2S_DIR, 66 .flags = SND_SOC_DAI_BFS_DIV, 67 .fs = 256, 68 .bfs = SND_SOC_FSBD(4), 69 .priv = 0x48, 70 }, 71 { 72 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, 73 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, 74 .pcmrate = SNDRV_PCM_RATE_11025, 75 .pcmdir = PXA_I2S_DIR, 76 .flags = SND_SOC_DAI_BFS_DIV, 77 .fs = 256, 78 .bfs = SND_SOC_FSBD(4), 79 .priv = 0x34, 80 }, 81 { 82 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, 83 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, 84 .pcmrate = SNDRV_PCM_RATE_16000, 85 .pcmdir = PXA_I2S_DIR, 86 .flags = SND_SOC_DAI_BFS_DIV, 87 .fs = 256, 88 .bfs = SND_SOC_FSBD(4), 89 .priv = 0x24, 90 }, 91 { 92 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, 93 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, 94 .pcmrate = SNDRV_PCM_RATE_22050, 95 .pcmdir = PXA_I2S_DIR, 96 .flags = SND_SOC_DAI_BFS_DIV, 97 .fs = 256, 98 .bfs = SND_SOC_FSBD(4), 99 .priv = 0x1a, 100 }, 101 { 102 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, 103 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, 104 .pcmrate = SNDRV_PCM_RATE_44100, 105 .pcmdir = PXA_I2S_DIR, 106 .flags = SND_SOC_DAI_BFS_DIV, 107 .fs = 256, 108 .bfs = SND_SOC_FSBD(4), 109 .priv = 0xd, 110 }, 111 { 112 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS, 113 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, 114 .pcmrate = SNDRV_PCM_RATE_48000, 115 .pcmdir = PXA_I2S_DIR, 116 .flags = SND_SOC_DAI_BFS_DIV, 117 .fs = 256, 118 .bfs = SND_SOC_FSBD(4), 119 .priv = 0xc, 120 }, 121 122 /* pxa2xx I2S frame master and clock slave mode */ 123 { 124 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS, 125 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, 126 .pcmrate = PXA_I2S_RATES, 127 .pcmdir = PXA_I2S_DIR, 128 .fs = SND_SOC_FS_ALL, 129 .bfs = SND_SOC_FSB(64), 130 .priv = 0x48, 131 }, 132 }; 133 134 static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { 135 .name = "I2S PCM Stereo out", 136 .dev_addr = __PREG(SADR), 137 .drcmr = &DRCMRTXSADR, 138 .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | 139 DCMD_BURST32 | DCMD_WIDTH4, 140 }; 141 142 static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = { 143 .name = "I2S PCM Stereo in", 144 .dev_addr = __PREG(SADR), 145 .drcmr = &DRCMRRXSADR, 146 .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | 147 DCMD_BURST32 | DCMD_WIDTH4, 148 }; 149 150 static struct pxa2xx_gpio gpio_bus[] = { 151 { /* I2S SoC Slave */ 152 .rx = GPIO29_SDATA_IN_I2S_MD, 153 .tx = GPIO30_SDATA_OUT_I2S_MD, 154 .clk = GPIO28_BITCLK_IN_I2S_MD, 155 .frm = GPIO31_SYNC_I2S_MD, 156 }, 157 { /* I2S SoC Master */ 158 #ifdef CONFIG_PXA27x 159 .sys = GPIO113_I2S_SYSCLK_MD, 160 #else 161 .sys = GPIO32_SYSCLK_I2S_MD, 162 #endif 163 .rx = GPIO29_SDATA_IN_I2S_MD, 164 .tx = GPIO30_SDATA_OUT_I2S_MD, 165 .clk = GPIO28_BITCLK_OUT_I2S_MD, 166 .frm = GPIO31_SYNC_I2S_MD, 167 }, 168 }; 169 170 static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream) 171 { 172 struct snd_soc_pcm_runtime *rtd = substream->private_data; 173 174 if (!rtd->cpu_dai->active) { 175 SACR0 |= SACR0_RST; 176 SACR0 = 0; 177 } 178 179 return 0; 180 } 181 182 /* wait for I2S controller to be ready */ 183 static int pxa_i2s_wait(void) 184 { 185 int i; 186 187 /* flush the Rx FIFO */ 188 for(i = 0; i < 16; i++) 189 SADR; 190 return 0; 191 } 192 193 static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, 194 struct snd_pcm_hw_params *params) 195 { 196 struct snd_soc_pcm_runtime *rtd = substream->private_data; 197 198 pxa_i2s.master = 0; 199 if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CBS_CFS) 200 pxa_i2s.master = 1; 201 202 if (pxa_i2s.master && !extclk) 203 pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys); 204 205 pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx); 206 pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx); 207 pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm); 208 pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk); 209 pxa_set_cken(CKEN8_I2S, 1); 210 pxa_i2s_wait(); 211 212 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 213 rtd->cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out; 214 else 215 rtd->cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in; 216 217 /* is port used by another stream */ 218 if (!(SACR0 & SACR0_ENB)) { 219 220 SACR0 = 0; 221 SACR1 = 0; 222 if (pxa_i2s.master) 223 SACR0 |= SACR0_BCKD; 224 225 SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1); 226 227 if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_LEFT_J) 228 SACR1 |= SACR1_AMSL; 229 } 230 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 231 SAIMR |= SAIMR_TFS; 232 else 233 SAIMR |= SAIMR_RFS; 234 235 SADIV = rtd->cpu_dai->dai_runtime.priv; 236 return 0; 237 } 238 239 static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd) 240 { 241 int ret = 0; 242 243 switch (cmd) { 244 case SNDRV_PCM_TRIGGER_START: 245 SACR0 |= SACR0_ENB; 246 break; 247 case SNDRV_PCM_TRIGGER_RESUME: 248 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 249 case SNDRV_PCM_TRIGGER_STOP: 250 case SNDRV_PCM_TRIGGER_SUSPEND: 251 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 252 break; 253 default: 254 ret = -EINVAL; 255 } 256 257 return ret; 258 } 259 260 static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream) 261 { 262 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 263 SACR1 |= SACR1_DRPL; 264 SAIMR &= ~SAIMR_TFS; 265 } else { 266 SACR1 |= SACR1_DREC; 267 SAIMR &= ~SAIMR_RFS; 268 } 269 270 if (SACR1 & (SACR1_DREC | SACR1_DRPL)) { 271 SACR0 &= ~SACR0_ENB; 272 pxa_i2s_wait(); 273 pxa_set_cken(CKEN8_I2S, 0); 274 } 275 } 276 277 #ifdef CONFIG_PM 278 static int pxa2xx_i2s_suspend(struct platform_device *dev, 279 struct snd_soc_cpu_dai *dai) 280 { 281 if (!dai->active) 282 return 0; 283 284 /* store registers */ 285 pxa_i2s.sacr0 = SACR0; 286 pxa_i2s.sacr1 = SACR1; 287 pxa_i2s.saimr = SAIMR; 288 pxa_i2s.sadiv = SADIV; 289 290 /* deactivate link */ 291 SACR0 &= ~SACR0_ENB; 292 pxa_i2s_wait(); 293 return 0; 294 } 295 296 static int pxa2xx_i2s_resume(struct platform_device *pdev, 297 struct snd_soc_cpu_dai *dai) 298 { 299 if (!dai->active) 300 return 0; 301 302 pxa_i2s_wait(); 303 304 SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB; 305 SACR1 = pxa_i2s.sacr1; 306 SAIMR = pxa_i2s.saimr; 307 SADIV = pxa_i2s.sadiv; 308 SACR0 |= SACR0_ENB; 309 310 return 0; 311 } 312 313 #else 314 #define pxa2xx_i2s_suspend NULL 315 #define pxa2xx_i2s_resume NULL 316 #endif 317 318 /* pxa2xx I2S sysclock is always 256 FS */ 319 static unsigned int pxa_i2s_config_sysclk(struct snd_soc_cpu_dai *iface, 320 struct snd_soc_clock_info *info, unsigned int clk) 321 { 322 return info->rate << 8; 323 } 324 325 struct snd_soc_cpu_dai pxa_i2s_dai = { 326 .name = "pxa2xx-i2s", 327 .id = 0, 328 .type = SND_SOC_DAI_I2S, 329 .suspend = pxa2xx_i2s_suspend, 330 .resume = pxa2xx_i2s_resume, 331 .config_sysclk = pxa_i2s_config_sysclk, 332 .playback = { 333 .channels_min = 2, 334 .channels_max = 2,}, 335 .capture = { 336 .channels_min = 2, 337 .channels_max = 2,}, 338 .ops = { 339 .startup = pxa2xx_i2s_startup, 340 .shutdown = pxa2xx_i2s_shutdown, 341 .trigger = pxa2xx_i2s_trigger, 342 .hw_params = pxa2xx_i2s_hw_params,}, 343 .caps = { 344 .num_modes = ARRAY_SIZE(pxa2xx_i2s_modes), 345 .mode = pxa2xx_i2s_modes,}, 346 }; 347 348 EXPORT_SYMBOL_GPL(pxa_i2s_dai); 349 350 /* Module information */ 351 MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); 352 MODULE_DESCRIPTION("pxa2xx I2S SoC Interface"); 353 MODULE_LICENSE("GPL"); 354