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