aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/sh/hac.c
diff options
context:
space:
mode:
authorManuel Lauss <mano@roarinelk.homelinux.net>2007-05-14 12:40:07 -0400
committerJaroslav Kysela <perex@suse.cz>2007-07-20 05:11:17 -0400
commitaef3b06ac69783d6a6d1e4357c62bab46dd16141 (patch)
tree7c4e86d880a08ee5639d87a3702f7318203710d5 /sound/soc/sh/hac.c
parent80ab1c0e9ea90467e34dd3187b1d8162e8be314b (diff)
[ALSA] SH7760 ASoC support
ALSA ASoC support for SH7760 This patch adds ALSA ASoC drivers for the Audio interfaces of the SH7760 SoC: Add driver for the SH7760 DMA engine (dmabrg) Add AC97 driver for HAC unit(s) found on SH7760/SH7780 Add I2S driver for SSI unit(s) found on SH7760/SH7780 Add a generic SH7760-AC97 machine driver. Hook it all up with the build system. Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound/soc/sh/hac.c')
-rw-r--r--sound/soc/sh/hac.c322
1 files changed, 322 insertions, 0 deletions
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
new file mode 100644
index 000000000000..8e3f03908cdb
--- /dev/null
+++ b/sound/soc/sh/hac.c
@@ -0,0 +1,322 @@
1/*
2 * Hitachi Audio Controller (AC97) support for SH7760/SH7780
3 *
4 * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
5 * licensed under the terms outlined in the file COPYING at the root
6 * of the linux kernel sources.
7 *
8 * dont forget to set IPSEL/OMSEL register bits (in your board code) to
9 * enable HAC output pins!
10 */
11
12/* BIG FAT FIXME: although the SH7760 has 2 independent AC97 units, only
13 * the FIRST can be used since ASoC does not pass any information to the
14 * ac97_read/write() functions regarding WHICH unit to use. You'll have
15 * to edit the code a bit to use the other AC97 unit. --mlau
16 */
17
18#include <linux/init.h>
19#include <linux/module.h>
20#include <linux/platform_device.h>
21#include <linux/interrupt.h>
22#include <linux/wait.h>
23#include <linux/delay.h>
24#include <sound/driver.h>
25#include <sound/core.h>
26#include <sound/pcm.h>
27#include <sound/ac97_codec.h>
28#include <sound/initval.h>
29#include <sound/soc.h>
30
31/* regs and bits */
32#define HACCR 0x08
33#define HACCSAR 0x20
34#define HACCSDR 0x24
35#define HACPCML 0x28
36#define HACPCMR 0x2C
37#define HACTIER 0x50
38#define HACTSR 0x54
39#define HACRIER 0x58
40#define HACRSR 0x5C
41#define HACACR 0x60
42
43#define CR_CR (1 << 15) /* "codec-ready" indicator */
44#define CR_CDRT (1 << 11) /* cold reset */
45#define CR_WMRT (1 << 10) /* warm reset */
46#define CR_B9 (1 << 9) /* the mysterious "bit 9" */
47#define CR_ST (1 << 5) /* AC97 link start bit */
48
49#define CSAR_RD (1 << 19) /* AC97 data read bit */
50#define CSAR_WR (0)
51
52#define TSR_CMDAMT (1 << 31)
53#define TSR_CMDDMT (1 << 30)
54
55#define RSR_STARY (1 << 22)
56#define RSR_STDRY (1 << 21)
57
58#define ACR_DMARX16 (1 << 30)
59#define ACR_DMATX16 (1 << 29)
60#define ACR_TX12ATOM (1 << 26)
61#define ACR_DMARX20 ((1 << 24) | (1 << 22))
62#define ACR_DMATX20 ((1 << 23) | (1 << 21))
63
64#define CSDR_SHIFT 4
65#define CSDR_MASK (0xffff << CSDR_SHIFT)
66#define CSAR_SHIFT 12
67#define CSAR_MASK (0x7f << CSAR_SHIFT)
68
69#define AC97_WRITE_RETRY 1
70#define AC97_READ_RETRY 5
71
72/* manual-suggested AC97 codec access timeouts (us) */
73#define TMO_E1 500 /* 21 < E1 < 1000 */
74#define TMO_E2 13 /* 13 < E2 */
75#define TMO_E3 21 /* 21 < E3 */
76#define TMO_E4 500 /* 21 < E4 < 1000 */
77
78struct hac_priv {
79 unsigned long mmio; /* HAC base address */
80} hac_cpu_data[] = {
81#if defined(CONFIG_CPU_SUBTYPE_SH7760)
82 {
83 .mmio = 0xFE240000,
84 },
85 {
86 .mmio = 0xFE250000,
87 },
88#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
89 {
90 .mmio = 0xFFE40000,
91 },
92#else
93#error "Unsupported SuperH SoC"
94#endif
95};
96
97#define HACREG(reg) (*(unsigned long *)(hac->mmio + (reg)))
98
99/*
100 * AC97 read/write flow as outlined in the SH7760 manual (pages 903-906)
101 */
102static int hac_get_codec_data(struct hac_priv *hac, unsigned short r,
103 unsigned short *v)
104{
105 unsigned int to1, to2, i;
106 unsigned short adr;
107
108 for (i = 0; i < AC97_READ_RETRY; ++i) {
109 *v = 0;
110 /* wait for HAC to receive something from the codec */
111 for (to1 = TMO_E4;
112 to1 && !(HACREG(HACRSR) & RSR_STARY);
113 --to1)
114 udelay(1);
115 for (to2 = TMO_E4;
116 to2 && !(HACREG(HACRSR) & RSR_STDRY);
117 --to2)
118 udelay(1);
119
120 if (!to1 && !to2)
121 return 0; /* codec comm is down */
122
123 adr = ((HACREG(HACCSAR) & CSAR_MASK) >> CSAR_SHIFT);
124 *v = ((HACREG(HACCSDR) & CSDR_MASK) >> CSDR_SHIFT);
125
126 HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
127
128 if (r == adr)
129 break;
130
131 /* manual says: wait at least 21 usec before retrying */
132 udelay(21);
133 }
134 HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
135 return (i < AC97_READ_RETRY);
136}
137
138static unsigned short hac_read_codec_aux(struct hac_priv *hac,
139 unsigned short reg)
140{
141 unsigned short val;
142 unsigned int i, to;
143
144 for (i = 0; i < AC97_READ_RETRY; i++) {
145 /* send_read_request */
146 local_irq_disable();
147 HACREG(HACTSR) &= ~(TSR_CMDAMT);
148 HACREG(HACCSAR) = (reg << CSAR_SHIFT) | CSAR_RD;
149 local_irq_enable();
150
151 for (to = TMO_E3;
152 to && !(HACREG(HACTSR) & TSR_CMDAMT);
153 --to)
154 udelay(1);
155
156 HACREG(HACTSR) &= ~TSR_CMDAMT;
157 val = 0;
158 if (hac_get_codec_data(hac, reg, &val) != 0)
159 break;
160 }
161
162 if (i == AC97_READ_RETRY)
163 return ~0;
164
165 return val;
166}
167
168static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
169 unsigned short val)
170{
171 int unit_id = 0 /* ac97->private_data */;
172 struct hac_priv *hac = &hac_cpu_data[unit_id];
173 unsigned int i, to;
174 /* write_codec_aux */
175 for (i = 0; i < AC97_WRITE_RETRY; i++) {
176 /* send_write_request */
177 local_irq_disable();
178 HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT);
179 HACREG(HACCSDR) = (val << CSDR_SHIFT);
180 HACREG(HACCSAR) = (reg << CSAR_SHIFT) & (~CSAR_RD);
181 local_irq_enable();
182
183 /* poll-wait for CMDAMT and CMDDMT */
184 for (to = TMO_E1;
185 to && !(HACREG(HACTSR) & (TSR_CMDAMT|TSR_CMDDMT));
186 --to)
187 udelay(1);
188
189 HACREG(HACTSR) &= ~(TSR_CMDAMT | TSR_CMDDMT);
190 if (to)
191 break;
192 /* timeout, try again */
193 }
194}
195
196static unsigned short hac_ac97_read(struct snd_ac97 *ac97,
197 unsigned short reg)
198{
199 int unit_id = 0 /* ac97->private_data */;
200 struct hac_priv *hac = &hac_cpu_data[unit_id];
201 return hac_read_codec_aux(hac, reg);
202}
203
204static void hac_ac97_warmrst(struct snd_ac97 *ac97)
205{
206 int unit_id = 0 /* ac97->private_data */;
207 struct hac_priv *hac = &hac_cpu_data[unit_id];
208 unsigned int tmo;
209
210 HACREG(HACCR) = CR_WMRT | CR_ST | CR_B9;
211 msleep(10);
212 HACREG(HACCR) = CR_ST | CR_B9;
213 for (tmo = 1000; (tmo > 0) && !(HACREG(HACCR) & CR_CR); tmo--)
214 udelay(1);
215
216 if (!tmo)
217 printk(KERN_INFO "hac: reset: AC97 link down!\n");
218 /* settings this bit lets us have a conversation with codec */
219 HACREG(HACACR) |= ACR_TX12ATOM;
220}
221
222static void hac_ac97_coldrst(struct snd_ac97 *ac97)
223{
224 int unit_id = 0 /* ac97->private_data */;
225 struct hac_priv *hac;
226 hac = &hac_cpu_data[unit_id];
227
228 HACREG(HACCR) = 0;
229 HACREG(HACCR) = CR_CDRT | CR_ST | CR_B9;
230 msleep(10);
231 hac_ac97_warmrst(ac97);
232}
233
234struct snd_ac97_bus_ops soc_ac97_ops = {
235 .read = hac_ac97_read,
236 .write = hac_ac97_write,
237 .reset = hac_ac97_coldrst,
238 .warm_reset = hac_ac97_warmrst,
239};
240EXPORT_SYMBOL_GPL(soc_ac97_ops);
241
242static int hac_hw_params(struct snd_pcm_substream *substream,
243 struct snd_pcm_hw_params *params)
244{
245 struct snd_soc_pcm_runtime *rtd = substream->private_data;
246 struct hac_priv *hac = &hac_cpu_data[rtd->dai->cpu_dai->id];
247 int d = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
248
249 switch (params->msbits) {
250 case 16:
251 HACREG(HACACR) |= d ? ACR_DMARX16 : ACR_DMATX16;
252 HACREG(HACACR) &= d ? ~ACR_DMARX20 : ~ACR_DMATX20;
253 break;
254 case 20:
255 HACREG(HACACR) &= d ? ~ACR_DMARX16 : ~ACR_DMATX16;
256 HACREG(HACACR) |= d ? ACR_DMARX20 : ACR_DMATX20;
257 break;
258 default:
259 pr_debug("hac: invalid depth %d bit\n", params->msbits);
260 return -EINVAL;
261 break;
262 }
263
264 return 0;
265}
266
267#define AC97_RATES \
268 SNDRV_PCM_RATE_8000_192000
269
270#define AC97_FMTS \
271 SNDRV_PCM_FMTBIT_S16_LE
272
273struct snd_soc_cpu_dai sh4_hac_dai[] = {
274{
275 .name = "HAC0",
276 .id = 0,
277 .type = SND_SOC_DAI_AC97,
278 .playback = {
279 .rates = AC97_RATES,
280 .formats = AC97_FMTS,
281 .channels_min = 2,
282 .channels_max = 2,
283 },
284 .capture = {
285 .rates = AC97_RATES,
286 .formats = AC97_FMTS,
287 .channels_min = 2,
288 .channels_max = 2,
289 },
290 .ops = {
291 .hw_params = hac_hw_params,
292 },
293},
294#ifdef CONFIG_CPU_SUBTYPE_SH7760
295{
296 .name = "HAC1",
297 .id = 1,
298 .type = SND_SOC_DAI_AC97,
299 .playback = {
300 .rates = AC97_RATES,
301 .formats = AC97_FMTS,
302 .channels_min = 2,
303 .channels_max = 2,
304 },
305 .capture = {
306 .rates = AC97_RATES,
307 .formats = AC97_FMTS,
308 .channels_min = 2,
309 .channels_max = 2,
310 },
311 .ops = {
312 .hw_params = hac_hw_params,
313 },
314
315},
316#endif
317};
318EXPORT_SYMBOL_GPL(sh4_hac_dai);
319
320MODULE_LICENSE("GPL");
321MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver");
322MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");