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