1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) ST-Ericsson SA 2012 |
4 | * |
5 | * Author: Ola Lilja <ola.o.lilja@stericsson.com>, |
6 | * Roger Nilsson <roger.xr.nilsson@stericsson.com> |
7 | * for ST-Ericsson. |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/bitops.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/clk.h> |
15 | #include <linux/of.h> |
16 | #include <linux/regulator/consumer.h> |
17 | #include <linux/mfd/dbx500-prcmu.h> |
18 | |
19 | #include <sound/soc.h> |
20 | #include <sound/soc-dai.h> |
21 | #include <sound/dmaengine_pcm.h> |
22 | |
23 | #include "ux500_msp_i2s.h" |
24 | #include "ux500_msp_dai.h" |
25 | #include "ux500_pcm.h" |
26 | |
27 | static int setup_pcm_multichan(struct snd_soc_dai *dai, |
28 | struct ux500_msp_config *msp_config) |
29 | { |
30 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: dai->dev); |
31 | struct msp_multichannel_config *multi = |
32 | &msp_config->multichannel_config; |
33 | |
34 | if (drvdata->slots > 1) { |
35 | msp_config->multichannel_configured = 1; |
36 | |
37 | multi->tx_multichannel_enable = true; |
38 | multi->rx_multichannel_enable = true; |
39 | multi->rx_comparison_enable_mode = MSP_COMPARISON_DISABLED; |
40 | |
41 | multi->tx_channel_0_enable = drvdata->tx_mask; |
42 | multi->tx_channel_1_enable = 0; |
43 | multi->tx_channel_2_enable = 0; |
44 | multi->tx_channel_3_enable = 0; |
45 | |
46 | multi->rx_channel_0_enable = drvdata->rx_mask; |
47 | multi->rx_channel_1_enable = 0; |
48 | multi->rx_channel_2_enable = 0; |
49 | multi->rx_channel_3_enable = 0; |
50 | |
51 | dev_dbg(dai->dev, |
52 | "%s: Multichannel enabled. Slots: %d, TX: %u, RX: %u\n" , |
53 | __func__, drvdata->slots, multi->tx_channel_0_enable, |
54 | multi->rx_channel_0_enable); |
55 | } |
56 | |
57 | return 0; |
58 | } |
59 | |
60 | static int setup_frameper(struct snd_soc_dai *dai, unsigned int rate, |
61 | struct msp_protdesc *prot_desc) |
62 | { |
63 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: dai->dev); |
64 | |
65 | switch (drvdata->slots) { |
66 | case 1: |
67 | switch (rate) { |
68 | case 8000: |
69 | prot_desc->frame_period = |
70 | FRAME_PER_SINGLE_SLOT_8_KHZ; |
71 | break; |
72 | |
73 | case 16000: |
74 | prot_desc->frame_period = |
75 | FRAME_PER_SINGLE_SLOT_16_KHZ; |
76 | break; |
77 | |
78 | case 44100: |
79 | prot_desc->frame_period = |
80 | FRAME_PER_SINGLE_SLOT_44_1_KHZ; |
81 | break; |
82 | |
83 | case 48000: |
84 | prot_desc->frame_period = |
85 | FRAME_PER_SINGLE_SLOT_48_KHZ; |
86 | break; |
87 | |
88 | default: |
89 | dev_err(dai->dev, |
90 | "%s: Error: Unsupported sample-rate (freq = %d)!\n" , |
91 | __func__, rate); |
92 | return -EINVAL; |
93 | } |
94 | break; |
95 | |
96 | case 2: |
97 | prot_desc->frame_period = FRAME_PER_2_SLOTS; |
98 | break; |
99 | |
100 | case 8: |
101 | prot_desc->frame_period = FRAME_PER_8_SLOTS; |
102 | break; |
103 | |
104 | case 16: |
105 | prot_desc->frame_period = FRAME_PER_16_SLOTS; |
106 | break; |
107 | default: |
108 | dev_err(dai->dev, |
109 | "%s: Error: Unsupported slot-count (slots = %d)!\n" , |
110 | __func__, drvdata->slots); |
111 | return -EINVAL; |
112 | } |
113 | |
114 | prot_desc->clocks_per_frame = |
115 | prot_desc->frame_period+1; |
116 | |
117 | dev_dbg(dai->dev, "%s: Clocks per frame: %u\n" , |
118 | __func__, |
119 | prot_desc->clocks_per_frame); |
120 | |
121 | return 0; |
122 | } |
123 | |
124 | static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate, |
125 | struct msp_protdesc *prot_desc) |
126 | { |
127 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: dai->dev); |
128 | |
129 | u32 frame_length = MSP_FRAME_LEN_1; |
130 | |
131 | prot_desc->frame_width = 0; |
132 | |
133 | switch (drvdata->slots) { |
134 | case 1: |
135 | frame_length = MSP_FRAME_LEN_1; |
136 | break; |
137 | |
138 | case 2: |
139 | frame_length = MSP_FRAME_LEN_2; |
140 | break; |
141 | |
142 | case 8: |
143 | frame_length = MSP_FRAME_LEN_8; |
144 | break; |
145 | |
146 | case 16: |
147 | frame_length = MSP_FRAME_LEN_16; |
148 | break; |
149 | default: |
150 | dev_err(dai->dev, |
151 | "%s: Error: Unsupported slot-count (slots = %d)!\n" , |
152 | __func__, drvdata->slots); |
153 | return -EINVAL; |
154 | } |
155 | |
156 | prot_desc->tx_frame_len_1 = frame_length; |
157 | prot_desc->rx_frame_len_1 = frame_length; |
158 | prot_desc->tx_frame_len_2 = frame_length; |
159 | prot_desc->rx_frame_len_2 = frame_length; |
160 | |
161 | prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16; |
162 | prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16; |
163 | prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16; |
164 | prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16; |
165 | |
166 | return setup_frameper(dai, rate, prot_desc); |
167 | } |
168 | |
169 | static int setup_clocking(struct snd_soc_dai *dai, |
170 | unsigned int fmt, |
171 | struct ux500_msp_config *msp_config) |
172 | { |
173 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
174 | case SND_SOC_DAIFMT_NB_NF: |
175 | break; |
176 | |
177 | case SND_SOC_DAIFMT_NB_IF: |
178 | msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT; |
179 | msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT; |
180 | |
181 | break; |
182 | |
183 | default: |
184 | dev_err(dai->dev, |
185 | "%s: Error: Unsupported inversion (fmt = 0x%x)!\n" , |
186 | __func__, fmt); |
187 | |
188 | return -EINVAL; |
189 | } |
190 | |
191 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
192 | case SND_SOC_DAIFMT_BC_FC: |
193 | dev_dbg(dai->dev, "%s: Codec is master.\n" , __func__); |
194 | |
195 | msp_config->iodelay = 0x20; |
196 | msp_config->rx_fsync_sel = 0; |
197 | msp_config->tx_fsync_sel = 1 << TFSSEL_SHIFT; |
198 | msp_config->tx_clk_sel = 0; |
199 | msp_config->rx_clk_sel = 0; |
200 | msp_config->srg_clk_sel = 0x2 << SCKSEL_SHIFT; |
201 | |
202 | break; |
203 | |
204 | case SND_SOC_DAIFMT_BP_FP: |
205 | dev_dbg(dai->dev, "%s: Codec is slave.\n" , __func__); |
206 | |
207 | msp_config->tx_clk_sel = TX_CLK_SEL_SRG; |
208 | msp_config->tx_fsync_sel = TX_SYNC_SRG_PROG; |
209 | msp_config->rx_clk_sel = RX_CLK_SEL_SRG; |
210 | msp_config->rx_fsync_sel = RX_SYNC_SRG; |
211 | msp_config->srg_clk_sel = 1 << SCKSEL_SHIFT; |
212 | |
213 | break; |
214 | |
215 | default: |
216 | dev_err(dai->dev, "%s: Error: Unsupported master (fmt = 0x%x)!\n" , |
217 | __func__, fmt); |
218 | |
219 | return -EINVAL; |
220 | } |
221 | |
222 | return 0; |
223 | } |
224 | |
225 | static int setup_pcm_protdesc(struct snd_soc_dai *dai, |
226 | unsigned int fmt, |
227 | struct msp_protdesc *prot_desc) |
228 | { |
229 | prot_desc->rx_phase_mode = MSP_SINGLE_PHASE; |
230 | prot_desc->tx_phase_mode = MSP_SINGLE_PHASE; |
231 | prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE; |
232 | prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE; |
233 | prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST; |
234 | prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST; |
235 | prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_HI); |
236 | prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_HI << RFSPOL_SHIFT; |
237 | |
238 | if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A) { |
239 | dev_dbg(dai->dev, "%s: DSP_A.\n" , __func__); |
240 | prot_desc->rx_clk_pol = MSP_RISING_EDGE; |
241 | prot_desc->tx_clk_pol = MSP_FALLING_EDGE; |
242 | |
243 | prot_desc->rx_data_delay = MSP_DELAY_1; |
244 | prot_desc->tx_data_delay = MSP_DELAY_1; |
245 | } else { |
246 | dev_dbg(dai->dev, "%s: DSP_B.\n" , __func__); |
247 | prot_desc->rx_clk_pol = MSP_FALLING_EDGE; |
248 | prot_desc->tx_clk_pol = MSP_RISING_EDGE; |
249 | |
250 | prot_desc->rx_data_delay = MSP_DELAY_0; |
251 | prot_desc->tx_data_delay = MSP_DELAY_0; |
252 | } |
253 | |
254 | prot_desc->rx_half_word_swap = MSP_SWAP_NONE; |
255 | prot_desc->tx_half_word_swap = MSP_SWAP_NONE; |
256 | prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR; |
257 | prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR; |
258 | prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE; |
259 | |
260 | return 0; |
261 | } |
262 | |
263 | static int setup_i2s_protdesc(struct msp_protdesc *prot_desc) |
264 | { |
265 | prot_desc->rx_phase_mode = MSP_DUAL_PHASE; |
266 | prot_desc->tx_phase_mode = MSP_DUAL_PHASE; |
267 | prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC; |
268 | prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC; |
269 | prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST; |
270 | prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST; |
271 | prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_LO); |
272 | prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_LO << RFSPOL_SHIFT; |
273 | |
274 | prot_desc->rx_frame_len_1 = MSP_FRAME_LEN_1; |
275 | prot_desc->rx_frame_len_2 = MSP_FRAME_LEN_1; |
276 | prot_desc->tx_frame_len_1 = MSP_FRAME_LEN_1; |
277 | prot_desc->tx_frame_len_2 = MSP_FRAME_LEN_1; |
278 | prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16; |
279 | prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16; |
280 | prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16; |
281 | prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16; |
282 | |
283 | prot_desc->rx_clk_pol = MSP_RISING_EDGE; |
284 | prot_desc->tx_clk_pol = MSP_FALLING_EDGE; |
285 | |
286 | prot_desc->rx_data_delay = MSP_DELAY_0; |
287 | prot_desc->tx_data_delay = MSP_DELAY_0; |
288 | |
289 | prot_desc->tx_half_word_swap = MSP_SWAP_NONE; |
290 | prot_desc->rx_half_word_swap = MSP_SWAP_NONE; |
291 | prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR; |
292 | prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR; |
293 | prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE; |
294 | |
295 | return 0; |
296 | } |
297 | |
298 | static int setup_msp_config(struct snd_pcm_substream *substream, |
299 | struct snd_soc_dai *dai, |
300 | struct ux500_msp_config *msp_config) |
301 | { |
302 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: dai->dev); |
303 | struct msp_protdesc *prot_desc = &msp_config->protdesc; |
304 | struct snd_pcm_runtime *runtime = substream->runtime; |
305 | unsigned int fmt = drvdata->fmt; |
306 | int ret; |
307 | |
308 | memset(msp_config, 0, sizeof(*msp_config)); |
309 | |
310 | msp_config->f_inputclk = drvdata->master_clk; |
311 | |
312 | msp_config->tx_fifo_config = TX_FIFO_ENABLE; |
313 | msp_config->rx_fifo_config = RX_FIFO_ENABLE; |
314 | msp_config->def_elem_len = 1; |
315 | msp_config->direction = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? |
316 | MSP_DIR_TX : MSP_DIR_RX; |
317 | msp_config->data_size = MSP_DATA_BITS_32; |
318 | msp_config->frame_freq = runtime->rate; |
319 | |
320 | dev_dbg(dai->dev, "%s: f_inputclk = %u, frame_freq = %u.\n" , |
321 | __func__, msp_config->f_inputclk, msp_config->frame_freq); |
322 | /* To avoid division by zero */ |
323 | prot_desc->clocks_per_frame = 1; |
324 | |
325 | dev_dbg(dai->dev, "%s: rate: %u, channels: %d.\n" , __func__, |
326 | runtime->rate, runtime->channels); |
327 | switch (fmt & |
328 | (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK)) { |
329 | case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_BP_FP: |
330 | dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n" , __func__); |
331 | |
332 | msp_config->default_protdesc = 1; |
333 | msp_config->protocol = MSP_I2S_PROTOCOL; |
334 | break; |
335 | |
336 | case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_BC_FC: |
337 | dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n" , __func__); |
338 | |
339 | msp_config->data_size = MSP_DATA_BITS_16; |
340 | msp_config->protocol = MSP_I2S_PROTOCOL; |
341 | |
342 | ret = setup_i2s_protdesc(prot_desc); |
343 | if (ret < 0) |
344 | return ret; |
345 | |
346 | break; |
347 | |
348 | case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_BP_FP: |
349 | case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_BC_FC: |
350 | case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_BP_FP: |
351 | case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_BC_FC: |
352 | dev_dbg(dai->dev, "%s: PCM format.\n" , __func__); |
353 | |
354 | msp_config->data_size = MSP_DATA_BITS_16; |
355 | msp_config->protocol = MSP_PCM_PROTOCOL; |
356 | |
357 | ret = setup_pcm_protdesc(dai, fmt, prot_desc); |
358 | if (ret < 0) |
359 | return ret; |
360 | |
361 | ret = setup_pcm_multichan(dai, msp_config); |
362 | if (ret < 0) |
363 | return ret; |
364 | |
365 | ret = setup_pcm_framing(dai, rate: runtime->rate, prot_desc); |
366 | if (ret < 0) |
367 | return ret; |
368 | |
369 | break; |
370 | |
371 | default: |
372 | dev_err(dai->dev, "%s: Error: Unsupported format (%d)!\n" , |
373 | __func__, fmt); |
374 | return -EINVAL; |
375 | } |
376 | |
377 | return setup_clocking(dai, fmt, msp_config); |
378 | } |
379 | |
380 | static int ux500_msp_dai_startup(struct snd_pcm_substream *substream, |
381 | struct snd_soc_dai *dai) |
382 | { |
383 | int ret = 0; |
384 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: dai->dev); |
385 | |
386 | dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n" , __func__, dai->id, |
387 | snd_pcm_stream_str(substream)); |
388 | |
389 | /* Enable regulator */ |
390 | ret = regulator_enable(regulator: drvdata->reg_vape); |
391 | if (ret != 0) { |
392 | dev_err(drvdata->msp->dev, |
393 | "%s: Failed to enable regulator!\n" , __func__); |
394 | return ret; |
395 | } |
396 | |
397 | /* Prepare and enable clocks */ |
398 | dev_dbg(dai->dev, "%s: Enabling MSP-clocks.\n" , __func__); |
399 | ret = clk_prepare_enable(clk: drvdata->pclk); |
400 | if (ret) { |
401 | dev_err(drvdata->msp->dev, |
402 | "%s: Failed to prepare/enable pclk!\n" , __func__); |
403 | goto err_pclk; |
404 | } |
405 | |
406 | ret = clk_prepare_enable(clk: drvdata->clk); |
407 | if (ret) { |
408 | dev_err(drvdata->msp->dev, |
409 | "%s: Failed to prepare/enable clk!\n" , __func__); |
410 | goto err_clk; |
411 | } |
412 | |
413 | return ret; |
414 | err_clk: |
415 | clk_disable_unprepare(clk: drvdata->pclk); |
416 | err_pclk: |
417 | regulator_disable(regulator: drvdata->reg_vape); |
418 | return ret; |
419 | } |
420 | |
421 | static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream, |
422 | struct snd_soc_dai *dai) |
423 | { |
424 | int ret; |
425 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: dai->dev); |
426 | bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); |
427 | |
428 | dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n" , __func__, dai->id, |
429 | snd_pcm_stream_str(substream)); |
430 | |
431 | if (drvdata->vape_opp_constraint == 1) { |
432 | prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, |
433 | name: "ux500_msp_i2s" , new_value: 50); |
434 | drvdata->vape_opp_constraint = 0; |
435 | } |
436 | |
437 | if (ux500_msp_i2s_close(msp: drvdata->msp, |
438 | dir: is_playback ? MSP_DIR_TX : MSP_DIR_RX)) { |
439 | dev_err(dai->dev, |
440 | "%s: Error: MSP %d (%s): Unable to close i2s.\n" , |
441 | __func__, dai->id, snd_pcm_stream_str(substream)); |
442 | } |
443 | |
444 | /* Disable and unprepare clocks */ |
445 | clk_disable_unprepare(clk: drvdata->clk); |
446 | clk_disable_unprepare(clk: drvdata->pclk); |
447 | |
448 | /* Disable regulator */ |
449 | ret = regulator_disable(regulator: drvdata->reg_vape); |
450 | if (ret < 0) |
451 | dev_err(dai->dev, |
452 | "%s: ERROR: Failed to disable regulator (%d)!\n" , |
453 | __func__, ret); |
454 | } |
455 | |
456 | static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream, |
457 | struct snd_soc_dai *dai) |
458 | { |
459 | int ret = 0; |
460 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: dai->dev); |
461 | struct snd_pcm_runtime *runtime = substream->runtime; |
462 | struct ux500_msp_config msp_config; |
463 | |
464 | dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (rate = %d).\n" , __func__, |
465 | dai->id, snd_pcm_stream_str(substream), runtime->rate); |
466 | |
467 | setup_msp_config(substream, dai, msp_config: &msp_config); |
468 | |
469 | ret = ux500_msp_i2s_open(msp: drvdata->msp, config: &msp_config); |
470 | if (ret < 0) { |
471 | dev_err(dai->dev, "%s: Error: msp_setup failed (ret = %d)!\n" , |
472 | __func__, ret); |
473 | return ret; |
474 | } |
475 | |
476 | /* Set OPP-level */ |
477 | if ((drvdata->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) && |
478 | (drvdata->msp->f_bitclk > 19200000)) { |
479 | /* If the bit-clock is higher than 19.2MHz, Vape should be |
480 | * run in 100% OPP. Only when bit-clock is used (MSP master) |
481 | */ |
482 | prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, |
483 | name: "ux500-msp-i2s" , new_value: 100); |
484 | drvdata->vape_opp_constraint = 1; |
485 | } else { |
486 | prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, |
487 | name: "ux500-msp-i2s" , new_value: 50); |
488 | drvdata->vape_opp_constraint = 0; |
489 | } |
490 | |
491 | return ret; |
492 | } |
493 | |
494 | static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, |
495 | struct snd_pcm_hw_params *params, |
496 | struct snd_soc_dai *dai) |
497 | { |
498 | unsigned int mask, slots_active; |
499 | struct snd_pcm_runtime *runtime = substream->runtime; |
500 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: dai->dev); |
501 | |
502 | dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n" , |
503 | __func__, dai->id, snd_pcm_stream_str(substream)); |
504 | |
505 | switch (drvdata->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
506 | case SND_SOC_DAIFMT_I2S: |
507 | snd_pcm_hw_constraint_minmax(runtime, |
508 | SNDRV_PCM_HW_PARAM_CHANNELS, |
509 | min: 1, max: 2); |
510 | break; |
511 | |
512 | case SND_SOC_DAIFMT_DSP_B: |
513 | case SND_SOC_DAIFMT_DSP_A: |
514 | mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? |
515 | drvdata->tx_mask : |
516 | drvdata->rx_mask; |
517 | |
518 | slots_active = hweight32(mask); |
519 | dev_dbg(dai->dev, "TDM-slots active: %d" , slots_active); |
520 | |
521 | snd_pcm_hw_constraint_single(runtime, |
522 | SNDRV_PCM_HW_PARAM_CHANNELS, |
523 | val: slots_active); |
524 | break; |
525 | |
526 | default: |
527 | dev_err(dai->dev, |
528 | "%s: Error: Unsupported protocol (fmt = 0x%x)!\n" , |
529 | __func__, drvdata->fmt); |
530 | return -EINVAL; |
531 | } |
532 | |
533 | return 0; |
534 | } |
535 | |
536 | static int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai, |
537 | unsigned int fmt) |
538 | { |
539 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: dai->dev); |
540 | |
541 | dev_dbg(dai->dev, "%s: MSP %d: Enter.\n" , __func__, dai->id); |
542 | |
543 | switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | |
544 | SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK)) { |
545 | case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_BP_FP: |
546 | case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_BC_FC: |
547 | case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_BP_FP: |
548 | case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_BC_FC: |
549 | case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_BP_FP: |
550 | case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_BC_FC: |
551 | break; |
552 | |
553 | default: |
554 | dev_err(dai->dev, |
555 | "%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n" , |
556 | __func__, drvdata->fmt); |
557 | return -EINVAL; |
558 | } |
559 | |
560 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
561 | case SND_SOC_DAIFMT_NB_NF: |
562 | case SND_SOC_DAIFMT_NB_IF: |
563 | case SND_SOC_DAIFMT_IB_IF: |
564 | break; |
565 | |
566 | default: |
567 | dev_err(dai->dev, |
568 | "%s: Error: Unsupported inversion (fmt = 0x%x)!\n" , |
569 | __func__, drvdata->fmt); |
570 | return -EINVAL; |
571 | } |
572 | |
573 | drvdata->fmt = fmt; |
574 | return 0; |
575 | } |
576 | |
577 | static int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai, |
578 | unsigned int tx_mask, |
579 | unsigned int rx_mask, |
580 | int slots, int slot_width) |
581 | { |
582 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: dai->dev); |
583 | unsigned int cap; |
584 | |
585 | switch (slots) { |
586 | case 1: |
587 | cap = 0x01; |
588 | break; |
589 | case 2: |
590 | cap = 0x03; |
591 | break; |
592 | case 8: |
593 | cap = 0xFF; |
594 | break; |
595 | case 16: |
596 | cap = 0xFFFF; |
597 | break; |
598 | default: |
599 | dev_err(dai->dev, "%s: Error: Unsupported slot-count (%d)!\n" , |
600 | __func__, slots); |
601 | return -EINVAL; |
602 | } |
603 | drvdata->slots = slots; |
604 | |
605 | if (!(slot_width == 16)) { |
606 | dev_err(dai->dev, "%s: Error: Unsupported slot-width (%d)!\n" , |
607 | __func__, slot_width); |
608 | return -EINVAL; |
609 | } |
610 | drvdata->slot_width = slot_width; |
611 | |
612 | drvdata->tx_mask = tx_mask & cap; |
613 | drvdata->rx_mask = rx_mask & cap; |
614 | |
615 | return 0; |
616 | } |
617 | |
618 | static int ux500_msp_dai_set_dai_sysclk(struct snd_soc_dai *dai, |
619 | int clk_id, unsigned int freq, int dir) |
620 | { |
621 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: dai->dev); |
622 | |
623 | dev_dbg(dai->dev, "%s: MSP %d: Enter. clk-id: %d, freq: %u.\n" , |
624 | __func__, dai->id, clk_id, freq); |
625 | |
626 | switch (clk_id) { |
627 | case UX500_MSP_MASTER_CLOCK: |
628 | drvdata->master_clk = freq; |
629 | break; |
630 | |
631 | default: |
632 | dev_err(dai->dev, "%s: MSP %d: Invalid clk-id (%d)!\n" , |
633 | __func__, dai->id, clk_id); |
634 | return -EINVAL; |
635 | } |
636 | |
637 | return 0; |
638 | } |
639 | |
640 | static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream, |
641 | int cmd, struct snd_soc_dai *dai) |
642 | { |
643 | int ret = 0; |
644 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: dai->dev); |
645 | |
646 | dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (msp->id = %d, cmd = %d).\n" , |
647 | __func__, dai->id, snd_pcm_stream_str(substream), |
648 | (int)drvdata->msp->id, cmd); |
649 | |
650 | ret = ux500_msp_i2s_trigger(msp: drvdata->msp, cmd, direction: substream->stream); |
651 | |
652 | return ret; |
653 | } |
654 | |
655 | static int ux500_msp_dai_of_probe(struct snd_soc_dai *dai) |
656 | { |
657 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: dai->dev); |
658 | struct snd_dmaengine_dai_dma_data *playback_dma_data; |
659 | struct snd_dmaengine_dai_dma_data *capture_dma_data; |
660 | |
661 | playback_dma_data = devm_kzalloc(dev: dai->dev, |
662 | size: sizeof(*playback_dma_data), |
663 | GFP_KERNEL); |
664 | if (!playback_dma_data) |
665 | return -ENOMEM; |
666 | |
667 | capture_dma_data = devm_kzalloc(dev: dai->dev, |
668 | size: sizeof(*capture_dma_data), |
669 | GFP_KERNEL); |
670 | if (!capture_dma_data) |
671 | return -ENOMEM; |
672 | |
673 | playback_dma_data->addr = drvdata->msp->tx_rx_addr; |
674 | capture_dma_data->addr = drvdata->msp->tx_rx_addr; |
675 | |
676 | playback_dma_data->maxburst = 4; |
677 | capture_dma_data->maxburst = 4; |
678 | |
679 | snd_soc_dai_init_dma_data(dai, playback: playback_dma_data, capture: capture_dma_data); |
680 | |
681 | return 0; |
682 | } |
683 | |
684 | static const struct snd_soc_dai_ops ux500_msp_dai_ops[] = { |
685 | { |
686 | .probe = ux500_msp_dai_of_probe, |
687 | .set_sysclk = ux500_msp_dai_set_dai_sysclk, |
688 | .set_fmt = ux500_msp_dai_set_dai_fmt, |
689 | .set_tdm_slot = ux500_msp_dai_set_tdm_slot, |
690 | .startup = ux500_msp_dai_startup, |
691 | .shutdown = ux500_msp_dai_shutdown, |
692 | .prepare = ux500_msp_dai_prepare, |
693 | .trigger = ux500_msp_dai_trigger, |
694 | .hw_params = ux500_msp_dai_hw_params, |
695 | } |
696 | }; |
697 | |
698 | static struct snd_soc_dai_driver ux500_msp_dai_drv = { |
699 | .playback.channels_min = UX500_MSP_MIN_CHANNELS, |
700 | .playback.channels_max = UX500_MSP_MAX_CHANNELS, |
701 | .playback.rates = UX500_I2S_RATES, |
702 | .playback.formats = UX500_I2S_FORMATS, |
703 | .capture.channels_min = UX500_MSP_MIN_CHANNELS, |
704 | .capture.channels_max = UX500_MSP_MAX_CHANNELS, |
705 | .capture.rates = UX500_I2S_RATES, |
706 | .capture.formats = UX500_I2S_FORMATS, |
707 | .ops = ux500_msp_dai_ops, |
708 | }; |
709 | |
710 | static const struct snd_soc_component_driver ux500_msp_component = { |
711 | .name = "ux500-msp" , |
712 | .legacy_dai_naming = 1, |
713 | }; |
714 | |
715 | |
716 | static int ux500_msp_drv_probe(struct platform_device *pdev) |
717 | { |
718 | struct ux500_msp_i2s_drvdata *drvdata; |
719 | int ret = 0; |
720 | |
721 | drvdata = devm_kzalloc(dev: &pdev->dev, |
722 | size: sizeof(struct ux500_msp_i2s_drvdata), |
723 | GFP_KERNEL); |
724 | if (!drvdata) |
725 | return -ENOMEM; |
726 | |
727 | drvdata->fmt = 0; |
728 | drvdata->slots = 1; |
729 | drvdata->tx_mask = 0x01; |
730 | drvdata->rx_mask = 0x01; |
731 | drvdata->slot_width = 16; |
732 | drvdata->master_clk = MSP_INPUT_FREQ_APB; |
733 | |
734 | drvdata->reg_vape = devm_regulator_get(dev: &pdev->dev, id: "v-ape" ); |
735 | if (IS_ERR(ptr: drvdata->reg_vape)) { |
736 | ret = (int)PTR_ERR(ptr: drvdata->reg_vape); |
737 | dev_err(&pdev->dev, |
738 | "%s: ERROR: Failed to get Vape supply (%d)!\n" , |
739 | __func__, ret); |
740 | return ret; |
741 | } |
742 | prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, name: (char *)pdev->name, value: 50); |
743 | |
744 | drvdata->pclk = devm_clk_get(dev: &pdev->dev, id: "apb_pclk" ); |
745 | if (IS_ERR(ptr: drvdata->pclk)) { |
746 | ret = (int)PTR_ERR(ptr: drvdata->pclk); |
747 | dev_err(&pdev->dev, |
748 | "%s: ERROR: devm_clk_get of pclk failed (%d)!\n" , |
749 | __func__, ret); |
750 | return ret; |
751 | } |
752 | |
753 | drvdata->clk = devm_clk_get(dev: &pdev->dev, NULL); |
754 | if (IS_ERR(ptr: drvdata->clk)) { |
755 | ret = (int)PTR_ERR(ptr: drvdata->clk); |
756 | dev_err(&pdev->dev, |
757 | "%s: ERROR: devm_clk_get failed (%d)!\n" , |
758 | __func__, ret); |
759 | return ret; |
760 | } |
761 | |
762 | ret = ux500_msp_i2s_init_msp(pdev, msp_p: &drvdata->msp); |
763 | if (!drvdata->msp) { |
764 | dev_err(&pdev->dev, |
765 | "%s: ERROR: Failed to init MSP-struct (%d)!" , |
766 | __func__, ret); |
767 | return ret; |
768 | } |
769 | dev_set_drvdata(dev: &pdev->dev, data: drvdata); |
770 | |
771 | ret = snd_soc_register_component(dev: &pdev->dev, component_driver: &ux500_msp_component, |
772 | dai_drv: &ux500_msp_dai_drv, num_dai: 1); |
773 | if (ret < 0) { |
774 | dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n" , |
775 | __func__, drvdata->msp->id); |
776 | return ret; |
777 | } |
778 | |
779 | ret = ux500_pcm_register_platform(pdev); |
780 | if (ret < 0) { |
781 | dev_err(&pdev->dev, |
782 | "Error: %s: Failed to register PCM platform device!\n" , |
783 | __func__); |
784 | goto err_reg_plat; |
785 | } |
786 | |
787 | return 0; |
788 | |
789 | err_reg_plat: |
790 | snd_soc_unregister_component(dev: &pdev->dev); |
791 | return ret; |
792 | } |
793 | |
794 | static void ux500_msp_drv_remove(struct platform_device *pdev) |
795 | { |
796 | struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dev: &pdev->dev); |
797 | |
798 | ux500_pcm_unregister_platform(pdev); |
799 | |
800 | snd_soc_unregister_component(dev: &pdev->dev); |
801 | |
802 | prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, name: "ux500_msp_i2s" ); |
803 | |
804 | ux500_msp_i2s_cleanup_msp(pdev, msp: drvdata->msp); |
805 | } |
806 | |
807 | static const struct of_device_id ux500_msp_i2s_match[] = { |
808 | { .compatible = "stericsson,ux500-msp-i2s" , }, |
809 | {}, |
810 | }; |
811 | MODULE_DEVICE_TABLE(of, ux500_msp_i2s_match); |
812 | |
813 | static struct platform_driver msp_i2s_driver = { |
814 | .driver = { |
815 | .name = "ux500-msp-i2s" , |
816 | .of_match_table = ux500_msp_i2s_match, |
817 | }, |
818 | .probe = ux500_msp_drv_probe, |
819 | .remove_new = ux500_msp_drv_remove, |
820 | }; |
821 | module_platform_driver(msp_i2s_driver); |
822 | |
823 | MODULE_LICENSE("GPL v2" ); |
824 | |