]>
Commit | Line | Data |
---|---|---|
a6ee05d9 SR |
1 | /* |
2 | * Copyright (C) 2014-2015 Broadcom Corporation | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License as | |
6 | * published by the Free Software Foundation version 2. | |
7 | * | |
8 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
9 | * kind, whether express or implied; without even the implied warranty | |
10 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | */ | |
13 | #include <linux/clk.h> | |
14 | #include <linux/delay.h> | |
15 | #include <linux/init.h> | |
16 | #include <linux/io.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/of_device.h> | |
19 | #include <linux/slab.h> | |
20 | #include <sound/core.h> | |
21 | #include <sound/pcm.h> | |
22 | #include <sound/pcm_params.h> | |
23 | #include <sound/soc.h> | |
24 | #include <sound/soc-dai.h> | |
25 | ||
26 | #include "cygnus-ssp.h" | |
27 | ||
28 | #define DEFAULT_VCO 1354750204 | |
29 | ||
30 | #define CYGNUS_TDM_RATE \ | |
31 | (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ | |
32 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 | \ | |
33 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ | |
34 | SNDRV_PCM_RATE_48000) | |
35 | ||
36 | #define CAPTURE_FCI_ID_BASE 0x180 | |
37 | #define CYGNUS_SSP_TRISTATE_MASK 0x001fff | |
38 | #define CYGNUS_PLLCLKSEL_MASK 0xf | |
39 | ||
40 | /* Used with stream_on field to indicate which streams are active */ | |
41 | #define PLAYBACK_STREAM_MASK BIT(0) | |
42 | #define CAPTURE_STREAM_MASK BIT(1) | |
43 | ||
44 | #define I2S_STREAM_CFG_MASK 0xff003ff | |
45 | #define I2S_CAP_STREAM_CFG_MASK 0xf0 | |
46 | #define SPDIF_STREAM_CFG_MASK 0x3ff | |
47 | #define CH_GRP_STEREO 0x1 | |
48 | ||
49 | /* Begin register offset defines */ | |
50 | #define AUD_MISC_SEROUT_OE_REG_BASE 0x01c | |
51 | #define AUD_MISC_SEROUT_SPDIF_OE 12 | |
52 | #define AUD_MISC_SEROUT_MCLK_OE 3 | |
53 | #define AUD_MISC_SEROUT_LRCK_OE 2 | |
54 | #define AUD_MISC_SEROUT_SCLK_OE 1 | |
55 | #define AUD_MISC_SEROUT_SDAT_OE 0 | |
56 | ||
57 | /* AUD_FMM_BF_CTRL_xxx regs */ | |
58 | #define BF_DST_CFG0_OFFSET 0x100 | |
59 | #define BF_DST_CFG1_OFFSET 0x104 | |
60 | #define BF_DST_CFG2_OFFSET 0x108 | |
61 | ||
62 | #define BF_DST_CTRL0_OFFSET 0x130 | |
63 | #define BF_DST_CTRL1_OFFSET 0x134 | |
64 | #define BF_DST_CTRL2_OFFSET 0x138 | |
65 | ||
66 | #define BF_SRC_CFG0_OFFSET 0x148 | |
67 | #define BF_SRC_CFG1_OFFSET 0x14c | |
68 | #define BF_SRC_CFG2_OFFSET 0x150 | |
69 | #define BF_SRC_CFG3_OFFSET 0x154 | |
70 | ||
71 | #define BF_SRC_CTRL0_OFFSET 0x1c0 | |
72 | #define BF_SRC_CTRL1_OFFSET 0x1c4 | |
73 | #define BF_SRC_CTRL2_OFFSET 0x1c8 | |
74 | #define BF_SRC_CTRL3_OFFSET 0x1cc | |
75 | ||
76 | #define BF_SRC_GRP0_OFFSET 0x1fc | |
77 | #define BF_SRC_GRP1_OFFSET 0x200 | |
78 | #define BF_SRC_GRP2_OFFSET 0x204 | |
79 | #define BF_SRC_GRP3_OFFSET 0x208 | |
80 | ||
81 | #define BF_SRC_GRP_EN_OFFSET 0x320 | |
82 | #define BF_SRC_GRP_FLOWON_OFFSET 0x324 | |
83 | #define BF_SRC_GRP_SYNC_DIS_OFFSET 0x328 | |
84 | ||
85 | /* AUD_FMM_IOP_OUT_I2S_xxx regs */ | |
86 | #define OUT_I2S_0_STREAM_CFG_OFFSET 0xa00 | |
87 | #define OUT_I2S_0_CFG_OFFSET 0xa04 | |
88 | #define OUT_I2S_0_MCLK_CFG_OFFSET 0xa0c | |
89 | ||
90 | #define OUT_I2S_1_STREAM_CFG_OFFSET 0xa40 | |
91 | #define OUT_I2S_1_CFG_OFFSET 0xa44 | |
92 | #define OUT_I2S_1_MCLK_CFG_OFFSET 0xa4c | |
93 | ||
94 | #define OUT_I2S_2_STREAM_CFG_OFFSET 0xa80 | |
95 | #define OUT_I2S_2_CFG_OFFSET 0xa84 | |
96 | #define OUT_I2S_2_MCLK_CFG_OFFSET 0xa8c | |
97 | ||
98 | /* AUD_FMM_IOP_OUT_SPDIF_xxx regs */ | |
99 | #define SPDIF_STREAM_CFG_OFFSET 0xac0 | |
100 | #define SPDIF_CTRL_OFFSET 0xac4 | |
101 | #define SPDIF_FORMAT_CFG_OFFSET 0xad8 | |
102 | #define SPDIF_MCLK_CFG_OFFSET 0xadc | |
103 | ||
104 | /* AUD_FMM_IOP_PLL_0_xxx regs */ | |
105 | #define IOP_PLL_0_MACRO_OFFSET 0xb00 | |
106 | #define IOP_PLL_0_MDIV_Ch0_OFFSET 0xb14 | |
107 | #define IOP_PLL_0_MDIV_Ch1_OFFSET 0xb18 | |
108 | #define IOP_PLL_0_MDIV_Ch2_OFFSET 0xb1c | |
109 | ||
110 | #define IOP_PLL_0_ACTIVE_MDIV_Ch0_OFFSET 0xb30 | |
111 | #define IOP_PLL_0_ACTIVE_MDIV_Ch1_OFFSET 0xb34 | |
112 | #define IOP_PLL_0_ACTIVE_MDIV_Ch2_OFFSET 0xb38 | |
113 | ||
114 | /* AUD_FMM_IOP_xxx regs */ | |
115 | #define IOP_PLL_0_CONTROL_OFFSET 0xb04 | |
116 | #define IOP_PLL_0_USER_NDIV_OFFSET 0xb08 | |
117 | #define IOP_PLL_0_ACTIVE_NDIV_OFFSET 0xb20 | |
118 | #define IOP_PLL_0_RESET_OFFSET 0xb5c | |
119 | ||
120 | /* AUD_FMM_IOP_IN_I2S_xxx regs */ | |
121 | #define IN_I2S_0_STREAM_CFG_OFFSET 0x00 | |
122 | #define IN_I2S_0_CFG_OFFSET 0x04 | |
123 | #define IN_I2S_1_STREAM_CFG_OFFSET 0x40 | |
124 | #define IN_I2S_1_CFG_OFFSET 0x44 | |
125 | #define IN_I2S_2_STREAM_CFG_OFFSET 0x80 | |
126 | #define IN_I2S_2_CFG_OFFSET 0x84 | |
127 | ||
128 | /* AUD_FMM_IOP_MISC_xxx regs */ | |
129 | #define IOP_SW_INIT_LOGIC 0x1c0 | |
130 | ||
131 | /* End register offset defines */ | |
132 | ||
133 | ||
134 | /* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_0_REG */ | |
135 | #define I2S_OUT_MCLKRATE_SHIFT 16 | |
136 | ||
137 | /* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_REG */ | |
138 | #define I2S_OUT_PLLCLKSEL_SHIFT 0 | |
139 | ||
140 | /* AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG */ | |
141 | #define I2S_OUT_STREAM_ENA 31 | |
142 | #define I2S_OUT_STREAM_CFG_GROUP_ID 20 | |
143 | #define I2S_OUT_STREAM_CFG_CHANNEL_GROUPING 24 | |
144 | ||
145 | /* AUD_FMM_IOP_IN_I2S_x_CAP */ | |
146 | #define I2S_IN_STREAM_CFG_CAP_ENA 31 | |
147 | #define I2S_IN_STREAM_CFG_0_GROUP_ID 4 | |
148 | ||
149 | /* AUD_FMM_IOP_OUT_I2S_x_I2S_CFG_REG */ | |
150 | #define I2S_OUT_CFGX_CLK_ENA 0 | |
151 | #define I2S_OUT_CFGX_DATA_ENABLE 1 | |
152 | #define I2S_OUT_CFGX_DATA_ALIGNMENT 6 | |
153 | #define I2S_OUT_CFGX_BITS_PER_SLOT 13 | |
154 | #define I2S_OUT_CFGX_VALID_SLOT 14 | |
155 | #define I2S_OUT_CFGX_FSYNC_WIDTH 18 | |
156 | #define I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32 26 | |
157 | #define I2S_OUT_CFGX_SLAVE_MODE 30 | |
158 | #define I2S_OUT_CFGX_TDM_MODE 31 | |
159 | ||
160 | /* AUD_FMM_BF_CTRL_SOURCECH_CFGx_REG */ | |
161 | #define BF_SRC_CFGX_SFIFO_ENA 0 | |
162 | #define BF_SRC_CFGX_BUFFER_PAIR_ENABLE 1 | |
163 | #define BF_SRC_CFGX_SAMPLE_CH_MODE 2 | |
164 | #define BF_SRC_CFGX_SFIFO_SZ_DOUBLE 5 | |
165 | #define BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY 10 | |
166 | #define BF_SRC_CFGX_BIT_RES 20 | |
167 | #define BF_SRC_CFGX_PROCESS_SEQ_ID_VALID 31 | |
168 | ||
169 | /* AUD_FMM_BF_CTRL_DESTCH_CFGx_REG */ | |
170 | #define BF_DST_CFGX_CAP_ENA 0 | |
171 | #define BF_DST_CFGX_BUFFER_PAIR_ENABLE 1 | |
172 | #define BF_DST_CFGX_DFIFO_SZ_DOUBLE 2 | |
173 | #define BF_DST_CFGX_NOT_PAUSE_WHEN_FULL 11 | |
174 | #define BF_DST_CFGX_FCI_ID 12 | |
175 | #define BF_DST_CFGX_CAP_MODE 24 | |
176 | #define BF_DST_CFGX_PROC_SEQ_ID_VALID 31 | |
177 | ||
178 | /* AUD_FMM_IOP_OUT_SPDIF_xxx */ | |
179 | #define SPDIF_0_OUT_DITHER_ENA 3 | |
180 | #define SPDIF_0_OUT_STREAM_ENA 31 | |
181 | ||
182 | /* AUD_FMM_IOP_PLL_0_USER */ | |
183 | #define IOP_PLL_0_USER_NDIV_FRAC 10 | |
184 | ||
185 | /* AUD_FMM_IOP_PLL_0_ACTIVE */ | |
186 | #define IOP_PLL_0_ACTIVE_NDIV_FRAC 10 | |
187 | ||
188 | ||
189 | #define INIT_SSP_REGS(num) (struct cygnus_ssp_regs){ \ | |
190 | .i2s_stream_cfg = OUT_I2S_ ##num## _STREAM_CFG_OFFSET, \ | |
191 | .i2s_cap_stream_cfg = IN_I2S_ ##num## _STREAM_CFG_OFFSET, \ | |
192 | .i2s_cfg = OUT_I2S_ ##num## _CFG_OFFSET, \ | |
193 | .i2s_cap_cfg = IN_I2S_ ##num## _CFG_OFFSET, \ | |
194 | .i2s_mclk_cfg = OUT_I2S_ ##num## _MCLK_CFG_OFFSET, \ | |
195 | .bf_destch_ctrl = BF_DST_CTRL ##num## _OFFSET, \ | |
196 | .bf_destch_cfg = BF_DST_CFG ##num## _OFFSET, \ | |
197 | .bf_sourcech_ctrl = BF_SRC_CTRL ##num## _OFFSET, \ | |
198 | .bf_sourcech_cfg = BF_SRC_CFG ##num## _OFFSET, \ | |
199 | .bf_sourcech_grp = BF_SRC_GRP ##num## _OFFSET \ | |
200 | } | |
201 | ||
202 | struct pll_macro_entry { | |
203 | u32 mclk; | |
204 | u32 pll_ch_num; | |
205 | }; | |
206 | ||
207 | /* | |
208 | * PLL has 3 output channels (1x, 2x, and 4x). Below are | |
209 | * the common MCLK frequencies used by audio driver | |
210 | */ | |
211 | static const struct pll_macro_entry pll_predef_mclk[] = { | |
212 | { 4096000, 0}, | |
213 | { 8192000, 1}, | |
214 | {16384000, 2}, | |
215 | ||
216 | { 5644800, 0}, | |
217 | {11289600, 1}, | |
218 | {22579200, 2}, | |
219 | ||
220 | { 6144000, 0}, | |
221 | {12288000, 1}, | |
222 | {24576000, 2}, | |
223 | ||
224 | {12288000, 0}, | |
225 | {24576000, 1}, | |
226 | {49152000, 2}, | |
227 | ||
228 | {22579200, 0}, | |
229 | {45158400, 1}, | |
230 | {90316800, 2}, | |
231 | ||
232 | {24576000, 0}, | |
233 | {49152000, 1}, | |
234 | {98304000, 2}, | |
235 | }; | |
236 | ||
237 | /* List of valid frame sizes for tdm mode */ | |
238 | static const int ssp_valid_tdm_framesize[] = {32, 64, 128, 256, 512}; | |
239 | ||
240 | /* | |
241 | * Use this relationship to derive the sampling rate (lrclk) | |
242 | * lrclk = (mclk) / ((2*mclk_to_sclk_ratio) * (32 * SCLK))). | |
243 | * | |
244 | * Use mclk and pll_ch from the table above | |
245 | * | |
246 | * Valid SCLK = 0/1/2/4/8/12 | |
247 | * | |
248 | * mclk_to_sclk_ratio = number of MCLK per SCLK. Division is twice the | |
249 | * value programmed in this field. | |
250 | * Valid mclk_to_sclk_ratio = 1 through to 15 | |
251 | * | |
252 | * eg: To set lrclk = 48khz, set mclk = 12288000, mclk_to_sclk_ratio = 2, | |
253 | * SCLK = 64 | |
254 | */ | |
255 | struct _ssp_clk_coeff { | |
256 | u32 mclk; | |
257 | u32 sclk_rate; | |
258 | u32 rate; | |
259 | u32 mclk_rate; | |
260 | }; | |
261 | ||
262 | static const struct _ssp_clk_coeff ssp_clk_coeff[] = { | |
263 | { 4096000, 32, 16000, 4}, | |
264 | { 4096000, 32, 32000, 2}, | |
265 | { 4096000, 64, 8000, 4}, | |
266 | { 4096000, 64, 16000, 2}, | |
267 | { 4096000, 64, 32000, 1}, | |
268 | { 4096000, 128, 8000, 2}, | |
269 | { 4096000, 128, 16000, 1}, | |
270 | { 4096000, 256, 8000, 1}, | |
271 | ||
272 | { 6144000, 32, 16000, 6}, | |
273 | { 6144000, 32, 32000, 3}, | |
274 | { 6144000, 32, 48000, 2}, | |
275 | { 6144000, 32, 96000, 1}, | |
276 | { 6144000, 64, 8000, 6}, | |
277 | { 6144000, 64, 16000, 3}, | |
278 | { 6144000, 64, 48000, 1}, | |
279 | { 6144000, 128, 8000, 3}, | |
280 | ||
281 | { 8192000, 32, 32000, 4}, | |
282 | { 8192000, 64, 16000, 4}, | |
283 | { 8192000, 64, 32000, 2}, | |
284 | { 8192000, 128, 8000, 4}, | |
285 | { 8192000, 128, 16000, 2}, | |
286 | { 8192000, 128, 32000, 1}, | |
287 | { 8192000, 256, 8000, 2}, | |
288 | { 8192000, 256, 16000, 1}, | |
289 | { 8192000, 512, 8000, 1}, | |
290 | ||
291 | {12288000, 32, 32000, 6}, | |
292 | {12288000, 32, 48000, 4}, | |
293 | {12288000, 32, 96000, 2}, | |
294 | {12288000, 32, 192000, 1}, | |
295 | {12288000, 64, 16000, 6}, | |
296 | {12288000, 64, 32000, 3}, | |
297 | {12288000, 64, 48000, 2}, | |
298 | {12288000, 64, 96000, 1}, | |
299 | {12288000, 128, 8000, 6}, | |
300 | {12288000, 128, 16000, 3}, | |
301 | {12288000, 128, 48000, 1}, | |
302 | {12288000, 256, 8000, 3}, | |
303 | ||
304 | {16384000, 64, 32000, 4}, | |
305 | {16384000, 128, 16000, 4}, | |
306 | {16384000, 128, 32000, 2}, | |
307 | {16384000, 256, 8000, 4}, | |
308 | {16384000, 256, 16000, 2}, | |
309 | {16384000, 256, 32000, 1}, | |
310 | {16384000, 512, 8000, 2}, | |
311 | {16384000, 512, 16000, 1}, | |
312 | ||
313 | {24576000, 32, 96000, 4}, | |
314 | {24576000, 32, 192000, 2}, | |
315 | {24576000, 64, 32000, 6}, | |
316 | {24576000, 64, 48000, 4}, | |
317 | {24576000, 64, 96000, 2}, | |
318 | {24576000, 64, 192000, 1}, | |
319 | {24576000, 128, 16000, 6}, | |
320 | {24576000, 128, 32000, 3}, | |
321 | {24576000, 128, 48000, 2}, | |
322 | {24576000, 256, 8000, 6}, | |
323 | {24576000, 256, 16000, 3}, | |
324 | {24576000, 256, 48000, 1}, | |
325 | {24576000, 512, 8000, 3}, | |
326 | ||
327 | {49152000, 32, 192000, 4}, | |
328 | {49152000, 64, 96000, 4}, | |
329 | {49152000, 64, 192000, 2}, | |
330 | {49152000, 128, 32000, 6}, | |
331 | {49152000, 128, 48000, 4}, | |
332 | {49152000, 128, 96000, 2}, | |
333 | {49152000, 128, 192000, 1}, | |
334 | {49152000, 256, 16000, 6}, | |
335 | {49152000, 256, 32000, 3}, | |
336 | {49152000, 256, 48000, 2}, | |
337 | {49152000, 256, 96000, 1}, | |
338 | {49152000, 512, 8000, 6}, | |
339 | {49152000, 512, 16000, 3}, | |
340 | {49152000, 512, 48000, 1}, | |
341 | ||
342 | { 5644800, 32, 22050, 4}, | |
343 | { 5644800, 32, 44100, 2}, | |
344 | { 5644800, 32, 88200, 1}, | |
345 | { 5644800, 64, 11025, 4}, | |
346 | { 5644800, 64, 22050, 2}, | |
347 | { 5644800, 64, 44100, 1}, | |
348 | ||
349 | {11289600, 32, 44100, 4}, | |
350 | {11289600, 32, 88200, 2}, | |
351 | {11289600, 32, 176400, 1}, | |
352 | {11289600, 64, 22050, 4}, | |
353 | {11289600, 64, 44100, 2}, | |
354 | {11289600, 64, 88200, 1}, | |
355 | {11289600, 128, 11025, 4}, | |
356 | {11289600, 128, 22050, 2}, | |
357 | {11289600, 128, 44100, 1}, | |
358 | ||
359 | {22579200, 32, 88200, 4}, | |
360 | {22579200, 32, 176400, 2}, | |
361 | {22579200, 64, 44100, 4}, | |
362 | {22579200, 64, 88200, 2}, | |
363 | {22579200, 64, 176400, 1}, | |
364 | {22579200, 128, 22050, 4}, | |
365 | {22579200, 128, 44100, 2}, | |
366 | {22579200, 128, 88200, 1}, | |
367 | {22579200, 256, 11025, 4}, | |
368 | {22579200, 256, 22050, 2}, | |
369 | {22579200, 256, 44100, 1}, | |
370 | ||
371 | {45158400, 32, 176400, 4}, | |
372 | {45158400, 64, 88200, 4}, | |
373 | {45158400, 64, 176400, 2}, | |
374 | {45158400, 128, 44100, 4}, | |
375 | {45158400, 128, 88200, 2}, | |
376 | {45158400, 128, 176400, 1}, | |
377 | {45158400, 256, 22050, 4}, | |
378 | {45158400, 256, 44100, 2}, | |
379 | {45158400, 256, 88200, 1}, | |
380 | {45158400, 512, 11025, 4}, | |
381 | {45158400, 512, 22050, 2}, | |
382 | {45158400, 512, 44100, 1}, | |
383 | }; | |
384 | ||
385 | static struct cygnus_aio_port *cygnus_dai_get_portinfo(struct snd_soc_dai *dai) | |
386 | { | |
387 | struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai); | |
388 | ||
389 | return &cygaud->portinfo[dai->id]; | |
390 | } | |
391 | ||
392 | static int audio_ssp_init_portregs(struct cygnus_aio_port *aio) | |
393 | { | |
394 | u32 value, fci_id; | |
395 | int status = 0; | |
396 | ||
397 | switch (aio->port_type) { | |
398 | case PORT_TDM: | |
399 | value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg); | |
400 | value &= ~I2S_STREAM_CFG_MASK; | |
401 | ||
402 | /* Set Group ID */ | |
403 | writel(aio->portnum, | |
404 | aio->cygaud->audio + aio->regs.bf_sourcech_grp); | |
405 | ||
406 | /* Configure the AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG reg */ | |
407 | value |= aio->portnum << I2S_OUT_STREAM_CFG_GROUP_ID; | |
408 | value |= aio->portnum; /* FCI ID is the port num */ | |
409 | value |= CH_GRP_STEREO << I2S_OUT_STREAM_CFG_CHANNEL_GROUPING; | |
410 | writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg); | |
411 | ||
412 | /* Configure the AUD_FMM_BF_CTRL_SOURCECH_CFGX reg */ | |
413 | value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
414 | value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY); | |
415 | value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE); | |
416 | value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID); | |
417 | writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
418 | ||
419 | /* Configure the AUD_FMM_IOP_IN_I2S_x_CAP_STREAM_CFG_0 reg */ | |
420 | value = readl(aio->cygaud->i2s_in + | |
421 | aio->regs.i2s_cap_stream_cfg); | |
422 | value &= ~I2S_CAP_STREAM_CFG_MASK; | |
423 | value |= aio->portnum << I2S_IN_STREAM_CFG_0_GROUP_ID; | |
424 | writel(value, aio->cygaud->i2s_in + | |
425 | aio->regs.i2s_cap_stream_cfg); | |
426 | ||
427 | /* Configure the AUD_FMM_BF_CTRL_DESTCH_CFGX_REG_BASE reg */ | |
428 | fci_id = CAPTURE_FCI_ID_BASE + aio->portnum; | |
429 | ||
430 | value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg); | |
431 | value |= BIT(BF_DST_CFGX_DFIFO_SZ_DOUBLE); | |
432 | value &= ~BIT(BF_DST_CFGX_NOT_PAUSE_WHEN_FULL); | |
433 | value |= (fci_id << BF_DST_CFGX_FCI_ID); | |
434 | value |= BIT(BF_DST_CFGX_PROC_SEQ_ID_VALID); | |
435 | writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg); | |
436 | ||
437 | /* Enable the transmit pin for this port */ | |
438 | value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); | |
439 | value &= ~BIT((aio->portnum * 4) + AUD_MISC_SEROUT_SDAT_OE); | |
440 | writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); | |
441 | break; | |
442 | case PORT_SPDIF: | |
443 | writel(aio->portnum, aio->cygaud->audio + BF_SRC_GRP3_OFFSET); | |
444 | ||
445 | value = readl(aio->cygaud->audio + SPDIF_CTRL_OFFSET); | |
446 | value |= BIT(SPDIF_0_OUT_DITHER_ENA); | |
447 | writel(value, aio->cygaud->audio + SPDIF_CTRL_OFFSET); | |
448 | ||
449 | /* Enable and set the FCI ID for the SPDIF channel */ | |
450 | value = readl(aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET); | |
451 | value &= ~SPDIF_STREAM_CFG_MASK; | |
452 | value |= aio->portnum; /* FCI ID is the port num */ | |
453 | value |= BIT(SPDIF_0_OUT_STREAM_ENA); | |
454 | writel(value, aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET); | |
455 | ||
456 | value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
457 | value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY); | |
458 | value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE); | |
459 | value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID); | |
460 | writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
461 | ||
462 | /* Enable the spdif output pin */ | |
463 | value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); | |
464 | value &= ~BIT(AUD_MISC_SEROUT_SPDIF_OE); | |
465 | writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); | |
466 | break; | |
467 | default: | |
468 | dev_err(aio->cygaud->dev, "Port not supported\n"); | |
469 | status = -EINVAL; | |
470 | } | |
471 | ||
472 | return status; | |
473 | } | |
474 | ||
475 | static void audio_ssp_in_enable(struct cygnus_aio_port *aio) | |
476 | { | |
477 | u32 value; | |
478 | ||
479 | value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg); | |
480 | value |= BIT(BF_DST_CFGX_CAP_ENA); | |
481 | writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg); | |
482 | ||
483 | writel(0x1, aio->cygaud->audio + aio->regs.bf_destch_ctrl); | |
484 | ||
485 | value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); | |
486 | value |= BIT(I2S_OUT_CFGX_CLK_ENA); | |
487 | value |= BIT(I2S_OUT_CFGX_DATA_ENABLE); | |
488 | writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); | |
489 | ||
490 | value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg); | |
491 | value |= BIT(I2S_IN_STREAM_CFG_CAP_ENA); | |
492 | writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg); | |
493 | ||
494 | aio->streams_on |= CAPTURE_STREAM_MASK; | |
495 | } | |
496 | ||
497 | static void audio_ssp_in_disable(struct cygnus_aio_port *aio) | |
498 | { | |
499 | u32 value; | |
500 | ||
501 | value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg); | |
502 | value &= ~BIT(I2S_IN_STREAM_CFG_CAP_ENA); | |
503 | writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg); | |
504 | ||
505 | aio->streams_on &= ~CAPTURE_STREAM_MASK; | |
506 | ||
507 | /* If both playback and capture are off */ | |
508 | if (!aio->streams_on) { | |
509 | value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); | |
510 | value &= ~BIT(I2S_OUT_CFGX_CLK_ENA); | |
511 | value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE); | |
512 | writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); | |
513 | } | |
514 | ||
515 | writel(0x0, aio->cygaud->audio + aio->regs.bf_destch_ctrl); | |
516 | ||
517 | value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg); | |
518 | value &= ~BIT(BF_DST_CFGX_CAP_ENA); | |
519 | writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg); | |
520 | } | |
521 | ||
522 | static int audio_ssp_out_enable(struct cygnus_aio_port *aio) | |
523 | { | |
524 | u32 value; | |
525 | int status = 0; | |
526 | ||
527 | switch (aio->port_type) { | |
528 | case PORT_TDM: | |
529 | value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg); | |
530 | value |= BIT(I2S_OUT_STREAM_ENA); | |
531 | writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg); | |
532 | ||
533 | writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl); | |
534 | ||
535 | value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); | |
536 | value |= BIT(I2S_OUT_CFGX_CLK_ENA); | |
537 | value |= BIT(I2S_OUT_CFGX_DATA_ENABLE); | |
538 | writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); | |
539 | ||
540 | value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
541 | value |= BIT(BF_SRC_CFGX_SFIFO_ENA); | |
542 | writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
543 | ||
544 | aio->streams_on |= PLAYBACK_STREAM_MASK; | |
545 | break; | |
546 | case PORT_SPDIF: | |
547 | value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET); | |
548 | value |= 0x3; | |
549 | writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET); | |
550 | ||
551 | writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl); | |
552 | ||
553 | value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
554 | value |= BIT(BF_SRC_CFGX_SFIFO_ENA); | |
555 | writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
556 | break; | |
557 | default: | |
558 | dev_err(aio->cygaud->dev, | |
559 | "Port not supported %d\n", aio->portnum); | |
560 | status = -EINVAL; | |
561 | } | |
562 | ||
563 | return status; | |
564 | } | |
565 | ||
566 | static int audio_ssp_out_disable(struct cygnus_aio_port *aio) | |
567 | { | |
568 | u32 value; | |
569 | int status = 0; | |
570 | ||
571 | switch (aio->port_type) { | |
572 | case PORT_TDM: | |
573 | aio->streams_on &= ~PLAYBACK_STREAM_MASK; | |
574 | ||
575 | /* If both playback and capture are off */ | |
576 | if (!aio->streams_on) { | |
577 | value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); | |
578 | value &= ~BIT(I2S_OUT_CFGX_CLK_ENA); | |
579 | value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE); | |
580 | writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); | |
581 | } | |
582 | ||
583 | /* set group_sync_dis = 1 */ | |
584 | value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET); | |
585 | value |= BIT(aio->portnum); | |
586 | writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET); | |
587 | ||
588 | writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl); | |
589 | ||
590 | value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
591 | value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA); | |
592 | writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
593 | ||
594 | /* set group_sync_dis = 0 */ | |
595 | value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET); | |
596 | value &= ~BIT(aio->portnum); | |
597 | writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET); | |
598 | ||
599 | value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg); | |
600 | value &= ~BIT(I2S_OUT_STREAM_ENA); | |
601 | writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg); | |
602 | ||
603 | /* IOP SW INIT on OUT_I2S_x */ | |
604 | value = readl(aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC); | |
605 | value |= BIT(aio->portnum); | |
606 | writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC); | |
607 | value &= ~BIT(aio->portnum); | |
608 | writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC); | |
609 | break; | |
610 | case PORT_SPDIF: | |
611 | value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET); | |
612 | value &= ~0x3; | |
613 | writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET); | |
614 | writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl); | |
615 | ||
616 | value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
617 | value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA); | |
618 | writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
619 | break; | |
620 | default: | |
621 | dev_err(aio->cygaud->dev, | |
622 | "Port not supported %d\n", aio->portnum); | |
623 | status = -EINVAL; | |
624 | } | |
625 | ||
626 | return status; | |
627 | } | |
628 | ||
629 | static int pll_configure_mclk(struct cygnus_audio *cygaud, u32 mclk, | |
630 | struct cygnus_aio_port *aio) | |
631 | { | |
632 | int i = 0, error; | |
633 | bool found = false; | |
634 | const struct pll_macro_entry *p_entry; | |
635 | struct clk *ch_clk; | |
636 | ||
637 | for (i = 0; i < ARRAY_SIZE(pll_predef_mclk); i++) { | |
638 | p_entry = &pll_predef_mclk[i]; | |
639 | if (p_entry->mclk == mclk) { | |
640 | found = true; | |
641 | break; | |
642 | } | |
643 | } | |
644 | if (!found) { | |
645 | dev_err(cygaud->dev, | |
646 | "%s No valid mclk freq (%u) found!\n", __func__, mclk); | |
647 | return -EINVAL; | |
648 | } | |
649 | ||
650 | ch_clk = cygaud->audio_clk[p_entry->pll_ch_num]; | |
651 | ||
652 | if ((aio->clk_trace.cap_en) && (!aio->clk_trace.cap_clk_en)) { | |
653 | error = clk_prepare_enable(ch_clk); | |
654 | if (error) { | |
655 | dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n", | |
656 | __func__, error); | |
657 | return error; | |
658 | } | |
659 | aio->clk_trace.cap_clk_en = true; | |
660 | } | |
661 | ||
662 | if ((aio->clk_trace.play_en) && (!aio->clk_trace.play_clk_en)) { | |
663 | error = clk_prepare_enable(ch_clk); | |
664 | if (error) { | |
665 | dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n", | |
666 | __func__, error); | |
667 | return error; | |
668 | } | |
669 | aio->clk_trace.play_clk_en = true; | |
670 | } | |
671 | ||
672 | error = clk_set_rate(ch_clk, mclk); | |
673 | if (error) { | |
674 | dev_err(cygaud->dev, "%s Set MCLK rate failed: %d\n", | |
675 | __func__, error); | |
676 | return error; | |
677 | } | |
678 | ||
679 | return p_entry->pll_ch_num; | |
680 | } | |
681 | ||
682 | static int cygnus_ssp_set_clocks(struct cygnus_aio_port *aio, | |
683 | struct cygnus_audio *cygaud) | |
684 | { | |
685 | u32 value, i = 0; | |
686 | u32 mask = 0xf; | |
687 | u32 sclk; | |
688 | bool found = false; | |
689 | const struct _ssp_clk_coeff *p_entry = NULL; | |
690 | ||
691 | for (i = 0; i < ARRAY_SIZE(ssp_clk_coeff); i++) { | |
692 | p_entry = &ssp_clk_coeff[i]; | |
693 | if ((p_entry->rate == aio->lrclk) && | |
694 | (p_entry->sclk_rate == aio->bit_per_frame) && | |
695 | (p_entry->mclk == aio->mclk)) { | |
696 | found = true; | |
697 | break; | |
698 | } | |
699 | } | |
700 | if (!found) { | |
701 | dev_err(aio->cygaud->dev, | |
702 | "No valid match found in ssp_clk_coeff array\n"); | |
703 | dev_err(aio->cygaud->dev, "lrclk = %u, bits/frame = %u, mclk = %u\n", | |
704 | aio->lrclk, aio->bit_per_frame, aio->mclk); | |
705 | return -EINVAL; | |
706 | } | |
707 | ||
708 | sclk = aio->bit_per_frame; | |
709 | if (sclk == 512) | |
710 | sclk = 0; | |
711 | /* sclks_per_1fs_div = sclk cycles/32 */ | |
712 | sclk /= 32; | |
713 | /* Set sclk rate */ | |
714 | switch (aio->port_type) { | |
715 | case PORT_TDM: | |
716 | /* Set number of bitclks per frame */ | |
717 | value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); | |
718 | value &= ~(mask << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32); | |
719 | value |= sclk << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32; | |
720 | writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); | |
721 | dev_dbg(aio->cygaud->dev, | |
722 | "SCLKS_PER_1FS_DIV32 = 0x%x\n", value); | |
723 | break; | |
724 | case PORT_SPDIF: | |
725 | break; | |
726 | default: | |
727 | dev_err(aio->cygaud->dev, "Unknown port type\n"); | |
728 | return -EINVAL; | |
729 | } | |
730 | ||
731 | /* Set MCLK_RATE ssp port (spdif and ssp are the same) */ | |
732 | value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg); | |
733 | value &= ~(0xf << I2S_OUT_MCLKRATE_SHIFT); | |
734 | value |= (p_entry->mclk_rate << I2S_OUT_MCLKRATE_SHIFT); | |
735 | writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg); | |
736 | ||
737 | dev_dbg(aio->cygaud->dev, "mclk cfg reg = 0x%x\n", value); | |
738 | dev_dbg(aio->cygaud->dev, "bits per frame = %u, mclk = %u Hz, lrclk = %u Hz\n", | |
739 | aio->bit_per_frame, aio->mclk, aio->lrclk); | |
740 | return 0; | |
741 | } | |
742 | ||
743 | static int cygnus_ssp_hw_params(struct snd_pcm_substream *substream, | |
744 | struct snd_pcm_hw_params *params, | |
745 | struct snd_soc_dai *dai) | |
746 | { | |
747 | struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); | |
748 | struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai); | |
749 | int rate, bitres; | |
750 | u32 value; | |
751 | u32 mask = 0x1f; | |
752 | int ret = 0; | |
753 | ||
754 | dev_dbg(aio->cygaud->dev, "%s port = %d\n", __func__, aio->portnum); | |
755 | dev_dbg(aio->cygaud->dev, "params_channels %d\n", | |
756 | params_channels(params)); | |
757 | dev_dbg(aio->cygaud->dev, "rate %d\n", params_rate(params)); | |
758 | dev_dbg(aio->cygaud->dev, "format %d\n", params_format(params)); | |
759 | ||
760 | rate = params_rate(params); | |
761 | ||
762 | switch (aio->mode) { | |
763 | case CYGNUS_SSPMODE_TDM: | |
764 | if ((rate == 192000) && (params_channels(params) > 4)) { | |
765 | dev_err(aio->cygaud->dev, "Cannot run %d channels at %dHz\n", | |
766 | params_channels(params), rate); | |
767 | return -EINVAL; | |
768 | } | |
769 | break; | |
770 | case CYGNUS_SSPMODE_I2S: | |
771 | aio->bit_per_frame = 64; /* I2S must be 64 bit per frame */ | |
772 | break; | |
773 | default: | |
774 | dev_err(aio->cygaud->dev, | |
775 | "%s port running in unknown mode\n", __func__); | |
776 | return -EINVAL; | |
777 | } | |
778 | ||
779 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
780 | value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
781 | value &= ~BIT(BF_SRC_CFGX_BUFFER_PAIR_ENABLE); | |
782 | /* Configure channels as mono or stereo/TDM */ | |
783 | if (params_channels(params) == 1) | |
784 | value |= BIT(BF_SRC_CFGX_SAMPLE_CH_MODE); | |
785 | else | |
786 | value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE); | |
787 | writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
788 | ||
789 | switch (params_format(params)) { | |
790 | case SNDRV_PCM_FORMAT_S8: | |
791 | if (aio->port_type == PORT_SPDIF) { | |
792 | dev_err(aio->cygaud->dev, | |
793 | "SPDIF does not support 8bit format\n"); | |
794 | return -EINVAL; | |
795 | } | |
796 | bitres = 8; | |
797 | break; | |
798 | ||
799 | case SNDRV_PCM_FORMAT_S16_LE: | |
800 | bitres = 16; | |
801 | break; | |
802 | ||
803 | case SNDRV_PCM_FORMAT_S32_LE: | |
804 | /* 32 bit mode is coded as 0 */ | |
805 | bitres = 0; | |
806 | break; | |
807 | ||
808 | default: | |
809 | return -EINVAL; | |
810 | } | |
811 | ||
812 | value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
813 | value &= ~(mask << BF_SRC_CFGX_BIT_RES); | |
814 | value |= (bitres << BF_SRC_CFGX_BIT_RES); | |
815 | writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); | |
816 | ||
817 | } else { | |
818 | ||
819 | switch (params_format(params)) { | |
820 | case SNDRV_PCM_FORMAT_S16_LE: | |
821 | value = readl(aio->cygaud->audio + | |
822 | aio->regs.bf_destch_cfg); | |
823 | value |= BIT(BF_DST_CFGX_CAP_MODE); | |
824 | writel(value, aio->cygaud->audio + | |
825 | aio->regs.bf_destch_cfg); | |
826 | break; | |
827 | ||
828 | case SNDRV_PCM_FORMAT_S32_LE: | |
829 | value = readl(aio->cygaud->audio + | |
830 | aio->regs.bf_destch_cfg); | |
831 | value &= ~BIT(BF_DST_CFGX_CAP_MODE); | |
832 | writel(value, aio->cygaud->audio + | |
833 | aio->regs.bf_destch_cfg); | |
834 | break; | |
835 | ||
836 | default: | |
837 | return -EINVAL; | |
838 | } | |
839 | } | |
840 | ||
841 | aio->lrclk = rate; | |
842 | ||
843 | if (!aio->is_slave) | |
844 | ret = cygnus_ssp_set_clocks(aio, cygaud); | |
845 | ||
846 | return ret; | |
847 | } | |
848 | ||
849 | /* | |
850 | * This function sets the mclk frequency for pll clock | |
851 | */ | |
852 | static int cygnus_ssp_set_sysclk(struct snd_soc_dai *dai, | |
853 | int clk_id, unsigned int freq, int dir) | |
854 | { | |
855 | int sel; | |
856 | u32 value; | |
857 | struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); | |
858 | struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai); | |
859 | ||
860 | dev_dbg(aio->cygaud->dev, | |
861 | "%s Enter port = %d\n", __func__, aio->portnum); | |
862 | sel = pll_configure_mclk(cygaud, freq, aio); | |
863 | if (sel < 0) { | |
864 | dev_err(aio->cygaud->dev, | |
865 | "%s Setting mclk failed.\n", __func__); | |
866 | return -EINVAL; | |
867 | } | |
868 | ||
869 | aio->mclk = freq; | |
870 | ||
871 | dev_dbg(aio->cygaud->dev, "%s Setting MCLKSEL to %d\n", __func__, sel); | |
872 | value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg); | |
873 | value &= ~(0xf << I2S_OUT_PLLCLKSEL_SHIFT); | |
874 | value |= (sel << I2S_OUT_PLLCLKSEL_SHIFT); | |
875 | writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg); | |
876 | ||
877 | return 0; | |
878 | } | |
879 | ||
880 | static int cygnus_ssp_startup(struct snd_pcm_substream *substream, | |
881 | struct snd_soc_dai *dai) | |
882 | { | |
883 | struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); | |
884 | ||
885 | snd_soc_dai_set_dma_data(dai, substream, aio); | |
886 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
887 | aio->clk_trace.play_en = true; | |
888 | else | |
889 | aio->clk_trace.cap_en = true; | |
890 | ||
891 | return 0; | |
892 | } | |
893 | ||
894 | static void cygnus_ssp_shutdown(struct snd_pcm_substream *substream, | |
895 | struct snd_soc_dai *dai) | |
896 | { | |
897 | struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); | |
898 | ||
899 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
900 | aio->clk_trace.play_en = false; | |
901 | else | |
902 | aio->clk_trace.cap_en = false; | |
903 | ||
904 | if (!aio->is_slave) { | |
905 | u32 val; | |
906 | ||
907 | val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg); | |
908 | val &= CYGNUS_PLLCLKSEL_MASK; | |
909 | if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) { | |
910 | dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n", | |
911 | val); | |
912 | return; | |
913 | } | |
914 | ||
915 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
916 | if (aio->clk_trace.play_clk_en) { | |
917 | clk_disable_unprepare(aio->cygaud-> | |
918 | audio_clk[val]); | |
919 | aio->clk_trace.play_clk_en = false; | |
920 | } | |
921 | } else { | |
922 | if (aio->clk_trace.cap_clk_en) { | |
923 | clk_disable_unprepare(aio->cygaud-> | |
924 | audio_clk[val]); | |
925 | aio->clk_trace.cap_clk_en = false; | |
926 | } | |
927 | } | |
928 | } | |
929 | } | |
930 | ||
931 | /* | |
932 | * Bit Update Notes | |
933 | * 31 Yes TDM Mode (1 = TDM, 0 = i2s) | |
934 | * 30 Yes Slave Mode (1 = Slave, 0 = Master) | |
935 | * 29:26 No Sclks per frame | |
936 | * 25:18 Yes FS Width | |
937 | * 17:14 No Valid Slots | |
938 | * 13 No Bits (1 = 16 bits, 0 = 32 bits) | |
939 | * 12:08 No Bits per samp | |
940 | * 07 Yes Justifcation (1 = LSB, 0 = MSB) | |
941 | * 06 Yes Alignment (1 = Delay 1 clk, 0 = no delay | |
942 | * 05 Yes SCLK polarity (1 = Rising, 0 = Falling) | |
943 | * 04 Yes LRCLK Polarity (1 = High for left, 0 = Low for left) | |
944 | * 03:02 Yes Reserved - write as zero | |
945 | * 01 No Data Enable | |
946 | * 00 No CLK Enable | |
947 | */ | |
948 | #define I2S_OUT_CFG_REG_UPDATE_MASK 0x3C03FF03 | |
949 | ||
950 | /* Input cfg is same as output, but the FS width is not a valid field */ | |
951 | #define I2S_IN_CFG_REG_UPDATE_MASK (I2S_OUT_CFG_REG_UPDATE_MASK | 0x03FC0000) | |
952 | ||
953 | int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len) | |
954 | { | |
955 | struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); | |
956 | ||
957 | if ((len > 0) && (len < 256)) { | |
958 | aio->fsync_width = len; | |
959 | return 0; | |
960 | } else { | |
961 | return -EINVAL; | |
962 | } | |
963 | } | |
964 | ||
965 | static int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) | |
966 | { | |
967 | struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); | |
968 | u32 ssp_curcfg; | |
969 | u32 ssp_newcfg; | |
970 | u32 ssp_outcfg; | |
971 | u32 ssp_incfg; | |
972 | u32 val; | |
973 | u32 mask; | |
974 | ||
975 | dev_dbg(aio->cygaud->dev, "%s Enter fmt: %x\n", __func__, fmt); | |
976 | ||
977 | if (aio->port_type == PORT_SPDIF) | |
978 | return -EINVAL; | |
979 | ||
980 | ssp_newcfg = 0; | |
981 | ||
982 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | |
983 | case SND_SOC_DAIFMT_CBM_CFM: | |
984 | ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE); | |
985 | aio->is_slave = 1; | |
986 | break; | |
987 | case SND_SOC_DAIFMT_CBS_CFS: | |
988 | ssp_newcfg &= ~BIT(I2S_OUT_CFGX_SLAVE_MODE); | |
989 | aio->is_slave = 0; | |
990 | break; | |
991 | default: | |
992 | return -EINVAL; | |
993 | } | |
994 | ||
995 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
996 | case SND_SOC_DAIFMT_I2S: | |
997 | ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT); | |
998 | ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH); | |
999 | aio->mode = CYGNUS_SSPMODE_I2S; | |
1000 | break; | |
1001 | ||
1002 | case SND_SOC_DAIFMT_DSP_A: | |
1003 | case SND_SOC_DAIFMT_DSP_B: | |
1004 | ssp_newcfg |= BIT(I2S_OUT_CFGX_TDM_MODE); | |
1005 | ||
1006 | /* DSP_A = data after FS, DSP_B = data during FS */ | |
1007 | if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A) | |
1008 | ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT); | |
1009 | ||
1010 | if ((aio->fsync_width > 0) && (aio->fsync_width < 256)) | |
1011 | ssp_newcfg |= | |
1012 | (aio->fsync_width << I2S_OUT_CFGX_FSYNC_WIDTH); | |
1013 | else | |
1014 | ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH); | |
1015 | ||
1016 | aio->mode = CYGNUS_SSPMODE_TDM; | |
1017 | break; | |
1018 | ||
1019 | default: | |
1020 | return -EINVAL; | |
1021 | } | |
1022 | ||
1023 | /* | |
1024 | * SSP out cfg. | |
1025 | * Retain bits we do not want to update, then OR in new bits | |
1026 | */ | |
1027 | ssp_curcfg = readl(aio->cygaud->audio + aio->regs.i2s_cfg); | |
1028 | ssp_outcfg = (ssp_curcfg & I2S_OUT_CFG_REG_UPDATE_MASK) | ssp_newcfg; | |
1029 | writel(ssp_outcfg, aio->cygaud->audio + aio->regs.i2s_cfg); | |
1030 | ||
1031 | /* | |
1032 | * SSP in cfg. | |
1033 | * Retain bits we do not want to update, then OR in new bits | |
1034 | */ | |
1035 | ssp_curcfg = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg); | |
1036 | ssp_incfg = (ssp_curcfg & I2S_IN_CFG_REG_UPDATE_MASK) | ssp_newcfg; | |
1037 | writel(ssp_incfg, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg); | |
1038 | ||
1039 | val = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); | |
1040 | ||
1041 | /* | |
1042 | * Configure the word clk and bit clk as output or tristate | |
1043 | * Each port has 4 bits for controlling its pins. | |
1044 | * Shift the mask based upon port number. | |
1045 | */ | |
1046 | mask = BIT(AUD_MISC_SEROUT_LRCK_OE) | |
1047 | | BIT(AUD_MISC_SEROUT_SCLK_OE) | |
1048 | | BIT(AUD_MISC_SEROUT_MCLK_OE); | |
1049 | mask = mask << (aio->portnum * 4); | |
1050 | if (aio->is_slave) | |
1051 | /* Set bit for tri-state */ | |
1052 | val |= mask; | |
1053 | else | |
1054 | /* Clear bit for drive */ | |
1055 | val &= ~mask; | |
1056 | ||
1057 | dev_dbg(aio->cygaud->dev, "%s Set OE bits 0x%x\n", __func__, val); | |
1058 | writel(val, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); | |
1059 | ||
1060 | return 0; | |
1061 | } | |
1062 | ||
1063 | static int cygnus_ssp_trigger(struct snd_pcm_substream *substream, int cmd, | |
1064 | struct snd_soc_dai *dai) | |
1065 | { | |
1066 | struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai); | |
1067 | struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai); | |
1068 | ||
1069 | dev_dbg(aio->cygaud->dev, | |
1070 | "%s cmd %d at port = %d\n", __func__, cmd, aio->portnum); | |
1071 | ||
1072 | switch (cmd) { | |
1073 | case SNDRV_PCM_TRIGGER_START: | |
1074 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
1075 | case SNDRV_PCM_TRIGGER_RESUME: | |
1076 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
1077 | audio_ssp_out_enable(aio); | |
1078 | else | |
1079 | audio_ssp_in_enable(aio); | |
1080 | cygaud->active_ports++; | |
1081 | ||
1082 | break; | |
1083 | ||
1084 | case SNDRV_PCM_TRIGGER_STOP: | |
1085 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
1086 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
1087 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
1088 | audio_ssp_out_disable(aio); | |
1089 | else | |
1090 | audio_ssp_in_disable(aio); | |
1091 | cygaud->active_ports--; | |
1092 | break; | |
1093 | ||
1094 | default: | |
1095 | return -EINVAL; | |
1096 | } | |
1097 | ||
1098 | return 0; | |
1099 | } | |
1100 | ||
1101 | static int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, | |
1102 | unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) | |
1103 | { | |
1104 | struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); | |
1105 | u32 value; | |
1106 | int bits_per_slot = 0; /* default to 32-bits per slot */ | |
1107 | int frame_bits; | |
1108 | unsigned int active_slots; | |
1109 | bool found = false; | |
1110 | int i; | |
1111 | ||
1112 | if (tx_mask != rx_mask) { | |
1113 | dev_err(aio->cygaud->dev, | |
1114 | "%s tx_mask must equal rx_mask\n", __func__); | |
1115 | return -EINVAL; | |
1116 | } | |
1117 | ||
1118 | active_slots = hweight32(tx_mask); | |
1119 | ||
1120 | if ((active_slots < 0) || (active_slots > 16)) | |
1121 | return -EINVAL; | |
1122 | ||
1123 | /* Slot value must be even */ | |
1124 | if (active_slots % 2) | |
1125 | return -EINVAL; | |
1126 | ||
1127 | /* We encode 16 slots as 0 in the reg */ | |
1128 | if (active_slots == 16) | |
1129 | active_slots = 0; | |
1130 | ||
1131 | /* Slot Width is either 16 or 32 */ | |
1132 | switch (slot_width) { | |
1133 | case 16: | |
1134 | bits_per_slot = 1; | |
1135 | break; | |
1136 | case 32: | |
1137 | bits_per_slot = 0; | |
1138 | break; | |
1139 | default: | |
1140 | bits_per_slot = 0; | |
1141 | dev_warn(aio->cygaud->dev, | |
1142 | "%s Defaulting Slot Width to 32\n", __func__); | |
1143 | } | |
1144 | ||
1145 | frame_bits = slots * slot_width; | |
1146 | ||
1147 | for (i = 0; i < ARRAY_SIZE(ssp_valid_tdm_framesize); i++) { | |
1148 | if (ssp_valid_tdm_framesize[i] == frame_bits) { | |
1149 | found = true; | |
1150 | break; | |
1151 | } | |
1152 | } | |
1153 | ||
1154 | if (!found) { | |
1155 | dev_err(aio->cygaud->dev, | |
1156 | "%s In TDM mode, frame bits INVALID (%d)\n", | |
1157 | __func__, frame_bits); | |
1158 | return -EINVAL; | |
1159 | } | |
1160 | ||
1161 | aio->bit_per_frame = frame_bits; | |
1162 | ||
1163 | dev_dbg(aio->cygaud->dev, "%s active_slots %u, bits per frame %d\n", | |
1164 | __func__, active_slots, frame_bits); | |
1165 | ||
1166 | /* Set capture side of ssp port */ | |
1167 | value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg); | |
1168 | value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT); | |
1169 | value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT); | |
1170 | value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT); | |
1171 | value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT); | |
1172 | writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg); | |
1173 | ||
1174 | /* Set playback side of ssp port */ | |
1175 | value = readl(aio->cygaud->audio + aio->regs.i2s_cfg); | |
1176 | value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT); | |
1177 | value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT); | |
1178 | value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT); | |
1179 | value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT); | |
1180 | writel(value, aio->cygaud->audio + aio->regs.i2s_cfg); | |
1181 | ||
1182 | return 0; | |
1183 | } | |
1184 | ||
1185 | #ifdef CONFIG_PM_SLEEP | |
1186 | static int cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai) | |
1187 | { | |
1188 | struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); | |
1189 | ||
1190 | if (!aio->is_slave) { | |
1191 | u32 val; | |
1192 | ||
1193 | val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg); | |
1194 | val &= CYGNUS_PLLCLKSEL_MASK; | |
1195 | if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) { | |
1196 | dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n", | |
1197 | val); | |
1198 | return -EINVAL; | |
1199 | } | |
1200 | ||
1201 | if (aio->clk_trace.cap_clk_en) | |
1202 | clk_disable_unprepare(aio->cygaud->audio_clk[val]); | |
1203 | if (aio->clk_trace.play_clk_en) | |
1204 | clk_disable_unprepare(aio->cygaud->audio_clk[val]); | |
1205 | ||
1206 | aio->pll_clk_num = val; | |
1207 | } | |
1208 | ||
1209 | return 0; | |
1210 | } | |
1211 | ||
1212 | static int cygnus_ssp_resume(struct snd_soc_dai *cpu_dai) | |
1213 | { | |
1214 | struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); | |
1215 | int error; | |
1216 | ||
1217 | if (!aio->is_slave) { | |
1218 | if (aio->clk_trace.cap_clk_en) { | |
1219 | error = clk_prepare_enable(aio->cygaud-> | |
1220 | audio_clk[aio->pll_clk_num]); | |
1221 | if (error) { | |
1222 | dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n", | |
1223 | __func__); | |
1224 | return -EINVAL; | |
1225 | } | |
1226 | } | |
1227 | if (aio->clk_trace.play_clk_en) { | |
1228 | error = clk_prepare_enable(aio->cygaud-> | |
1229 | audio_clk[aio->pll_clk_num]); | |
1230 | if (error) { | |
1231 | if (aio->clk_trace.cap_clk_en) | |
1232 | clk_disable_unprepare(aio->cygaud-> | |
1233 | audio_clk[aio->pll_clk_num]); | |
1234 | dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n", | |
1235 | __func__); | |
1236 | return -EINVAL; | |
1237 | } | |
1238 | } | |
1239 | } | |
1240 | ||
1241 | return 0; | |
1242 | } | |
1243 | #else | |
1244 | #define cygnus_ssp_suspend NULL | |
1245 | #define cygnus_ssp_resume NULL | |
1246 | #endif | |
1247 | ||
1248 | static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { | |
1249 | .startup = cygnus_ssp_startup, | |
1250 | .shutdown = cygnus_ssp_shutdown, | |
1251 | .trigger = cygnus_ssp_trigger, | |
1252 | .hw_params = cygnus_ssp_hw_params, | |
1253 | .set_fmt = cygnus_ssp_set_fmt, | |
1254 | .set_sysclk = cygnus_ssp_set_sysclk, | |
1255 | .set_tdm_slot = cygnus_set_dai_tdm_slot, | |
1256 | }; | |
1257 | ||
1258 | ||
1259 | #define INIT_CPU_DAI(num) { \ | |
1260 | .name = "cygnus-ssp" #num, \ | |
1261 | .playback = { \ | |
1262 | .channels_min = 1, \ | |
1263 | .channels_max = 16, \ | |
1264 | .rates = CYGNUS_TDM_RATE | SNDRV_PCM_RATE_88200 | \ | |
1265 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \ | |
1266 | SNDRV_PCM_RATE_192000, \ | |
1267 | .formats = SNDRV_PCM_FMTBIT_S8 | \ | |
1268 | SNDRV_PCM_FMTBIT_S16_LE | \ | |
1269 | SNDRV_PCM_FMTBIT_S32_LE, \ | |
1270 | }, \ | |
1271 | .capture = { \ | |
1272 | .channels_min = 2, \ | |
1273 | .channels_max = 16, \ | |
1274 | .rates = CYGNUS_TDM_RATE | SNDRV_PCM_RATE_88200 | \ | |
1275 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \ | |
1276 | SNDRV_PCM_RATE_192000, \ | |
1277 | .formats = SNDRV_PCM_FMTBIT_S16_LE | \ | |
1278 | SNDRV_PCM_FMTBIT_S32_LE, \ | |
1279 | }, \ | |
1280 | .ops = &cygnus_ssp_dai_ops, \ | |
1281 | .suspend = cygnus_ssp_suspend, \ | |
1282 | .resume = cygnus_ssp_resume, \ | |
1283 | } | |
1284 | ||
1285 | static const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = { | |
1286 | INIT_CPU_DAI(0), | |
1287 | INIT_CPU_DAI(1), | |
1288 | INIT_CPU_DAI(2), | |
1289 | }; | |
1290 | ||
1291 | static struct snd_soc_dai_driver cygnus_spdif_dai_info = { | |
1292 | .name = "cygnus-spdif", | |
1293 | .playback = { | |
1294 | .channels_min = 2, | |
1295 | .channels_max = 2, | |
1296 | .rates = CYGNUS_TDM_RATE | SNDRV_PCM_RATE_88200 | | |
1297 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | | |
1298 | SNDRV_PCM_RATE_192000, | |
1299 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | |
1300 | SNDRV_PCM_FMTBIT_S32_LE, | |
1301 | }, | |
1302 | .ops = &cygnus_ssp_dai_ops, | |
1303 | .suspend = cygnus_ssp_suspend, | |
1304 | .resume = cygnus_ssp_resume, | |
1305 | }; | |
1306 | ||
1307 | static struct snd_soc_dai_driver cygnus_ssp_dai[CYGNUS_MAX_PORTS]; | |
1308 | ||
1309 | static const struct snd_soc_component_driver cygnus_ssp_component = { | |
1310 | .name = "cygnus-audio", | |
1311 | }; | |
1312 | ||
1313 | /* | |
1314 | * Return < 0 if error | |
1315 | * Return 0 if disabled | |
1316 | * Return 1 if enabled and node is parsed successfully | |
1317 | */ | |
1318 | static int parse_ssp_child_node(struct platform_device *pdev, | |
1319 | struct device_node *dn, | |
1320 | struct cygnus_audio *cygaud, | |
1321 | struct snd_soc_dai_driver *p_dai) | |
1322 | { | |
1323 | struct cygnus_aio_port *aio; | |
1324 | struct cygnus_ssp_regs ssp_regs[3]; | |
1325 | u32 rawval; | |
1326 | int portnum = -1; | |
1327 | enum cygnus_audio_port_type port_type; | |
1328 | ||
1329 | if (of_property_read_u32(dn, "reg", &rawval)) { | |
1330 | dev_err(&pdev->dev, "Missing reg property\n"); | |
1331 | return -EINVAL; | |
1332 | } | |
1333 | ||
1334 | portnum = rawval; | |
1335 | switch (rawval) { | |
1336 | case 0: | |
1337 | ssp_regs[0] = INIT_SSP_REGS(0); | |
1338 | port_type = PORT_TDM; | |
1339 | break; | |
1340 | case 1: | |
1341 | ssp_regs[1] = INIT_SSP_REGS(1); | |
1342 | port_type = PORT_TDM; | |
1343 | break; | |
1344 | case 2: | |
1345 | ssp_regs[2] = INIT_SSP_REGS(2); | |
1346 | port_type = PORT_TDM; | |
1347 | break; | |
1348 | case 3: | |
1349 | port_type = PORT_SPDIF; | |
1350 | break; | |
1351 | default: | |
1352 | dev_err(&pdev->dev, "Bad value for reg %u\n", rawval); | |
1353 | return -EINVAL; | |
1354 | } | |
1355 | ||
1356 | aio = &cygaud->portinfo[portnum]; | |
1357 | aio->cygaud = cygaud; | |
1358 | aio->portnum = portnum; | |
1359 | aio->port_type = port_type; | |
1360 | aio->fsync_width = -1; | |
1361 | ||
1362 | switch (port_type) { | |
1363 | case PORT_TDM: | |
1364 | aio->regs = ssp_regs[portnum]; | |
1365 | *p_dai = cygnus_ssp_dai_info[portnum]; | |
1366 | aio->mode = CYGNUS_SSPMODE_UNKNOWN; | |
1367 | break; | |
1368 | ||
1369 | case PORT_SPDIF: | |
1370 | aio->regs.bf_sourcech_cfg = BF_SRC_CFG3_OFFSET; | |
1371 | aio->regs.bf_sourcech_ctrl = BF_SRC_CTRL3_OFFSET; | |
1372 | aio->regs.i2s_mclk_cfg = SPDIF_MCLK_CFG_OFFSET; | |
1373 | aio->regs.i2s_stream_cfg = SPDIF_STREAM_CFG_OFFSET; | |
1374 | *p_dai = cygnus_spdif_dai_info; | |
1375 | ||
1376 | /* For the purposes of this code SPDIF can be I2S mode */ | |
1377 | aio->mode = CYGNUS_SSPMODE_I2S; | |
1378 | break; | |
1379 | default: | |
1380 | dev_err(&pdev->dev, "Bad value for port_type %d\n", port_type); | |
1381 | return -EINVAL; | |
1382 | } | |
1383 | ||
1384 | dev_dbg(&pdev->dev, "%s portnum = %d\n", __func__, aio->portnum); | |
1385 | aio->streams_on = 0; | |
1386 | aio->cygaud->dev = &pdev->dev; | |
1387 | aio->clk_trace.play_en = false; | |
1388 | aio->clk_trace.cap_en = false; | |
1389 | ||
1390 | audio_ssp_init_portregs(aio); | |
1391 | return 0; | |
1392 | } | |
1393 | ||
1394 | static int audio_clk_init(struct platform_device *pdev, | |
1395 | struct cygnus_audio *cygaud) | |
1396 | { | |
1397 | int i; | |
1398 | char clk_name[PROP_LEN_MAX]; | |
1399 | ||
1400 | for (i = 0; i < ARRAY_SIZE(cygaud->audio_clk); i++) { | |
1401 | snprintf(clk_name, PROP_LEN_MAX, "ch%d_audio", i); | |
1402 | ||
1403 | cygaud->audio_clk[i] = devm_clk_get(&pdev->dev, clk_name); | |
1404 | if (IS_ERR(cygaud->audio_clk[i])) | |
1405 | return PTR_ERR(cygaud->audio_clk[i]); | |
1406 | } | |
1407 | ||
1408 | return 0; | |
1409 | } | |
1410 | ||
1411 | static int cygnus_ssp_probe(struct platform_device *pdev) | |
1412 | { | |
1413 | struct device *dev = &pdev->dev; | |
1414 | struct device_node *child_node; | |
1415 | struct resource *res = pdev->resource; | |
1416 | struct cygnus_audio *cygaud; | |
1417 | int err = -EINVAL; | |
1418 | int node_count; | |
1419 | int active_port_count; | |
1420 | ||
1421 | cygaud = devm_kzalloc(dev, sizeof(struct cygnus_audio), GFP_KERNEL); | |
1422 | if (!cygaud) | |
1423 | return -ENOMEM; | |
1424 | ||
1425 | dev_set_drvdata(dev, cygaud); | |
1426 | ||
1427 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aud"); | |
1428 | cygaud->audio = devm_ioremap_resource(dev, res); | |
1429 | if (IS_ERR(cygaud->audio)) | |
1430 | return PTR_ERR(cygaud->audio); | |
1431 | ||
1432 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "i2s_in"); | |
1433 | cygaud->i2s_in = devm_ioremap_resource(dev, res); | |
1434 | if (IS_ERR(cygaud->i2s_in)) | |
1435 | return PTR_ERR(cygaud->i2s_in); | |
1436 | ||
1437 | /* Tri-state all controlable pins until we know that we need them */ | |
1438 | writel(CYGNUS_SSP_TRISTATE_MASK, | |
1439 | cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE); | |
1440 | ||
1441 | node_count = of_get_child_count(pdev->dev.of_node); | |
1442 | if ((node_count < 1) || (node_count > CYGNUS_MAX_PORTS)) { | |
1443 | dev_err(dev, "child nodes is %d. Must be between 1 and %d\n", | |
1444 | node_count, CYGNUS_MAX_PORTS); | |
1445 | return -EINVAL; | |
1446 | } | |
1447 | ||
1448 | active_port_count = 0; | |
1449 | ||
1450 | for_each_available_child_of_node(pdev->dev.of_node, child_node) { | |
1451 | err = parse_ssp_child_node(pdev, child_node, cygaud, | |
1452 | &cygnus_ssp_dai[active_port_count]); | |
1453 | ||
1454 | /* negative is err, 0 is active and good, 1 is disabled */ | |
1455 | if (err < 0) | |
1456 | return err; | |
1457 | else if (!err) { | |
1458 | dev_dbg(dev, "Activating DAI: %s\n", | |
1459 | cygnus_ssp_dai[active_port_count].name); | |
1460 | active_port_count++; | |
1461 | } | |
1462 | } | |
1463 | ||
1464 | cygaud->dev = dev; | |
1465 | cygaud->active_ports = 0; | |
1466 | ||
1467 | dev_dbg(dev, "Registering %d DAIs\n", active_port_count); | |
1468 | err = snd_soc_register_component(dev, &cygnus_ssp_component, | |
1469 | cygnus_ssp_dai, active_port_count); | |
1470 | if (err) { | |
1471 | dev_err(dev, "snd_soc_register_dai failed\n"); | |
1472 | return err; | |
1473 | } | |
1474 | ||
1475 | cygaud->irq_num = platform_get_irq(pdev, 0); | |
1476 | if (cygaud->irq_num <= 0) { | |
1477 | dev_err(dev, "platform_get_irq failed\n"); | |
1478 | err = cygaud->irq_num; | |
1479 | goto err_irq; | |
1480 | } | |
1481 | ||
1482 | err = audio_clk_init(pdev, cygaud); | |
1483 | if (err) { | |
1484 | dev_err(dev, "audio clock initialization failed\n"); | |
1485 | goto err_irq; | |
1486 | } | |
1487 | ||
1488 | err = cygnus_soc_platform_register(dev, cygaud); | |
1489 | if (err) { | |
1490 | dev_err(dev, "platform reg error %d\n", err); | |
1491 | goto err_irq; | |
1492 | } | |
1493 | ||
1494 | return 0; | |
1495 | ||
1496 | err_irq: | |
1497 | snd_soc_unregister_component(dev); | |
1498 | return err; | |
1499 | } | |
1500 | ||
1501 | static int cygnus_ssp_remove(struct platform_device *pdev) | |
1502 | { | |
1503 | cygnus_soc_platform_unregister(&pdev->dev); | |
1504 | snd_soc_unregister_component(&pdev->dev); | |
1505 | ||
1506 | return 0; | |
1507 | } | |
1508 | ||
1509 | static const struct of_device_id cygnus_ssp_of_match[] = { | |
1510 | { .compatible = "brcm,cygnus-audio" }, | |
1511 | {}, | |
1512 | }; | |
1513 | MODULE_DEVICE_TABLE(of, cygnus_ssp_of_match); | |
1514 | ||
1515 | static struct platform_driver cygnus_ssp_driver = { | |
1516 | .probe = cygnus_ssp_probe, | |
1517 | .remove = cygnus_ssp_remove, | |
1518 | .driver = { | |
1519 | .name = "cygnus-ssp", | |
1520 | .of_match_table = cygnus_ssp_of_match, | |
1521 | }, | |
1522 | }; | |
1523 | ||
1524 | module_platform_driver(cygnus_ssp_driver); | |
1525 | ||
1526 | MODULE_ALIAS("platform:cygnus-ssp"); | |
1527 | MODULE_LICENSE("GPL v2"); | |
1528 | MODULE_AUTHOR("Broadcom"); | |
1529 | MODULE_DESCRIPTION("Cygnus ASoC SSP Interface"); |