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