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 #include "pxa2xx-i2s.h" 33 34 struct pxa_i2s_port { 35 u32 sadiv; 36 u32 sacr0; 37 u32 sacr1; 38 u32 saimr; 39 int master; 40 u32 fmt; 41 }; 42 static struct pxa_i2s_port pxa_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 = &DRCMRTXSADR, 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 = &DRCMRRXSADR, 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_cpu_dai *cpu_dai = rtd->dai->cpu_dai; 84 85 if (!cpu_dai->active) { 86 SACR0 |= SACR0_RST; 87 SACR0 = 0; 88 } 89 90 return 0; 91 } 92 93 /* wait for I2S controller to be ready */ 94 static int pxa_i2s_wait(void) 95 { 96 int i; 97 98 /* flush the Rx FIFO */ 99 for(i = 0; i < 16; i++) 100 SADR; 101 return 0; 102 } 103 104 static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai, 105 unsigned int fmt) 106 { 107 /* interface format */ 108 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 109 case SND_SOC_DAIFMT_I2S: 110 pxa_i2s.fmt = 0; 111 break; 112 case SND_SOC_DAIFMT_LEFT_J: 113 pxa_i2s.fmt = SACR1_AMSL; 114 break; 115 } 116 117 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 118 case SND_SOC_DAIFMT_CBS_CFS: 119 pxa_i2s.master = 1; 120 break; 121 case SND_SOC_DAIFMT_CBM_CFS: 122 pxa_i2s.master = 0; 123 break; 124 default: 125 break; 126 } 127 return 0; 128 } 129 130 static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai, 131 int clk_id, unsigned int freq, int dir) 132 { 133 if (clk_id != PXA2XX_I2S_SYSCLK) 134 return -ENODEV; 135 136 if (pxa_i2s.master && dir == SND_SOC_CLOCK_OUT) 137 pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys); 138 139 return 0; 140 } 141 142 static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, 143 struct snd_pcm_hw_params *params) 144 { 145 struct snd_soc_pcm_runtime *rtd = substream->private_data; 146 struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; 147 148 pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx); 149 pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx); 150 pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm); 151 pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk); 152 pxa_set_cken(CKEN_I2S, 1); 153 pxa_i2s_wait(); 154 155 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 156 cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out; 157 else 158 cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in; 159 160 /* is port used by another stream */ 161 if (!(SACR0 & SACR0_ENB)) { 162 163 SACR0 = 0; 164 SACR1 = 0; 165 if (pxa_i2s.master) 166 SACR0 |= SACR0_BCKD; 167 168 SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1); 169 SACR1 |= pxa_i2s.fmt; 170 } 171 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 172 SAIMR |= SAIMR_TFS; 173 else 174 SAIMR |= SAIMR_RFS; 175 176 switch (params_rate(params)) { 177 case 8000: 178 SADIV = 0x48; 179 break; 180 case 11025: 181 SADIV = 0x34; 182 break; 183 case 16000: 184 SADIV = 0x24; 185 break; 186 case 22050: 187 SADIV = 0x1a; 188 break; 189 case 44100: 190 SADIV = 0xd; 191 break; 192 case 48000: 193 SADIV = 0xc; 194 break; 195 case 96000: /* not in manual and possibly slightly inaccurate */ 196 SADIV = 0x6; 197 break; 198 } 199 200 return 0; 201 } 202 203 static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd) 204 { 205 int ret = 0; 206 207 switch (cmd) { 208 case SNDRV_PCM_TRIGGER_START: 209 SACR0 |= SACR0_ENB; 210 break; 211 case SNDRV_PCM_TRIGGER_RESUME: 212 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 213 case SNDRV_PCM_TRIGGER_STOP: 214 case SNDRV_PCM_TRIGGER_SUSPEND: 215 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 216 break; 217 default: 218 ret = -EINVAL; 219 } 220 221 return ret; 222 } 223 224 static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream) 225 { 226 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 227 SACR1 |= SACR1_DRPL; 228 SAIMR &= ~SAIMR_TFS; 229 } else { 230 SACR1 |= SACR1_DREC; 231 SAIMR &= ~SAIMR_RFS; 232 } 233 234 if (SACR1 & (SACR1_DREC | SACR1_DRPL)) { 235 SACR0 &= ~SACR0_ENB; 236 pxa_i2s_wait(); 237 pxa_set_cken(CKEN_I2S, 0); 238 } 239 } 240 241 #ifdef CONFIG_PM 242 static int pxa2xx_i2s_suspend(struct platform_device *dev, 243 struct snd_soc_cpu_dai *dai) 244 { 245 if (!dai->active) 246 return 0; 247 248 /* store registers */ 249 pxa_i2s.sacr0 = SACR0; 250 pxa_i2s.sacr1 = SACR1; 251 pxa_i2s.saimr = SAIMR; 252 pxa_i2s.sadiv = SADIV; 253 254 /* deactivate link */ 255 SACR0 &= ~SACR0_ENB; 256 pxa_i2s_wait(); 257 return 0; 258 } 259 260 static int pxa2xx_i2s_resume(struct platform_device *pdev, 261 struct snd_soc_cpu_dai *dai) 262 { 263 if (!dai->active) 264 return 0; 265 266 pxa_i2s_wait(); 267 268 SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB; 269 SACR1 = pxa_i2s.sacr1; 270 SAIMR = pxa_i2s.saimr; 271 SADIV = pxa_i2s.sadiv; 272 SACR0 |= SACR0_ENB; 273 274 return 0; 275 } 276 277 #else 278 #define pxa2xx_i2s_suspend NULL 279 #define pxa2xx_i2s_resume NULL 280 #endif 281 282 #define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 283 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ 284 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) 285 286 struct snd_soc_cpu_dai pxa_i2s_dai = { 287 .name = "pxa2xx-i2s", 288 .id = 0, 289 .type = SND_SOC_DAI_I2S, 290 .suspend = pxa2xx_i2s_suspend, 291 .resume = pxa2xx_i2s_resume, 292 .playback = { 293 .channels_min = 2, 294 .channels_max = 2, 295 .rates = PXA2XX_I2S_RATES, 296 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 297 .capture = { 298 .channels_min = 2, 299 .channels_max = 2, 300 .rates = PXA2XX_I2S_RATES, 301 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 302 .ops = { 303 .startup = pxa2xx_i2s_startup, 304 .shutdown = pxa2xx_i2s_shutdown, 305 .trigger = pxa2xx_i2s_trigger, 306 .hw_params = pxa2xx_i2s_hw_params,}, 307 .dai_ops = { 308 .set_fmt = pxa2xx_i2s_set_dai_fmt, 309 .set_sysclk = pxa2xx_i2s_set_dai_sysclk, 310 }, 311 }; 312 313 EXPORT_SYMBOL_GPL(pxa_i2s_dai); 314 315 /* Module information */ 316 MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); 317 MODULE_DESCRIPTION("pxa2xx I2S SoC Interface"); 318 MODULE_LICENSE("GPL"); 319