| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | * s3c24xx-i2s.c  --  ALSA Soc Audio Layer | 
|  | 3 | * | 
|  | 4 | * (c) 2006 Wolfson Microelectronics PLC. | 
|  | 5 | * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com | 
|  | 6 | * | 
| Ben Dooks | c8efef1 | 2009-02-28 17:09:57 +0000 | [diff] [blame] | 7 | * Copyright 2004-2005 Simtec Electronics | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 8 | *	http://armlinux.simtec.co.uk/ | 
|  | 9 | *	Ben Dooks <ben@simtec.co.uk> | 
|  | 10 | * | 
|  | 11 | *  This program is free software; you can redistribute  it and/or modify it | 
|  | 12 | *  under  the terms of  the GNU General  Public License as published by the | 
|  | 13 | *  Free Software Foundation;  either version 2 of the  License, or (at your | 
|  | 14 | *  option) any later version. | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 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> | 
| Julia Lawall | f11b799 | 2008-01-07 13:33:45 +0100 | [diff] [blame] | 22 | #include <linux/jiffies.h> | 
| Mark Brown | 40efc15 | 2008-04-23 15:09:31 +0200 | [diff] [blame] | 23 | #include <linux/io.h> | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 24 | #include <sound/core.h> | 
|  | 25 | #include <sound/pcm.h> | 
|  | 26 | #include <sound/pcm_params.h> | 
|  | 27 | #include <sound/initval.h> | 
|  | 28 | #include <sound/soc.h> | 
|  | 29 |  | 
| Russell King | a09e64f | 2008-08-05 16:14:15 +0100 | [diff] [blame] | 30 | #include <mach/hardware.h> | 
|  | 31 | #include <mach/regs-gpio.h> | 
|  | 32 | #include <mach/regs-clock.h> | 
| Ben Dooks | 899e6cf | 2009-03-04 00:49:28 +0000 | [diff] [blame] | 33 | #include <plat/audio.h> | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 34 | #include <asm/dma.h> | 
| Russell King | a09e64f | 2008-08-05 16:14:15 +0100 | [diff] [blame] | 35 | #include <mach/dma.h> | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 36 |  | 
| Ben Dooks | 8150bc8 | 2009-03-04 00:49:26 +0000 | [diff] [blame] | 37 | #include <plat/regs-iis.h> | 
| Harald Welte | aa9673c | 2007-12-19 15:37:49 +0100 | [diff] [blame] | 38 |  | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 39 | #include "s3c24xx-pcm.h" | 
|  | 40 | #include "s3c24xx-i2s.h" | 
|  | 41 |  | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 42 | static struct s3c2410_dma_client s3c24xx_dma_client_out = { | 
|  | 43 | .name = "I2S PCM Stereo out" | 
|  | 44 | }; | 
|  | 45 |  | 
|  | 46 | static struct s3c2410_dma_client s3c24xx_dma_client_in = { | 
|  | 47 | .name = "I2S PCM Stereo in" | 
|  | 48 | }; | 
|  | 49 |  | 
|  | 50 | static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_out = { | 
|  | 51 | .client		= &s3c24xx_dma_client_out, | 
|  | 52 | .channel	= DMACH_I2S_OUT, | 
| Graeme Gregory | e81208f | 2007-04-17 12:35:48 +0200 | [diff] [blame] | 53 | .dma_addr	= S3C2410_PA_IIS + S3C2410_IISFIFO, | 
|  | 54 | .dma_size	= 2, | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 55 | }; | 
|  | 56 |  | 
|  | 57 | static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = { | 
|  | 58 | .client		= &s3c24xx_dma_client_in, | 
|  | 59 | .channel	= DMACH_I2S_IN, | 
| Graeme Gregory | e81208f | 2007-04-17 12:35:48 +0200 | [diff] [blame] | 60 | .dma_addr	= S3C2410_PA_IIS + S3C2410_IISFIFO, | 
|  | 61 | .dma_size	= 2, | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 62 | }; | 
|  | 63 |  | 
|  | 64 | struct s3c24xx_i2s_info { | 
|  | 65 | void __iomem	*regs; | 
|  | 66 | struct clk	*iis_clk; | 
| Graeme Gregory | 5cd919a | 2008-01-10 14:44:58 +0100 | [diff] [blame] | 67 | u32		iiscon; | 
|  | 68 | u32		iismod; | 
|  | 69 | u32		iisfcon; | 
|  | 70 | u32		iispsr; | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 71 | }; | 
|  | 72 | static struct s3c24xx_i2s_info s3c24xx_i2s; | 
|  | 73 |  | 
|  | 74 | static void s3c24xx_snd_txctrl(int on) | 
|  | 75 | { | 
|  | 76 | u32 iisfcon; | 
|  | 77 | u32 iiscon; | 
|  | 78 | u32 iismod; | 
|  | 79 |  | 
| Mark Brown | ee7d476 | 2009-03-06 18:04:34 +0000 | [diff] [blame] | 80 | pr_debug("Entered %s\n", __func__); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 81 |  | 
|  | 82 | iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); | 
|  | 83 | iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON); | 
|  | 84 | iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); | 
|  | 85 |  | 
| Mark Brown | 5314adc | 2009-03-11 16:28:29 +0000 | [diff] [blame] | 86 | pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 87 |  | 
|  | 88 | if (on) { | 
|  | 89 | iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE; | 
|  | 90 | iiscon  |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN; | 
|  | 91 | iiscon  &= ~S3C2410_IISCON_TXIDLE; | 
|  | 92 | iismod  |= S3C2410_IISMOD_TXMODE; | 
|  | 93 |  | 
|  | 94 | writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD); | 
|  | 95 | writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON); | 
|  | 96 | writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON); | 
|  | 97 | } else { | 
|  | 98 | /* note, we have to disable the FIFOs otherwise bad things | 
|  | 99 | * seem to happen when the DMA stops. According to the | 
|  | 100 | * Samsung supplied kernel, this should allow the DMA | 
|  | 101 | * engine and FIFOs to reset. If this isn't allowed, the | 
|  | 102 | * DMA engine will simply freeze randomly. | 
|  | 103 | */ | 
|  | 104 |  | 
|  | 105 | iisfcon &= ~S3C2410_IISFCON_TXENABLE; | 
|  | 106 | iisfcon &= ~S3C2410_IISFCON_TXDMA; | 
|  | 107 | iiscon  |=  S3C2410_IISCON_TXIDLE; | 
|  | 108 | iiscon  &= ~S3C2410_IISCON_TXDMAEN; | 
|  | 109 | iismod  &= ~S3C2410_IISMOD_TXMODE; | 
|  | 110 |  | 
|  | 111 | writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON); | 
|  | 112 | writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON); | 
|  | 113 | writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD); | 
|  | 114 | } | 
|  | 115 |  | 
| Mark Brown | 5314adc | 2009-03-11 16:28:29 +0000 | [diff] [blame] | 116 | pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 117 | } | 
|  | 118 |  | 
|  | 119 | static void s3c24xx_snd_rxctrl(int on) | 
|  | 120 | { | 
|  | 121 | u32 iisfcon; | 
|  | 122 | u32 iiscon; | 
|  | 123 | u32 iismod; | 
|  | 124 |  | 
| Mark Brown | ee7d476 | 2009-03-06 18:04:34 +0000 | [diff] [blame] | 125 | pr_debug("Entered %s\n", __func__); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 126 |  | 
|  | 127 | iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); | 
|  | 128 | iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON); | 
|  | 129 | iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); | 
|  | 130 |  | 
| Mark Brown | 5314adc | 2009-03-11 16:28:29 +0000 | [diff] [blame] | 131 | pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 132 |  | 
|  | 133 | if (on) { | 
|  | 134 | iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE; | 
|  | 135 | iiscon  |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN; | 
|  | 136 | iiscon  &= ~S3C2410_IISCON_RXIDLE; | 
|  | 137 | iismod  |= S3C2410_IISMOD_RXMODE; | 
|  | 138 |  | 
|  | 139 | writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD); | 
|  | 140 | writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON); | 
|  | 141 | writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON); | 
|  | 142 | } else { | 
|  | 143 | /* note, we have to disable the FIFOs otherwise bad things | 
|  | 144 | * seem to happen when the DMA stops. According to the | 
|  | 145 | * Samsung supplied kernel, this should allow the DMA | 
|  | 146 | * engine and FIFOs to reset. If this isn't allowed, the | 
|  | 147 | * DMA engine will simply freeze randomly. | 
|  | 148 | */ | 
|  | 149 |  | 
| Mark Brown | 0015e7d | 2008-04-23 15:09:57 +0200 | [diff] [blame] | 150 | iisfcon &= ~S3C2410_IISFCON_RXENABLE; | 
|  | 151 | iisfcon &= ~S3C2410_IISFCON_RXDMA; | 
|  | 152 | iiscon  |= S3C2410_IISCON_RXIDLE; | 
|  | 153 | iiscon  &= ~S3C2410_IISCON_RXDMAEN; | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 154 | iismod  &= ~S3C2410_IISMOD_RXMODE; | 
|  | 155 |  | 
|  | 156 | writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON); | 
|  | 157 | writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON); | 
|  | 158 | writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD); | 
|  | 159 | } | 
|  | 160 |  | 
| Mark Brown | 5314adc | 2009-03-11 16:28:29 +0000 | [diff] [blame] | 161 | pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 162 | } | 
|  | 163 |  | 
|  | 164 | /* | 
|  | 165 | * Wait for the LR signal to allow synchronisation to the L/R clock | 
|  | 166 | * from the codec. May only be needed for slave mode. | 
|  | 167 | */ | 
|  | 168 | static int s3c24xx_snd_lrsync(void) | 
|  | 169 | { | 
|  | 170 | u32 iiscon; | 
| Werner Almesberger | 33e5b22 | 2008-04-14 14:26:44 +0200 | [diff] [blame] | 171 | int timeout = 50; /* 5ms */ | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 172 |  | 
| Mark Brown | ee7d476 | 2009-03-06 18:04:34 +0000 | [diff] [blame] | 173 | pr_debug("Entered %s\n", __func__); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 174 |  | 
|  | 175 | while (1) { | 
|  | 176 | iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); | 
|  | 177 | if (iiscon & S3C2410_IISCON_LRINDEX) | 
|  | 178 | break; | 
|  | 179 |  | 
| Werner Almesberger | 33e5b22 | 2008-04-14 14:26:44 +0200 | [diff] [blame] | 180 | if (!timeout--) | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 181 | return -ETIMEDOUT; | 
| Werner Almesberger | 33e5b22 | 2008-04-14 14:26:44 +0200 | [diff] [blame] | 182 | udelay(100); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 183 | } | 
|  | 184 |  | 
|  | 185 | return 0; | 
|  | 186 | } | 
|  | 187 |  | 
|  | 188 | /* | 
|  | 189 | * Check whether CPU is the master or slave | 
|  | 190 | */ | 
|  | 191 | static inline int s3c24xx_snd_is_clkmaster(void) | 
|  | 192 | { | 
| Mark Brown | ee7d476 | 2009-03-06 18:04:34 +0000 | [diff] [blame] | 193 | pr_debug("Entered %s\n", __func__); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 194 |  | 
|  | 195 | return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1; | 
|  | 196 | } | 
|  | 197 |  | 
|  | 198 | /* | 
|  | 199 | * Set S3C24xx I2S DAI format | 
|  | 200 | */ | 
| Liam Girdwood | 1992a6f | 2008-07-07 16:08:24 +0100 | [diff] [blame] | 201 | static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai, | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 202 | unsigned int fmt) | 
|  | 203 | { | 
|  | 204 | u32 iismod; | 
|  | 205 |  | 
| Mark Brown | ee7d476 | 2009-03-06 18:04:34 +0000 | [diff] [blame] | 206 | pr_debug("Entered %s\n", __func__); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 207 |  | 
|  | 208 | iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); | 
| Mark Brown | 5314adc | 2009-03-11 16:28:29 +0000 | [diff] [blame] | 209 | pr_debug("hw_params r: IISMOD: %x \n", iismod); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 210 |  | 
|  | 211 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | 
|  | 212 | case SND_SOC_DAIFMT_CBM_CFM: | 
|  | 213 | iismod |= S3C2410_IISMOD_SLAVE; | 
|  | 214 | break; | 
|  | 215 | case SND_SOC_DAIFMT_CBS_CFS: | 
| Davide Rizzo | 2c36eec | 2008-05-05 14:59:39 +0200 | [diff] [blame] | 216 | iismod &= ~S3C2410_IISMOD_SLAVE; | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 217 | break; | 
|  | 218 | default: | 
|  | 219 | return -EINVAL; | 
|  | 220 | } | 
|  | 221 |  | 
|  | 222 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | 
|  | 223 | case SND_SOC_DAIFMT_LEFT_J: | 
|  | 224 | iismod |= S3C2410_IISMOD_MSB; | 
|  | 225 | break; | 
|  | 226 | case SND_SOC_DAIFMT_I2S: | 
| Davide Rizzo | 2c36eec | 2008-05-05 14:59:39 +0200 | [diff] [blame] | 227 | iismod &= ~S3C2410_IISMOD_MSB; | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 228 | break; | 
|  | 229 | default: | 
|  | 230 | return -EINVAL; | 
|  | 231 | } | 
|  | 232 |  | 
|  | 233 | writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); | 
| Mark Brown | 5314adc | 2009-03-11 16:28:29 +0000 | [diff] [blame] | 234 | pr_debug("hw_params w: IISMOD: %x \n", iismod); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 235 | return 0; | 
|  | 236 | } | 
|  | 237 |  | 
|  | 238 | static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream, | 
| Mark Brown | dee89c4 | 2008-11-18 22:11:38 +0000 | [diff] [blame] | 239 | struct snd_pcm_hw_params *params, | 
|  | 240 | struct snd_soc_dai *dai) | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 241 | { | 
|  | 242 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
|  | 243 | u32 iismod; | 
|  | 244 |  | 
| Mark Brown | ee7d476 | 2009-03-06 18:04:34 +0000 | [diff] [blame] | 245 | pr_debug("Entered %s\n", __func__); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 246 |  | 
|  | 247 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 
|  | 248 | rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out; | 
|  | 249 | else | 
|  | 250 | rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_in; | 
|  | 251 |  | 
|  | 252 | /* Working copies of register */ | 
|  | 253 | iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); | 
| Mark Brown | 5314adc | 2009-03-11 16:28:29 +0000 | [diff] [blame] | 254 | pr_debug("hw_params r: IISMOD: %x\n", iismod); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 255 |  | 
|  | 256 | switch (params_format(params)) { | 
|  | 257 | case SNDRV_PCM_FORMAT_S8: | 
| Christian Pellegrin | 53599bb | 2008-11-08 08:44:16 +0100 | [diff] [blame] | 258 | iismod &= ~S3C2410_IISMOD_16BIT; | 
|  | 259 | ((struct s3c24xx_pcm_dma_params *) | 
|  | 260 | rtd->dai->cpu_dai->dma_data)->dma_size = 1; | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 261 | break; | 
|  | 262 | case SNDRV_PCM_FORMAT_S16_LE: | 
|  | 263 | iismod |= S3C2410_IISMOD_16BIT; | 
| Christian Pellegrin | 53599bb | 2008-11-08 08:44:16 +0100 | [diff] [blame] | 264 | ((struct s3c24xx_pcm_dma_params *) | 
|  | 265 | rtd->dai->cpu_dai->dma_data)->dma_size = 2; | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 266 | break; | 
| Christian Pellegrin | 53599bb | 2008-11-08 08:44:16 +0100 | [diff] [blame] | 267 | default: | 
|  | 268 | return -EINVAL; | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 269 | } | 
|  | 270 |  | 
|  | 271 | writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); | 
| Mark Brown | 5314adc | 2009-03-11 16:28:29 +0000 | [diff] [blame] | 272 | pr_debug("hw_params w: IISMOD: %x\n", iismod); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 273 | return 0; | 
|  | 274 | } | 
|  | 275 |  | 
| Mark Brown | dee89c4 | 2008-11-18 22:11:38 +0000 | [diff] [blame] | 276 | static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | 
|  | 277 | struct snd_soc_dai *dai) | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 278 | { | 
|  | 279 | int ret = 0; | 
|  | 280 |  | 
| Mark Brown | ee7d476 | 2009-03-06 18:04:34 +0000 | [diff] [blame] | 281 | pr_debug("Entered %s\n", __func__); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 282 |  | 
|  | 283 | switch (cmd) { | 
|  | 284 | case SNDRV_PCM_TRIGGER_START: | 
|  | 285 | case SNDRV_PCM_TRIGGER_RESUME: | 
|  | 286 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 
|  | 287 | if (!s3c24xx_snd_is_clkmaster()) { | 
|  | 288 | ret = s3c24xx_snd_lrsync(); | 
|  | 289 | if (ret) | 
|  | 290 | goto exit_err; | 
|  | 291 | } | 
|  | 292 |  | 
|  | 293 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | 
|  | 294 | s3c24xx_snd_rxctrl(1); | 
|  | 295 | else | 
|  | 296 | s3c24xx_snd_txctrl(1); | 
|  | 297 | break; | 
|  | 298 | case SNDRV_PCM_TRIGGER_STOP: | 
|  | 299 | case SNDRV_PCM_TRIGGER_SUSPEND: | 
|  | 300 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 
|  | 301 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | 
|  | 302 | s3c24xx_snd_rxctrl(0); | 
|  | 303 | else | 
|  | 304 | s3c24xx_snd_txctrl(0); | 
|  | 305 | break; | 
|  | 306 | default: | 
|  | 307 | ret = -EINVAL; | 
|  | 308 | break; | 
|  | 309 | } | 
|  | 310 |  | 
|  | 311 | exit_err: | 
|  | 312 | return ret; | 
|  | 313 | } | 
|  | 314 |  | 
|  | 315 | /* | 
|  | 316 | * Set S3C24xx Clock source | 
|  | 317 | */ | 
| Liam Girdwood | 1992a6f | 2008-07-07 16:08:24 +0100 | [diff] [blame] | 318 | static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 319 | int clk_id, unsigned int freq, int dir) | 
|  | 320 | { | 
|  | 321 | u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); | 
|  | 322 |  | 
| Mark Brown | ee7d476 | 2009-03-06 18:04:34 +0000 | [diff] [blame] | 323 | pr_debug("Entered %s\n", __func__); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 324 |  | 
|  | 325 | iismod &= ~S3C2440_IISMOD_MPLL; | 
|  | 326 |  | 
|  | 327 | switch (clk_id) { | 
|  | 328 | case S3C24XX_CLKSRC_PCLK: | 
|  | 329 | break; | 
|  | 330 | case S3C24XX_CLKSRC_MPLL: | 
|  | 331 | iismod |= S3C2440_IISMOD_MPLL; | 
|  | 332 | break; | 
|  | 333 | default: | 
|  | 334 | return -EINVAL; | 
|  | 335 | } | 
|  | 336 |  | 
|  | 337 | writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); | 
|  | 338 | return 0; | 
|  | 339 | } | 
|  | 340 |  | 
|  | 341 | /* | 
|  | 342 | * Set S3C24xx Clock dividers | 
|  | 343 | */ | 
| Liam Girdwood | 1992a6f | 2008-07-07 16:08:24 +0100 | [diff] [blame] | 344 | static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 345 | int div_id, int div) | 
|  | 346 | { | 
|  | 347 | u32 reg; | 
|  | 348 |  | 
| Mark Brown | ee7d476 | 2009-03-06 18:04:34 +0000 | [diff] [blame] | 349 | pr_debug("Entered %s\n", __func__); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 350 |  | 
|  | 351 | switch (div_id) { | 
| Matt Reimer | 82fb159 | 2007-07-12 12:27:24 +0200 | [diff] [blame] | 352 | case S3C24XX_DIV_BCLK: | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 353 | reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK; | 
|  | 354 | writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD); | 
|  | 355 | break; | 
| Matt Reimer | 82fb159 | 2007-07-12 12:27:24 +0200 | [diff] [blame] | 356 | case S3C24XX_DIV_MCLK: | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 357 | reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS); | 
|  | 358 | writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD); | 
|  | 359 | break; | 
|  | 360 | case S3C24XX_DIV_PRESCALER: | 
|  | 361 | writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR); | 
|  | 362 | reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON); | 
|  | 363 | writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON); | 
|  | 364 | break; | 
|  | 365 | default: | 
|  | 366 | return -EINVAL; | 
|  | 367 | } | 
|  | 368 |  | 
|  | 369 | return 0; | 
|  | 370 | } | 
|  | 371 |  | 
|  | 372 | /* | 
|  | 373 | * To avoid duplicating clock code, allow machine driver to | 
|  | 374 | * get the clockrate from here. | 
|  | 375 | */ | 
|  | 376 | u32 s3c24xx_i2s_get_clockrate(void) | 
|  | 377 | { | 
|  | 378 | return clk_get_rate(s3c24xx_i2s.iis_clk); | 
|  | 379 | } | 
|  | 380 | EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate); | 
|  | 381 |  | 
| Mark Brown | bdb9287 | 2008-06-11 13:47:10 +0100 | [diff] [blame] | 382 | static int s3c24xx_i2s_probe(struct platform_device *pdev, | 
| Liam Girdwood | 1992a6f | 2008-07-07 16:08:24 +0100 | [diff] [blame] | 383 | struct snd_soc_dai *dai) | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 384 | { | 
| Mark Brown | ee7d476 | 2009-03-06 18:04:34 +0000 | [diff] [blame] | 385 | pr_debug("Entered %s\n", __func__); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 386 |  | 
|  | 387 | s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100); | 
|  | 388 | if (s3c24xx_i2s.regs == NULL) | 
|  | 389 | return -ENXIO; | 
|  | 390 |  | 
| Mark Brown | 0fe564a | 2008-04-23 15:10:28 +0200 | [diff] [blame] | 391 | s3c24xx_i2s.iis_clk = clk_get(&pdev->dev, "iis"); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 392 | if (s3c24xx_i2s.iis_clk == NULL) { | 
| Mark Brown | b52a519 | 2009-03-06 18:13:43 +0000 | [diff] [blame] | 393 | pr_err("failed to get iis_clock\n"); | 
| Scott Thompson | 8642a4b | 2007-08-01 13:38:59 +0200 | [diff] [blame] | 394 | iounmap(s3c24xx_i2s.regs); | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 395 | return -ENODEV; | 
|  | 396 | } | 
|  | 397 | clk_enable(s3c24xx_i2s.iis_clk); | 
|  | 398 |  | 
|  | 399 | /* Configure the I2S pins in correct mode */ | 
|  | 400 | s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK); | 
|  | 401 | s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK); | 
|  | 402 | s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK); | 
|  | 403 | s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI); | 
|  | 404 | s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO); | 
|  | 405 |  | 
|  | 406 | writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON); | 
|  | 407 |  | 
|  | 408 | s3c24xx_snd_txctrl(0); | 
|  | 409 | s3c24xx_snd_rxctrl(0); | 
|  | 410 |  | 
|  | 411 | return 0; | 
|  | 412 | } | 
|  | 413 |  | 
| Graeme Gregory | 5cd919a | 2008-01-10 14:44:58 +0100 | [diff] [blame] | 414 | #ifdef CONFIG_PM | 
| Mark Brown | dc7d7b8 | 2008-12-03 18:21:52 +0000 | [diff] [blame] | 415 | static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai) | 
| Graeme Gregory | 5cd919a | 2008-01-10 14:44:58 +0100 | [diff] [blame] | 416 | { | 
| Mark Brown | ee7d476 | 2009-03-06 18:04:34 +0000 | [diff] [blame] | 417 | pr_debug("Entered %s\n", __func__); | 
| Tim Niemeyer | 4092030 | 2008-04-22 18:26:59 +0200 | [diff] [blame] | 418 |  | 
| Graeme Gregory | 5cd919a | 2008-01-10 14:44:58 +0100 | [diff] [blame] | 419 | s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); | 
|  | 420 | s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); | 
|  | 421 | s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); | 
|  | 422 | s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR); | 
|  | 423 |  | 
|  | 424 | clk_disable(s3c24xx_i2s.iis_clk); | 
|  | 425 |  | 
|  | 426 | return 0; | 
|  | 427 | } | 
|  | 428 |  | 
| Mark Brown | dc7d7b8 | 2008-12-03 18:21:52 +0000 | [diff] [blame] | 429 | static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai) | 
| Graeme Gregory | 5cd919a | 2008-01-10 14:44:58 +0100 | [diff] [blame] | 430 | { | 
| Mark Brown | ee7d476 | 2009-03-06 18:04:34 +0000 | [diff] [blame] | 431 | pr_debug("Entered %s\n", __func__); | 
| Graeme Gregory | 5cd919a | 2008-01-10 14:44:58 +0100 | [diff] [blame] | 432 | clk_enable(s3c24xx_i2s.iis_clk); | 
|  | 433 |  | 
|  | 434 | writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON); | 
|  | 435 | writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); | 
|  | 436 | writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON); | 
|  | 437 | writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR); | 
|  | 438 |  | 
|  | 439 | return 0; | 
|  | 440 | } | 
|  | 441 | #else | 
|  | 442 | #define s3c24xx_i2s_suspend NULL | 
|  | 443 | #define s3c24xx_i2s_resume NULL | 
|  | 444 | #endif | 
|  | 445 |  | 
|  | 446 |  | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 447 | #define S3C24XX_I2S_RATES \ | 
|  | 448 | (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ | 
|  | 449 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ | 
|  | 450 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) | 
|  | 451 |  | 
| Eric Miao | 6335d05 | 2009-03-03 09:41:00 +0800 | [diff] [blame] | 452 | static struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = { | 
|  | 453 | .trigger	= s3c24xx_i2s_trigger, | 
|  | 454 | .hw_params	= s3c24xx_i2s_hw_params, | 
|  | 455 | .set_fmt	= s3c24xx_i2s_set_fmt, | 
|  | 456 | .set_clkdiv	= s3c24xx_i2s_set_clkdiv, | 
|  | 457 | .set_sysclk	= s3c24xx_i2s_set_sysclk, | 
|  | 458 | }; | 
|  | 459 |  | 
| Liam Girdwood | 1992a6f | 2008-07-07 16:08:24 +0100 | [diff] [blame] | 460 | struct snd_soc_dai s3c24xx_i2s_dai = { | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 461 | .name = "s3c24xx-i2s", | 
|  | 462 | .id = 0, | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 463 | .probe = s3c24xx_i2s_probe, | 
| Graeme Gregory | 5cd919a | 2008-01-10 14:44:58 +0100 | [diff] [blame] | 464 | .suspend = s3c24xx_i2s_suspend, | 
|  | 465 | .resume = s3c24xx_i2s_resume, | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 466 | .playback = { | 
|  | 467 | .channels_min = 2, | 
|  | 468 | .channels_max = 2, | 
|  | 469 | .rates = S3C24XX_I2S_RATES, | 
|  | 470 | .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,}, | 
|  | 471 | .capture = { | 
|  | 472 | .channels_min = 2, | 
|  | 473 | .channels_max = 2, | 
|  | 474 | .rates = S3C24XX_I2S_RATES, | 
|  | 475 | .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,}, | 
| Eric Miao | 6335d05 | 2009-03-03 09:41:00 +0800 | [diff] [blame] | 476 | .ops = &s3c24xx_i2s_dai_ops, | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 477 | }; | 
|  | 478 | EXPORT_SYMBOL_GPL(s3c24xx_i2s_dai); | 
|  | 479 |  | 
| Takashi Iwai | c9b3a40 | 2008-12-10 07:47:22 +0100 | [diff] [blame] | 480 | static int __init s3c24xx_i2s_init(void) | 
| Mark Brown | 3f4b783 | 2008-12-03 19:26:35 +0000 | [diff] [blame] | 481 | { | 
|  | 482 | return snd_soc_register_dai(&s3c24xx_i2s_dai); | 
|  | 483 | } | 
|  | 484 | module_init(s3c24xx_i2s_init); | 
|  | 485 |  | 
|  | 486 | static void __exit s3c24xx_i2s_exit(void) | 
|  | 487 | { | 
|  | 488 | snd_soc_unregister_dai(&s3c24xx_i2s_dai); | 
|  | 489 | } | 
|  | 490 | module_exit(s3c24xx_i2s_exit); | 
|  | 491 |  | 
| Ben Dooks | c1422a6 | 2007-02-14 13:17:49 +0100 | [diff] [blame] | 492 | /* Module information */ | 
|  | 493 | MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); | 
|  | 494 | MODULE_DESCRIPTION("s3c24xx I2S SoC Interface"); | 
|  | 495 | MODULE_LICENSE("GPL"); |