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