hc
2023-12-08 01573e231f18eb2d99162747186f59511f56b64d
kernel/sound/soc/codecs/rt5677-spi.c
....@@ -1,12 +1,9 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * rt5677-spi.c -- RT5677 ALSA SoC audio codec driver
34 *
45 * Copyright 2013 Realtek Semiconductor Corp.
56 * Author: Oder Chiou <oder_chiou@realtek.com>
6
- *
7
- * This program is free software; you can redistribute it and/or modify
8
- * it under the terms of the GNU General Public License version 2 as
9
- * published by the Free Software Foundation.
107 */
118
129 #include <linux/module.h>
....@@ -18,7 +15,6 @@
1815 #include <linux/interrupt.h>
1916 #include <linux/irq.h>
2017 #include <linux/slab.h>
21
-#include <linux/gpio.h>
2218 #include <linux/sched.h>
2319 #include <linux/uaccess.h>
2420 #include <linux/regulator/consumer.h>
....@@ -26,8 +22,14 @@
2622 #include <linux/sysfs.h>
2723 #include <linux/clk.h>
2824 #include <linux/firmware.h>
25
+#include <linux/acpi.h>
2926
27
+#include <sound/soc.h>
28
+
29
+#include "rt5677.h"
3030 #include "rt5677-spi.h"
31
+
32
+#define DRV_NAME "rt5677spi"
3133
3234 #define RT5677_SPI_BURST_LEN 240
3335 #define RT5677_SPI_HEADER 5
....@@ -46,8 +48,364 @@
4648 #define RT5677_SPI_WRITE_16 0x1
4749 #define RT5677_SPI_READ_16 0x0
4850
51
+#define RT5677_BUF_BYTES_TOTAL 0x20000
52
+#define RT5677_MIC_BUF_ADDR 0x60030000
53
+#define RT5677_MODEL_ADDR 0x5FFC9800
54
+#define RT5677_MIC_BUF_BYTES ((u32)(RT5677_BUF_BYTES_TOTAL - \
55
+ sizeof(u32)))
56
+#define RT5677_MIC_BUF_FIRST_READ_SIZE 0x10000
57
+
4958 static struct spi_device *g_spi;
5059 static DEFINE_MUTEX(spi_mutex);
60
+
61
+struct rt5677_dsp {
62
+ struct device *dev;
63
+ struct delayed_work copy_work;
64
+ struct mutex dma_lock;
65
+ struct snd_pcm_substream *substream;
66
+ size_t dma_offset; /* zero-based offset into runtime->dma_area */
67
+ size_t avail_bytes; /* number of new bytes since last period */
68
+ u32 mic_read_offset; /* zero-based offset into DSP's mic buffer */
69
+ bool new_hotword; /* a new hotword is fired */
70
+};
71
+
72
+static const struct snd_pcm_hardware rt5677_spi_pcm_hardware = {
73
+ .info = SNDRV_PCM_INFO_MMAP |
74
+ SNDRV_PCM_INFO_MMAP_VALID |
75
+ SNDRV_PCM_INFO_INTERLEAVED,
76
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
77
+ .period_bytes_min = PAGE_SIZE,
78
+ .period_bytes_max = RT5677_BUF_BYTES_TOTAL / 8,
79
+ .periods_min = 8,
80
+ .periods_max = 8,
81
+ .channels_min = 1,
82
+ .channels_max = 1,
83
+ .buffer_bytes_max = RT5677_BUF_BYTES_TOTAL,
84
+};
85
+
86
+static struct snd_soc_dai_driver rt5677_spi_dai = {
87
+ /* The DAI name "rt5677-dsp-cpu-dai" is not used. The actual DAI name
88
+ * registered with ASoC is the name of the device "spi-RT5677AA:00",
89
+ * because we only have one DAI. See snd_soc_register_dais().
90
+ */
91
+ .name = "rt5677-dsp-cpu-dai",
92
+ .id = 0,
93
+ .capture = {
94
+ .stream_name = "DSP Capture",
95
+ .channels_min = 1,
96
+ .channels_max = 1,
97
+ .rates = SNDRV_PCM_RATE_16000,
98
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
99
+ },
100
+};
101
+
102
+/* PCM for streaming audio from the DSP buffer */
103
+static int rt5677_spi_pcm_open(
104
+ struct snd_soc_component *component,
105
+ struct snd_pcm_substream *substream)
106
+{
107
+ snd_soc_set_runtime_hwparams(substream, &rt5677_spi_pcm_hardware);
108
+ return 0;
109
+}
110
+
111
+static int rt5677_spi_pcm_close(
112
+ struct snd_soc_component *component,
113
+ struct snd_pcm_substream *substream)
114
+{
115
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
116
+ struct snd_soc_component *codec_component =
117
+ snd_soc_rtdcom_lookup(rtd, "rt5677");
118
+ struct rt5677_priv *rt5677 =
119
+ snd_soc_component_get_drvdata(codec_component);
120
+ struct rt5677_dsp *rt5677_dsp =
121
+ snd_soc_component_get_drvdata(component);
122
+
123
+ cancel_delayed_work_sync(&rt5677_dsp->copy_work);
124
+ rt5677->set_dsp_vad(codec_component, false);
125
+ return 0;
126
+}
127
+
128
+static int rt5677_spi_hw_params(
129
+ struct snd_soc_component *component,
130
+ struct snd_pcm_substream *substream,
131
+ struct snd_pcm_hw_params *hw_params)
132
+{
133
+ struct rt5677_dsp *rt5677_dsp =
134
+ snd_soc_component_get_drvdata(component);
135
+
136
+ mutex_lock(&rt5677_dsp->dma_lock);
137
+ rt5677_dsp->substream = substream;
138
+ mutex_unlock(&rt5677_dsp->dma_lock);
139
+
140
+ return 0;
141
+}
142
+
143
+static int rt5677_spi_hw_free(
144
+ struct snd_soc_component *component,
145
+ struct snd_pcm_substream *substream)
146
+{
147
+ struct rt5677_dsp *rt5677_dsp =
148
+ snd_soc_component_get_drvdata(component);
149
+
150
+ mutex_lock(&rt5677_dsp->dma_lock);
151
+ rt5677_dsp->substream = NULL;
152
+ mutex_unlock(&rt5677_dsp->dma_lock);
153
+
154
+ return 0;
155
+}
156
+
157
+static int rt5677_spi_prepare(
158
+ struct snd_soc_component *component,
159
+ struct snd_pcm_substream *substream)
160
+{
161
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
162
+ struct snd_soc_component *rt5677_component =
163
+ snd_soc_rtdcom_lookup(rtd, "rt5677");
164
+ struct rt5677_priv *rt5677 =
165
+ snd_soc_component_get_drvdata(rt5677_component);
166
+ struct rt5677_dsp *rt5677_dsp =
167
+ snd_soc_component_get_drvdata(component);
168
+
169
+ rt5677->set_dsp_vad(rt5677_component, true);
170
+ rt5677_dsp->dma_offset = 0;
171
+ rt5677_dsp->avail_bytes = 0;
172
+ return 0;
173
+}
174
+
175
+static snd_pcm_uframes_t rt5677_spi_pcm_pointer(
176
+ struct snd_soc_component *component,
177
+ struct snd_pcm_substream *substream)
178
+{
179
+ struct snd_pcm_runtime *runtime = substream->runtime;
180
+ struct rt5677_dsp *rt5677_dsp =
181
+ snd_soc_component_get_drvdata(component);
182
+
183
+ return bytes_to_frames(runtime, rt5677_dsp->dma_offset);
184
+}
185
+
186
+static int rt5677_spi_mic_write_offset(u32 *mic_write_offset)
187
+{
188
+ int ret;
189
+ /* Grab the first 4 bytes that hold the write pointer on the
190
+ * dsp, and check to make sure that it points somewhere inside the
191
+ * buffer.
192
+ */
193
+ ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR, mic_write_offset,
194
+ sizeof(u32));
195
+ if (ret)
196
+ return ret;
197
+ /* Adjust the offset so that it's zero-based */
198
+ *mic_write_offset = *mic_write_offset - sizeof(u32);
199
+ return *mic_write_offset < RT5677_MIC_BUF_BYTES ? 0 : -EFAULT;
200
+}
201
+
202
+/*
203
+ * Copy one contiguous block of audio samples from the DSP mic buffer to the
204
+ * dma_area of the pcm runtime. The receiving buffer may wrap around.
205
+ * @begin: start offset of the block to copy, in bytes.
206
+ * @end: offset of the first byte after the block to copy, must be greater
207
+ * than or equal to begin.
208
+ *
209
+ * Return: Zero if successful, or a negative error code on failure.
210
+ */
211
+static int rt5677_spi_copy_block(struct rt5677_dsp *rt5677_dsp,
212
+ u32 begin, u32 end)
213
+{
214
+ struct snd_pcm_runtime *runtime = rt5677_dsp->substream->runtime;
215
+ size_t bytes_per_frame = frames_to_bytes(runtime, 1);
216
+ size_t first_chunk_len, second_chunk_len;
217
+ int ret;
218
+
219
+ if (begin > end || runtime->dma_bytes < 2 * bytes_per_frame) {
220
+ dev_err(rt5677_dsp->dev,
221
+ "Invalid copy from (%u, %u), dma_area size %zu\n",
222
+ begin, end, runtime->dma_bytes);
223
+ return -EINVAL;
224
+ }
225
+
226
+ /* The block to copy is empty */
227
+ if (begin == end)
228
+ return 0;
229
+
230
+ /* If the incoming chunk is too big for the receiving buffer, only the
231
+ * last "receiving buffer size - one frame" bytes are copied.
232
+ */
233
+ if (end - begin > runtime->dma_bytes - bytes_per_frame)
234
+ begin = end - (runtime->dma_bytes - bytes_per_frame);
235
+
236
+ /* May need to split to two chunks, calculate the size of each */
237
+ first_chunk_len = end - begin;
238
+ second_chunk_len = 0;
239
+ if (rt5677_dsp->dma_offset + first_chunk_len > runtime->dma_bytes) {
240
+ /* Receiving buffer wrapped around */
241
+ second_chunk_len = first_chunk_len;
242
+ first_chunk_len = runtime->dma_bytes - rt5677_dsp->dma_offset;
243
+ second_chunk_len -= first_chunk_len;
244
+ }
245
+
246
+ /* Copy first chunk */
247
+ ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) + begin,
248
+ runtime->dma_area + rt5677_dsp->dma_offset,
249
+ first_chunk_len);
250
+ if (ret)
251
+ return ret;
252
+ rt5677_dsp->dma_offset += first_chunk_len;
253
+ if (rt5677_dsp->dma_offset == runtime->dma_bytes)
254
+ rt5677_dsp->dma_offset = 0;
255
+
256
+ /* Copy second chunk */
257
+ if (second_chunk_len) {
258
+ ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) +
259
+ begin + first_chunk_len, runtime->dma_area,
260
+ second_chunk_len);
261
+ if (!ret)
262
+ rt5677_dsp->dma_offset = second_chunk_len;
263
+ }
264
+ return ret;
265
+}
266
+
267
+/*
268
+ * Copy a given amount of audio samples from the DSP mic buffer starting at
269
+ * mic_read_offset, to the dma_area of the pcm runtime. The source buffer may
270
+ * wrap around. mic_read_offset is updated after successful copy.
271
+ * @amount: amount of samples to copy, in bytes.
272
+ *
273
+ * Return: Zero if successful, or a negative error code on failure.
274
+ */
275
+static int rt5677_spi_copy(struct rt5677_dsp *rt5677_dsp, u32 amount)
276
+{
277
+ int ret = 0;
278
+ u32 target;
279
+
280
+ if (amount == 0)
281
+ return ret;
282
+
283
+ target = rt5677_dsp->mic_read_offset + amount;
284
+ /* Copy the first chunk in DSP's mic buffer */
285
+ ret |= rt5677_spi_copy_block(rt5677_dsp, rt5677_dsp->mic_read_offset,
286
+ min(target, RT5677_MIC_BUF_BYTES));
287
+
288
+ if (target >= RT5677_MIC_BUF_BYTES) {
289
+ /* Wrap around, copy the second chunk */
290
+ target -= RT5677_MIC_BUF_BYTES;
291
+ ret |= rt5677_spi_copy_block(rt5677_dsp, 0, target);
292
+ }
293
+
294
+ if (!ret)
295
+ rt5677_dsp->mic_read_offset = target;
296
+ return ret;
297
+}
298
+
299
+/*
300
+ * A delayed work that streams audio samples from the DSP mic buffer to the
301
+ * dma_area of the pcm runtime via SPI.
302
+ */
303
+static void rt5677_spi_copy_work(struct work_struct *work)
304
+{
305
+ struct rt5677_dsp *rt5677_dsp =
306
+ container_of(work, struct rt5677_dsp, copy_work.work);
307
+ struct snd_pcm_runtime *runtime;
308
+ u32 mic_write_offset;
309
+ size_t new_bytes, copy_bytes, period_bytes;
310
+ unsigned int delay;
311
+ int ret = 0;
312
+
313
+ /* Ensure runtime->dma_area buffer does not go away while copying. */
314
+ mutex_lock(&rt5677_dsp->dma_lock);
315
+ if (!rt5677_dsp->substream) {
316
+ dev_err(rt5677_dsp->dev, "No pcm substream\n");
317
+ goto done;
318
+ }
319
+
320
+ runtime = rt5677_dsp->substream->runtime;
321
+
322
+ if (rt5677_spi_mic_write_offset(&mic_write_offset)) {
323
+ dev_err(rt5677_dsp->dev, "No mic_write_offset\n");
324
+ goto done;
325
+ }
326
+
327
+ /* If this is the first time that we've asked for streaming data after
328
+ * a hotword is fired, we should start reading from the previous 2
329
+ * seconds of audio from wherever the mic_write_offset is currently.
330
+ */
331
+ if (rt5677_dsp->new_hotword) {
332
+ rt5677_dsp->new_hotword = false;
333
+ /* See if buffer wraparound happens */
334
+ if (mic_write_offset < RT5677_MIC_BUF_FIRST_READ_SIZE)
335
+ rt5677_dsp->mic_read_offset = RT5677_MIC_BUF_BYTES -
336
+ (RT5677_MIC_BUF_FIRST_READ_SIZE -
337
+ mic_write_offset);
338
+ else
339
+ rt5677_dsp->mic_read_offset = mic_write_offset -
340
+ RT5677_MIC_BUF_FIRST_READ_SIZE;
341
+ }
342
+
343
+ /* Calculate the amount of new samples in bytes */
344
+ if (rt5677_dsp->mic_read_offset <= mic_write_offset)
345
+ new_bytes = mic_write_offset - rt5677_dsp->mic_read_offset;
346
+ else
347
+ new_bytes = RT5677_MIC_BUF_BYTES + mic_write_offset
348
+ - rt5677_dsp->mic_read_offset;
349
+
350
+ /* Copy all new samples from DSP mic buffer, one period at a time */
351
+ period_bytes = snd_pcm_lib_period_bytes(rt5677_dsp->substream);
352
+ while (new_bytes) {
353
+ copy_bytes = min(new_bytes, period_bytes
354
+ - rt5677_dsp->avail_bytes);
355
+ ret = rt5677_spi_copy(rt5677_dsp, copy_bytes);
356
+ if (ret) {
357
+ dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret);
358
+ goto done;
359
+ }
360
+ rt5677_dsp->avail_bytes += copy_bytes;
361
+ if (rt5677_dsp->avail_bytes >= period_bytes) {
362
+ snd_pcm_period_elapsed(rt5677_dsp->substream);
363
+ rt5677_dsp->avail_bytes = 0;
364
+ }
365
+ new_bytes -= copy_bytes;
366
+ }
367
+
368
+ delay = bytes_to_frames(runtime, period_bytes) / (runtime->rate / 1000);
369
+ schedule_delayed_work(&rt5677_dsp->copy_work, msecs_to_jiffies(delay));
370
+done:
371
+ mutex_unlock(&rt5677_dsp->dma_lock);
372
+}
373
+
374
+static int rt5677_spi_pcm_new(struct snd_soc_component *component,
375
+ struct snd_soc_pcm_runtime *rtd)
376
+{
377
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
378
+ NULL, 0, 0);
379
+ return 0;
380
+}
381
+
382
+static int rt5677_spi_pcm_probe(struct snd_soc_component *component)
383
+{
384
+ struct rt5677_dsp *rt5677_dsp;
385
+
386
+ rt5677_dsp = devm_kzalloc(component->dev, sizeof(*rt5677_dsp),
387
+ GFP_KERNEL);
388
+ if (!rt5677_dsp)
389
+ return -ENOMEM;
390
+ rt5677_dsp->dev = &g_spi->dev;
391
+ mutex_init(&rt5677_dsp->dma_lock);
392
+ INIT_DELAYED_WORK(&rt5677_dsp->copy_work, rt5677_spi_copy_work);
393
+
394
+ snd_soc_component_set_drvdata(component, rt5677_dsp);
395
+ return 0;
396
+}
397
+
398
+static const struct snd_soc_component_driver rt5677_spi_dai_component = {
399
+ .name = DRV_NAME,
400
+ .probe = rt5677_spi_pcm_probe,
401
+ .open = rt5677_spi_pcm_open,
402
+ .close = rt5677_spi_pcm_close,
403
+ .hw_params = rt5677_spi_hw_params,
404
+ .hw_free = rt5677_spi_hw_free,
405
+ .prepare = rt5677_spi_prepare,
406
+ .pointer = rt5677_spi_pcm_pointer,
407
+ .pcm_construct = rt5677_spi_pcm_new,
408
+};
51409
52410 /* Select a suitable transfer command for the next transfer to ensure
53411 * the transfer address is always naturally aligned while minimizing
....@@ -101,7 +459,7 @@
101459 u32 word_size = min_t(u32, dstlen, 8);
102460
103461 for (w = 0; w < dstlen; w += word_size) {
104
- for (i = 0; i < word_size; i++) {
462
+ for (i = 0; i < word_size && i + w < dstlen; i++) {
105463 si = w + word_size - i - 1;
106464 dst[w + i] = si < srclen ? src[si] : 0;
107465 }
....@@ -152,8 +510,9 @@
152510 status |= spi_sync(g_spi, &m);
153511 mutex_unlock(&spi_mutex);
154512
513
+
155514 /* Copy data back to caller buffer */
156
- rt5677_spi_reverse(cb + offset, t[1].len, body, t[1].len);
515
+ rt5677_spi_reverse(cb + offset, len - offset, body, t[1].len);
157516 }
158517 return status;
159518 }
....@@ -218,15 +577,55 @@
218577 }
219578 EXPORT_SYMBOL_GPL(rt5677_spi_write_firmware);
220579
580
+void rt5677_spi_hotword_detected(void)
581
+{
582
+ struct rt5677_dsp *rt5677_dsp;
583
+
584
+ if (!g_spi)
585
+ return;
586
+
587
+ rt5677_dsp = dev_get_drvdata(&g_spi->dev);
588
+ if (!rt5677_dsp) {
589
+ dev_err(&g_spi->dev, "Can't get rt5677_dsp\n");
590
+ return;
591
+ }
592
+
593
+ mutex_lock(&rt5677_dsp->dma_lock);
594
+ dev_info(rt5677_dsp->dev, "Hotword detected\n");
595
+ rt5677_dsp->new_hotword = true;
596
+ mutex_unlock(&rt5677_dsp->dma_lock);
597
+
598
+ schedule_delayed_work(&rt5677_dsp->copy_work, 0);
599
+}
600
+EXPORT_SYMBOL_GPL(rt5677_spi_hotword_detected);
601
+
221602 static int rt5677_spi_probe(struct spi_device *spi)
222603 {
604
+ int ret;
605
+
223606 g_spi = spi;
224
- return 0;
607
+
608
+ ret = devm_snd_soc_register_component(&spi->dev,
609
+ &rt5677_spi_dai_component,
610
+ &rt5677_spi_dai, 1);
611
+ if (ret < 0)
612
+ dev_err(&spi->dev, "Failed to register component.\n");
613
+
614
+ return ret;
225615 }
616
+
617
+#ifdef CONFIG_ACPI
618
+static const struct acpi_device_id rt5677_spi_acpi_id[] = {
619
+ { "RT5677AA", 0 },
620
+ { }
621
+};
622
+MODULE_DEVICE_TABLE(acpi, rt5677_spi_acpi_id);
623
+#endif
226624
227625 static struct spi_driver rt5677_spi_driver = {
228626 .driver = {
229
- .name = "rt5677",
627
+ .name = DRV_NAME,
628
+ .acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id),
230629 },
231630 .probe = rt5677_spi_probe,
232631 };