1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Xilinx ASoC audio formatter support |
4 | // |
5 | // Copyright (C) 2018 Xilinx, Inc. |
6 | // |
7 | // Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com> |
8 | |
9 | #include <linux/clk.h> |
10 | #include <linux/io.h> |
11 | #include <linux/module.h> |
12 | #include <linux/of_address.h> |
13 | #include <linux/of_irq.h> |
14 | #include <linux/sizes.h> |
15 | |
16 | #include <sound/asoundef.h> |
17 | #include <sound/soc.h> |
18 | #include <sound/pcm_params.h> |
19 | |
20 | #define DRV_NAME "xlnx_formatter_pcm" |
21 | |
22 | #define XLNX_S2MM_OFFSET 0 |
23 | #define XLNX_MM2S_OFFSET 0x100 |
24 | |
25 | #define XLNX_AUD_CORE_CONFIG 0x4 |
26 | #define XLNX_AUD_CTRL 0x10 |
27 | #define XLNX_AUD_STS 0x14 |
28 | |
29 | #define AUD_CTRL_RESET_MASK BIT(1) |
30 | #define AUD_CFG_MM2S_MASK BIT(15) |
31 | #define AUD_CFG_S2MM_MASK BIT(31) |
32 | |
33 | #define XLNX_AUD_FS_MULTIPLIER 0x18 |
34 | #define XLNX_AUD_PERIOD_CONFIG 0x1C |
35 | #define XLNX_AUD_BUFF_ADDR_LSB 0x20 |
36 | #define XLNX_AUD_BUFF_ADDR_MSB 0x24 |
37 | #define XLNX_AUD_XFER_COUNT 0x28 |
38 | #define XLNX_AUD_CH_STS_START 0x2C |
39 | #define XLNX_BYTES_PER_CH 0x44 |
40 | #define XLNX_AUD_ALIGN_BYTES 64 |
41 | |
42 | #define AUD_STS_IOC_IRQ_MASK BIT(31) |
43 | #define AUD_STS_CH_STS_MASK BIT(29) |
44 | #define AUD_CTRL_IOC_IRQ_MASK BIT(13) |
45 | #define AUD_CTRL_TOUT_IRQ_MASK BIT(14) |
46 | #define AUD_CTRL_DMA_EN_MASK BIT(0) |
47 | |
48 | #define CFG_MM2S_CH_MASK GENMASK(11, 8) |
49 | #define CFG_MM2S_CH_SHIFT 8 |
50 | #define CFG_MM2S_XFER_MASK GENMASK(14, 13) |
51 | #define CFG_MM2S_XFER_SHIFT 13 |
52 | #define CFG_MM2S_PKG_MASK BIT(12) |
53 | |
54 | #define CFG_S2MM_CH_MASK GENMASK(27, 24) |
55 | #define CFG_S2MM_CH_SHIFT 24 |
56 | #define CFG_S2MM_XFER_MASK GENMASK(30, 29) |
57 | #define CFG_S2MM_XFER_SHIFT 29 |
58 | #define CFG_S2MM_PKG_MASK BIT(28) |
59 | |
60 | #define AUD_CTRL_DATA_WIDTH_SHIFT 16 |
61 | #define AUD_CTRL_ACTIVE_CH_SHIFT 19 |
62 | #define PERIOD_CFG_PERIODS_SHIFT 16 |
63 | |
64 | #define PERIODS_MIN 2 |
65 | #define PERIODS_MAX 6 |
66 | #define PERIOD_BYTES_MIN 192 |
67 | #define PERIOD_BYTES_MAX (50 * 1024) |
68 | #define XLNX_PARAM_UNKNOWN 0 |
69 | |
70 | enum bit_depth { |
71 | BIT_DEPTH_8, |
72 | BIT_DEPTH_16, |
73 | BIT_DEPTH_20, |
74 | BIT_DEPTH_24, |
75 | BIT_DEPTH_32, |
76 | }; |
77 | |
78 | struct xlnx_pcm_drv_data { |
79 | void __iomem *mmio; |
80 | bool s2mm_presence; |
81 | bool mm2s_presence; |
82 | int s2mm_irq; |
83 | int mm2s_irq; |
84 | struct snd_pcm_substream *play_stream; |
85 | struct snd_pcm_substream *capture_stream; |
86 | struct clk *axi_clk; |
87 | unsigned int sysclk; |
88 | }; |
89 | |
90 | /* |
91 | * struct xlnx_pcm_stream_param - stream configuration |
92 | * @mmio: base address offset |
93 | * @interleaved: audio channels arrangement in buffer |
94 | * @xfer_mode: data formatting mode during transfer |
95 | * @ch_limit: Maximum channels supported |
96 | * @buffer_size: stream ring buffer size |
97 | */ |
98 | struct xlnx_pcm_stream_param { |
99 | void __iomem *mmio; |
100 | bool interleaved; |
101 | u32 xfer_mode; |
102 | u32 ch_limit; |
103 | u64 buffer_size; |
104 | }; |
105 | |
106 | static const struct snd_pcm_hardware xlnx_pcm_hardware = { |
107 | .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
108 | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_PAUSE | |
109 | SNDRV_PCM_INFO_RESUME, |
110 | .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | |
111 | SNDRV_PCM_FMTBIT_S24_LE, |
112 | .channels_min = 2, |
113 | .channels_max = 2, |
114 | .rates = SNDRV_PCM_RATE_8000_192000, |
115 | .rate_min = 8000, |
116 | .rate_max = 192000, |
117 | .buffer_bytes_max = PERIODS_MAX * PERIOD_BYTES_MAX, |
118 | .period_bytes_min = PERIOD_BYTES_MIN, |
119 | .period_bytes_max = PERIOD_BYTES_MAX, |
120 | .periods_min = PERIODS_MIN, |
121 | .periods_max = PERIODS_MAX, |
122 | }; |
123 | |
124 | enum { |
125 | AES_TO_AES, |
126 | AES_TO_PCM, |
127 | PCM_TO_PCM, |
128 | PCM_TO_AES |
129 | }; |
130 | |
131 | static void xlnx_parse_aes_params(u32 chsts_reg1_val, u32 chsts_reg2_val, |
132 | struct device *dev) |
133 | { |
134 | u32 padded, srate, bit_depth, status[2]; |
135 | |
136 | if (chsts_reg1_val & IEC958_AES0_PROFESSIONAL) { |
137 | status[0] = chsts_reg1_val & 0xff; |
138 | status[1] = (chsts_reg1_val >> 16) & 0xff; |
139 | |
140 | switch (status[0] & IEC958_AES0_PRO_FS) { |
141 | case IEC958_AES0_PRO_FS_44100: |
142 | srate = 44100; |
143 | break; |
144 | case IEC958_AES0_PRO_FS_48000: |
145 | srate = 48000; |
146 | break; |
147 | case IEC958_AES0_PRO_FS_32000: |
148 | srate = 32000; |
149 | break; |
150 | case IEC958_AES0_PRO_FS_NOTID: |
151 | default: |
152 | srate = XLNX_PARAM_UNKNOWN; |
153 | break; |
154 | } |
155 | |
156 | switch (status[1] & IEC958_AES2_PRO_SBITS) { |
157 | case IEC958_AES2_PRO_WORDLEN_NOTID: |
158 | case IEC958_AES2_PRO_SBITS_20: |
159 | padded = 0; |
160 | break; |
161 | case IEC958_AES2_PRO_SBITS_24: |
162 | padded = 4; |
163 | break; |
164 | default: |
165 | bit_depth = XLNX_PARAM_UNKNOWN; |
166 | goto log_params; |
167 | } |
168 | |
169 | switch (status[1] & IEC958_AES2_PRO_WORDLEN) { |
170 | case IEC958_AES2_PRO_WORDLEN_20_16: |
171 | bit_depth = 16 + padded; |
172 | break; |
173 | case IEC958_AES2_PRO_WORDLEN_22_18: |
174 | bit_depth = 18 + padded; |
175 | break; |
176 | case IEC958_AES2_PRO_WORDLEN_23_19: |
177 | bit_depth = 19 + padded; |
178 | break; |
179 | case IEC958_AES2_PRO_WORDLEN_24_20: |
180 | bit_depth = 20 + padded; |
181 | break; |
182 | case IEC958_AES2_PRO_WORDLEN_NOTID: |
183 | default: |
184 | bit_depth = XLNX_PARAM_UNKNOWN; |
185 | break; |
186 | } |
187 | |
188 | } else { |
189 | status[0] = (chsts_reg1_val >> 24) & 0xff; |
190 | status[1] = chsts_reg2_val & 0xff; |
191 | |
192 | switch (status[0] & IEC958_AES3_CON_FS) { |
193 | case IEC958_AES3_CON_FS_44100: |
194 | srate = 44100; |
195 | break; |
196 | case IEC958_AES3_CON_FS_48000: |
197 | srate = 48000; |
198 | break; |
199 | case IEC958_AES3_CON_FS_32000: |
200 | srate = 32000; |
201 | break; |
202 | default: |
203 | srate = XLNX_PARAM_UNKNOWN; |
204 | break; |
205 | } |
206 | |
207 | if (status[1] & IEC958_AES4_CON_MAX_WORDLEN_24) |
208 | padded = 4; |
209 | else |
210 | padded = 0; |
211 | |
212 | switch (status[1] & IEC958_AES4_CON_WORDLEN) { |
213 | case IEC958_AES4_CON_WORDLEN_20_16: |
214 | bit_depth = 16 + padded; |
215 | break; |
216 | case IEC958_AES4_CON_WORDLEN_22_18: |
217 | bit_depth = 18 + padded; |
218 | break; |
219 | case IEC958_AES4_CON_WORDLEN_23_19: |
220 | bit_depth = 19 + padded; |
221 | break; |
222 | case IEC958_AES4_CON_WORDLEN_24_20: |
223 | bit_depth = 20 + padded; |
224 | break; |
225 | case IEC958_AES4_CON_WORDLEN_21_17: |
226 | bit_depth = 17 + padded; |
227 | break; |
228 | case IEC958_AES4_CON_WORDLEN_NOTID: |
229 | default: |
230 | bit_depth = XLNX_PARAM_UNKNOWN; |
231 | break; |
232 | } |
233 | } |
234 | |
235 | log_params: |
236 | if (srate != XLNX_PARAM_UNKNOWN) |
237 | dev_info(dev, "sample rate = %d\n" , srate); |
238 | else |
239 | dev_info(dev, "sample rate = unknown\n" ); |
240 | |
241 | if (bit_depth != XLNX_PARAM_UNKNOWN) |
242 | dev_info(dev, "bit_depth = %d\n" , bit_depth); |
243 | else |
244 | dev_info(dev, "bit_depth = unknown\n" ); |
245 | } |
246 | |
247 | static int xlnx_formatter_pcm_reset(void __iomem *mmio_base) |
248 | { |
249 | u32 val, retries = 0; |
250 | |
251 | val = readl(addr: mmio_base + XLNX_AUD_CTRL); |
252 | val |= AUD_CTRL_RESET_MASK; |
253 | writel(val, addr: mmio_base + XLNX_AUD_CTRL); |
254 | |
255 | val = readl(addr: mmio_base + XLNX_AUD_CTRL); |
256 | /* Poll for maximum timeout of approximately 100ms (1 * 100)*/ |
257 | while ((val & AUD_CTRL_RESET_MASK) && (retries < 100)) { |
258 | mdelay(1); |
259 | retries++; |
260 | val = readl(addr: mmio_base + XLNX_AUD_CTRL); |
261 | } |
262 | if (val & AUD_CTRL_RESET_MASK) |
263 | return -ENODEV; |
264 | |
265 | return 0; |
266 | } |
267 | |
268 | static void xlnx_formatter_disable_irqs(void __iomem *mmio_base, int stream) |
269 | { |
270 | u32 val; |
271 | |
272 | val = readl(addr: mmio_base + XLNX_AUD_CTRL); |
273 | val &= ~AUD_CTRL_IOC_IRQ_MASK; |
274 | if (stream == SNDRV_PCM_STREAM_CAPTURE) |
275 | val &= ~AUD_CTRL_TOUT_IRQ_MASK; |
276 | |
277 | writel(val, addr: mmio_base + XLNX_AUD_CTRL); |
278 | } |
279 | |
280 | static irqreturn_t xlnx_mm2s_irq_handler(int irq, void *arg) |
281 | { |
282 | u32 val; |
283 | void __iomem *reg; |
284 | struct device *dev = arg; |
285 | struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev); |
286 | |
287 | reg = adata->mmio + XLNX_MM2S_OFFSET + XLNX_AUD_STS; |
288 | val = readl(addr: reg); |
289 | if (val & AUD_STS_IOC_IRQ_MASK) { |
290 | writel(val: val & AUD_STS_IOC_IRQ_MASK, addr: reg); |
291 | if (adata->play_stream) |
292 | snd_pcm_period_elapsed(substream: adata->play_stream); |
293 | return IRQ_HANDLED; |
294 | } |
295 | |
296 | return IRQ_NONE; |
297 | } |
298 | |
299 | static irqreturn_t xlnx_s2mm_irq_handler(int irq, void *arg) |
300 | { |
301 | u32 val; |
302 | void __iomem *reg; |
303 | struct device *dev = arg; |
304 | struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev); |
305 | |
306 | reg = adata->mmio + XLNX_S2MM_OFFSET + XLNX_AUD_STS; |
307 | val = readl(addr: reg); |
308 | if (val & AUD_STS_IOC_IRQ_MASK) { |
309 | writel(val: val & AUD_STS_IOC_IRQ_MASK, addr: reg); |
310 | if (adata->capture_stream) |
311 | snd_pcm_period_elapsed(substream: adata->capture_stream); |
312 | return IRQ_HANDLED; |
313 | } |
314 | |
315 | return IRQ_NONE; |
316 | } |
317 | |
318 | static int xlnx_formatter_set_sysclk(struct snd_soc_component *component, |
319 | int clk_id, int source, unsigned int freq, int dir) |
320 | { |
321 | struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev: component->dev); |
322 | |
323 | adata->sysclk = freq; |
324 | return 0; |
325 | } |
326 | |
327 | static int xlnx_formatter_pcm_open(struct snd_soc_component *component, |
328 | struct snd_pcm_substream *substream) |
329 | { |
330 | int err; |
331 | u32 val, data_format_mode; |
332 | u32 ch_count_mask, ch_count_shift, data_xfer_mode, data_xfer_shift; |
333 | struct xlnx_pcm_stream_param *stream_data; |
334 | struct snd_pcm_runtime *runtime = substream->runtime; |
335 | struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev: component->dev); |
336 | |
337 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && |
338 | !adata->mm2s_presence) |
339 | return -ENODEV; |
340 | else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && |
341 | !adata->s2mm_presence) |
342 | return -ENODEV; |
343 | |
344 | stream_data = kzalloc(size: sizeof(*stream_data), GFP_KERNEL); |
345 | if (!stream_data) |
346 | return -ENOMEM; |
347 | |
348 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
349 | ch_count_mask = CFG_MM2S_CH_MASK; |
350 | ch_count_shift = CFG_MM2S_CH_SHIFT; |
351 | data_xfer_mode = CFG_MM2S_XFER_MASK; |
352 | data_xfer_shift = CFG_MM2S_XFER_SHIFT; |
353 | data_format_mode = CFG_MM2S_PKG_MASK; |
354 | stream_data->mmio = adata->mmio + XLNX_MM2S_OFFSET; |
355 | adata->play_stream = substream; |
356 | |
357 | } else { |
358 | ch_count_mask = CFG_S2MM_CH_MASK; |
359 | ch_count_shift = CFG_S2MM_CH_SHIFT; |
360 | data_xfer_mode = CFG_S2MM_XFER_MASK; |
361 | data_xfer_shift = CFG_S2MM_XFER_SHIFT; |
362 | data_format_mode = CFG_S2MM_PKG_MASK; |
363 | stream_data->mmio = adata->mmio + XLNX_S2MM_OFFSET; |
364 | adata->capture_stream = substream; |
365 | } |
366 | |
367 | val = readl(addr: adata->mmio + XLNX_AUD_CORE_CONFIG); |
368 | |
369 | if (!(val & data_format_mode)) |
370 | stream_data->interleaved = true; |
371 | |
372 | stream_data->xfer_mode = (val & data_xfer_mode) >> data_xfer_shift; |
373 | stream_data->ch_limit = (val & ch_count_mask) >> ch_count_shift; |
374 | dev_info(component->dev, |
375 | "stream %d : format = %d mode = %d ch_limit = %d\n" , |
376 | substream->stream, stream_data->interleaved, |
377 | stream_data->xfer_mode, stream_data->ch_limit); |
378 | |
379 | snd_soc_set_runtime_hwparams(substream, hw: &xlnx_pcm_hardware); |
380 | runtime->private_data = stream_data; |
381 | |
382 | /* Resize the period bytes as divisible by 64 */ |
383 | err = snd_pcm_hw_constraint_step(runtime, cond: 0, |
384 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, |
385 | XLNX_AUD_ALIGN_BYTES); |
386 | if (err) { |
387 | dev_err(component->dev, |
388 | "Unable to set constraint on period bytes\n" ); |
389 | return err; |
390 | } |
391 | |
392 | /* Resize the buffer bytes as divisible by 64 */ |
393 | err = snd_pcm_hw_constraint_step(runtime, cond: 0, |
394 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, |
395 | XLNX_AUD_ALIGN_BYTES); |
396 | if (err) { |
397 | dev_err(component->dev, |
398 | "Unable to set constraint on buffer bytes\n" ); |
399 | return err; |
400 | } |
401 | |
402 | /* Set periods as integer multiple */ |
403 | err = snd_pcm_hw_constraint_integer(runtime, |
404 | SNDRV_PCM_HW_PARAM_PERIODS); |
405 | if (err < 0) { |
406 | dev_err(component->dev, |
407 | "Unable to set constraint on periods to be integer\n" ); |
408 | return err; |
409 | } |
410 | |
411 | /* enable DMA IOC irq */ |
412 | val = readl(addr: stream_data->mmio + XLNX_AUD_CTRL); |
413 | val |= AUD_CTRL_IOC_IRQ_MASK; |
414 | writel(val, addr: stream_data->mmio + XLNX_AUD_CTRL); |
415 | |
416 | return 0; |
417 | } |
418 | |
419 | static int xlnx_formatter_pcm_close(struct snd_soc_component *component, |
420 | struct snd_pcm_substream *substream) |
421 | { |
422 | int ret; |
423 | struct xlnx_pcm_stream_param *stream_data = |
424 | substream->runtime->private_data; |
425 | |
426 | ret = xlnx_formatter_pcm_reset(mmio_base: stream_data->mmio); |
427 | if (ret) { |
428 | dev_err(component->dev, "audio formatter reset failed\n" ); |
429 | goto err_reset; |
430 | } |
431 | xlnx_formatter_disable_irqs(mmio_base: stream_data->mmio, stream: substream->stream); |
432 | |
433 | err_reset: |
434 | kfree(objp: stream_data); |
435 | return 0; |
436 | } |
437 | |
438 | static snd_pcm_uframes_t |
439 | xlnx_formatter_pcm_pointer(struct snd_soc_component *component, |
440 | struct snd_pcm_substream *substream) |
441 | { |
442 | u32 pos; |
443 | struct snd_pcm_runtime *runtime = substream->runtime; |
444 | struct xlnx_pcm_stream_param *stream_data = runtime->private_data; |
445 | |
446 | pos = readl(addr: stream_data->mmio + XLNX_AUD_XFER_COUNT); |
447 | |
448 | if (pos >= stream_data->buffer_size) |
449 | pos = 0; |
450 | |
451 | return bytes_to_frames(runtime, size: pos); |
452 | } |
453 | |
454 | static int xlnx_formatter_pcm_hw_params(struct snd_soc_component *component, |
455 | struct snd_pcm_substream *substream, |
456 | struct snd_pcm_hw_params *params) |
457 | { |
458 | u32 low, high, active_ch, val, bytes_per_ch, bits_per_sample; |
459 | u32 aes_reg1_val, aes_reg2_val; |
460 | u64 size; |
461 | struct snd_pcm_runtime *runtime = substream->runtime; |
462 | struct xlnx_pcm_stream_param *stream_data = runtime->private_data; |
463 | struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev: component->dev); |
464 | |
465 | active_ch = params_channels(p: params); |
466 | if (active_ch > stream_data->ch_limit) |
467 | return -EINVAL; |
468 | |
469 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && |
470 | adata->sysclk) { |
471 | unsigned int mclk_fs = adata->sysclk / params_rate(p: params); |
472 | |
473 | if (adata->sysclk % params_rate(p: params) != 0) { |
474 | dev_warn(component->dev, "sysclk %u not divisible by rate %u\n" , |
475 | adata->sysclk, params_rate(params)); |
476 | return -EINVAL; |
477 | } |
478 | |
479 | writel(val: mclk_fs, addr: stream_data->mmio + XLNX_AUD_FS_MULTIPLIER); |
480 | } |
481 | |
482 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && |
483 | stream_data->xfer_mode == AES_TO_PCM) { |
484 | val = readl(addr: stream_data->mmio + XLNX_AUD_STS); |
485 | if (val & AUD_STS_CH_STS_MASK) { |
486 | aes_reg1_val = readl(addr: stream_data->mmio + |
487 | XLNX_AUD_CH_STS_START); |
488 | aes_reg2_val = readl(addr: stream_data->mmio + |
489 | XLNX_AUD_CH_STS_START + 0x4); |
490 | |
491 | xlnx_parse_aes_params(chsts_reg1_val: aes_reg1_val, chsts_reg2_val: aes_reg2_val, |
492 | dev: component->dev); |
493 | } |
494 | } |
495 | |
496 | size = params_buffer_bytes(p: params); |
497 | |
498 | stream_data->buffer_size = size; |
499 | |
500 | low = lower_32_bits(runtime->dma_addr); |
501 | high = upper_32_bits(runtime->dma_addr); |
502 | writel(val: low, addr: stream_data->mmio + XLNX_AUD_BUFF_ADDR_LSB); |
503 | writel(val: high, addr: stream_data->mmio + XLNX_AUD_BUFF_ADDR_MSB); |
504 | |
505 | val = readl(addr: stream_data->mmio + XLNX_AUD_CTRL); |
506 | bits_per_sample = params_width(p: params); |
507 | switch (bits_per_sample) { |
508 | case 8: |
509 | val |= (BIT_DEPTH_8 << AUD_CTRL_DATA_WIDTH_SHIFT); |
510 | break; |
511 | case 16: |
512 | val |= (BIT_DEPTH_16 << AUD_CTRL_DATA_WIDTH_SHIFT); |
513 | break; |
514 | case 20: |
515 | val |= (BIT_DEPTH_20 << AUD_CTRL_DATA_WIDTH_SHIFT); |
516 | break; |
517 | case 24: |
518 | val |= (BIT_DEPTH_24 << AUD_CTRL_DATA_WIDTH_SHIFT); |
519 | break; |
520 | case 32: |
521 | val |= (BIT_DEPTH_32 << AUD_CTRL_DATA_WIDTH_SHIFT); |
522 | break; |
523 | default: |
524 | return -EINVAL; |
525 | } |
526 | |
527 | val |= active_ch << AUD_CTRL_ACTIVE_CH_SHIFT; |
528 | writel(val, addr: stream_data->mmio + XLNX_AUD_CTRL); |
529 | |
530 | val = (params_periods(p: params) << PERIOD_CFG_PERIODS_SHIFT) |
531 | | params_period_bytes(p: params); |
532 | writel(val, addr: stream_data->mmio + XLNX_AUD_PERIOD_CONFIG); |
533 | bytes_per_ch = DIV_ROUND_UP(params_period_bytes(params), active_ch); |
534 | writel(val: bytes_per_ch, addr: stream_data->mmio + XLNX_BYTES_PER_CH); |
535 | |
536 | return 0; |
537 | } |
538 | |
539 | static int xlnx_formatter_pcm_trigger(struct snd_soc_component *component, |
540 | struct snd_pcm_substream *substream, |
541 | int cmd) |
542 | { |
543 | u32 val; |
544 | struct xlnx_pcm_stream_param *stream_data = |
545 | substream->runtime->private_data; |
546 | |
547 | switch (cmd) { |
548 | case SNDRV_PCM_TRIGGER_START: |
549 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
550 | case SNDRV_PCM_TRIGGER_RESUME: |
551 | val = readl(addr: stream_data->mmio + XLNX_AUD_CTRL); |
552 | val |= AUD_CTRL_DMA_EN_MASK; |
553 | writel(val, addr: stream_data->mmio + XLNX_AUD_CTRL); |
554 | break; |
555 | case SNDRV_PCM_TRIGGER_STOP: |
556 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
557 | case SNDRV_PCM_TRIGGER_SUSPEND: |
558 | val = readl(addr: stream_data->mmio + XLNX_AUD_CTRL); |
559 | val &= ~AUD_CTRL_DMA_EN_MASK; |
560 | writel(val, addr: stream_data->mmio + XLNX_AUD_CTRL); |
561 | break; |
562 | } |
563 | |
564 | return 0; |
565 | } |
566 | |
567 | static int xlnx_formatter_pcm_new(struct snd_soc_component *component, |
568 | struct snd_soc_pcm_runtime *rtd) |
569 | { |
570 | snd_pcm_set_managed_buffer_all(pcm: rtd->pcm, |
571 | SNDRV_DMA_TYPE_DEV, data: component->dev, |
572 | size: xlnx_pcm_hardware.buffer_bytes_max, |
573 | max: xlnx_pcm_hardware.buffer_bytes_max); |
574 | return 0; |
575 | } |
576 | |
577 | static const struct snd_soc_component_driver xlnx_asoc_component = { |
578 | .name = DRV_NAME, |
579 | .set_sysclk = xlnx_formatter_set_sysclk, |
580 | .open = xlnx_formatter_pcm_open, |
581 | .close = xlnx_formatter_pcm_close, |
582 | .hw_params = xlnx_formatter_pcm_hw_params, |
583 | .trigger = xlnx_formatter_pcm_trigger, |
584 | .pointer = xlnx_formatter_pcm_pointer, |
585 | .pcm_construct = xlnx_formatter_pcm_new, |
586 | }; |
587 | |
588 | static int xlnx_formatter_pcm_probe(struct platform_device *pdev) |
589 | { |
590 | int ret; |
591 | u32 val; |
592 | struct xlnx_pcm_drv_data *aud_drv_data; |
593 | struct device *dev = &pdev->dev; |
594 | |
595 | aud_drv_data = devm_kzalloc(dev, size: sizeof(*aud_drv_data), GFP_KERNEL); |
596 | if (!aud_drv_data) |
597 | return -ENOMEM; |
598 | |
599 | aud_drv_data->axi_clk = devm_clk_get(dev, id: "s_axi_lite_aclk" ); |
600 | if (IS_ERR(ptr: aud_drv_data->axi_clk)) { |
601 | ret = PTR_ERR(ptr: aud_drv_data->axi_clk); |
602 | dev_err(dev, "failed to get s_axi_lite_aclk(%d)\n" , ret); |
603 | return ret; |
604 | } |
605 | ret = clk_prepare_enable(clk: aud_drv_data->axi_clk); |
606 | if (ret) { |
607 | dev_err(dev, |
608 | "failed to enable s_axi_lite_aclk(%d)\n" , ret); |
609 | return ret; |
610 | } |
611 | |
612 | aud_drv_data->mmio = devm_platform_ioremap_resource(pdev, index: 0); |
613 | if (IS_ERR(ptr: aud_drv_data->mmio)) { |
614 | dev_err(dev, "audio formatter ioremap failed\n" ); |
615 | ret = PTR_ERR(ptr: aud_drv_data->mmio); |
616 | goto clk_err; |
617 | } |
618 | |
619 | val = readl(addr: aud_drv_data->mmio + XLNX_AUD_CORE_CONFIG); |
620 | if (val & AUD_CFG_MM2S_MASK) { |
621 | aud_drv_data->mm2s_presence = true; |
622 | ret = xlnx_formatter_pcm_reset(mmio_base: aud_drv_data->mmio + |
623 | XLNX_MM2S_OFFSET); |
624 | if (ret) { |
625 | dev_err(dev, "audio formatter reset failed\n" ); |
626 | goto clk_err; |
627 | } |
628 | xlnx_formatter_disable_irqs(mmio_base: aud_drv_data->mmio + |
629 | XLNX_MM2S_OFFSET, |
630 | stream: SNDRV_PCM_STREAM_PLAYBACK); |
631 | |
632 | aud_drv_data->mm2s_irq = platform_get_irq_byname(pdev, |
633 | "irq_mm2s" ); |
634 | if (aud_drv_data->mm2s_irq < 0) { |
635 | ret = aud_drv_data->mm2s_irq; |
636 | goto clk_err; |
637 | } |
638 | ret = devm_request_irq(dev, irq: aud_drv_data->mm2s_irq, |
639 | handler: xlnx_mm2s_irq_handler, irqflags: 0, |
640 | devname: "xlnx_formatter_pcm_mm2s_irq" , dev_id: dev); |
641 | if (ret) { |
642 | dev_err(dev, "xlnx audio mm2s irq request failed\n" ); |
643 | goto clk_err; |
644 | } |
645 | } |
646 | if (val & AUD_CFG_S2MM_MASK) { |
647 | aud_drv_data->s2mm_presence = true; |
648 | ret = xlnx_formatter_pcm_reset(mmio_base: aud_drv_data->mmio + |
649 | XLNX_S2MM_OFFSET); |
650 | if (ret) { |
651 | dev_err(dev, "audio formatter reset failed\n" ); |
652 | goto clk_err; |
653 | } |
654 | xlnx_formatter_disable_irqs(mmio_base: aud_drv_data->mmio + |
655 | XLNX_S2MM_OFFSET, |
656 | stream: SNDRV_PCM_STREAM_CAPTURE); |
657 | |
658 | aud_drv_data->s2mm_irq = platform_get_irq_byname(pdev, |
659 | "irq_s2mm" ); |
660 | if (aud_drv_data->s2mm_irq < 0) { |
661 | ret = aud_drv_data->s2mm_irq; |
662 | goto clk_err; |
663 | } |
664 | ret = devm_request_irq(dev, irq: aud_drv_data->s2mm_irq, |
665 | handler: xlnx_s2mm_irq_handler, irqflags: 0, |
666 | devname: "xlnx_formatter_pcm_s2mm_irq" , |
667 | dev_id: dev); |
668 | if (ret) { |
669 | dev_err(dev, "xlnx audio s2mm irq request failed\n" ); |
670 | goto clk_err; |
671 | } |
672 | } |
673 | |
674 | dev_set_drvdata(dev, data: aud_drv_data); |
675 | |
676 | ret = devm_snd_soc_register_component(dev, component_driver: &xlnx_asoc_component, |
677 | NULL, num_dai: 0); |
678 | if (ret) { |
679 | dev_err(dev, "pcm platform device register failed\n" ); |
680 | goto clk_err; |
681 | } |
682 | |
683 | return 0; |
684 | |
685 | clk_err: |
686 | clk_disable_unprepare(clk: aud_drv_data->axi_clk); |
687 | return ret; |
688 | } |
689 | |
690 | static void xlnx_formatter_pcm_remove(struct platform_device *pdev) |
691 | { |
692 | int ret = 0; |
693 | struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev: &pdev->dev); |
694 | |
695 | if (adata->s2mm_presence) |
696 | ret = xlnx_formatter_pcm_reset(mmio_base: adata->mmio + XLNX_S2MM_OFFSET); |
697 | |
698 | /* Try MM2S reset, even if S2MM reset fails */ |
699 | if (adata->mm2s_presence) |
700 | ret = xlnx_formatter_pcm_reset(mmio_base: adata->mmio + XLNX_MM2S_OFFSET); |
701 | |
702 | if (ret) |
703 | dev_err(&pdev->dev, "audio formatter reset failed\n" ); |
704 | |
705 | clk_disable_unprepare(clk: adata->axi_clk); |
706 | } |
707 | |
708 | static const struct of_device_id xlnx_formatter_pcm_of_match[] = { |
709 | { .compatible = "xlnx,audio-formatter-1.0" }, |
710 | {}, |
711 | }; |
712 | MODULE_DEVICE_TABLE(of, xlnx_formatter_pcm_of_match); |
713 | |
714 | static struct platform_driver xlnx_formatter_pcm_driver = { |
715 | .probe = xlnx_formatter_pcm_probe, |
716 | .remove_new = xlnx_formatter_pcm_remove, |
717 | .driver = { |
718 | .name = DRV_NAME, |
719 | .of_match_table = xlnx_formatter_pcm_of_match, |
720 | }, |
721 | }; |
722 | |
723 | module_platform_driver(xlnx_formatter_pcm_driver); |
724 | MODULE_AUTHOR("Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>" ); |
725 | MODULE_LICENSE("GPL v2" ); |
726 | |