14e6fdaf1SKuninori Morimoto // SPDX-License-Identifier: GPL-2.0+
24e6fdaf1SKuninori Morimoto //
34e6fdaf1SKuninori Morimoto // siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral.
44e6fdaf1SKuninori Morimoto //
54e6fdaf1SKuninori Morimoto // Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
64e6fdaf1SKuninori Morimoto // Copyright (C) 2006 Carlos Munoz <carlos@kenati.com>
7895d4509SGuennadi Liakhovetski
8895d4509SGuennadi Liakhovetski #include <linux/delay.h>
9895d4509SGuennadi Liakhovetski #include <linux/firmware.h>
10895d4509SGuennadi Liakhovetski #include <linux/pm_runtime.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
12da155d5bSPaul Gortmaker #include <linux/module.h>
13895d4509SGuennadi Liakhovetski
14895d4509SGuennadi Liakhovetski #include <asm/clock.h>
15895d4509SGuennadi Liakhovetski #include <asm/siu.h>
16895d4509SGuennadi Liakhovetski
17895d4509SGuennadi Liakhovetski #include <sound/control.h>
180d911baeSJarkko Nikula #include <sound/soc.h>
19895d4509SGuennadi Liakhovetski
20895d4509SGuennadi Liakhovetski #include "siu.h"
21895d4509SGuennadi Liakhovetski
22895d4509SGuennadi Liakhovetski /* Board specifics */
23895d4509SGuennadi Liakhovetski #if defined(CONFIG_CPU_SUBTYPE_SH7722)
24895d4509SGuennadi Liakhovetski # define SIU_MAX_VOLUME 0x1000
25895d4509SGuennadi Liakhovetski #else
26895d4509SGuennadi Liakhovetski # define SIU_MAX_VOLUME 0x7fff
27895d4509SGuennadi Liakhovetski #endif
28895d4509SGuennadi Liakhovetski
29895d4509SGuennadi Liakhovetski #define PRAM_SIZE 0x2000
30895d4509SGuennadi Liakhovetski #define XRAM_SIZE 0x800
31895d4509SGuennadi Liakhovetski #define YRAM_SIZE 0x800
32895d4509SGuennadi Liakhovetski
33895d4509SGuennadi Liakhovetski #define XRAM_OFFSET 0x4000
34895d4509SGuennadi Liakhovetski #define YRAM_OFFSET 0x6000
35895d4509SGuennadi Liakhovetski #define REG_OFFSET 0xc000
36895d4509SGuennadi Liakhovetski
37895d4509SGuennadi Liakhovetski #define PLAYBACK_ENABLED 1
38895d4509SGuennadi Liakhovetski #define CAPTURE_ENABLED 2
39895d4509SGuennadi Liakhovetski
40895d4509SGuennadi Liakhovetski #define VOLUME_CAPTURE 0
41895d4509SGuennadi Liakhovetski #define VOLUME_PLAYBACK 1
42895d4509SGuennadi Liakhovetski #define DFLT_VOLUME_LEVEL 0x08000800
43895d4509SGuennadi Liakhovetski
44895d4509SGuennadi Liakhovetski /*
45895d4509SGuennadi Liakhovetski * SPDIF is only available on port A and on some SIU implementations it is only
46895d4509SGuennadi Liakhovetski * available for input. Due to the lack of hardware to test it, SPDIF is left
47895d4509SGuennadi Liakhovetski * disabled in this driver version
48895d4509SGuennadi Liakhovetski */
49895d4509SGuennadi Liakhovetski struct format_flag {
50895d4509SGuennadi Liakhovetski u32 i2s;
51895d4509SGuennadi Liakhovetski u32 pcm;
52895d4509SGuennadi Liakhovetski u32 spdif;
53895d4509SGuennadi Liakhovetski u32 mask;
54895d4509SGuennadi Liakhovetski };
55895d4509SGuennadi Liakhovetski
56895d4509SGuennadi Liakhovetski struct port_flag {
57895d4509SGuennadi Liakhovetski struct format_flag playback;
58895d4509SGuennadi Liakhovetski struct format_flag capture;
59895d4509SGuennadi Liakhovetski };
60895d4509SGuennadi Liakhovetski
61c6834dd2SGuennadi Liakhovetski struct siu_info *siu_i2s_data;
62f0fba2adSLiam Girdwood
63895d4509SGuennadi Liakhovetski static struct port_flag siu_flags[SIU_PORT_NUM] = {
64895d4509SGuennadi Liakhovetski [SIU_PORT_A] = {
65895d4509SGuennadi Liakhovetski .playback = {
66895d4509SGuennadi Liakhovetski .i2s = 0x50000000,
67895d4509SGuennadi Liakhovetski .pcm = 0x40000000,
68895d4509SGuennadi Liakhovetski .spdif = 0x80000000, /* not on all SIU versions */
69895d4509SGuennadi Liakhovetski .mask = 0xd0000000,
70895d4509SGuennadi Liakhovetski },
71895d4509SGuennadi Liakhovetski .capture = {
72895d4509SGuennadi Liakhovetski .i2s = 0x05000000,
73895d4509SGuennadi Liakhovetski .pcm = 0x04000000,
74895d4509SGuennadi Liakhovetski .spdif = 0x08000000,
75895d4509SGuennadi Liakhovetski .mask = 0x0d000000,
76895d4509SGuennadi Liakhovetski },
77895d4509SGuennadi Liakhovetski },
78895d4509SGuennadi Liakhovetski [SIU_PORT_B] = {
79895d4509SGuennadi Liakhovetski .playback = {
80895d4509SGuennadi Liakhovetski .i2s = 0x00500000,
81895d4509SGuennadi Liakhovetski .pcm = 0x00400000,
82895d4509SGuennadi Liakhovetski .spdif = 0, /* impossible - turn off */
83895d4509SGuennadi Liakhovetski .mask = 0x00500000,
84895d4509SGuennadi Liakhovetski },
85895d4509SGuennadi Liakhovetski .capture = {
86895d4509SGuennadi Liakhovetski .i2s = 0x00050000,
87895d4509SGuennadi Liakhovetski .pcm = 0x00040000,
88895d4509SGuennadi Liakhovetski .spdif = 0, /* impossible - turn off */
89895d4509SGuennadi Liakhovetski .mask = 0x00050000,
90895d4509SGuennadi Liakhovetski },
91895d4509SGuennadi Liakhovetski },
92895d4509SGuennadi Liakhovetski };
93895d4509SGuennadi Liakhovetski
siu_dai_start(struct siu_port * port_info)94895d4509SGuennadi Liakhovetski static void siu_dai_start(struct siu_port *port_info)
95895d4509SGuennadi Liakhovetski {
96f0fba2adSLiam Girdwood struct siu_info *info = siu_i2s_data;
97895d4509SGuennadi Liakhovetski u32 __iomem *base = info->reg;
98895d4509SGuennadi Liakhovetski
99895d4509SGuennadi Liakhovetski dev_dbg(port_info->pcm->card->dev, "%s\n", __func__);
100895d4509SGuennadi Liakhovetski
101895d4509SGuennadi Liakhovetski /* Issue software reset to siu */
102895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SRCTL, 0);
103895d4509SGuennadi Liakhovetski
104895d4509SGuennadi Liakhovetski /* Wait for the reset to take effect */
105895d4509SGuennadi Liakhovetski udelay(1);
106895d4509SGuennadi Liakhovetski
107895d4509SGuennadi Liakhovetski port_info->stfifo = 0;
108895d4509SGuennadi Liakhovetski port_info->trdat = 0;
109895d4509SGuennadi Liakhovetski
110895d4509SGuennadi Liakhovetski /* portA, portB, SIU operate */
111895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SRCTL, 0x301);
112895d4509SGuennadi Liakhovetski
113895d4509SGuennadi Liakhovetski /* portA=256fs, portB=256fs */
114895d4509SGuennadi Liakhovetski siu_write32(base + SIU_CKCTL, 0x40400000);
115895d4509SGuennadi Liakhovetski
116895d4509SGuennadi Liakhovetski /* portA's BRG does not divide SIUCKA */
117895d4509SGuennadi Liakhovetski siu_write32(base + SIU_BRGASEL, 0);
118895d4509SGuennadi Liakhovetski siu_write32(base + SIU_BRRA, 0);
119895d4509SGuennadi Liakhovetski
120895d4509SGuennadi Liakhovetski /* portB's BRG divides SIUCKB by half */
121895d4509SGuennadi Liakhovetski siu_write32(base + SIU_BRGBSEL, 1);
122895d4509SGuennadi Liakhovetski siu_write32(base + SIU_BRRB, 0);
123895d4509SGuennadi Liakhovetski
124895d4509SGuennadi Liakhovetski siu_write32(base + SIU_IFCTL, 0x44440000);
125895d4509SGuennadi Liakhovetski
126895d4509SGuennadi Liakhovetski /* portA: 32 bit/fs, master; portB: 32 bit/fs, master */
127895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SFORM, 0x0c0c0000);
128895d4509SGuennadi Liakhovetski
129895d4509SGuennadi Liakhovetski /*
130895d4509SGuennadi Liakhovetski * Volume levels: looks like the DSP firmware implements volume controls
131895d4509SGuennadi Liakhovetski * differently from what's described in the datasheet
132895d4509SGuennadi Liakhovetski */
133895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SBDVCA, port_info->playback.volume);
134895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SBDVCB, port_info->capture.volume);
135895d4509SGuennadi Liakhovetski }
136895d4509SGuennadi Liakhovetski
siu_dai_stop(struct siu_port * port_info)137f0fba2adSLiam Girdwood static void siu_dai_stop(struct siu_port *port_info)
138895d4509SGuennadi Liakhovetski {
139f0fba2adSLiam Girdwood struct siu_info *info = siu_i2s_data;
140895d4509SGuennadi Liakhovetski u32 __iomem *base = info->reg;
141895d4509SGuennadi Liakhovetski
142895d4509SGuennadi Liakhovetski /* SIU software reset */
143895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SRCTL, 0);
144895d4509SGuennadi Liakhovetski }
145895d4509SGuennadi Liakhovetski
siu_dai_spbAselect(struct siu_port * port_info)146895d4509SGuennadi Liakhovetski static void siu_dai_spbAselect(struct siu_port *port_info)
147895d4509SGuennadi Liakhovetski {
148f0fba2adSLiam Girdwood struct siu_info *info = siu_i2s_data;
149895d4509SGuennadi Liakhovetski struct siu_firmware *fw = &info->fw;
150895d4509SGuennadi Liakhovetski u32 *ydef = fw->yram0;
151895d4509SGuennadi Liakhovetski u32 idx;
152895d4509SGuennadi Liakhovetski
153895d4509SGuennadi Liakhovetski /* path A use */
154895d4509SGuennadi Liakhovetski if (!info->port_id)
155895d4509SGuennadi Liakhovetski idx = 1; /* portA */
156895d4509SGuennadi Liakhovetski else
157895d4509SGuennadi Liakhovetski idx = 2; /* portB */
158895d4509SGuennadi Liakhovetski
159895d4509SGuennadi Liakhovetski ydef[0] = (fw->spbpar[idx].ab1a << 16) |
160895d4509SGuennadi Liakhovetski (fw->spbpar[idx].ab0a << 8) |
161895d4509SGuennadi Liakhovetski (fw->spbpar[idx].dir << 7) | 3;
162895d4509SGuennadi Liakhovetski ydef[1] = fw->yram0[1]; /* 0x03000300 */
163895d4509SGuennadi Liakhovetski ydef[2] = (16 / 2) << 24;
164895d4509SGuennadi Liakhovetski ydef[3] = fw->yram0[3]; /* 0 */
165895d4509SGuennadi Liakhovetski ydef[4] = fw->yram0[4]; /* 0 */
166895d4509SGuennadi Liakhovetski ydef[7] = fw->spbpar[idx].event;
167895d4509SGuennadi Liakhovetski port_info->stfifo |= fw->spbpar[idx].stfifo;
168895d4509SGuennadi Liakhovetski port_info->trdat |= fw->spbpar[idx].trdat;
169895d4509SGuennadi Liakhovetski }
170895d4509SGuennadi Liakhovetski
siu_dai_spbBselect(struct siu_port * port_info)171895d4509SGuennadi Liakhovetski static void siu_dai_spbBselect(struct siu_port *port_info)
172895d4509SGuennadi Liakhovetski {
173f0fba2adSLiam Girdwood struct siu_info *info = siu_i2s_data;
174895d4509SGuennadi Liakhovetski struct siu_firmware *fw = &info->fw;
175895d4509SGuennadi Liakhovetski u32 *ydef = fw->yram0;
176895d4509SGuennadi Liakhovetski u32 idx;
177895d4509SGuennadi Liakhovetski
178895d4509SGuennadi Liakhovetski /* path B use */
179895d4509SGuennadi Liakhovetski if (!info->port_id)
180895d4509SGuennadi Liakhovetski idx = 7; /* portA */
181895d4509SGuennadi Liakhovetski else
182895d4509SGuennadi Liakhovetski idx = 8; /* portB */
183895d4509SGuennadi Liakhovetski
184895d4509SGuennadi Liakhovetski ydef[5] = (fw->spbpar[idx].ab1a << 16) |
185895d4509SGuennadi Liakhovetski (fw->spbpar[idx].ab0a << 8) | 1;
186895d4509SGuennadi Liakhovetski ydef[6] = fw->spbpar[idx].event;
187895d4509SGuennadi Liakhovetski port_info->stfifo |= fw->spbpar[idx].stfifo;
188895d4509SGuennadi Liakhovetski port_info->trdat |= fw->spbpar[idx].trdat;
189895d4509SGuennadi Liakhovetski }
190895d4509SGuennadi Liakhovetski
siu_dai_open(struct siu_stream * siu_stream)191895d4509SGuennadi Liakhovetski static void siu_dai_open(struct siu_stream *siu_stream)
192895d4509SGuennadi Liakhovetski {
193f0fba2adSLiam Girdwood struct siu_info *info = siu_i2s_data;
194895d4509SGuennadi Liakhovetski u32 __iomem *base = info->reg;
195895d4509SGuennadi Liakhovetski u32 srctl, ifctl;
196895d4509SGuennadi Liakhovetski
197895d4509SGuennadi Liakhovetski srctl = siu_read32(base + SIU_SRCTL);
198895d4509SGuennadi Liakhovetski ifctl = siu_read32(base + SIU_IFCTL);
199895d4509SGuennadi Liakhovetski
200895d4509SGuennadi Liakhovetski switch (info->port_id) {
201895d4509SGuennadi Liakhovetski case SIU_PORT_A:
202895d4509SGuennadi Liakhovetski /* portA operates */
203895d4509SGuennadi Liakhovetski srctl |= 0x200;
204895d4509SGuennadi Liakhovetski ifctl &= ~0xc2;
205895d4509SGuennadi Liakhovetski break;
206895d4509SGuennadi Liakhovetski case SIU_PORT_B:
207895d4509SGuennadi Liakhovetski /* portB operates */
208895d4509SGuennadi Liakhovetski srctl |= 0x100;
209895d4509SGuennadi Liakhovetski ifctl &= ~0x31;
210895d4509SGuennadi Liakhovetski break;
211895d4509SGuennadi Liakhovetski }
212895d4509SGuennadi Liakhovetski
213895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SRCTL, srctl);
214895d4509SGuennadi Liakhovetski /* Unmute and configure portA */
215895d4509SGuennadi Liakhovetski siu_write32(base + SIU_IFCTL, ifctl);
216895d4509SGuennadi Liakhovetski }
217895d4509SGuennadi Liakhovetski
218895d4509SGuennadi Liakhovetski /*
219895d4509SGuennadi Liakhovetski * At the moment only fixed Left-upper, Left-lower, Right-upper, Right-lower
220895d4509SGuennadi Liakhovetski * packing is supported
221895d4509SGuennadi Liakhovetski */
siu_dai_pcmdatapack(struct siu_stream * siu_stream)222895d4509SGuennadi Liakhovetski static void siu_dai_pcmdatapack(struct siu_stream *siu_stream)
223895d4509SGuennadi Liakhovetski {
224f0fba2adSLiam Girdwood struct siu_info *info = siu_i2s_data;
225895d4509SGuennadi Liakhovetski u32 __iomem *base = info->reg;
226895d4509SGuennadi Liakhovetski u32 dpak;
227895d4509SGuennadi Liakhovetski
228895d4509SGuennadi Liakhovetski dpak = siu_read32(base + SIU_DPAK);
229895d4509SGuennadi Liakhovetski
230895d4509SGuennadi Liakhovetski switch (info->port_id) {
231895d4509SGuennadi Liakhovetski case SIU_PORT_A:
232895d4509SGuennadi Liakhovetski dpak &= ~0xc0000000;
233895d4509SGuennadi Liakhovetski break;
234895d4509SGuennadi Liakhovetski case SIU_PORT_B:
235895d4509SGuennadi Liakhovetski dpak &= ~0x00c00000;
236895d4509SGuennadi Liakhovetski break;
237895d4509SGuennadi Liakhovetski }
238895d4509SGuennadi Liakhovetski
239895d4509SGuennadi Liakhovetski siu_write32(base + SIU_DPAK, dpak);
240895d4509SGuennadi Liakhovetski }
241895d4509SGuennadi Liakhovetski
siu_dai_spbstart(struct siu_port * port_info)242895d4509SGuennadi Liakhovetski static int siu_dai_spbstart(struct siu_port *port_info)
243895d4509SGuennadi Liakhovetski {
244f0fba2adSLiam Girdwood struct siu_info *info = siu_i2s_data;
245895d4509SGuennadi Liakhovetski u32 __iomem *base = info->reg;
246895d4509SGuennadi Liakhovetski struct siu_firmware *fw = &info->fw;
247895d4509SGuennadi Liakhovetski u32 *ydef = fw->yram0;
248895d4509SGuennadi Liakhovetski int cnt;
249895d4509SGuennadi Liakhovetski u32 __iomem *add;
250895d4509SGuennadi Liakhovetski u32 *ptr;
251895d4509SGuennadi Liakhovetski
252895d4509SGuennadi Liakhovetski /* Load SPB Program in PRAM */
253895d4509SGuennadi Liakhovetski ptr = fw->pram0;
254895d4509SGuennadi Liakhovetski add = info->pram;
255895d4509SGuennadi Liakhovetski for (cnt = 0; cnt < PRAM0_SIZE; cnt++, add++, ptr++)
256895d4509SGuennadi Liakhovetski siu_write32(add, *ptr);
257895d4509SGuennadi Liakhovetski
258895d4509SGuennadi Liakhovetski ptr = fw->pram1;
259895d4509SGuennadi Liakhovetski add = info->pram + (0x0100 / sizeof(u32));
260895d4509SGuennadi Liakhovetski for (cnt = 0; cnt < PRAM1_SIZE; cnt++, add++, ptr++)
261895d4509SGuennadi Liakhovetski siu_write32(add, *ptr);
262895d4509SGuennadi Liakhovetski
263895d4509SGuennadi Liakhovetski /* XRAM initialization */
264895d4509SGuennadi Liakhovetski add = info->xram;
265895d4509SGuennadi Liakhovetski for (cnt = 0; cnt < XRAM0_SIZE + XRAM1_SIZE + XRAM2_SIZE; cnt++, add++)
266895d4509SGuennadi Liakhovetski siu_write32(add, 0);
267895d4509SGuennadi Liakhovetski
268895d4509SGuennadi Liakhovetski /* YRAM variable area initialization */
269895d4509SGuennadi Liakhovetski add = info->yram;
270895d4509SGuennadi Liakhovetski for (cnt = 0; cnt < YRAM_DEF_SIZE; cnt++, add++)
271895d4509SGuennadi Liakhovetski siu_write32(add, ydef[cnt]);
272895d4509SGuennadi Liakhovetski
273895d4509SGuennadi Liakhovetski /* YRAM FIR coefficient area initialization */
274895d4509SGuennadi Liakhovetski add = info->yram + (0x0200 / sizeof(u32));
275895d4509SGuennadi Liakhovetski for (cnt = 0; cnt < YRAM_FIR_SIZE; cnt++, add++)
276895d4509SGuennadi Liakhovetski siu_write32(add, fw->yram_fir_coeff[cnt]);
277895d4509SGuennadi Liakhovetski
278895d4509SGuennadi Liakhovetski /* YRAM IIR coefficient area initialization */
279895d4509SGuennadi Liakhovetski add = info->yram + (0x0600 / sizeof(u32));
280895d4509SGuennadi Liakhovetski for (cnt = 0; cnt < YRAM_IIR_SIZE; cnt++, add++)
281895d4509SGuennadi Liakhovetski siu_write32(add, 0);
282895d4509SGuennadi Liakhovetski
283895d4509SGuennadi Liakhovetski siu_write32(base + SIU_TRDAT, port_info->trdat);
284895d4509SGuennadi Liakhovetski port_info->trdat = 0x0;
285895d4509SGuennadi Liakhovetski
286895d4509SGuennadi Liakhovetski
287895d4509SGuennadi Liakhovetski /* SPB start condition: software */
288895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SBACTIV, 0);
289895d4509SGuennadi Liakhovetski /* Start SPB */
290895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SBCTL, 0xc0000000);
291895d4509SGuennadi Liakhovetski /* Wait for program to halt */
292895d4509SGuennadi Liakhovetski cnt = 0x10000;
293895d4509SGuennadi Liakhovetski while (--cnt && siu_read32(base + SIU_SBCTL) != 0x80000000)
294895d4509SGuennadi Liakhovetski cpu_relax();
295895d4509SGuennadi Liakhovetski
296895d4509SGuennadi Liakhovetski if (!cnt)
297895d4509SGuennadi Liakhovetski return -EBUSY;
298895d4509SGuennadi Liakhovetski
299895d4509SGuennadi Liakhovetski /* SPB program start address setting */
300895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SBPSET, 0x00400000);
301895d4509SGuennadi Liakhovetski /* SPB hardware start(FIFOCTL source) */
302895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SBACTIV, 0xc0000000);
303895d4509SGuennadi Liakhovetski
304895d4509SGuennadi Liakhovetski return 0;
305895d4509SGuennadi Liakhovetski }
306895d4509SGuennadi Liakhovetski
siu_dai_spbstop(struct siu_port * port_info)307895d4509SGuennadi Liakhovetski static void siu_dai_spbstop(struct siu_port *port_info)
308895d4509SGuennadi Liakhovetski {
309f0fba2adSLiam Girdwood struct siu_info *info = siu_i2s_data;
310895d4509SGuennadi Liakhovetski u32 __iomem *base = info->reg;
311895d4509SGuennadi Liakhovetski
312895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SBACTIV, 0);
313895d4509SGuennadi Liakhovetski /* SPB stop */
314895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SBCTL, 0);
315895d4509SGuennadi Liakhovetski
316895d4509SGuennadi Liakhovetski port_info->stfifo = 0;
317895d4509SGuennadi Liakhovetski }
318895d4509SGuennadi Liakhovetski
319895d4509SGuennadi Liakhovetski /* API functions */
320895d4509SGuennadi Liakhovetski
321895d4509SGuennadi Liakhovetski /* Playback and capture hardware properties are identical */
3225c2e035eSBhumika Goyal static const struct snd_pcm_hardware siu_dai_pcm_hw = {
323895d4509SGuennadi Liakhovetski .info = SNDRV_PCM_INFO_INTERLEAVED,
324895d4509SGuennadi Liakhovetski .formats = SNDRV_PCM_FMTBIT_S16,
325895d4509SGuennadi Liakhovetski .rates = SNDRV_PCM_RATE_8000_48000,
326895d4509SGuennadi Liakhovetski .rate_min = 8000,
327895d4509SGuennadi Liakhovetski .rate_max = 48000,
328895d4509SGuennadi Liakhovetski .channels_min = 2,
329895d4509SGuennadi Liakhovetski .channels_max = 2,
330895d4509SGuennadi Liakhovetski .buffer_bytes_max = SIU_BUFFER_BYTES_MAX,
331895d4509SGuennadi Liakhovetski .period_bytes_min = SIU_PERIOD_BYTES_MIN,
332895d4509SGuennadi Liakhovetski .period_bytes_max = SIU_PERIOD_BYTES_MAX,
333895d4509SGuennadi Liakhovetski .periods_min = SIU_PERIODS_MIN,
334895d4509SGuennadi Liakhovetski .periods_max = SIU_PERIODS_MAX,
335895d4509SGuennadi Liakhovetski };
336895d4509SGuennadi Liakhovetski
siu_dai_info_volume(struct snd_kcontrol * kctrl,struct snd_ctl_elem_info * uinfo)337895d4509SGuennadi Liakhovetski static int siu_dai_info_volume(struct snd_kcontrol *kctrl,
338895d4509SGuennadi Liakhovetski struct snd_ctl_elem_info *uinfo)
339895d4509SGuennadi Liakhovetski {
340895d4509SGuennadi Liakhovetski struct siu_port *port_info = snd_kcontrol_chip(kctrl);
341895d4509SGuennadi Liakhovetski
342895d4509SGuennadi Liakhovetski dev_dbg(port_info->pcm->card->dev, "%s\n", __func__);
343895d4509SGuennadi Liakhovetski
344895d4509SGuennadi Liakhovetski uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
345895d4509SGuennadi Liakhovetski uinfo->count = 2;
346895d4509SGuennadi Liakhovetski uinfo->value.integer.min = 0;
347895d4509SGuennadi Liakhovetski uinfo->value.integer.max = SIU_MAX_VOLUME;
348895d4509SGuennadi Liakhovetski
349895d4509SGuennadi Liakhovetski return 0;
350895d4509SGuennadi Liakhovetski }
351895d4509SGuennadi Liakhovetski
siu_dai_get_volume(struct snd_kcontrol * kctrl,struct snd_ctl_elem_value * ucontrol)352895d4509SGuennadi Liakhovetski static int siu_dai_get_volume(struct snd_kcontrol *kctrl,
353895d4509SGuennadi Liakhovetski struct snd_ctl_elem_value *ucontrol)
354895d4509SGuennadi Liakhovetski {
355895d4509SGuennadi Liakhovetski struct siu_port *port_info = snd_kcontrol_chip(kctrl);
356895d4509SGuennadi Liakhovetski struct device *dev = port_info->pcm->card->dev;
357895d4509SGuennadi Liakhovetski u32 vol;
358895d4509SGuennadi Liakhovetski
359895d4509SGuennadi Liakhovetski dev_dbg(dev, "%s\n", __func__);
360895d4509SGuennadi Liakhovetski
361895d4509SGuennadi Liakhovetski switch (kctrl->private_value) {
362895d4509SGuennadi Liakhovetski case VOLUME_PLAYBACK:
363895d4509SGuennadi Liakhovetski /* Playback is always on port 0 */
364895d4509SGuennadi Liakhovetski vol = port_info->playback.volume;
365895d4509SGuennadi Liakhovetski ucontrol->value.integer.value[0] = vol & 0xffff;
366895d4509SGuennadi Liakhovetski ucontrol->value.integer.value[1] = vol >> 16 & 0xffff;
367895d4509SGuennadi Liakhovetski break;
368895d4509SGuennadi Liakhovetski case VOLUME_CAPTURE:
369895d4509SGuennadi Liakhovetski /* Capture is always on port 1 */
370895d4509SGuennadi Liakhovetski vol = port_info->capture.volume;
371895d4509SGuennadi Liakhovetski ucontrol->value.integer.value[0] = vol & 0xffff;
372895d4509SGuennadi Liakhovetski ucontrol->value.integer.value[1] = vol >> 16 & 0xffff;
373895d4509SGuennadi Liakhovetski break;
374895d4509SGuennadi Liakhovetski default:
375895d4509SGuennadi Liakhovetski dev_err(dev, "%s() invalid private_value=%ld\n",
376895d4509SGuennadi Liakhovetski __func__, kctrl->private_value);
377895d4509SGuennadi Liakhovetski return -EINVAL;
378895d4509SGuennadi Liakhovetski }
379895d4509SGuennadi Liakhovetski
380895d4509SGuennadi Liakhovetski return 0;
381895d4509SGuennadi Liakhovetski }
382895d4509SGuennadi Liakhovetski
siu_dai_put_volume(struct snd_kcontrol * kctrl,struct snd_ctl_elem_value * ucontrol)383895d4509SGuennadi Liakhovetski static int siu_dai_put_volume(struct snd_kcontrol *kctrl,
384895d4509SGuennadi Liakhovetski struct snd_ctl_elem_value *ucontrol)
385895d4509SGuennadi Liakhovetski {
386895d4509SGuennadi Liakhovetski struct siu_port *port_info = snd_kcontrol_chip(kctrl);
387895d4509SGuennadi Liakhovetski struct device *dev = port_info->pcm->card->dev;
388f0fba2adSLiam Girdwood struct siu_info *info = siu_i2s_data;
389895d4509SGuennadi Liakhovetski u32 __iomem *base = info->reg;
390895d4509SGuennadi Liakhovetski u32 new_vol;
391895d4509SGuennadi Liakhovetski u32 cur_vol;
392895d4509SGuennadi Liakhovetski
393895d4509SGuennadi Liakhovetski dev_dbg(dev, "%s\n", __func__);
394895d4509SGuennadi Liakhovetski
395895d4509SGuennadi Liakhovetski if (ucontrol->value.integer.value[0] < 0 ||
396895d4509SGuennadi Liakhovetski ucontrol->value.integer.value[0] > SIU_MAX_VOLUME ||
397895d4509SGuennadi Liakhovetski ucontrol->value.integer.value[1] < 0 ||
398895d4509SGuennadi Liakhovetski ucontrol->value.integer.value[1] > SIU_MAX_VOLUME)
399895d4509SGuennadi Liakhovetski return -EINVAL;
400895d4509SGuennadi Liakhovetski
401895d4509SGuennadi Liakhovetski new_vol = ucontrol->value.integer.value[0] |
402895d4509SGuennadi Liakhovetski ucontrol->value.integer.value[1] << 16;
403895d4509SGuennadi Liakhovetski
404895d4509SGuennadi Liakhovetski /* See comment above - DSP firmware implementation */
405895d4509SGuennadi Liakhovetski switch (kctrl->private_value) {
406895d4509SGuennadi Liakhovetski case VOLUME_PLAYBACK:
407895d4509SGuennadi Liakhovetski /* Playback is always on port 0 */
408895d4509SGuennadi Liakhovetski cur_vol = port_info->playback.volume;
409895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SBDVCA, new_vol);
410895d4509SGuennadi Liakhovetski port_info->playback.volume = new_vol;
411895d4509SGuennadi Liakhovetski break;
412895d4509SGuennadi Liakhovetski case VOLUME_CAPTURE:
413895d4509SGuennadi Liakhovetski /* Capture is always on port 1 */
414895d4509SGuennadi Liakhovetski cur_vol = port_info->capture.volume;
415895d4509SGuennadi Liakhovetski siu_write32(base + SIU_SBDVCB, new_vol);
416895d4509SGuennadi Liakhovetski port_info->capture.volume = new_vol;
417895d4509SGuennadi Liakhovetski break;
418895d4509SGuennadi Liakhovetski default:
419895d4509SGuennadi Liakhovetski dev_err(dev, "%s() invalid private_value=%ld\n",
420895d4509SGuennadi Liakhovetski __func__, kctrl->private_value);
421895d4509SGuennadi Liakhovetski return -EINVAL;
422895d4509SGuennadi Liakhovetski }
423895d4509SGuennadi Liakhovetski
424895d4509SGuennadi Liakhovetski if (cur_vol != new_vol)
425895d4509SGuennadi Liakhovetski return 1;
426895d4509SGuennadi Liakhovetski
427895d4509SGuennadi Liakhovetski return 0;
428895d4509SGuennadi Liakhovetski }
429895d4509SGuennadi Liakhovetski
430905e46acSBhumika Goyal static const struct snd_kcontrol_new playback_controls = {
431895d4509SGuennadi Liakhovetski .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
432895d4509SGuennadi Liakhovetski .name = "PCM Playback Volume",
433895d4509SGuennadi Liakhovetski .index = 0,
434895d4509SGuennadi Liakhovetski .info = siu_dai_info_volume,
435895d4509SGuennadi Liakhovetski .get = siu_dai_get_volume,
436895d4509SGuennadi Liakhovetski .put = siu_dai_put_volume,
437895d4509SGuennadi Liakhovetski .private_value = VOLUME_PLAYBACK,
438895d4509SGuennadi Liakhovetski };
439895d4509SGuennadi Liakhovetski
440905e46acSBhumika Goyal static const struct snd_kcontrol_new capture_controls = {
441895d4509SGuennadi Liakhovetski .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
442895d4509SGuennadi Liakhovetski .name = "PCM Capture Volume",
443895d4509SGuennadi Liakhovetski .index = 0,
444895d4509SGuennadi Liakhovetski .info = siu_dai_info_volume,
445895d4509SGuennadi Liakhovetski .get = siu_dai_get_volume,
446895d4509SGuennadi Liakhovetski .put = siu_dai_put_volume,
447895d4509SGuennadi Liakhovetski .private_value = VOLUME_CAPTURE,
448895d4509SGuennadi Liakhovetski };
449895d4509SGuennadi Liakhovetski
siu_init_port(int port,struct siu_port ** port_info,struct snd_card * card)450895d4509SGuennadi Liakhovetski int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card)
451895d4509SGuennadi Liakhovetski {
452895d4509SGuennadi Liakhovetski struct device *dev = card->dev;
453895d4509SGuennadi Liakhovetski struct snd_kcontrol *kctrl;
454895d4509SGuennadi Liakhovetski int ret;
455895d4509SGuennadi Liakhovetski
456895d4509SGuennadi Liakhovetski *port_info = kzalloc(sizeof(**port_info), GFP_KERNEL);
457895d4509SGuennadi Liakhovetski if (!*port_info)
458895d4509SGuennadi Liakhovetski return -ENOMEM;
459895d4509SGuennadi Liakhovetski
460895d4509SGuennadi Liakhovetski dev_dbg(dev, "%s: port #%d@%p\n", __func__, port, *port_info);
461895d4509SGuennadi Liakhovetski
462895d4509SGuennadi Liakhovetski (*port_info)->playback.volume = DFLT_VOLUME_LEVEL;
463895d4509SGuennadi Liakhovetski (*port_info)->capture.volume = DFLT_VOLUME_LEVEL;
464895d4509SGuennadi Liakhovetski
465895d4509SGuennadi Liakhovetski /*
466895d4509SGuennadi Liakhovetski * Add mixer support. The SPB is used to change the volume. Both
467895d4509SGuennadi Liakhovetski * ports use the same SPB. Therefore, we only register one
468895d4509SGuennadi Liakhovetski * control instance since it will be used by both channels.
469895d4509SGuennadi Liakhovetski * In error case we continue without controls.
470895d4509SGuennadi Liakhovetski */
471895d4509SGuennadi Liakhovetski kctrl = snd_ctl_new1(&playback_controls, *port_info);
472895d4509SGuennadi Liakhovetski ret = snd_ctl_add(card, kctrl);
473895d4509SGuennadi Liakhovetski if (ret < 0)
474895d4509SGuennadi Liakhovetski dev_err(dev,
475895d4509SGuennadi Liakhovetski "failed to add playback controls %p port=%d err=%d\n",
476895d4509SGuennadi Liakhovetski kctrl, port, ret);
477895d4509SGuennadi Liakhovetski
478895d4509SGuennadi Liakhovetski kctrl = snd_ctl_new1(&capture_controls, *port_info);
479895d4509SGuennadi Liakhovetski ret = snd_ctl_add(card, kctrl);
480895d4509SGuennadi Liakhovetski if (ret < 0)
481895d4509SGuennadi Liakhovetski dev_err(dev,
482895d4509SGuennadi Liakhovetski "failed to add capture controls %p port=%d err=%d\n",
483895d4509SGuennadi Liakhovetski kctrl, port, ret);
484895d4509SGuennadi Liakhovetski
485895d4509SGuennadi Liakhovetski return 0;
486895d4509SGuennadi Liakhovetski }
487895d4509SGuennadi Liakhovetski
siu_free_port(struct siu_port * port_info)488895d4509SGuennadi Liakhovetski void siu_free_port(struct siu_port *port_info)
489895d4509SGuennadi Liakhovetski {
490895d4509SGuennadi Liakhovetski kfree(port_info);
491895d4509SGuennadi Liakhovetski }
492895d4509SGuennadi Liakhovetski
siu_dai_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)493895d4509SGuennadi Liakhovetski static int siu_dai_startup(struct snd_pcm_substream *substream,
494895d4509SGuennadi Liakhovetski struct snd_soc_dai *dai)
495895d4509SGuennadi Liakhovetski {
496f0fba2adSLiam Girdwood struct siu_info *info = snd_soc_dai_get_drvdata(dai);
497895d4509SGuennadi Liakhovetski struct snd_pcm_runtime *rt = substream->runtime;
498895d4509SGuennadi Liakhovetski struct siu_port *port_info = siu_port_info(substream);
499895d4509SGuennadi Liakhovetski int ret;
500895d4509SGuennadi Liakhovetski
501895d4509SGuennadi Liakhovetski dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__,
502895d4509SGuennadi Liakhovetski info->port_id, port_info);
503895d4509SGuennadi Liakhovetski
504895d4509SGuennadi Liakhovetski snd_soc_set_runtime_hwparams(substream, &siu_dai_pcm_hw);
505895d4509SGuennadi Liakhovetski
506895d4509SGuennadi Liakhovetski ret = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
507895d4509SGuennadi Liakhovetski if (unlikely(ret < 0))
508895d4509SGuennadi Liakhovetski return ret;
509895d4509SGuennadi Liakhovetski
510895d4509SGuennadi Liakhovetski siu_dai_start(port_info);
511895d4509SGuennadi Liakhovetski
512895d4509SGuennadi Liakhovetski return 0;
513895d4509SGuennadi Liakhovetski }
514895d4509SGuennadi Liakhovetski
siu_dai_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)515895d4509SGuennadi Liakhovetski static void siu_dai_shutdown(struct snd_pcm_substream *substream,
516895d4509SGuennadi Liakhovetski struct snd_soc_dai *dai)
517895d4509SGuennadi Liakhovetski {
518f0fba2adSLiam Girdwood struct siu_info *info = snd_soc_dai_get_drvdata(dai);
519895d4509SGuennadi Liakhovetski struct siu_port *port_info = siu_port_info(substream);
520895d4509SGuennadi Liakhovetski
521895d4509SGuennadi Liakhovetski dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__,
522895d4509SGuennadi Liakhovetski info->port_id, port_info);
523895d4509SGuennadi Liakhovetski
524895d4509SGuennadi Liakhovetski if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
525895d4509SGuennadi Liakhovetski port_info->play_cap &= ~PLAYBACK_ENABLED;
526895d4509SGuennadi Liakhovetski else
527895d4509SGuennadi Liakhovetski port_info->play_cap &= ~CAPTURE_ENABLED;
528895d4509SGuennadi Liakhovetski
529895d4509SGuennadi Liakhovetski /* Stop the siu if the other stream is not using it */
530895d4509SGuennadi Liakhovetski if (!port_info->play_cap) {
531895d4509SGuennadi Liakhovetski /* during stmread or stmwrite ? */
5325f29d445STakashi Iwai if (WARN_ON(port_info->playback.rw_flg || port_info->capture.rw_flg))
5335f29d445STakashi Iwai return;
534895d4509SGuennadi Liakhovetski siu_dai_spbstop(port_info);
535f0fba2adSLiam Girdwood siu_dai_stop(port_info);
536895d4509SGuennadi Liakhovetski }
537895d4509SGuennadi Liakhovetski }
538895d4509SGuennadi Liakhovetski
539895d4509SGuennadi Liakhovetski /* PCM part of siu_dai_playback_prepare() / siu_dai_capture_prepare() */
siu_dai_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)540895d4509SGuennadi Liakhovetski static int siu_dai_prepare(struct snd_pcm_substream *substream,
541895d4509SGuennadi Liakhovetski struct snd_soc_dai *dai)
542895d4509SGuennadi Liakhovetski {
543f0fba2adSLiam Girdwood struct siu_info *info = snd_soc_dai_get_drvdata(dai);
544895d4509SGuennadi Liakhovetski struct snd_pcm_runtime *rt = substream->runtime;
545895d4509SGuennadi Liakhovetski struct siu_port *port_info = siu_port_info(substream);
546895d4509SGuennadi Liakhovetski struct siu_stream *siu_stream;
547895d4509SGuennadi Liakhovetski int self, ret;
548895d4509SGuennadi Liakhovetski
549895d4509SGuennadi Liakhovetski dev_dbg(substream->pcm->card->dev,
550895d4509SGuennadi Liakhovetski "%s: port %d, active streams %lx, %d channels\n",
551895d4509SGuennadi Liakhovetski __func__, info->port_id, port_info->play_cap, rt->channels);
552895d4509SGuennadi Liakhovetski
553895d4509SGuennadi Liakhovetski if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
554895d4509SGuennadi Liakhovetski self = PLAYBACK_ENABLED;
555895d4509SGuennadi Liakhovetski siu_stream = &port_info->playback;
556895d4509SGuennadi Liakhovetski } else {
557895d4509SGuennadi Liakhovetski self = CAPTURE_ENABLED;
558895d4509SGuennadi Liakhovetski siu_stream = &port_info->capture;
559895d4509SGuennadi Liakhovetski }
560895d4509SGuennadi Liakhovetski
561895d4509SGuennadi Liakhovetski /* Set up the siu if not already done */
562895d4509SGuennadi Liakhovetski if (!port_info->play_cap) {
563895d4509SGuennadi Liakhovetski siu_stream->rw_flg = 0; /* stream-data transfer flag */
564895d4509SGuennadi Liakhovetski
565895d4509SGuennadi Liakhovetski siu_dai_spbAselect(port_info);
566895d4509SGuennadi Liakhovetski siu_dai_spbBselect(port_info);
567895d4509SGuennadi Liakhovetski
568895d4509SGuennadi Liakhovetski siu_dai_open(siu_stream);
569895d4509SGuennadi Liakhovetski
570895d4509SGuennadi Liakhovetski siu_dai_pcmdatapack(siu_stream);
571895d4509SGuennadi Liakhovetski
572895d4509SGuennadi Liakhovetski ret = siu_dai_spbstart(port_info);
573895d4509SGuennadi Liakhovetski if (ret < 0)
574895d4509SGuennadi Liakhovetski goto fail;
5753ca34149SGuennadi Liakhovetski } else {
5763ca34149SGuennadi Liakhovetski ret = 0;
577895d4509SGuennadi Liakhovetski }
578895d4509SGuennadi Liakhovetski
579895d4509SGuennadi Liakhovetski port_info->play_cap |= self;
580895d4509SGuennadi Liakhovetski
581895d4509SGuennadi Liakhovetski fail:
582895d4509SGuennadi Liakhovetski return ret;
583895d4509SGuennadi Liakhovetski }
584895d4509SGuennadi Liakhovetski
585895d4509SGuennadi Liakhovetski /*
586895d4509SGuennadi Liakhovetski * SIU can set bus format to I2S / PCM / SPDIF independently for playback and
587895d4509SGuennadi Liakhovetski * capture, however, the current API sets the bus format globally for a DAI.
588895d4509SGuennadi Liakhovetski */
siu_dai_set_fmt(struct snd_soc_dai * dai,unsigned int fmt)589895d4509SGuennadi Liakhovetski static int siu_dai_set_fmt(struct snd_soc_dai *dai,
590895d4509SGuennadi Liakhovetski unsigned int fmt)
591895d4509SGuennadi Liakhovetski {
592f0fba2adSLiam Girdwood struct siu_info *info = snd_soc_dai_get_drvdata(dai);
593895d4509SGuennadi Liakhovetski u32 __iomem *base = info->reg;
594895d4509SGuennadi Liakhovetski u32 ifctl;
595895d4509SGuennadi Liakhovetski
596895d4509SGuennadi Liakhovetski dev_dbg(dai->dev, "%s: fmt 0x%x on port %d\n",
597895d4509SGuennadi Liakhovetski __func__, fmt, info->port_id);
598895d4509SGuennadi Liakhovetski
599895d4509SGuennadi Liakhovetski if (info->port_id < 0)
600895d4509SGuennadi Liakhovetski return -ENODEV;
601895d4509SGuennadi Liakhovetski
602895d4509SGuennadi Liakhovetski /* Here select between I2S / PCM / SPDIF */
603895d4509SGuennadi Liakhovetski switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
604895d4509SGuennadi Liakhovetski case SND_SOC_DAIFMT_I2S:
605895d4509SGuennadi Liakhovetski ifctl = siu_flags[info->port_id].playback.i2s |
606895d4509SGuennadi Liakhovetski siu_flags[info->port_id].capture.i2s;
607895d4509SGuennadi Liakhovetski break;
608895d4509SGuennadi Liakhovetski case SND_SOC_DAIFMT_LEFT_J:
609895d4509SGuennadi Liakhovetski ifctl = siu_flags[info->port_id].playback.pcm |
610895d4509SGuennadi Liakhovetski siu_flags[info->port_id].capture.pcm;
611895d4509SGuennadi Liakhovetski break;
612895d4509SGuennadi Liakhovetski /* SPDIF disabled - see comment at the top */
613895d4509SGuennadi Liakhovetski default:
614895d4509SGuennadi Liakhovetski return -EINVAL;
615895d4509SGuennadi Liakhovetski }
616895d4509SGuennadi Liakhovetski
617895d4509SGuennadi Liakhovetski ifctl |= ~(siu_flags[info->port_id].playback.mask |
618895d4509SGuennadi Liakhovetski siu_flags[info->port_id].capture.mask) &
619895d4509SGuennadi Liakhovetski siu_read32(base + SIU_IFCTL);
620895d4509SGuennadi Liakhovetski siu_write32(base + SIU_IFCTL, ifctl);
621895d4509SGuennadi Liakhovetski
622895d4509SGuennadi Liakhovetski return 0;
623895d4509SGuennadi Liakhovetski }
624895d4509SGuennadi Liakhovetski
siu_dai_set_sysclk(struct snd_soc_dai * dai,int clk_id,unsigned int freq,int dir)625895d4509SGuennadi Liakhovetski static int siu_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
626895d4509SGuennadi Liakhovetski unsigned int freq, int dir)
627895d4509SGuennadi Liakhovetski {
628895d4509SGuennadi Liakhovetski struct clk *siu_clk, *parent_clk;
629895d4509SGuennadi Liakhovetski char *siu_name, *parent_name;
630895d4509SGuennadi Liakhovetski int ret;
631895d4509SGuennadi Liakhovetski
632895d4509SGuennadi Liakhovetski if (dir != SND_SOC_CLOCK_IN)
633895d4509SGuennadi Liakhovetski return -EINVAL;
634895d4509SGuennadi Liakhovetski
635895d4509SGuennadi Liakhovetski dev_dbg(dai->dev, "%s: using clock %d\n", __func__, clk_id);
636895d4509SGuennadi Liakhovetski
637895d4509SGuennadi Liakhovetski switch (clk_id) {
638895d4509SGuennadi Liakhovetski case SIU_CLKA_PLL:
639895d4509SGuennadi Liakhovetski siu_name = "siua_clk";
640895d4509SGuennadi Liakhovetski parent_name = "pll_clk";
641895d4509SGuennadi Liakhovetski break;
642895d4509SGuennadi Liakhovetski case SIU_CLKA_EXT:
643895d4509SGuennadi Liakhovetski siu_name = "siua_clk";
644895d4509SGuennadi Liakhovetski parent_name = "siumcka_clk";
645895d4509SGuennadi Liakhovetski break;
646895d4509SGuennadi Liakhovetski case SIU_CLKB_PLL:
647895d4509SGuennadi Liakhovetski siu_name = "siub_clk";
648895d4509SGuennadi Liakhovetski parent_name = "pll_clk";
649895d4509SGuennadi Liakhovetski break;
650895d4509SGuennadi Liakhovetski case SIU_CLKB_EXT:
651895d4509SGuennadi Liakhovetski siu_name = "siub_clk";
652895d4509SGuennadi Liakhovetski parent_name = "siumckb_clk";
653895d4509SGuennadi Liakhovetski break;
654895d4509SGuennadi Liakhovetski default:
655895d4509SGuennadi Liakhovetski return -EINVAL;
656895d4509SGuennadi Liakhovetski }
657895d4509SGuennadi Liakhovetski
658f0fba2adSLiam Girdwood siu_clk = clk_get(dai->dev, siu_name);
659c6834dd2SGuennadi Liakhovetski if (IS_ERR(siu_clk)) {
660c6834dd2SGuennadi Liakhovetski dev_err(dai->dev, "%s: cannot get a SIU clock: %ld\n", __func__,
661c6834dd2SGuennadi Liakhovetski PTR_ERR(siu_clk));
662895d4509SGuennadi Liakhovetski return PTR_ERR(siu_clk);
663895d4509SGuennadi Liakhovetski }
664895d4509SGuennadi Liakhovetski
665c6834dd2SGuennadi Liakhovetski parent_clk = clk_get(dai->dev, parent_name);
666c6834dd2SGuennadi Liakhovetski if (IS_ERR(parent_clk)) {
667c6834dd2SGuennadi Liakhovetski ret = PTR_ERR(parent_clk);
668c6834dd2SGuennadi Liakhovetski dev_err(dai->dev, "cannot get a SIU clock parent: %d\n", ret);
669c6834dd2SGuennadi Liakhovetski goto epclkget;
670c6834dd2SGuennadi Liakhovetski }
671c6834dd2SGuennadi Liakhovetski
672c6834dd2SGuennadi Liakhovetski ret = clk_set_parent(siu_clk, parent_clk);
673c6834dd2SGuennadi Liakhovetski if (ret < 0) {
674c6834dd2SGuennadi Liakhovetski dev_err(dai->dev, "cannot reparent the SIU clock: %d\n", ret);
675c6834dd2SGuennadi Liakhovetski goto eclksetp;
676c6834dd2SGuennadi Liakhovetski }
677c6834dd2SGuennadi Liakhovetski
678c6834dd2SGuennadi Liakhovetski ret = clk_set_rate(siu_clk, freq);
679c6834dd2SGuennadi Liakhovetski if (ret < 0)
680c6834dd2SGuennadi Liakhovetski dev_err(dai->dev, "cannot set SIU clock rate: %d\n", ret);
681c6834dd2SGuennadi Liakhovetski
682c6834dd2SGuennadi Liakhovetski /* TODO: when clkdev gets reference counting we'll move these to siu_dai_shutdown() */
683c6834dd2SGuennadi Liakhovetski eclksetp:
684c6834dd2SGuennadi Liakhovetski clk_put(parent_clk);
685c6834dd2SGuennadi Liakhovetski epclkget:
686895d4509SGuennadi Liakhovetski clk_put(siu_clk);
687895d4509SGuennadi Liakhovetski
688c6834dd2SGuennadi Liakhovetski return ret;
689895d4509SGuennadi Liakhovetski }
690895d4509SGuennadi Liakhovetski
69185e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops siu_dai_ops = {
692895d4509SGuennadi Liakhovetski .startup = siu_dai_startup,
693895d4509SGuennadi Liakhovetski .shutdown = siu_dai_shutdown,
694895d4509SGuennadi Liakhovetski .prepare = siu_dai_prepare,
695895d4509SGuennadi Liakhovetski .set_sysclk = siu_dai_set_sysclk,
696895d4509SGuennadi Liakhovetski .set_fmt = siu_dai_set_fmt,
697895d4509SGuennadi Liakhovetski };
698895d4509SGuennadi Liakhovetski
699f0fba2adSLiam Girdwood static struct snd_soc_dai_driver siu_i2s_dai = {
700c6834dd2SGuennadi Liakhovetski .name = "siu-i2s-dai",
701895d4509SGuennadi Liakhovetski .playback = {
702895d4509SGuennadi Liakhovetski .channels_min = 2,
703895d4509SGuennadi Liakhovetski .channels_max = 2,
704895d4509SGuennadi Liakhovetski .formats = SNDRV_PCM_FMTBIT_S16,
705895d4509SGuennadi Liakhovetski .rates = SNDRV_PCM_RATE_8000_48000,
706895d4509SGuennadi Liakhovetski },
707895d4509SGuennadi Liakhovetski .capture = {
708895d4509SGuennadi Liakhovetski .channels_min = 2,
709895d4509SGuennadi Liakhovetski .channels_max = 2,
710895d4509SGuennadi Liakhovetski .formats = SNDRV_PCM_FMTBIT_S16,
711895d4509SGuennadi Liakhovetski .rates = SNDRV_PCM_RATE_8000_48000,
712895d4509SGuennadi Liakhovetski },
713895d4509SGuennadi Liakhovetski .ops = &siu_dai_ops,
714895d4509SGuennadi Liakhovetski };
715895d4509SGuennadi Liakhovetski
siu_probe(struct platform_device * pdev)716bb5eb6ecSBill Pemberton static int siu_probe(struct platform_device *pdev)
717895d4509SGuennadi Liakhovetski {
718895d4509SGuennadi Liakhovetski const struct firmware *fw_entry;
719895d4509SGuennadi Liakhovetski struct resource *res, *region;
720895d4509SGuennadi Liakhovetski struct siu_info *info;
721895d4509SGuennadi Liakhovetski int ret;
722895d4509SGuennadi Liakhovetski
7231ff68f55SAxel Lin info = devm_kmalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
724895d4509SGuennadi Liakhovetski if (!info)
725895d4509SGuennadi Liakhovetski return -ENOMEM;
726f0fba2adSLiam Girdwood siu_i2s_data = info;
727c6834dd2SGuennadi Liakhovetski info->dev = &pdev->dev;
728895d4509SGuennadi Liakhovetski
729895d4509SGuennadi Liakhovetski ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev);
730895d4509SGuennadi Liakhovetski if (ret)
7311ff68f55SAxel Lin return ret;
732895d4509SGuennadi Liakhovetski
733895d4509SGuennadi Liakhovetski /*
734895d4509SGuennadi Liakhovetski * Loaded firmware is "const" - read only, but we have to modify it in
735895d4509SGuennadi Liakhovetski * snd_siu_sh7343_spbAselect() and snd_siu_sh7343_spbBselect()
736895d4509SGuennadi Liakhovetski */
737895d4509SGuennadi Liakhovetski memcpy(&info->fw, fw_entry->data, fw_entry->size);
738895d4509SGuennadi Liakhovetski
739895d4509SGuennadi Liakhovetski release_firmware(fw_entry);
740895d4509SGuennadi Liakhovetski
741895d4509SGuennadi Liakhovetski res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
7421ff68f55SAxel Lin if (!res)
7431ff68f55SAxel Lin return -ENODEV;
744895d4509SGuennadi Liakhovetski
7451ff68f55SAxel Lin region = devm_request_mem_region(&pdev->dev, res->start,
7461ff68f55SAxel Lin resource_size(res), pdev->name);
747895d4509SGuennadi Liakhovetski if (!region) {
748895d4509SGuennadi Liakhovetski dev_err(&pdev->dev, "SIU region already claimed\n");
7491ff68f55SAxel Lin return -EBUSY;
750895d4509SGuennadi Liakhovetski }
751895d4509SGuennadi Liakhovetski
7521ff68f55SAxel Lin info->pram = devm_ioremap(&pdev->dev, res->start, PRAM_SIZE);
753895d4509SGuennadi Liakhovetski if (!info->pram)
7541ff68f55SAxel Lin return -ENOMEM;
7551ff68f55SAxel Lin info->xram = devm_ioremap(&pdev->dev, res->start + XRAM_OFFSET,
7561ff68f55SAxel Lin XRAM_SIZE);
757895d4509SGuennadi Liakhovetski if (!info->xram)
7581ff68f55SAxel Lin return -ENOMEM;
7591ff68f55SAxel Lin info->yram = devm_ioremap(&pdev->dev, res->start + YRAM_OFFSET,
7601ff68f55SAxel Lin YRAM_SIZE);
761895d4509SGuennadi Liakhovetski if (!info->yram)
7621ff68f55SAxel Lin return -ENOMEM;
7631ff68f55SAxel Lin info->reg = devm_ioremap(&pdev->dev, res->start + REG_OFFSET,
7641ff68f55SAxel Lin resource_size(res) - REG_OFFSET);
765895d4509SGuennadi Liakhovetski if (!info->reg)
7661ff68f55SAxel Lin return -ENOMEM;
767895d4509SGuennadi Liakhovetski
768f0fba2adSLiam Girdwood dev_set_drvdata(&pdev->dev, info);
769895d4509SGuennadi Liakhovetski
770f0fba2adSLiam Girdwood /* register using ARRAY version so we can keep dai name */
771be3eabe9SKuninori Morimoto ret = devm_snd_soc_register_component(&pdev->dev, &siu_component,
7720a09dfa0SKuninori Morimoto &siu_i2s_dai, 1);
773895d4509SGuennadi Liakhovetski if (ret < 0)
7741ff68f55SAxel Lin return ret;
775895d4509SGuennadi Liakhovetski
776895d4509SGuennadi Liakhovetski pm_runtime_enable(&pdev->dev);
777895d4509SGuennadi Liakhovetski
7781ff68f55SAxel Lin return 0;
779895d4509SGuennadi Liakhovetski }
780895d4509SGuennadi Liakhovetski
siu_remove(struct platform_device * pdev)781*4136b45dSUwe Kleine-König static void siu_remove(struct platform_device *pdev)
782895d4509SGuennadi Liakhovetski {
783895d4509SGuennadi Liakhovetski pm_runtime_disable(&pdev->dev);
784895d4509SGuennadi Liakhovetski }
785895d4509SGuennadi Liakhovetski
786895d4509SGuennadi Liakhovetski static struct platform_driver siu_driver = {
787895d4509SGuennadi Liakhovetski .driver = {
788f0fba2adSLiam Girdwood .name = "siu-pcm-audio",
789895d4509SGuennadi Liakhovetski },
790895d4509SGuennadi Liakhovetski .probe = siu_probe,
791*4136b45dSUwe Kleine-König .remove_new = siu_remove,
792895d4509SGuennadi Liakhovetski };
793895d4509SGuennadi Liakhovetski
794cb5e8738SAxel Lin module_platform_driver(siu_driver);
795895d4509SGuennadi Liakhovetski
796895d4509SGuennadi Liakhovetski MODULE_AUTHOR("Carlos Munoz <carlos@kenati.com>");
797895d4509SGuennadi Liakhovetski MODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver");
798895d4509SGuennadi Liakhovetski MODULE_LICENSE("GPL");
799
800 MODULE_FIRMWARE("siu_spb.bin");
801