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 14 #include <linux/init.h> 15 #include <linux/module.h> 16 #include <linux/device.h> 17 #include <linux/delay.h> 18 #include <linux/clk.h> 19 #include <linux/platform_device.h> 20 #include <sound/core.h> 21 #include <sound/pcm.h> 22 #include <sound/initval.h> 23 #include <sound/soc.h> 24 25 #include <mach/hardware.h> 26 #include <mach/pxa-regs.h> 27 #include <mach/pxa2xx-gpio.h> 28 #include <mach/audio.h> 29 30 #include "pxa2xx-pcm.h" 31 #include "pxa2xx-i2s.h" 32 33 struct pxa_i2s_port { 34 u32 sadiv; 35 u32 sacr0; 36 u32 sacr1; 37 u32 saimr; 38 int master; 39 u32 fmt; 40 }; 41 static struct pxa_i2s_port pxa_i2s; 42 static struct clk *clk_i2s; 43 44 static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { 45 .name = "I2S PCM Stereo out", 46 .dev_addr = __PREG(SADR), 47 .drcmr = &DRCMR(3), 48 .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | 49 DCMD_BURST32 | DCMD_WIDTH4, 50 }; 51 52 static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = { 53 .name = "I2S PCM Stereo in", 54 .dev_addr = __PREG(SADR), 55 .drcmr = &DRCMR(2), 56 .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | 57 DCMD_BURST32 | DCMD_WIDTH4, 58 }; 59 60 static struct pxa2xx_gpio gpio_bus[] = { 61 { /* I2S SoC Slave */ 62 .rx = GPIO29_SDATA_IN_I2S_MD, 63 .tx = GPIO30_SDATA_OUT_I2S_MD, 64 .clk = GPIO28_BITCLK_IN_I2S_MD, 65 .frm = GPIO31_SYNC_I2S_MD, 66 }, 67 { /* I2S SoC Master */ 68 #ifdef CONFIG_PXA27x 69 .sys = GPIO113_I2S_SYSCLK_MD, 70 #else 71 .sys = GPIO32_SYSCLK_I2S_MD, 72 #endif 73 .rx = GPIO29_SDATA_IN_I2S_MD, 74 .tx = GPIO30_SDATA_OUT_I2S_MD, 75 .clk = GPIO28_BITCLK_OUT_I2S_MD, 76 .frm = GPIO31_SYNC_I2S_MD, 77 }, 78 }; 79 80 static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream) 81 { 82 struct snd_soc_pcm_runtime *rtd = substream->private_data; 83 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 84 85 if (IS_ERR(clk_i2s)) 86 return PTR_ERR(clk_i2s); 87 88 if (!cpu_dai->active) { 89 SACR0 |= SACR0_RST; 90 SACR0 = 0; 91 } 92 93 return 0; 94 } 95 96 /* wait for I2S controller to be ready */ 97 static int pxa_i2s_wait(void) 98 { 99 int i; 100 101 /* flush the Rx FIFO */ 102 for(i = 0; i < 16; i++) 103 SADR; 104 return 0; 105 } 106 107 static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, 108 unsigned int fmt) 109 { 110 /* interface format */ 111 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 112 case SND_SOC_DAIFMT_I2S: 113 pxa_i2s.fmt = 0; 114 break; 115 case SND_SOC_DAIFMT_LEFT_J: 116 pxa_i2s.fmt = SACR1_AMSL; 117 break; 118 } 119 120 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 121 case SND_SOC_DAIFMT_CBS_CFS: 122 pxa_i2s.master = 1; 123 break; 124 case SND_SOC_DAIFMT_CBM_CFS: 125 pxa_i2s.master = 0; 126 break; 127 default: 128 break; 129 } 130 return 0; 131 } 132 133 static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai, 134 int clk_id, unsigned int freq, int dir) 135 { 136 if (clk_id != PXA2XX_I2S_SYSCLK) 137 return -ENODEV; 138 139 if (pxa_i2s.master && dir == SND_SOC_CLOCK_OUT) 140 pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys); 141 142 return 0; 143 } 144 145 static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, 146 struct snd_pcm_hw_params *params) 147 { 148 struct snd_soc_pcm_runtime *rtd = substream->private_data; 149 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 150 151 pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx); 152 pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx); 153 pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm); 154 pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk); 155 BUG_ON(IS_ERR(clk_i2s)); 156 clk_enable(clk_i2s); 157 pxa_i2s_wait(); 158 159 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 160 cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out; 161 else 162 cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in; 163 164 /* is port used by another stream */ 165 if (!(SACR0 & SACR0_ENB)) { 166 167 SACR0 = 0; 168 SACR1 = 0; 169 if (pxa_i2s.master) 170 SACR0 |= SACR0_BCKD; 171 172 SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1); 173 SACR1 |= pxa_i2s.fmt; 174 } 175 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 176 SAIMR |= SAIMR_TFS; 177 else 178 SAIMR |= SAIMR_RFS; 179 180 switch (params_rate(params)) { 181 case 8000: 182 SADIV = 0x48; 183 break; 184 case 11025: 185 SADIV = 0x34; 186 break; 187 case 16000: 188 SADIV = 0x24; 189 break; 190 case 22050: 191 SADIV = 0x1a; 192 break; 193 case 44100: 194 SADIV = 0xd; 195 break; 196 case 48000: 197 SADIV = 0xc; 198 break; 199 case 96000: /* not in manual and possibly slightly inaccurate */ 200 SADIV = 0x6; 201 break; 202 } 203 204 return 0; 205 } 206 207 static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd) 208 { 209 int ret = 0; 210 211 switch (cmd) { 212 case SNDRV_PCM_TRIGGER_START: 213 SACR0 |= SACR0_ENB; 214 break; 215 case SNDRV_PCM_TRIGGER_RESUME: 216 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 217 case SNDRV_PCM_TRIGGER_STOP: 218 case SNDRV_PCM_TRIGGER_SUSPEND: 219 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 220 break; 221 default: 222 ret = -EINVAL; 223 } 224 225 return ret; 226 } 227 228 static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream) 229 { 230 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 231 SACR1 |= SACR1_DRPL; 232 SAIMR &= ~SAIMR_TFS; 233 } else { 234 SACR1 |= SACR1_DREC; 235 SAIMR &= ~SAIMR_RFS; 236 } 237 238 if (SACR1 & (SACR1_DREC | SACR1_DRPL)) { 239 SACR0 &= ~SACR0_ENB; 240 pxa_i2s_wait(); 241 clk_disable(clk_i2s); 242 } 243 244 clk_put(clk_i2s); 245 } 246 247 #ifdef CONFIG_PM 248 static int pxa2xx_i2s_suspend(struct platform_device *dev, 249 struct snd_soc_dai *dai) 250 { 251 if (!dai->active) 252 return 0; 253 254 /* store registers */ 255 pxa_i2s.sacr0 = SACR0; 256 pxa_i2s.sacr1 = SACR1; 257 pxa_i2s.saimr = SAIMR; 258 pxa_i2s.sadiv = SADIV; 259 260 /* deactivate link */ 261 SACR0 &= ~SACR0_ENB; 262 pxa_i2s_wait(); 263 return 0; 264 } 265 266 static int pxa2xx_i2s_resume(struct platform_device *pdev, 267 struct snd_soc_dai *dai) 268 { 269 if (!dai->active) 270 return 0; 271 272 pxa_i2s_wait(); 273 274 SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB; 275 SACR1 = pxa_i2s.sacr1; 276 SAIMR = pxa_i2s.saimr; 277 SADIV = pxa_i2s.sadiv; 278 SACR0 |= SACR0_ENB; 279 280 return 0; 281 } 282 283 #else 284 #define pxa2xx_i2s_suspend NULL 285 #define pxa2xx_i2s_resume NULL 286 #endif 287 288 #define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 289 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ 290 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) 291 292 struct snd_soc_dai pxa_i2s_dai = { 293 .name = "pxa2xx-i2s", 294 .id = 0, 295 .type = SND_SOC_DAI_I2S, 296 .suspend = pxa2xx_i2s_suspend, 297 .resume = pxa2xx_i2s_resume, 298 .playback = { 299 .channels_min = 2, 300 .channels_max = 2, 301 .rates = PXA2XX_I2S_RATES, 302 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 303 .capture = { 304 .channels_min = 2, 305 .channels_max = 2, 306 .rates = PXA2XX_I2S_RATES, 307 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 308 .ops = { 309 .startup = pxa2xx_i2s_startup, 310 .shutdown = pxa2xx_i2s_shutdown, 311 .trigger = pxa2xx_i2s_trigger, 312 .hw_params = pxa2xx_i2s_hw_params,}, 313 .dai_ops = { 314 .set_fmt = pxa2xx_i2s_set_dai_fmt, 315 .set_sysclk = pxa2xx_i2s_set_dai_sysclk, 316 }, 317 }; 318 319 EXPORT_SYMBOL_GPL(pxa_i2s_dai); 320 321 static int pxa2xx_i2s_probe(struct platform_device *dev) 322 { 323 clk_i2s = clk_get(&dev->dev, "I2SCLK"); 324 return IS_ERR(clk_i2s) ? PTR_ERR(clk_i2s) : 0; 325 } 326 327 static int __devexit pxa2xx_i2s_remove(struct platform_device *dev) 328 { 329 clk_put(clk_i2s); 330 clk_i2s = ERR_PTR(-ENOENT); 331 return 0; 332 } 333 334 static struct platform_driver pxa2xx_i2s_driver = { 335 .probe = pxa2xx_i2s_probe, 336 .remove = __devexit_p(pxa2xx_i2s_remove), 337 338 .driver = { 339 .name = "pxa2xx-i2s", 340 .owner = THIS_MODULE, 341 }, 342 }; 343 344 static int __init pxa2xx_i2s_init(void) 345 { 346 clk_i2s = ERR_PTR(-ENOENT); 347 return platform_driver_register(&pxa2xx_i2s_driver); 348 } 349 350 static void __exit pxa2xx_i2s_exit(void) 351 { 352 platform_driver_unregister(&pxa2xx_i2s_driver); 353 } 354 355 module_init(pxa2xx_i2s_init); 356 module_exit(pxa2xx_i2s_exit); 357 358 /* Module information */ 359 MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); 360 MODULE_DESCRIPTION("pxa2xx I2S SoC Interface"); 361 MODULE_LICENSE("GPL"); 362