diff options
Diffstat (limited to 'sound/soc/sh/siu_dai.c')
-rw-r--r-- | sound/soc/sh/siu_dai.c | 848 |
1 files changed, 848 insertions, 0 deletions
diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c new file mode 100644 index 000000000000..d86ee1bfc03a --- /dev/null +++ b/sound/soc/sh/siu_dai.c | |||
@@ -0,0 +1,848 @@ | |||
1 | /* | ||
2 | * siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> | ||
5 | * Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/delay.h> | ||
23 | #include <linux/firmware.h> | ||
24 | #include <linux/pm_runtime.h> | ||
25 | #include <linux/slab.h> | ||
26 | |||
27 | #include <asm/clock.h> | ||
28 | #include <asm/siu.h> | ||
29 | |||
30 | #include <sound/control.h> | ||
31 | #include <sound/soc-dai.h> | ||
32 | |||
33 | #include "siu.h" | ||
34 | |||
35 | /* Board specifics */ | ||
36 | #if defined(CONFIG_CPU_SUBTYPE_SH7722) | ||
37 | # define SIU_MAX_VOLUME 0x1000 | ||
38 | #else | ||
39 | # define SIU_MAX_VOLUME 0x7fff | ||
40 | #endif | ||
41 | |||
42 | #define PRAM_SIZE 0x2000 | ||
43 | #define XRAM_SIZE 0x800 | ||
44 | #define YRAM_SIZE 0x800 | ||
45 | |||
46 | #define XRAM_OFFSET 0x4000 | ||
47 | #define YRAM_OFFSET 0x6000 | ||
48 | #define REG_OFFSET 0xc000 | ||
49 | |||
50 | #define PLAYBACK_ENABLED 1 | ||
51 | #define CAPTURE_ENABLED 2 | ||
52 | |||
53 | #define VOLUME_CAPTURE 0 | ||
54 | #define VOLUME_PLAYBACK 1 | ||
55 | #define DFLT_VOLUME_LEVEL 0x08000800 | ||
56 | |||
57 | /* | ||
58 | * SPDIF is only available on port A and on some SIU implementations it is only | ||
59 | * available for input. Due to the lack of hardware to test it, SPDIF is left | ||
60 | * disabled in this driver version | ||
61 | */ | ||
62 | struct format_flag { | ||
63 | u32 i2s; | ||
64 | u32 pcm; | ||
65 | u32 spdif; | ||
66 | u32 mask; | ||
67 | }; | ||
68 | |||
69 | struct port_flag { | ||
70 | struct format_flag playback; | ||
71 | struct format_flag capture; | ||
72 | }; | ||
73 | |||
74 | static struct port_flag siu_flags[SIU_PORT_NUM] = { | ||
75 | [SIU_PORT_A] = { | ||
76 | .playback = { | ||
77 | .i2s = 0x50000000, | ||
78 | .pcm = 0x40000000, | ||
79 | .spdif = 0x80000000, /* not on all SIU versions */ | ||
80 | .mask = 0xd0000000, | ||
81 | }, | ||
82 | .capture = { | ||
83 | .i2s = 0x05000000, | ||
84 | .pcm = 0x04000000, | ||
85 | .spdif = 0x08000000, | ||
86 | .mask = 0x0d000000, | ||
87 | }, | ||
88 | }, | ||
89 | [SIU_PORT_B] = { | ||
90 | .playback = { | ||
91 | .i2s = 0x00500000, | ||
92 | .pcm = 0x00400000, | ||
93 | .spdif = 0, /* impossible - turn off */ | ||
94 | .mask = 0x00500000, | ||
95 | }, | ||
96 | .capture = { | ||
97 | .i2s = 0x00050000, | ||
98 | .pcm = 0x00040000, | ||
99 | .spdif = 0, /* impossible - turn off */ | ||
100 | .mask = 0x00050000, | ||
101 | }, | ||
102 | }, | ||
103 | }; | ||
104 | |||
105 | static void siu_dai_start(struct siu_port *port_info) | ||
106 | { | ||
107 | struct siu_info *info = siu_i2s_dai.private_data; | ||
108 | u32 __iomem *base = info->reg; | ||
109 | |||
110 | dev_dbg(port_info->pcm->card->dev, "%s\n", __func__); | ||
111 | |||
112 | /* Turn on SIU clock */ | ||
113 | pm_runtime_get_sync(siu_i2s_dai.dev); | ||
114 | |||
115 | /* Issue software reset to siu */ | ||
116 | siu_write32(base + SIU_SRCTL, 0); | ||
117 | |||
118 | /* Wait for the reset to take effect */ | ||
119 | udelay(1); | ||
120 | |||
121 | port_info->stfifo = 0; | ||
122 | port_info->trdat = 0; | ||
123 | |||
124 | /* portA, portB, SIU operate */ | ||
125 | siu_write32(base + SIU_SRCTL, 0x301); | ||
126 | |||
127 | /* portA=256fs, portB=256fs */ | ||
128 | siu_write32(base + SIU_CKCTL, 0x40400000); | ||
129 | |||
130 | /* portA's BRG does not divide SIUCKA */ | ||
131 | siu_write32(base + SIU_BRGASEL, 0); | ||
132 | siu_write32(base + SIU_BRRA, 0); | ||
133 | |||
134 | /* portB's BRG divides SIUCKB by half */ | ||
135 | siu_write32(base + SIU_BRGBSEL, 1); | ||
136 | siu_write32(base + SIU_BRRB, 0); | ||
137 | |||
138 | siu_write32(base + SIU_IFCTL, 0x44440000); | ||
139 | |||
140 | /* portA: 32 bit/fs, master; portB: 32 bit/fs, master */ | ||
141 | siu_write32(base + SIU_SFORM, 0x0c0c0000); | ||
142 | |||
143 | /* | ||
144 | * Volume levels: looks like the DSP firmware implements volume controls | ||
145 | * differently from what's described in the datasheet | ||
146 | */ | ||
147 | siu_write32(base + SIU_SBDVCA, port_info->playback.volume); | ||
148 | siu_write32(base + SIU_SBDVCB, port_info->capture.volume); | ||
149 | } | ||
150 | |||
151 | static void siu_dai_stop(void) | ||
152 | { | ||
153 | struct siu_info *info = siu_i2s_dai.private_data; | ||
154 | u32 __iomem *base = info->reg; | ||
155 | |||
156 | /* SIU software reset */ | ||
157 | siu_write32(base + SIU_SRCTL, 0); | ||
158 | |||
159 | /* Turn off SIU clock */ | ||
160 | pm_runtime_put_sync(siu_i2s_dai.dev); | ||
161 | } | ||
162 | |||
163 | static void siu_dai_spbAselect(struct siu_port *port_info) | ||
164 | { | ||
165 | struct siu_info *info = siu_i2s_dai.private_data; | ||
166 | struct siu_firmware *fw = &info->fw; | ||
167 | u32 *ydef = fw->yram0; | ||
168 | u32 idx; | ||
169 | |||
170 | /* path A use */ | ||
171 | if (!info->port_id) | ||
172 | idx = 1; /* portA */ | ||
173 | else | ||
174 | idx = 2; /* portB */ | ||
175 | |||
176 | ydef[0] = (fw->spbpar[idx].ab1a << 16) | | ||
177 | (fw->spbpar[idx].ab0a << 8) | | ||
178 | (fw->spbpar[idx].dir << 7) | 3; | ||
179 | ydef[1] = fw->yram0[1]; /* 0x03000300 */ | ||
180 | ydef[2] = (16 / 2) << 24; | ||
181 | ydef[3] = fw->yram0[3]; /* 0 */ | ||
182 | ydef[4] = fw->yram0[4]; /* 0 */ | ||
183 | ydef[7] = fw->spbpar[idx].event; | ||
184 | port_info->stfifo |= fw->spbpar[idx].stfifo; | ||
185 | port_info->trdat |= fw->spbpar[idx].trdat; | ||
186 | } | ||
187 | |||
188 | static void siu_dai_spbBselect(struct siu_port *port_info) | ||
189 | { | ||
190 | struct siu_info *info = siu_i2s_dai.private_data; | ||
191 | struct siu_firmware *fw = &info->fw; | ||
192 | u32 *ydef = fw->yram0; | ||
193 | u32 idx; | ||
194 | |||
195 | /* path B use */ | ||
196 | if (!info->port_id) | ||
197 | idx = 7; /* portA */ | ||
198 | else | ||
199 | idx = 8; /* portB */ | ||
200 | |||
201 | ydef[5] = (fw->spbpar[idx].ab1a << 16) | | ||
202 | (fw->spbpar[idx].ab0a << 8) | 1; | ||
203 | ydef[6] = fw->spbpar[idx].event; | ||
204 | port_info->stfifo |= fw->spbpar[idx].stfifo; | ||
205 | port_info->trdat |= fw->spbpar[idx].trdat; | ||
206 | } | ||
207 | |||
208 | static void siu_dai_open(struct siu_stream *siu_stream) | ||
209 | { | ||
210 | struct siu_info *info = siu_i2s_dai.private_data; | ||
211 | u32 __iomem *base = info->reg; | ||
212 | u32 srctl, ifctl; | ||
213 | |||
214 | srctl = siu_read32(base + SIU_SRCTL); | ||
215 | ifctl = siu_read32(base + SIU_IFCTL); | ||
216 | |||
217 | switch (info->port_id) { | ||
218 | case SIU_PORT_A: | ||
219 | /* portA operates */ | ||
220 | srctl |= 0x200; | ||
221 | ifctl &= ~0xc2; | ||
222 | break; | ||
223 | case SIU_PORT_B: | ||
224 | /* portB operates */ | ||
225 | srctl |= 0x100; | ||
226 | ifctl &= ~0x31; | ||
227 | break; | ||
228 | } | ||
229 | |||
230 | siu_write32(base + SIU_SRCTL, srctl); | ||
231 | /* Unmute and configure portA */ | ||
232 | siu_write32(base + SIU_IFCTL, ifctl); | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * At the moment only fixed Left-upper, Left-lower, Right-upper, Right-lower | ||
237 | * packing is supported | ||
238 | */ | ||
239 | static void siu_dai_pcmdatapack(struct siu_stream *siu_stream) | ||
240 | { | ||
241 | struct siu_info *info = siu_i2s_dai.private_data; | ||
242 | u32 __iomem *base = info->reg; | ||
243 | u32 dpak; | ||
244 | |||
245 | dpak = siu_read32(base + SIU_DPAK); | ||
246 | |||
247 | switch (info->port_id) { | ||
248 | case SIU_PORT_A: | ||
249 | dpak &= ~0xc0000000; | ||
250 | break; | ||
251 | case SIU_PORT_B: | ||
252 | dpak &= ~0x00c00000; | ||
253 | break; | ||
254 | } | ||
255 | |||
256 | siu_write32(base + SIU_DPAK, dpak); | ||
257 | } | ||
258 | |||
259 | static int siu_dai_spbstart(struct siu_port *port_info) | ||
260 | { | ||
261 | struct siu_info *info = siu_i2s_dai.private_data; | ||
262 | u32 __iomem *base = info->reg; | ||
263 | struct siu_firmware *fw = &info->fw; | ||
264 | u32 *ydef = fw->yram0; | ||
265 | int cnt; | ||
266 | u32 __iomem *add; | ||
267 | u32 *ptr; | ||
268 | |||
269 | /* Load SPB Program in PRAM */ | ||
270 | ptr = fw->pram0; | ||
271 | add = info->pram; | ||
272 | for (cnt = 0; cnt < PRAM0_SIZE; cnt++, add++, ptr++) | ||
273 | siu_write32(add, *ptr); | ||
274 | |||
275 | ptr = fw->pram1; | ||
276 | add = info->pram + (0x0100 / sizeof(u32)); | ||
277 | for (cnt = 0; cnt < PRAM1_SIZE; cnt++, add++, ptr++) | ||
278 | siu_write32(add, *ptr); | ||
279 | |||
280 | /* XRAM initialization */ | ||
281 | add = info->xram; | ||
282 | for (cnt = 0; cnt < XRAM0_SIZE + XRAM1_SIZE + XRAM2_SIZE; cnt++, add++) | ||
283 | siu_write32(add, 0); | ||
284 | |||
285 | /* YRAM variable area initialization */ | ||
286 | add = info->yram; | ||
287 | for (cnt = 0; cnt < YRAM_DEF_SIZE; cnt++, add++) | ||
288 | siu_write32(add, ydef[cnt]); | ||
289 | |||
290 | /* YRAM FIR coefficient area initialization */ | ||
291 | add = info->yram + (0x0200 / sizeof(u32)); | ||
292 | for (cnt = 0; cnt < YRAM_FIR_SIZE; cnt++, add++) | ||
293 | siu_write32(add, fw->yram_fir_coeff[cnt]); | ||
294 | |||
295 | /* YRAM IIR coefficient area initialization */ | ||
296 | add = info->yram + (0x0600 / sizeof(u32)); | ||
297 | for (cnt = 0; cnt < YRAM_IIR_SIZE; cnt++, add++) | ||
298 | siu_write32(add, 0); | ||
299 | |||
300 | siu_write32(base + SIU_TRDAT, port_info->trdat); | ||
301 | port_info->trdat = 0x0; | ||
302 | |||
303 | |||
304 | /* SPB start condition: software */ | ||
305 | siu_write32(base + SIU_SBACTIV, 0); | ||
306 | /* Start SPB */ | ||
307 | siu_write32(base + SIU_SBCTL, 0xc0000000); | ||
308 | /* Wait for program to halt */ | ||
309 | cnt = 0x10000; | ||
310 | while (--cnt && siu_read32(base + SIU_SBCTL) != 0x80000000) | ||
311 | cpu_relax(); | ||
312 | |||
313 | if (!cnt) | ||
314 | return -EBUSY; | ||
315 | |||
316 | /* SPB program start address setting */ | ||
317 | siu_write32(base + SIU_SBPSET, 0x00400000); | ||
318 | /* SPB hardware start(FIFOCTL source) */ | ||
319 | siu_write32(base + SIU_SBACTIV, 0xc0000000); | ||
320 | |||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static void siu_dai_spbstop(struct siu_port *port_info) | ||
325 | { | ||
326 | struct siu_info *info = siu_i2s_dai.private_data; | ||
327 | u32 __iomem *base = info->reg; | ||
328 | |||
329 | siu_write32(base + SIU_SBACTIV, 0); | ||
330 | /* SPB stop */ | ||
331 | siu_write32(base + SIU_SBCTL, 0); | ||
332 | |||
333 | port_info->stfifo = 0; | ||
334 | } | ||
335 | |||
336 | /* API functions */ | ||
337 | |||
338 | /* Playback and capture hardware properties are identical */ | ||
339 | static struct snd_pcm_hardware siu_dai_pcm_hw = { | ||
340 | .info = SNDRV_PCM_INFO_INTERLEAVED, | ||
341 | .formats = SNDRV_PCM_FMTBIT_S16, | ||
342 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
343 | .rate_min = 8000, | ||
344 | .rate_max = 48000, | ||
345 | .channels_min = 2, | ||
346 | .channels_max = 2, | ||
347 | .buffer_bytes_max = SIU_BUFFER_BYTES_MAX, | ||
348 | .period_bytes_min = SIU_PERIOD_BYTES_MIN, | ||
349 | .period_bytes_max = SIU_PERIOD_BYTES_MAX, | ||
350 | .periods_min = SIU_PERIODS_MIN, | ||
351 | .periods_max = SIU_PERIODS_MAX, | ||
352 | }; | ||
353 | |||
354 | static int siu_dai_info_volume(struct snd_kcontrol *kctrl, | ||
355 | struct snd_ctl_elem_info *uinfo) | ||
356 | { | ||
357 | struct siu_port *port_info = snd_kcontrol_chip(kctrl); | ||
358 | |||
359 | dev_dbg(port_info->pcm->card->dev, "%s\n", __func__); | ||
360 | |||
361 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
362 | uinfo->count = 2; | ||
363 | uinfo->value.integer.min = 0; | ||
364 | uinfo->value.integer.max = SIU_MAX_VOLUME; | ||
365 | |||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | static int siu_dai_get_volume(struct snd_kcontrol *kctrl, | ||
370 | struct snd_ctl_elem_value *ucontrol) | ||
371 | { | ||
372 | struct siu_port *port_info = snd_kcontrol_chip(kctrl); | ||
373 | struct device *dev = port_info->pcm->card->dev; | ||
374 | u32 vol; | ||
375 | |||
376 | dev_dbg(dev, "%s\n", __func__); | ||
377 | |||
378 | switch (kctrl->private_value) { | ||
379 | case VOLUME_PLAYBACK: | ||
380 | /* Playback is always on port 0 */ | ||
381 | vol = port_info->playback.volume; | ||
382 | ucontrol->value.integer.value[0] = vol & 0xffff; | ||
383 | ucontrol->value.integer.value[1] = vol >> 16 & 0xffff; | ||
384 | break; | ||
385 | case VOLUME_CAPTURE: | ||
386 | /* Capture is always on port 1 */ | ||
387 | vol = port_info->capture.volume; | ||
388 | ucontrol->value.integer.value[0] = vol & 0xffff; | ||
389 | ucontrol->value.integer.value[1] = vol >> 16 & 0xffff; | ||
390 | break; | ||
391 | default: | ||
392 | dev_err(dev, "%s() invalid private_value=%ld\n", | ||
393 | __func__, kctrl->private_value); | ||
394 | return -EINVAL; | ||
395 | } | ||
396 | |||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | static int siu_dai_put_volume(struct snd_kcontrol *kctrl, | ||
401 | struct snd_ctl_elem_value *ucontrol) | ||
402 | { | ||
403 | struct siu_port *port_info = snd_kcontrol_chip(kctrl); | ||
404 | struct device *dev = port_info->pcm->card->dev; | ||
405 | struct siu_info *info = siu_i2s_dai.private_data; | ||
406 | u32 __iomem *base = info->reg; | ||
407 | u32 new_vol; | ||
408 | u32 cur_vol; | ||
409 | |||
410 | dev_dbg(dev, "%s\n", __func__); | ||
411 | |||
412 | if (ucontrol->value.integer.value[0] < 0 || | ||
413 | ucontrol->value.integer.value[0] > SIU_MAX_VOLUME || | ||
414 | ucontrol->value.integer.value[1] < 0 || | ||
415 | ucontrol->value.integer.value[1] > SIU_MAX_VOLUME) | ||
416 | return -EINVAL; | ||
417 | |||
418 | new_vol = ucontrol->value.integer.value[0] | | ||
419 | ucontrol->value.integer.value[1] << 16; | ||
420 | |||
421 | /* See comment above - DSP firmware implementation */ | ||
422 | switch (kctrl->private_value) { | ||
423 | case VOLUME_PLAYBACK: | ||
424 | /* Playback is always on port 0 */ | ||
425 | cur_vol = port_info->playback.volume; | ||
426 | siu_write32(base + SIU_SBDVCA, new_vol); | ||
427 | port_info->playback.volume = new_vol; | ||
428 | break; | ||
429 | case VOLUME_CAPTURE: | ||
430 | /* Capture is always on port 1 */ | ||
431 | cur_vol = port_info->capture.volume; | ||
432 | siu_write32(base + SIU_SBDVCB, new_vol); | ||
433 | port_info->capture.volume = new_vol; | ||
434 | break; | ||
435 | default: | ||
436 | dev_err(dev, "%s() invalid private_value=%ld\n", | ||
437 | __func__, kctrl->private_value); | ||
438 | return -EINVAL; | ||
439 | } | ||
440 | |||
441 | if (cur_vol != new_vol) | ||
442 | return 1; | ||
443 | |||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | static struct snd_kcontrol_new playback_controls = { | ||
448 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
449 | .name = "PCM Playback Volume", | ||
450 | .index = 0, | ||
451 | .info = siu_dai_info_volume, | ||
452 | .get = siu_dai_get_volume, | ||
453 | .put = siu_dai_put_volume, | ||
454 | .private_value = VOLUME_PLAYBACK, | ||
455 | }; | ||
456 | |||
457 | static struct snd_kcontrol_new capture_controls = { | ||
458 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
459 | .name = "PCM Capture Volume", | ||
460 | .index = 0, | ||
461 | .info = siu_dai_info_volume, | ||
462 | .get = siu_dai_get_volume, | ||
463 | .put = siu_dai_put_volume, | ||
464 | .private_value = VOLUME_CAPTURE, | ||
465 | }; | ||
466 | |||
467 | int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card) | ||
468 | { | ||
469 | struct device *dev = card->dev; | ||
470 | struct snd_kcontrol *kctrl; | ||
471 | int ret; | ||
472 | |||
473 | *port_info = kzalloc(sizeof(**port_info), GFP_KERNEL); | ||
474 | if (!*port_info) | ||
475 | return -ENOMEM; | ||
476 | |||
477 | dev_dbg(dev, "%s: port #%d@%p\n", __func__, port, *port_info); | ||
478 | |||
479 | (*port_info)->playback.volume = DFLT_VOLUME_LEVEL; | ||
480 | (*port_info)->capture.volume = DFLT_VOLUME_LEVEL; | ||
481 | |||
482 | /* | ||
483 | * Add mixer support. The SPB is used to change the volume. Both | ||
484 | * ports use the same SPB. Therefore, we only register one | ||
485 | * control instance since it will be used by both channels. | ||
486 | * In error case we continue without controls. | ||
487 | */ | ||
488 | kctrl = snd_ctl_new1(&playback_controls, *port_info); | ||
489 | ret = snd_ctl_add(card, kctrl); | ||
490 | if (ret < 0) | ||
491 | dev_err(dev, | ||
492 | "failed to add playback controls %p port=%d err=%d\n", | ||
493 | kctrl, port, ret); | ||
494 | |||
495 | kctrl = snd_ctl_new1(&capture_controls, *port_info); | ||
496 | ret = snd_ctl_add(card, kctrl); | ||
497 | if (ret < 0) | ||
498 | dev_err(dev, | ||
499 | "failed to add capture controls %p port=%d err=%d\n", | ||
500 | kctrl, port, ret); | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | void siu_free_port(struct siu_port *port_info) | ||
506 | { | ||
507 | kfree(port_info); | ||
508 | } | ||
509 | |||
510 | static int siu_dai_startup(struct snd_pcm_substream *substream, | ||
511 | struct snd_soc_dai *dai) | ||
512 | { | ||
513 | struct siu_info *info = siu_i2s_dai.private_data; | ||
514 | struct snd_pcm_runtime *rt = substream->runtime; | ||
515 | struct siu_port *port_info = siu_port_info(substream); | ||
516 | int ret; | ||
517 | |||
518 | dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__, | ||
519 | info->port_id, port_info); | ||
520 | |||
521 | snd_soc_set_runtime_hwparams(substream, &siu_dai_pcm_hw); | ||
522 | |||
523 | ret = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); | ||
524 | if (unlikely(ret < 0)) | ||
525 | return ret; | ||
526 | |||
527 | siu_dai_start(port_info); | ||
528 | |||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | static void siu_dai_shutdown(struct snd_pcm_substream *substream, | ||
533 | struct snd_soc_dai *dai) | ||
534 | { | ||
535 | struct siu_info *info = siu_i2s_dai.private_data; | ||
536 | struct siu_port *port_info = siu_port_info(substream); | ||
537 | |||
538 | dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__, | ||
539 | info->port_id, port_info); | ||
540 | |||
541 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
542 | port_info->play_cap &= ~PLAYBACK_ENABLED; | ||
543 | else | ||
544 | port_info->play_cap &= ~CAPTURE_ENABLED; | ||
545 | |||
546 | /* Stop the siu if the other stream is not using it */ | ||
547 | if (!port_info->play_cap) { | ||
548 | /* during stmread or stmwrite ? */ | ||
549 | BUG_ON(port_info->playback.rw_flg || port_info->capture.rw_flg); | ||
550 | siu_dai_spbstop(port_info); | ||
551 | siu_dai_stop(); | ||
552 | } | ||
553 | } | ||
554 | |||
555 | /* PCM part of siu_dai_playback_prepare() / siu_dai_capture_prepare() */ | ||
556 | static int siu_dai_prepare(struct snd_pcm_substream *substream, | ||
557 | struct snd_soc_dai *dai) | ||
558 | { | ||
559 | struct siu_info *info = siu_i2s_dai.private_data; | ||
560 | struct snd_pcm_runtime *rt = substream->runtime; | ||
561 | struct siu_port *port_info = siu_port_info(substream); | ||
562 | struct siu_stream *siu_stream; | ||
563 | int self, ret; | ||
564 | |||
565 | dev_dbg(substream->pcm->card->dev, | ||
566 | "%s: port %d, active streams %lx, %d channels\n", | ||
567 | __func__, info->port_id, port_info->play_cap, rt->channels); | ||
568 | |||
569 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
570 | self = PLAYBACK_ENABLED; | ||
571 | siu_stream = &port_info->playback; | ||
572 | } else { | ||
573 | self = CAPTURE_ENABLED; | ||
574 | siu_stream = &port_info->capture; | ||
575 | } | ||
576 | |||
577 | /* Set up the siu if not already done */ | ||
578 | if (!port_info->play_cap) { | ||
579 | siu_stream->rw_flg = 0; /* stream-data transfer flag */ | ||
580 | |||
581 | siu_dai_spbAselect(port_info); | ||
582 | siu_dai_spbBselect(port_info); | ||
583 | |||
584 | siu_dai_open(siu_stream); | ||
585 | |||
586 | siu_dai_pcmdatapack(siu_stream); | ||
587 | |||
588 | ret = siu_dai_spbstart(port_info); | ||
589 | if (ret < 0) | ||
590 | goto fail; | ||
591 | } | ||
592 | |||
593 | port_info->play_cap |= self; | ||
594 | |||
595 | fail: | ||
596 | return ret; | ||
597 | } | ||
598 | |||
599 | /* | ||
600 | * SIU can set bus format to I2S / PCM / SPDIF independently for playback and | ||
601 | * capture, however, the current API sets the bus format globally for a DAI. | ||
602 | */ | ||
603 | static int siu_dai_set_fmt(struct snd_soc_dai *dai, | ||
604 | unsigned int fmt) | ||
605 | { | ||
606 | struct siu_info *info = siu_i2s_dai.private_data; | ||
607 | u32 __iomem *base = info->reg; | ||
608 | u32 ifctl; | ||
609 | |||
610 | dev_dbg(dai->dev, "%s: fmt 0x%x on port %d\n", | ||
611 | __func__, fmt, info->port_id); | ||
612 | |||
613 | if (info->port_id < 0) | ||
614 | return -ENODEV; | ||
615 | |||
616 | /* Here select between I2S / PCM / SPDIF */ | ||
617 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
618 | case SND_SOC_DAIFMT_I2S: | ||
619 | ifctl = siu_flags[info->port_id].playback.i2s | | ||
620 | siu_flags[info->port_id].capture.i2s; | ||
621 | break; | ||
622 | case SND_SOC_DAIFMT_LEFT_J: | ||
623 | ifctl = siu_flags[info->port_id].playback.pcm | | ||
624 | siu_flags[info->port_id].capture.pcm; | ||
625 | break; | ||
626 | /* SPDIF disabled - see comment at the top */ | ||
627 | default: | ||
628 | return -EINVAL; | ||
629 | } | ||
630 | |||
631 | ifctl |= ~(siu_flags[info->port_id].playback.mask | | ||
632 | siu_flags[info->port_id].capture.mask) & | ||
633 | siu_read32(base + SIU_IFCTL); | ||
634 | siu_write32(base + SIU_IFCTL, ifctl); | ||
635 | |||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static int siu_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, | ||
640 | unsigned int freq, int dir) | ||
641 | { | ||
642 | struct clk *siu_clk, *parent_clk; | ||
643 | char *siu_name, *parent_name; | ||
644 | int ret; | ||
645 | |||
646 | if (dir != SND_SOC_CLOCK_IN) | ||
647 | return -EINVAL; | ||
648 | |||
649 | dev_dbg(dai->dev, "%s: using clock %d\n", __func__, clk_id); | ||
650 | |||
651 | switch (clk_id) { | ||
652 | case SIU_CLKA_PLL: | ||
653 | siu_name = "siua_clk"; | ||
654 | parent_name = "pll_clk"; | ||
655 | break; | ||
656 | case SIU_CLKA_EXT: | ||
657 | siu_name = "siua_clk"; | ||
658 | parent_name = "siumcka_clk"; | ||
659 | break; | ||
660 | case SIU_CLKB_PLL: | ||
661 | siu_name = "siub_clk"; | ||
662 | parent_name = "pll_clk"; | ||
663 | break; | ||
664 | case SIU_CLKB_EXT: | ||
665 | siu_name = "siub_clk"; | ||
666 | parent_name = "siumckb_clk"; | ||
667 | break; | ||
668 | default: | ||
669 | return -EINVAL; | ||
670 | } | ||
671 | |||
672 | siu_clk = clk_get(siu_i2s_dai.dev, siu_name); | ||
673 | if (IS_ERR(siu_clk)) | ||
674 | return PTR_ERR(siu_clk); | ||
675 | |||
676 | parent_clk = clk_get(siu_i2s_dai.dev, parent_name); | ||
677 | if (!IS_ERR(parent_clk)) { | ||
678 | ret = clk_set_parent(siu_clk, parent_clk); | ||
679 | if (!ret) | ||
680 | clk_set_rate(siu_clk, freq); | ||
681 | clk_put(parent_clk); | ||
682 | } | ||
683 | |||
684 | clk_put(siu_clk); | ||
685 | |||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | static struct snd_soc_dai_ops siu_dai_ops = { | ||
690 | .startup = siu_dai_startup, | ||
691 | .shutdown = siu_dai_shutdown, | ||
692 | .prepare = siu_dai_prepare, | ||
693 | .set_sysclk = siu_dai_set_sysclk, | ||
694 | .set_fmt = siu_dai_set_fmt, | ||
695 | }; | ||
696 | |||
697 | struct snd_soc_dai siu_i2s_dai = { | ||
698 | .name = "sh-siu", | ||
699 | .id = 0, | ||
700 | .playback = { | ||
701 | .channels_min = 2, | ||
702 | .channels_max = 2, | ||
703 | .formats = SNDRV_PCM_FMTBIT_S16, | ||
704 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
705 | }, | ||
706 | .capture = { | ||
707 | .channels_min = 2, | ||
708 | .channels_max = 2, | ||
709 | .formats = SNDRV_PCM_FMTBIT_S16, | ||
710 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
711 | }, | ||
712 | .ops = &siu_dai_ops, | ||
713 | }; | ||
714 | EXPORT_SYMBOL_GPL(siu_i2s_dai); | ||
715 | |||
716 | static int __devinit siu_probe(struct platform_device *pdev) | ||
717 | { | ||
718 | const struct firmware *fw_entry; | ||
719 | struct resource *res, *region; | ||
720 | struct siu_info *info; | ||
721 | int ret; | ||
722 | |||
723 | info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
724 | if (!info) | ||
725 | return -ENOMEM; | ||
726 | |||
727 | ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); | ||
728 | if (ret) | ||
729 | goto ereqfw; | ||
730 | |||
731 | /* | ||
732 | * Loaded firmware is "const" - read only, but we have to modify it in | ||
733 | * snd_siu_sh7343_spbAselect() and snd_siu_sh7343_spbBselect() | ||
734 | */ | ||
735 | memcpy(&info->fw, fw_entry->data, fw_entry->size); | ||
736 | |||
737 | release_firmware(fw_entry); | ||
738 | |||
739 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
740 | if (!res) { | ||
741 | ret = -ENODEV; | ||
742 | goto egetres; | ||
743 | } | ||
744 | |||
745 | region = request_mem_region(res->start, resource_size(res), | ||
746 | pdev->name); | ||
747 | if (!region) { | ||
748 | dev_err(&pdev->dev, "SIU region already claimed\n"); | ||
749 | ret = -EBUSY; | ||
750 | goto ereqmemreg; | ||
751 | } | ||
752 | |||
753 | ret = -ENOMEM; | ||
754 | info->pram = ioremap(res->start, PRAM_SIZE); | ||
755 | if (!info->pram) | ||
756 | goto emappram; | ||
757 | info->xram = ioremap(res->start + XRAM_OFFSET, XRAM_SIZE); | ||
758 | if (!info->xram) | ||
759 | goto emapxram; | ||
760 | info->yram = ioremap(res->start + YRAM_OFFSET, YRAM_SIZE); | ||
761 | if (!info->yram) | ||
762 | goto emapyram; | ||
763 | info->reg = ioremap(res->start + REG_OFFSET, resource_size(res) - | ||
764 | REG_OFFSET); | ||
765 | if (!info->reg) | ||
766 | goto emapreg; | ||
767 | |||
768 | siu_i2s_dai.dev = &pdev->dev; | ||
769 | siu_i2s_dai.private_data = info; | ||
770 | |||
771 | ret = snd_soc_register_dais(&siu_i2s_dai, 1); | ||
772 | if (ret < 0) | ||
773 | goto edaiinit; | ||
774 | |||
775 | ret = snd_soc_register_platform(&siu_platform); | ||
776 | if (ret < 0) | ||
777 | goto esocregp; | ||
778 | |||
779 | pm_runtime_enable(&pdev->dev); | ||
780 | |||
781 | return ret; | ||
782 | |||
783 | esocregp: | ||
784 | snd_soc_unregister_dais(&siu_i2s_dai, 1); | ||
785 | edaiinit: | ||
786 | iounmap(info->reg); | ||
787 | emapreg: | ||
788 | iounmap(info->yram); | ||
789 | emapyram: | ||
790 | iounmap(info->xram); | ||
791 | emapxram: | ||
792 | iounmap(info->pram); | ||
793 | emappram: | ||
794 | release_mem_region(res->start, resource_size(res)); | ||
795 | ereqmemreg: | ||
796 | egetres: | ||
797 | ereqfw: | ||
798 | kfree(info); | ||
799 | |||
800 | return ret; | ||
801 | } | ||
802 | |||
803 | static int __devexit siu_remove(struct platform_device *pdev) | ||
804 | { | ||
805 | struct siu_info *info = siu_i2s_dai.private_data; | ||
806 | struct resource *res; | ||
807 | |||
808 | pm_runtime_disable(&pdev->dev); | ||
809 | |||
810 | snd_soc_unregister_platform(&siu_platform); | ||
811 | snd_soc_unregister_dais(&siu_i2s_dai, 1); | ||
812 | |||
813 | iounmap(info->reg); | ||
814 | iounmap(info->yram); | ||
815 | iounmap(info->xram); | ||
816 | iounmap(info->pram); | ||
817 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
818 | if (res) | ||
819 | release_mem_region(res->start, resource_size(res)); | ||
820 | kfree(info); | ||
821 | |||
822 | return 0; | ||
823 | } | ||
824 | |||
825 | static struct platform_driver siu_driver = { | ||
826 | .driver = { | ||
827 | .name = "sh_siu", | ||
828 | }, | ||
829 | .probe = siu_probe, | ||
830 | .remove = __devexit_p(siu_remove), | ||
831 | }; | ||
832 | |||
833 | static int __init siu_init(void) | ||
834 | { | ||
835 | return platform_driver_register(&siu_driver); | ||
836 | } | ||
837 | |||
838 | static void __exit siu_exit(void) | ||
839 | { | ||
840 | platform_driver_unregister(&siu_driver); | ||
841 | } | ||
842 | |||
843 | module_init(siu_init) | ||
844 | module_exit(siu_exit) | ||
845 | |||
846 | MODULE_AUTHOR("Carlos Munoz <carlos@kenati.com>"); | ||
847 | MODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver"); | ||
848 | MODULE_LICENSE("GPL"); | ||