dt-bindings: soc: bcm: use absolute path to other schema
[linux-2.6-microblaze.git] / sound / soc / qcom / qdsp6 / q6apm-dai.c
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2021, Linaro Limited
3
4 #include <linux/init.h>
5 #include <linux/err.h>
6 #include <linux/module.h>
7 #include <linux/platform_device.h>
8 #include <linux/slab.h>
9 #include <sound/soc.h>
10 #include <sound/soc-dapm.h>
11 #include <sound/pcm.h>
12 #include <asm/dma.h>
13 #include <linux/dma-mapping.h>
14 #include <linux/of_device.h>
15 #include <sound/pcm_params.h>
16 #include "q6apm.h"
17
18 #define DRV_NAME "q6apm-dai"
19
20 #define PLAYBACK_MIN_NUM_PERIODS        2
21 #define PLAYBACK_MAX_NUM_PERIODS        8
22 #define PLAYBACK_MAX_PERIOD_SIZE        65536
23 #define PLAYBACK_MIN_PERIOD_SIZE        128
24 #define CAPTURE_MIN_NUM_PERIODS         2
25 #define CAPTURE_MAX_NUM_PERIODS         8
26 #define CAPTURE_MAX_PERIOD_SIZE         4096
27 #define CAPTURE_MIN_PERIOD_SIZE         320
28 #define BUFFER_BYTES_MAX (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE)
29 #define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE)
30 #define SID_MASK_DEFAULT        0xF
31
32 enum stream_state {
33         Q6APM_STREAM_IDLE = 0,
34         Q6APM_STREAM_STOPPED,
35         Q6APM_STREAM_RUNNING,
36 };
37
38 struct q6apm_dai_rtd {
39         struct snd_pcm_substream *substream;
40         struct snd_compr_stream *cstream;
41         struct snd_compr_params codec_param;
42         struct snd_dma_buffer dma_buffer;
43         phys_addr_t phys;
44         unsigned int pcm_size;
45         unsigned int pcm_count;
46         unsigned int pos;       /* Buffer position */
47         unsigned int periods;
48         unsigned int bytes_sent;
49         unsigned int bytes_received;
50         unsigned int copied_total;
51         uint16_t bits_per_sample;
52         uint16_t source; /* Encoding source bit mask */
53         uint16_t session_id;
54         enum stream_state state;
55         struct q6apm_graph *graph;
56 };
57
58 struct q6apm_dai_data {
59         long long sid;
60 };
61
62 static struct snd_pcm_hardware q6apm_dai_hardware_capture = {
63         .info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER |
64                                  SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED |
65                                  SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
66         .formats =              (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE),
67         .rates =                SNDRV_PCM_RATE_8000_48000,
68         .rate_min =             8000,
69         .rate_max =             48000,
70         .channels_min =         2,
71         .channels_max =         4,
72         .buffer_bytes_max =     CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
73         .period_bytes_min =     CAPTURE_MIN_PERIOD_SIZE,
74         .period_bytes_max =     CAPTURE_MAX_PERIOD_SIZE,
75         .periods_min =          CAPTURE_MIN_NUM_PERIODS,
76         .periods_max =          CAPTURE_MAX_NUM_PERIODS,
77         .fifo_size =            0,
78 };
79
80 static struct snd_pcm_hardware q6apm_dai_hardware_playback = {
81         .info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER |
82                                  SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED |
83                                  SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
84         .formats =              (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE),
85         .rates =                SNDRV_PCM_RATE_8000_192000,
86         .rate_min =             8000,
87         .rate_max =             192000,
88         .channels_min =         2,
89         .channels_max =         8,
90         .buffer_bytes_max =     (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE),
91         .period_bytes_min =     PLAYBACK_MIN_PERIOD_SIZE,
92         .period_bytes_max =     PLAYBACK_MAX_PERIOD_SIZE,
93         .periods_min =          PLAYBACK_MIN_NUM_PERIODS,
94         .periods_max =          PLAYBACK_MAX_NUM_PERIODS,
95         .fifo_size =            0,
96 };
97
98 static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, void *priv)
99 {
100         struct q6apm_dai_rtd *prtd = priv;
101         struct snd_pcm_substream *substream = prtd->substream;
102
103         switch (opcode) {
104         case APM_CLIENT_EVENT_CMD_EOS_DONE:
105                 prtd->state = Q6APM_STREAM_STOPPED;
106                 break;
107         case APM_CLIENT_EVENT_DATA_WRITE_DONE:
108                 prtd->pos += prtd->pcm_count;
109                 snd_pcm_period_elapsed(substream);
110                 if (prtd->state == Q6APM_STREAM_RUNNING)
111                         q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0);
112
113                 break;
114         case APM_CLIENT_EVENT_DATA_READ_DONE:
115                 prtd->pos += prtd->pcm_count;
116                 snd_pcm_period_elapsed(substream);
117                 if (prtd->state == Q6APM_STREAM_RUNNING)
118                         q6apm_read(prtd->graph);
119
120                 break;
121         default:
122                 break;
123         }
124 }
125
126 static int q6apm_dai_prepare(struct snd_soc_component *component,
127                              struct snd_pcm_substream *substream)
128 {
129         struct snd_pcm_runtime *runtime = substream->runtime;
130         struct q6apm_dai_rtd *prtd = runtime->private_data;
131         struct audioreach_module_config cfg;
132         struct device *dev = component->dev;
133         struct q6apm_dai_data *pdata;
134         int ret;
135
136         pdata = snd_soc_component_get_drvdata(component);
137         if (!pdata)
138                 return -EINVAL;
139
140         if (!prtd || !prtd->graph) {
141                 dev_err(dev, "%s: private data null or audio client freed\n", __func__);
142                 return -EINVAL;
143         }
144
145         cfg.direction = substream->stream;
146         cfg.sample_rate = runtime->rate;
147         cfg.num_channels = runtime->channels;
148         cfg.bit_width = prtd->bits_per_sample;
149
150         prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
151         prtd->pos = 0;
152         /* rate and channels are sent to audio driver */
153         ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg);
154         if (ret < 0) {
155                 dev_err(dev, "%s: q6apm_open_write failed\n", __func__);
156                 return ret;
157         }
158
159         ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg);
160         if (ret < 0)
161                 dev_err(dev, "%s: CMD Format block failed\n", __func__);
162
163         ret = q6apm_map_memory_regions(prtd->graph, substream->stream, prtd->phys,
164                                        (prtd->pcm_size / prtd->periods), prtd->periods);
165
166         if (ret < 0) {
167                 dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret);
168                 return -ENOMEM;
169         }
170
171         ret = q6apm_graph_prepare(prtd->graph);
172         if (ret) {
173                 dev_err(dev, "Failed to prepare Graph %d\n", ret);
174                 return ret;
175         }
176
177         ret = q6apm_graph_start(prtd->graph);
178         if (ret) {
179                 dev_err(dev, "Failed to Start Graph %d\n", ret);
180                 return ret;
181         }
182
183         if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
184                 int i;
185                 /* Queue the buffers for Capture ONLY after graph is started */
186                 for (i = 0; i < runtime->periods; i++)
187                         q6apm_read(prtd->graph);
188
189         }
190
191         /* Now that graph as been prepared and started update the internal state accordingly */
192         prtd->state = Q6APM_STREAM_RUNNING;
193
194         return 0;
195 }
196
197 static int q6apm_dai_trigger(struct snd_soc_component *component,
198                              struct snd_pcm_substream *substream, int cmd)
199 {
200         struct snd_pcm_runtime *runtime = substream->runtime;
201         struct q6apm_dai_rtd *prtd = runtime->private_data;
202         int ret = 0;
203
204         switch (cmd) {
205         case SNDRV_PCM_TRIGGER_START:
206         case SNDRV_PCM_TRIGGER_RESUME:
207         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
208                  /* start writing buffers for playback only as we already queued capture buffers */
209                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
210                         ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0);
211                 break;
212         case SNDRV_PCM_TRIGGER_STOP:
213                 /* TODO support be handled via SoftPause Module */
214                 prtd->state = Q6APM_STREAM_STOPPED;
215                 break;
216         case SNDRV_PCM_TRIGGER_SUSPEND:
217         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
218                 break;
219         default:
220                 ret = -EINVAL;
221                 break;
222         }
223
224         return ret;
225 }
226
227 static int q6apm_dai_open(struct snd_soc_component *component,
228                           struct snd_pcm_substream *substream)
229 {
230         struct snd_pcm_runtime *runtime = substream->runtime;
231         struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
232         struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_prtd, 0);
233         struct device *dev = component->dev;
234         struct q6apm_dai_data *pdata;
235         struct q6apm_dai_rtd *prtd;
236         int graph_id, ret;
237
238         graph_id = cpu_dai->driver->id;
239
240         pdata = snd_soc_component_get_drvdata(component);
241         if (!pdata) {
242                 dev_err(dev, "Drv data not found ..\n");
243                 return -EINVAL;
244         }
245
246         prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
247         if (prtd == NULL)
248                 return -ENOMEM;
249
250         prtd->substream = substream;
251         prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler, prtd, graph_id);
252         if (IS_ERR(prtd->graph)) {
253                 dev_err(dev, "%s: Could not allocate memory\n", __func__);
254                 ret = PTR_ERR(prtd->graph);
255                 goto err;
256         }
257
258         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
259                 runtime->hw = q6apm_dai_hardware_playback;
260         else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
261                 runtime->hw = q6apm_dai_hardware_capture;
262
263         /* Ensure that buffer size is a multiple of period size */
264         ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
265         if (ret < 0) {
266                 dev_err(dev, "snd_pcm_hw_constraint_integer failed\n");
267                 goto err;
268         }
269
270         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
271                 ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
272                                                    BUFFER_BYTES_MIN, BUFFER_BYTES_MAX);
273                 if (ret < 0) {
274                         dev_err(dev, "constraint for buffer bytes min max ret = %d\n", ret);
275                         goto err;
276                 }
277         }
278
279         ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
280         if (ret < 0) {
281                 dev_err(dev, "constraint for period bytes step ret = %d\n", ret);
282                 goto err;
283         }
284
285         ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
286         if (ret < 0) {
287                 dev_err(dev, "constraint for buffer bytes step ret = %d\n", ret);
288                 goto err;
289         }
290
291         runtime->private_data = prtd;
292         runtime->dma_bytes = BUFFER_BYTES_MAX;
293         if (pdata->sid < 0)
294                 prtd->phys = substream->dma_buffer.addr;
295         else
296                 prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32);
297
298         return 0;
299 err:
300         kfree(prtd);
301
302         return ret;
303 }
304
305 static int q6apm_dai_close(struct snd_soc_component *component,
306                            struct snd_pcm_substream *substream)
307 {
308         struct snd_pcm_runtime *runtime = substream->runtime;
309         struct q6apm_dai_rtd *prtd = runtime->private_data;
310
311         if (prtd->state) { /* only stop graph that is started */
312                 q6apm_graph_stop(prtd->graph);
313                 q6apm_unmap_memory_regions(prtd->graph, substream->stream);
314         }
315
316         q6apm_graph_close(prtd->graph);
317         prtd->graph = NULL;
318         kfree(prtd);
319         runtime->private_data = NULL;
320
321         return 0;
322 }
323
324 static snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component,
325                                            struct snd_pcm_substream *substream)
326 {
327         struct snd_pcm_runtime *runtime = substream->runtime;
328         struct q6apm_dai_rtd *prtd = runtime->private_data;
329
330         if (prtd->pos == prtd->pcm_size)
331                 prtd->pos = 0;
332
333         return bytes_to_frames(runtime, prtd->pos);
334 }
335
336 static int q6apm_dai_hw_params(struct snd_soc_component *component,
337                                struct snd_pcm_substream *substream,
338                                struct snd_pcm_hw_params *params)
339 {
340         struct snd_pcm_runtime *runtime = substream->runtime;
341         struct q6apm_dai_rtd *prtd = runtime->private_data;
342
343         prtd->pcm_size = params_buffer_bytes(params);
344         prtd->periods = params_periods(params);
345
346         switch (params_format(params)) {
347         case SNDRV_PCM_FORMAT_S16_LE:
348                 prtd->bits_per_sample = 16;
349                 break;
350         case SNDRV_PCM_FORMAT_S24_LE:
351                 prtd->bits_per_sample = 24;
352                 break;
353         default:
354                 return -EINVAL;
355         }
356
357         return 0;
358 }
359
360 static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd)
361 {
362         int size = BUFFER_BYTES_MAX;
363
364         return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, size);
365 }
366
367 static const struct snd_soc_component_driver q6apm_fe_dai_component = {
368         .name           = DRV_NAME,
369         .open           = q6apm_dai_open,
370         .close          = q6apm_dai_close,
371         .prepare        = q6apm_dai_prepare,
372         .pcm_construct  = q6apm_dai_pcm_new,
373         .hw_params      = q6apm_dai_hw_params,
374         .pointer        = q6apm_dai_pointer,
375         .trigger        = q6apm_dai_trigger,
376 };
377
378 static int q6apm_dai_probe(struct platform_device *pdev)
379 {
380         struct device *dev = &pdev->dev;
381         struct device_node *node = dev->of_node;
382         struct q6apm_dai_data *pdata;
383         struct of_phandle_args args;
384         int rc;
385
386         pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
387         if (!pdata)
388                 return -ENOMEM;
389
390         rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args);
391         if (rc < 0)
392                 pdata->sid = -1;
393         else
394                 pdata->sid = args.args[0] & SID_MASK_DEFAULT;
395
396         dev_set_drvdata(dev, pdata);
397
398         return devm_snd_soc_register_component(dev, &q6apm_fe_dai_component, NULL, 0);
399 }
400
401 #ifdef CONFIG_OF
402 static const struct of_device_id q6apm_dai_device_id[] = {
403         { .compatible = "qcom,q6apm-dais" },
404         {},
405 };
406 MODULE_DEVICE_TABLE(of, q6apm_dai_device_id);
407 #endif
408
409 static struct platform_driver q6apm_dai_platform_driver = {
410         .driver = {
411                 .name = "q6apm-dai",
412                 .of_match_table = of_match_ptr(q6apm_dai_device_id),
413         },
414         .probe = q6apm_dai_probe,
415 };
416 module_platform_driver(q6apm_dai_platform_driver);
417
418 MODULE_DESCRIPTION("Q6APM dai driver");
419 MODULE_LICENSE("GPL");