diff options
author | Kuninori Morimoto <morimoto.kuninori@renesas.com> | 2009-08-20 08:01:05 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-08-20 15:01:42 -0400 |
commit | a4d7d550a9cfdfbc615383a08e9afa39d5a6d875 (patch) | |
tree | b4a9076fe9e0e5f0f5175fe858e7a28fe94e8a29 /sound/soc | |
parent | f61c890ec631884c5b7cd8723cd8ae1917dca544 (diff) |
ASoC: Add SuperH FSI driver support for ALSA
This driver is very simple.
It support playback only now.
This patch is tested by ms7724se board.
Signed-off-by: Kuninori Morimoto <morimoto.kuninori@renesas.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc')
-rw-r--r-- | sound/soc/sh/Kconfig | 7 | ||||
-rw-r--r-- | sound/soc/sh/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/sh/fsi.c | 1004 |
3 files changed, 1012 insertions, 1 deletions
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 54bd604012af..01943a188cff 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig | |||
@@ -20,7 +20,12 @@ config SND_SOC_SH4_HAC | |||
20 | config SND_SOC_SH4_SSI | 20 | config SND_SOC_SH4_SSI |
21 | tristate | 21 | tristate |
22 | 22 | ||
23 | 23 | config SND_SOC_SH4_FSI | |
24 | tristate "SH4 FSI support" | ||
25 | depends on CPU_SUBTYPE_SH7724 | ||
26 | select SH_DMA | ||
27 | help | ||
28 | This option enables FSI sound support | ||
24 | 29 | ||
25 | ## | 30 | ## |
26 | ## Boards | 31 | ## Boards |
diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile index a8e8ab81cc6a..9fbcc4ac9420 100644 --- a/sound/soc/sh/Makefile +++ b/sound/soc/sh/Makefile | |||
@@ -5,8 +5,10 @@ obj-$(CONFIG_SND_SOC_PCM_SH7760) += snd-soc-dma-sh7760.o | |||
5 | ## audio units found on some SH-4 | 5 | ## audio units found on some SH-4 |
6 | snd-soc-hac-objs := hac.o | 6 | snd-soc-hac-objs := hac.o |
7 | snd-soc-ssi-objs := ssi.o | 7 | snd-soc-ssi-objs := ssi.o |
8 | snd-soc-fsi-objs := fsi.o | ||
8 | obj-$(CONFIG_SND_SOC_SH4_HAC) += snd-soc-hac.o | 9 | obj-$(CONFIG_SND_SOC_SH4_HAC) += snd-soc-hac.o |
9 | obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o | 10 | obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o |
11 | obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o | ||
10 | 12 | ||
11 | ## boards | 13 | ## boards |
12 | snd-soc-sh7760-ac97-objs := sh7760-ac97.o | 14 | snd-soc-sh7760-ac97-objs := sh7760-ac97.o |
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c new file mode 100644 index 000000000000..44123248b630 --- /dev/null +++ b/sound/soc/sh/fsi.c | |||
@@ -0,0 +1,1004 @@ | |||
1 | /* | ||
2 | * Fifo-attached Serial Interface (FSI) support for SH7724 | ||
3 | * | ||
4 | * Copyright (C) 2009 Renesas Solutions Corp. | ||
5 | * Kuninori Morimoto <morimoto.kuninori@renesas.com> | ||
6 | * | ||
7 | * Based on ssi.c | ||
8 | * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/init.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/list.h> | ||
20 | #include <linux/clk.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <sound/core.h> | ||
23 | #include <sound/pcm.h> | ||
24 | #include <sound/initval.h> | ||
25 | #include <sound/soc.h> | ||
26 | #include <sound/pcm_params.h> | ||
27 | #include <sound/sh_fsi.h> | ||
28 | #include <asm/atomic.h> | ||
29 | #include <asm/dma.h> | ||
30 | #include <asm/dma-sh.h> | ||
31 | |||
32 | #define DO_FMT 0x0000 | ||
33 | #define DOFF_CTL 0x0004 | ||
34 | #define DOFF_ST 0x0008 | ||
35 | #define DI_FMT 0x000C | ||
36 | #define DIFF_CTL 0x0010 | ||
37 | #define DIFF_ST 0x0014 | ||
38 | #define CKG1 0x0018 | ||
39 | #define CKG2 0x001C | ||
40 | #define DIDT 0x0020 | ||
41 | #define DODT 0x0024 | ||
42 | #define MUTE_ST 0x0028 | ||
43 | #define REG_END MUTE_ST | ||
44 | |||
45 | #define INT_ST 0x0200 | ||
46 | #define IEMSK 0x0204 | ||
47 | #define IMSK 0x0208 | ||
48 | #define MUTE 0x020C | ||
49 | #define CLK_RST 0x0210 | ||
50 | #define SOFT_RST 0x0214 | ||
51 | #define MREG_START INT_ST | ||
52 | #define MREG_END SOFT_RST | ||
53 | |||
54 | /* DO_FMT */ | ||
55 | /* DI_FMT */ | ||
56 | #define CR_FMT(param) ((param) << 4) | ||
57 | # define CR_MONO 0x0 | ||
58 | # define CR_MONO_D 0x1 | ||
59 | # define CR_PCM 0x2 | ||
60 | # define CR_I2S 0x3 | ||
61 | # define CR_TDM 0x4 | ||
62 | # define CR_TDM_D 0x5 | ||
63 | |||
64 | /* DOFF_CTL */ | ||
65 | /* DIFF_CTL */ | ||
66 | #define IRQ_HALF 0x00100000 | ||
67 | #define FIFO_CLR 0x00000001 | ||
68 | |||
69 | /* DOFF_ST */ | ||
70 | #define ERR_OVER 0x00000010 | ||
71 | #define ERR_UNDER 0x00000001 | ||
72 | |||
73 | /* CLK_RST */ | ||
74 | #define B_CLK 0x00000010 | ||
75 | #define A_CLK 0x00000001 | ||
76 | |||
77 | /* INT_ST */ | ||
78 | #define INT_B_IN (1 << 12) | ||
79 | #define INT_B_OUT (1 << 8) | ||
80 | #define INT_A_IN (1 << 4) | ||
81 | #define INT_A_OUT (1 << 0) | ||
82 | |||
83 | #define FSI_RATES SNDRV_PCM_RATE_8000_96000 | ||
84 | |||
85 | #define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) | ||
86 | |||
87 | /************************************************************************ | ||
88 | |||
89 | |||
90 | struct | ||
91 | |||
92 | |||
93 | ************************************************************************/ | ||
94 | struct fsi_priv { | ||
95 | void __iomem *base; | ||
96 | struct snd_pcm_substream *substream; | ||
97 | |||
98 | int fifo_max; | ||
99 | int chan; | ||
100 | int dma_chan; | ||
101 | |||
102 | int byte_offset; | ||
103 | int period_len; | ||
104 | int buffer_len; | ||
105 | int periods; | ||
106 | }; | ||
107 | |||
108 | struct fsi_master { | ||
109 | void __iomem *base; | ||
110 | int irq; | ||
111 | struct clk *clk; | ||
112 | struct fsi_priv fsia; | ||
113 | struct fsi_priv fsib; | ||
114 | struct sh_fsi_platform_info *info; | ||
115 | }; | ||
116 | |||
117 | static struct fsi_master *master; | ||
118 | |||
119 | /************************************************************************ | ||
120 | |||
121 | |||
122 | basic read write function | ||
123 | |||
124 | |||
125 | ************************************************************************/ | ||
126 | static int __fsi_reg_write(u32 reg, u32 data) | ||
127 | { | ||
128 | /* valid data area is 24bit */ | ||
129 | data &= 0x00ffffff; | ||
130 | |||
131 | return ctrl_outl(data, reg); | ||
132 | } | ||
133 | |||
134 | static u32 __fsi_reg_read(u32 reg) | ||
135 | { | ||
136 | return ctrl_inl(reg); | ||
137 | } | ||
138 | |||
139 | static int __fsi_reg_mask_set(u32 reg, u32 mask, u32 data) | ||
140 | { | ||
141 | u32 val = __fsi_reg_read(reg); | ||
142 | |||
143 | val &= ~mask; | ||
144 | val |= data & mask; | ||
145 | |||
146 | return __fsi_reg_write(reg, val); | ||
147 | } | ||
148 | |||
149 | static int fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data) | ||
150 | { | ||
151 | if (reg > REG_END) | ||
152 | return -1; | ||
153 | |||
154 | return __fsi_reg_write((u32)(fsi->base + reg), data); | ||
155 | } | ||
156 | |||
157 | static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg) | ||
158 | { | ||
159 | if (reg > REG_END) | ||
160 | return 0; | ||
161 | |||
162 | return __fsi_reg_read((u32)(fsi->base + reg)); | ||
163 | } | ||
164 | |||
165 | static int fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data) | ||
166 | { | ||
167 | if (reg > REG_END) | ||
168 | return -1; | ||
169 | |||
170 | return __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data); | ||
171 | } | ||
172 | |||
173 | static int fsi_master_write(u32 reg, u32 data) | ||
174 | { | ||
175 | if ((reg < MREG_START) || | ||
176 | (reg > MREG_END)) | ||
177 | return -1; | ||
178 | |||
179 | return __fsi_reg_write((u32)(master->base + reg), data); | ||
180 | } | ||
181 | |||
182 | static u32 fsi_master_read(u32 reg) | ||
183 | { | ||
184 | if ((reg < MREG_START) || | ||
185 | (reg > MREG_END)) | ||
186 | return 0; | ||
187 | |||
188 | return __fsi_reg_read((u32)(master->base + reg)); | ||
189 | } | ||
190 | |||
191 | static int fsi_master_mask_set(u32 reg, u32 mask, u32 data) | ||
192 | { | ||
193 | if ((reg < MREG_START) || | ||
194 | (reg > MREG_END)) | ||
195 | return -1; | ||
196 | |||
197 | return __fsi_reg_mask_set((u32)(master->base + reg), mask, data); | ||
198 | } | ||
199 | |||
200 | /************************************************************************ | ||
201 | |||
202 | |||
203 | basic function | ||
204 | |||
205 | |||
206 | ************************************************************************/ | ||
207 | static struct fsi_priv *fsi_get(struct snd_pcm_substream *substream) | ||
208 | { | ||
209 | struct snd_soc_pcm_runtime *rtd; | ||
210 | struct fsi_priv *fsi = NULL; | ||
211 | |||
212 | if (!substream || !master) | ||
213 | return NULL; | ||
214 | |||
215 | rtd = substream->private_data; | ||
216 | switch (rtd->dai->cpu_dai->id) { | ||
217 | case 0: | ||
218 | fsi = &master->fsia; | ||
219 | break; | ||
220 | case 1: | ||
221 | fsi = &master->fsib; | ||
222 | break; | ||
223 | } | ||
224 | |||
225 | return fsi; | ||
226 | } | ||
227 | |||
228 | static int fsi_is_port_a(struct fsi_priv *fsi) | ||
229 | { | ||
230 | /* return | ||
231 | * 1 : port a | ||
232 | * 0 : port b | ||
233 | */ | ||
234 | |||
235 | if (fsi == &master->fsia) | ||
236 | return 1; | ||
237 | |||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | static u32 fsi_get_info_flags(struct fsi_priv *fsi) | ||
242 | { | ||
243 | int is_porta = fsi_is_port_a(fsi); | ||
244 | |||
245 | return is_porta ? master->info->porta_flags : | ||
246 | master->info->portb_flags; | ||
247 | } | ||
248 | |||
249 | static int fsi_is_master_mode(struct fsi_priv *fsi, int is_play) | ||
250 | { | ||
251 | u32 mode; | ||
252 | u32 flags = fsi_get_info_flags(fsi); | ||
253 | |||
254 | mode = is_play ? SH_FSI_OUT_SLAVE_MODE : SH_FSI_IN_SLAVE_MODE; | ||
255 | |||
256 | /* return | ||
257 | * 1 : master mode | ||
258 | * 0 : slave mode | ||
259 | */ | ||
260 | |||
261 | return (mode & flags) != mode; | ||
262 | } | ||
263 | |||
264 | static u32 fsi_port_ab_io_bit(struct fsi_priv *fsi, int is_play) | ||
265 | { | ||
266 | int is_porta = fsi_is_port_a(fsi); | ||
267 | u32 data; | ||
268 | |||
269 | if (is_porta) | ||
270 | data = is_play ? (1 << 0) : (1 << 4); | ||
271 | else | ||
272 | data = is_play ? (1 << 8) : (1 << 12); | ||
273 | |||
274 | return data; | ||
275 | } | ||
276 | |||
277 | static void fsi_stream_push(struct fsi_priv *fsi, | ||
278 | struct snd_pcm_substream *substream, | ||
279 | u32 buffer_len, | ||
280 | u32 period_len) | ||
281 | { | ||
282 | fsi->substream = substream; | ||
283 | fsi->buffer_len = buffer_len; | ||
284 | fsi->period_len = period_len; | ||
285 | fsi->byte_offset = 0; | ||
286 | fsi->periods = 0; | ||
287 | } | ||
288 | |||
289 | static void fsi_stream_pop(struct fsi_priv *fsi) | ||
290 | { | ||
291 | fsi->substream = NULL; | ||
292 | fsi->buffer_len = 0; | ||
293 | fsi->period_len = 0; | ||
294 | fsi->byte_offset = 0; | ||
295 | fsi->periods = 0; | ||
296 | } | ||
297 | |||
298 | static int fsi_get_fifo_residue(struct fsi_priv *fsi, int is_play) | ||
299 | { | ||
300 | u32 status; | ||
301 | u32 reg = is_play ? DOFF_ST : DIFF_ST; | ||
302 | int residue; | ||
303 | |||
304 | status = fsi_reg_read(fsi, reg); | ||
305 | residue = 0x1ff & (status >> 8); | ||
306 | residue *= fsi->chan; | ||
307 | |||
308 | return residue; | ||
309 | } | ||
310 | |||
311 | static int fsi_get_residue(struct fsi_priv *fsi, int is_play) | ||
312 | { | ||
313 | int residue; | ||
314 | int width; | ||
315 | struct snd_pcm_runtime *runtime; | ||
316 | |||
317 | runtime = fsi->substream->runtime; | ||
318 | |||
319 | /* get 1 channel data width */ | ||
320 | width = frames_to_bytes(runtime, 1) / fsi->chan; | ||
321 | |||
322 | if (2 == width) | ||
323 | residue = fsi_get_fifo_residue(fsi, is_play); | ||
324 | else | ||
325 | residue = get_dma_residue(fsi->dma_chan); | ||
326 | |||
327 | return residue; | ||
328 | } | ||
329 | |||
330 | /************************************************************************ | ||
331 | |||
332 | |||
333 | basic dma function | ||
334 | |||
335 | |||
336 | ************************************************************************/ | ||
337 | #define PORTA_DMA 0 | ||
338 | #define PORTB_DMA 1 | ||
339 | |||
340 | static int fsi_get_dma_chan(void) | ||
341 | { | ||
342 | if (0 != request_dma(PORTA_DMA, "fsia")) | ||
343 | return -EIO; | ||
344 | |||
345 | if (0 != request_dma(PORTB_DMA, "fsib")) { | ||
346 | free_dma(PORTA_DMA); | ||
347 | return -EIO; | ||
348 | } | ||
349 | |||
350 | master->fsia.dma_chan = PORTA_DMA; | ||
351 | master->fsib.dma_chan = PORTB_DMA; | ||
352 | |||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | static void fsi_free_dma_chan(void) | ||
357 | { | ||
358 | dma_wait_for_completion(PORTA_DMA); | ||
359 | dma_wait_for_completion(PORTB_DMA); | ||
360 | free_dma(PORTA_DMA); | ||
361 | free_dma(PORTB_DMA); | ||
362 | |||
363 | master->fsia.dma_chan = -1; | ||
364 | master->fsib.dma_chan = -1; | ||
365 | } | ||
366 | |||
367 | /************************************************************************ | ||
368 | |||
369 | |||
370 | ctrl function | ||
371 | |||
372 | |||
373 | ************************************************************************/ | ||
374 | static void fsi_irq_enable(struct fsi_priv *fsi, int is_play) | ||
375 | { | ||
376 | u32 data = fsi_port_ab_io_bit(fsi, is_play); | ||
377 | |||
378 | fsi_master_mask_set(IMSK, data, data); | ||
379 | fsi_master_mask_set(IEMSK, data, data); | ||
380 | } | ||
381 | |||
382 | static void fsi_irq_disable(struct fsi_priv *fsi, int is_play) | ||
383 | { | ||
384 | u32 data = fsi_port_ab_io_bit(fsi, is_play); | ||
385 | |||
386 | fsi_master_mask_set(IMSK, data, 0); | ||
387 | fsi_master_mask_set(IEMSK, data, 0); | ||
388 | } | ||
389 | |||
390 | static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable) | ||
391 | { | ||
392 | u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4); | ||
393 | |||
394 | if (enable) | ||
395 | fsi_master_mask_set(CLK_RST, val, val); | ||
396 | else | ||
397 | fsi_master_mask_set(CLK_RST, val, 0); | ||
398 | } | ||
399 | |||
400 | static void fsi_irq_init(struct fsi_priv *fsi, int is_play) | ||
401 | { | ||
402 | u32 data; | ||
403 | u32 ctrl; | ||
404 | |||
405 | data = fsi_port_ab_io_bit(fsi, is_play); | ||
406 | ctrl = is_play ? DOFF_CTL : DIFF_CTL; | ||
407 | |||
408 | /* set IMSK */ | ||
409 | fsi_irq_disable(fsi, is_play); | ||
410 | |||
411 | /* set interrupt generation factor */ | ||
412 | fsi_reg_write(fsi, ctrl, IRQ_HALF); | ||
413 | |||
414 | /* clear FIFO */ | ||
415 | fsi_reg_mask_set(fsi, ctrl, FIFO_CLR, FIFO_CLR); | ||
416 | |||
417 | /* clear interrupt factor */ | ||
418 | fsi_master_mask_set(INT_ST, data, 0); | ||
419 | } | ||
420 | |||
421 | static void fsi_soft_all_reset(void) | ||
422 | { | ||
423 | u32 status = fsi_master_read(SOFT_RST); | ||
424 | |||
425 | /* port AB reset */ | ||
426 | status &= 0x000000ff; | ||
427 | fsi_master_write(SOFT_RST, status); | ||
428 | mdelay(10); | ||
429 | |||
430 | /* soft reset */ | ||
431 | status &= 0x000000f0; | ||
432 | fsi_master_write(SOFT_RST, status); | ||
433 | status |= 0x00000001; | ||
434 | fsi_master_write(SOFT_RST, status); | ||
435 | mdelay(10); | ||
436 | } | ||
437 | |||
438 | static void fsi_16data_push(struct fsi_priv *fsi, | ||
439 | struct snd_pcm_runtime *runtime, | ||
440 | int send) | ||
441 | { | ||
442 | u16 *dma_start; | ||
443 | u32 snd; | ||
444 | int i; | ||
445 | |||
446 | /* get dma start position for FSI */ | ||
447 | dma_start = (u16 *)runtime->dma_area; | ||
448 | dma_start += fsi->byte_offset / 2; | ||
449 | |||
450 | /* | ||
451 | * soft dma | ||
452 | * FSI can not use DMA when 16bpp | ||
453 | */ | ||
454 | for (i = 0; i < send; i++) { | ||
455 | snd = (u32)dma_start[i]; | ||
456 | fsi_reg_write(fsi, DODT, snd << 8); | ||
457 | } | ||
458 | } | ||
459 | |||
460 | static void fsi_32data_push(struct fsi_priv *fsi, | ||
461 | struct snd_pcm_runtime *runtime, | ||
462 | int send) | ||
463 | { | ||
464 | u32 *dma_start; | ||
465 | |||
466 | /* get dma start position for FSI */ | ||
467 | dma_start = (u32 *)runtime->dma_area; | ||
468 | dma_start += fsi->byte_offset / 4; | ||
469 | |||
470 | dma_wait_for_completion(fsi->dma_chan); | ||
471 | dma_configure_channel(fsi->dma_chan, (SM_INC|0x400|TS_32|TM_BUR)); | ||
472 | dma_write(fsi->dma_chan, (u32)dma_start, | ||
473 | (u32)(fsi->base + DODT), send * 4); | ||
474 | } | ||
475 | |||
476 | /* playback interrupt */ | ||
477 | static int fsi_data_push(struct fsi_priv *fsi) | ||
478 | { | ||
479 | struct snd_pcm_runtime *runtime; | ||
480 | struct snd_pcm_substream *substream = NULL; | ||
481 | int send; | ||
482 | int fifo_free; | ||
483 | int width; | ||
484 | |||
485 | if (!fsi || | ||
486 | !fsi->substream || | ||
487 | !fsi->substream->runtime) | ||
488 | return -EINVAL; | ||
489 | |||
490 | runtime = fsi->substream->runtime; | ||
491 | |||
492 | /* FSI FIFO has limit. | ||
493 | * So, this driver can not send periods data at a time | ||
494 | */ | ||
495 | if (fsi->byte_offset >= | ||
496 | fsi->period_len * (fsi->periods + 1)) { | ||
497 | |||
498 | substream = fsi->substream; | ||
499 | fsi->periods = (fsi->periods + 1) % runtime->periods; | ||
500 | |||
501 | if (0 == fsi->periods) | ||
502 | fsi->byte_offset = 0; | ||
503 | } | ||
504 | |||
505 | /* get 1 channel data width */ | ||
506 | width = frames_to_bytes(runtime, 1) / fsi->chan; | ||
507 | |||
508 | /* get send size for alsa */ | ||
509 | send = (fsi->buffer_len - fsi->byte_offset) / width; | ||
510 | |||
511 | /* get FIFO free size */ | ||
512 | fifo_free = (fsi->fifo_max * fsi->chan) - fsi_get_fifo_residue(fsi, 1); | ||
513 | |||
514 | /* size check */ | ||
515 | if (fifo_free < send) | ||
516 | send = fifo_free; | ||
517 | |||
518 | if (2 == width) | ||
519 | fsi_16data_push(fsi, runtime, send); | ||
520 | else if (4 == width) | ||
521 | fsi_32data_push(fsi, runtime, send); | ||
522 | else | ||
523 | return -EINVAL; | ||
524 | |||
525 | fsi->byte_offset += send * width; | ||
526 | |||
527 | fsi_irq_enable(fsi, 1); | ||
528 | |||
529 | if (substream) | ||
530 | snd_pcm_period_elapsed(substream); | ||
531 | |||
532 | return 0; | ||
533 | } | ||
534 | |||
535 | static irqreturn_t fsi_interrupt(int irq, void *data) | ||
536 | { | ||
537 | u32 status = fsi_master_read(SOFT_RST) & ~0x00000010; | ||
538 | u32 int_st = fsi_master_read(INT_ST); | ||
539 | |||
540 | /* clear irq status */ | ||
541 | fsi_master_write(SOFT_RST, status); | ||
542 | fsi_master_write(SOFT_RST, status | 0x00000010); | ||
543 | |||
544 | if (int_st & INT_A_OUT) | ||
545 | fsi_data_push(&master->fsia); | ||
546 | if (int_st & INT_B_OUT) | ||
547 | fsi_data_push(&master->fsib); | ||
548 | |||
549 | fsi_master_write(INT_ST, 0x0000000); | ||
550 | |||
551 | return IRQ_HANDLED; | ||
552 | } | ||
553 | |||
554 | /************************************************************************ | ||
555 | |||
556 | |||
557 | dai ops | ||
558 | |||
559 | |||
560 | ************************************************************************/ | ||
561 | static int fsi_dai_startup(struct snd_pcm_substream *substream, | ||
562 | struct snd_soc_dai *dai) | ||
563 | { | ||
564 | struct fsi_priv *fsi = fsi_get(substream); | ||
565 | const char *msg; | ||
566 | u32 flags = fsi_get_info_flags(fsi); | ||
567 | u32 fmt; | ||
568 | u32 reg; | ||
569 | u32 data; | ||
570 | int is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); | ||
571 | int is_master; | ||
572 | int ret = 0; | ||
573 | |||
574 | clk_enable(master->clk); | ||
575 | |||
576 | /* CKG1 */ | ||
577 | data = is_play ? (1 << 0) : (1 << 4); | ||
578 | is_master = fsi_is_master_mode(fsi, is_play); | ||
579 | if (is_master) | ||
580 | fsi_reg_mask_set(fsi, CKG1, data, data); | ||
581 | else | ||
582 | fsi_reg_mask_set(fsi, CKG1, data, 0); | ||
583 | |||
584 | /* clock inversion (CKG2) */ | ||
585 | data = 0; | ||
586 | switch (SH_FSI_INVERSION_MASK & flags) { | ||
587 | case SH_FSI_LRM_INV: | ||
588 | data = 1 << 12; | ||
589 | break; | ||
590 | case SH_FSI_BRM_INV: | ||
591 | data = 1 << 8; | ||
592 | break; | ||
593 | case SH_FSI_LRS_INV: | ||
594 | data = 1 << 4; | ||
595 | break; | ||
596 | case SH_FSI_BRS_INV: | ||
597 | data = 1 << 0; | ||
598 | break; | ||
599 | } | ||
600 | fsi_reg_write(fsi, CKG2, data); | ||
601 | |||
602 | /* do fmt, di fmt */ | ||
603 | data = 0; | ||
604 | reg = is_play ? DO_FMT : DI_FMT; | ||
605 | fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags); | ||
606 | switch (fmt) { | ||
607 | case SH_FSI_FMT_MONO: | ||
608 | msg = "MONO"; | ||
609 | data = CR_FMT(CR_MONO); | ||
610 | fsi->chan = 1; | ||
611 | break; | ||
612 | case SH_FSI_FMT_MONO_DELAY: | ||
613 | msg = "MONO Delay"; | ||
614 | data = CR_FMT(CR_MONO_D); | ||
615 | fsi->chan = 1; | ||
616 | break; | ||
617 | case SH_FSI_FMT_PCM: | ||
618 | msg = "PCM"; | ||
619 | data = CR_FMT(CR_PCM); | ||
620 | fsi->chan = 2; | ||
621 | break; | ||
622 | case SH_FSI_FMT_I2S: | ||
623 | msg = "I2S"; | ||
624 | data = CR_FMT(CR_I2S); | ||
625 | fsi->chan = 2; | ||
626 | break; | ||
627 | case SH_FSI_FMT_TDM: | ||
628 | msg = "TDM"; | ||
629 | data = CR_FMT(CR_TDM) | (fsi->chan - 1); | ||
630 | fsi->chan = is_play ? | ||
631 | SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); | ||
632 | break; | ||
633 | case SH_FSI_FMT_TDM_DELAY: | ||
634 | msg = "TDM Delay"; | ||
635 | data = CR_FMT(CR_TDM_D) | (fsi->chan - 1); | ||
636 | fsi->chan = is_play ? | ||
637 | SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); | ||
638 | break; | ||
639 | default: | ||
640 | dev_err(dai->dev, "unknown format.\n"); | ||
641 | return -EINVAL; | ||
642 | } | ||
643 | |||
644 | switch (fsi->chan) { | ||
645 | case 1: | ||
646 | fsi->fifo_max = 256; | ||
647 | break; | ||
648 | case 2: | ||
649 | fsi->fifo_max = 128; | ||
650 | break; | ||
651 | case 3: | ||
652 | case 4: | ||
653 | fsi->fifo_max = 64; | ||
654 | break; | ||
655 | case 5: | ||
656 | case 6: | ||
657 | case 7: | ||
658 | case 8: | ||
659 | fsi->fifo_max = 32; | ||
660 | break; | ||
661 | default: | ||
662 | dev_err(dai->dev, "channel size error.\n"); | ||
663 | return -EINVAL; | ||
664 | } | ||
665 | |||
666 | fsi_reg_write(fsi, reg, data); | ||
667 | dev_dbg(dai->dev, "use %s format (%d channel) use %d DMAC\n", | ||
668 | msg, fsi->chan, fsi->dma_chan); | ||
669 | |||
670 | /* | ||
671 | * clear clk reset if master mode | ||
672 | */ | ||
673 | if (is_master) | ||
674 | fsi_clk_ctrl(fsi, 1); | ||
675 | |||
676 | /* irq setting */ | ||
677 | fsi_irq_init(fsi, is_play); | ||
678 | |||
679 | return ret; | ||
680 | } | ||
681 | |||
682 | static void fsi_dai_shutdown(struct snd_pcm_substream *substream, | ||
683 | struct snd_soc_dai *dai) | ||
684 | { | ||
685 | struct fsi_priv *fsi = fsi_get(substream); | ||
686 | int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; | ||
687 | |||
688 | fsi_irq_disable(fsi, is_play); | ||
689 | fsi_clk_ctrl(fsi, 0); | ||
690 | |||
691 | clk_disable(master->clk); | ||
692 | } | ||
693 | |||
694 | static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, | ||
695 | struct snd_soc_dai *dai) | ||
696 | { | ||
697 | struct fsi_priv *fsi = fsi_get(substream); | ||
698 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
699 | int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; | ||
700 | int ret = 0; | ||
701 | |||
702 | /* capture not supported */ | ||
703 | if (!is_play) | ||
704 | return -ENODEV; | ||
705 | |||
706 | switch (cmd) { | ||
707 | case SNDRV_PCM_TRIGGER_START: | ||
708 | fsi_stream_push(fsi, substream, | ||
709 | frames_to_bytes(runtime, runtime->buffer_size), | ||
710 | frames_to_bytes(runtime, runtime->period_size)); | ||
711 | ret = fsi_data_push(fsi); | ||
712 | break; | ||
713 | case SNDRV_PCM_TRIGGER_STOP: | ||
714 | fsi_irq_disable(fsi, is_play); | ||
715 | fsi_stream_pop(fsi); | ||
716 | break; | ||
717 | } | ||
718 | |||
719 | return ret; | ||
720 | } | ||
721 | |||
722 | static struct snd_soc_dai_ops fsi_dai_ops = { | ||
723 | .startup = fsi_dai_startup, | ||
724 | .shutdown = fsi_dai_shutdown, | ||
725 | .trigger = fsi_dai_trigger, | ||
726 | }; | ||
727 | |||
728 | /************************************************************************ | ||
729 | |||
730 | |||
731 | pcm ops | ||
732 | |||
733 | |||
734 | ************************************************************************/ | ||
735 | static struct snd_pcm_hardware fsi_pcm_hardware = { | ||
736 | .info = SNDRV_PCM_INFO_INTERLEAVED | | ||
737 | SNDRV_PCM_INFO_MMAP | | ||
738 | SNDRV_PCM_INFO_MMAP_VALID | | ||
739 | SNDRV_PCM_INFO_PAUSE, | ||
740 | .formats = FSI_FMTS, | ||
741 | .rates = FSI_RATES, | ||
742 | .rate_min = 8000, | ||
743 | .rate_max = 192000, | ||
744 | .channels_min = 1, | ||
745 | .channels_max = 2, | ||
746 | .buffer_bytes_max = 64 * 1024, | ||
747 | .period_bytes_min = 32, | ||
748 | .period_bytes_max = 8192, | ||
749 | .periods_min = 1, | ||
750 | .periods_max = 32, | ||
751 | .fifo_size = 256, | ||
752 | }; | ||
753 | |||
754 | static int fsi_pcm_open(struct snd_pcm_substream *substream) | ||
755 | { | ||
756 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
757 | int ret = 0; | ||
758 | |||
759 | snd_soc_set_runtime_hwparams(substream, &fsi_pcm_hardware); | ||
760 | |||
761 | ret = snd_pcm_hw_constraint_integer(runtime, | ||
762 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
763 | |||
764 | return ret; | ||
765 | } | ||
766 | |||
767 | static int fsi_hw_params(struct snd_pcm_substream *substream, | ||
768 | struct snd_pcm_hw_params *hw_params) | ||
769 | { | ||
770 | return snd_pcm_lib_malloc_pages(substream, | ||
771 | params_buffer_bytes(hw_params)); | ||
772 | } | ||
773 | |||
774 | static int fsi_hw_free(struct snd_pcm_substream *substream) | ||
775 | { | ||
776 | return snd_pcm_lib_free_pages(substream); | ||
777 | } | ||
778 | |||
779 | static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream) | ||
780 | { | ||
781 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
782 | struct fsi_priv *fsi = fsi_get(substream); | ||
783 | int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; | ||
784 | long location; | ||
785 | |||
786 | location = (fsi->byte_offset - 1) - fsi_get_residue(fsi, is_play); | ||
787 | if (location < 0) | ||
788 | location = 0; | ||
789 | |||
790 | return bytes_to_frames(runtime, location); | ||
791 | } | ||
792 | |||
793 | static struct snd_pcm_ops fsi_pcm_ops = { | ||
794 | .open = fsi_pcm_open, | ||
795 | .ioctl = snd_pcm_lib_ioctl, | ||
796 | .hw_params = fsi_hw_params, | ||
797 | .hw_free = fsi_hw_free, | ||
798 | .pointer = fsi_pointer, | ||
799 | }; | ||
800 | |||
801 | /************************************************************************ | ||
802 | |||
803 | |||
804 | snd_soc_platform | ||
805 | |||
806 | |||
807 | ************************************************************************/ | ||
808 | #define PREALLOC_BUFFER (32 * 1024) | ||
809 | #define PREALLOC_BUFFER_MAX (32 * 1024) | ||
810 | |||
811 | static void fsi_pcm_free(struct snd_pcm *pcm) | ||
812 | { | ||
813 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
814 | } | ||
815 | |||
816 | static int fsi_pcm_new(struct snd_card *card, | ||
817 | struct snd_soc_dai *dai, | ||
818 | struct snd_pcm *pcm) | ||
819 | { | ||
820 | /* | ||
821 | * dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel | ||
822 | * in MMAP mode (i.e. aplay -M) | ||
823 | */ | ||
824 | return snd_pcm_lib_preallocate_pages_for_all( | ||
825 | pcm, | ||
826 | SNDRV_DMA_TYPE_CONTINUOUS, | ||
827 | snd_dma_continuous_data(GFP_KERNEL), | ||
828 | PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); | ||
829 | } | ||
830 | |||
831 | /************************************************************************ | ||
832 | |||
833 | |||
834 | alsa struct | ||
835 | |||
836 | |||
837 | ************************************************************************/ | ||
838 | struct snd_soc_dai fsi_soc_dai[] = { | ||
839 | { | ||
840 | .name = "FSIA", | ||
841 | .id = 0, | ||
842 | .playback = { | ||
843 | .rates = FSI_RATES, | ||
844 | .formats = FSI_FMTS, | ||
845 | .channels_min = 1, | ||
846 | .channels_max = 8, | ||
847 | }, | ||
848 | /* capture not supported */ | ||
849 | .ops = &fsi_dai_ops, | ||
850 | }, | ||
851 | { | ||
852 | .name = "FSIB", | ||
853 | .id = 1, | ||
854 | .playback = { | ||
855 | .rates = FSI_RATES, | ||
856 | .formats = FSI_FMTS, | ||
857 | .channels_min = 1, | ||
858 | .channels_max = 8, | ||
859 | }, | ||
860 | /* capture not supported */ | ||
861 | .ops = &fsi_dai_ops, | ||
862 | }, | ||
863 | }; | ||
864 | EXPORT_SYMBOL_GPL(fsi_soc_dai); | ||
865 | |||
866 | struct snd_soc_platform fsi_soc_platform = { | ||
867 | .name = "fsi-pcm", | ||
868 | .pcm_ops = &fsi_pcm_ops, | ||
869 | .pcm_new = fsi_pcm_new, | ||
870 | .pcm_free = fsi_pcm_free, | ||
871 | }; | ||
872 | EXPORT_SYMBOL_GPL(fsi_soc_platform); | ||
873 | |||
874 | /************************************************************************ | ||
875 | |||
876 | |||
877 | platform function | ||
878 | |||
879 | |||
880 | ************************************************************************/ | ||
881 | static int fsi_probe(struct platform_device *pdev) | ||
882 | { | ||
883 | struct resource *res; | ||
884 | char clk_name[8]; | ||
885 | unsigned int irq; | ||
886 | int ret; | ||
887 | |||
888 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
889 | irq = platform_get_irq(pdev, 0); | ||
890 | if (!res || !irq) { | ||
891 | dev_err(&pdev->dev, "Not enough FSI platform resources.\n"); | ||
892 | ret = -ENODEV; | ||
893 | goto exit; | ||
894 | } | ||
895 | |||
896 | master = kzalloc(sizeof(*master), GFP_KERNEL); | ||
897 | if (!master) { | ||
898 | dev_err(&pdev->dev, "Could not allocate master\n"); | ||
899 | ret = -ENOMEM; | ||
900 | goto exit; | ||
901 | } | ||
902 | |||
903 | master->base = ioremap_nocache(res->start, resource_size(res)); | ||
904 | if (!master->base) { | ||
905 | ret = -ENXIO; | ||
906 | dev_err(&pdev->dev, "Unable to ioremap FSI registers.\n"); | ||
907 | goto exit_kfree; | ||
908 | } | ||
909 | |||
910 | master->irq = irq; | ||
911 | master->info = pdev->dev.platform_data; | ||
912 | master->fsia.base = master->base; | ||
913 | master->fsib.base = master->base + 0x40; | ||
914 | |||
915 | master->fsia.dma_chan = -1; | ||
916 | master->fsib.dma_chan = -1; | ||
917 | |||
918 | ret = fsi_get_dma_chan(); | ||
919 | if (ret < 0) { | ||
920 | dev_err(&pdev->dev, "cannot get dma api\n"); | ||
921 | goto exit_iounmap; | ||
922 | } | ||
923 | |||
924 | /* FSI is based on SPU mstp */ | ||
925 | snprintf(clk_name, sizeof(clk_name), "spu%d", pdev->id); | ||
926 | master->clk = clk_get(NULL, clk_name); | ||
927 | if (IS_ERR(master->clk)) { | ||
928 | dev_err(&pdev->dev, "cannot get %s mstp\n", clk_name); | ||
929 | ret = -EIO; | ||
930 | goto exit_free_dma; | ||
931 | } | ||
932 | |||
933 | fsi_soc_dai[0].dev = &pdev->dev; | ||
934 | fsi_soc_dai[1].dev = &pdev->dev; | ||
935 | |||
936 | fsi_soft_all_reset(); | ||
937 | |||
938 | ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master); | ||
939 | if (ret) { | ||
940 | dev_err(&pdev->dev, "irq request err\n"); | ||
941 | goto exit_free_dma; | ||
942 | } | ||
943 | |||
944 | ret = snd_soc_register_platform(&fsi_soc_platform); | ||
945 | if (ret < 0) { | ||
946 | dev_err(&pdev->dev, "cannot snd soc register\n"); | ||
947 | goto exit_free_irq; | ||
948 | } | ||
949 | |||
950 | return snd_soc_register_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai)); | ||
951 | |||
952 | exit_free_irq: | ||
953 | free_irq(irq, master); | ||
954 | exit_free_dma: | ||
955 | fsi_free_dma_chan(); | ||
956 | exit_iounmap: | ||
957 | iounmap(master->base); | ||
958 | exit_kfree: | ||
959 | kfree(master); | ||
960 | master = NULL; | ||
961 | exit: | ||
962 | return ret; | ||
963 | } | ||
964 | |||
965 | static int fsi_remove(struct platform_device *pdev) | ||
966 | { | ||
967 | snd_soc_unregister_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai)); | ||
968 | snd_soc_unregister_platform(&fsi_soc_platform); | ||
969 | |||
970 | clk_put(master->clk); | ||
971 | |||
972 | fsi_free_dma_chan(); | ||
973 | |||
974 | free_irq(master->irq, master); | ||
975 | |||
976 | iounmap(master->base); | ||
977 | kfree(master); | ||
978 | master = NULL; | ||
979 | return 0; | ||
980 | } | ||
981 | |||
982 | static struct platform_driver fsi_driver = { | ||
983 | .driver = { | ||
984 | .name = "sh_fsi", | ||
985 | }, | ||
986 | .probe = fsi_probe, | ||
987 | .remove = fsi_remove, | ||
988 | }; | ||
989 | |||
990 | static int __init fsi_mobile_init(void) | ||
991 | { | ||
992 | return platform_driver_register(&fsi_driver); | ||
993 | } | ||
994 | |||
995 | static void __exit fsi_mobile_exit(void) | ||
996 | { | ||
997 | platform_driver_unregister(&fsi_driver); | ||
998 | } | ||
999 | module_init(fsi_mobile_init); | ||
1000 | module_exit(fsi_mobile_exit); | ||
1001 | |||
1002 | MODULE_LICENSE("GPL"); | ||
1003 | MODULE_DESCRIPTION("SuperH onchip FSI audio driver"); | ||
1004 | MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>"); | ||