xref: /openbmc/linux/sound/soc/codecs/rt5677-spi.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2af48f1d0SOder Chiou /*
3af48f1d0SOder Chiou  * rt5677-spi.c  --  RT5677 ALSA SoC audio codec driver
4af48f1d0SOder Chiou  *
5af48f1d0SOder Chiou  * Copyright 2013 Realtek Semiconductor Corp.
6af48f1d0SOder Chiou  * Author: Oder Chiou <oder_chiou@realtek.com>
7af48f1d0SOder Chiou  */
8af48f1d0SOder Chiou 
9af48f1d0SOder Chiou #include <linux/module.h>
10af48f1d0SOder Chiou #include <linux/input.h>
11af48f1d0SOder Chiou #include <linux/spi/spi.h>
12af48f1d0SOder Chiou #include <linux/device.h>
13af48f1d0SOder Chiou #include <linux/init.h>
14af48f1d0SOder Chiou #include <linux/delay.h>
15af48f1d0SOder Chiou #include <linux/interrupt.h>
16af48f1d0SOder Chiou #include <linux/irq.h>
17af48f1d0SOder Chiou #include <linux/slab.h>
18af48f1d0SOder Chiou #include <linux/sched.h>
19af48f1d0SOder Chiou #include <linux/uaccess.h>
20af48f1d0SOder Chiou #include <linux/regulator/consumer.h>
21af48f1d0SOder Chiou #include <linux/pm_qos.h>
22af48f1d0SOder Chiou #include <linux/sysfs.h>
23af48f1d0SOder Chiou #include <linux/clk.h>
24af48f1d0SOder Chiou #include <linux/firmware.h>
252b070f67SOder Chiou #include <linux/acpi.h>
26af48f1d0SOder Chiou 
27a0e0d135SBen Zhang #include <sound/soc.h>
28a0e0d135SBen Zhang 
29461c6232SBen Zhang #include "rt5677.h"
30af48f1d0SOder Chiou #include "rt5677-spi.h"
31af48f1d0SOder Chiou 
32b9960f6eSCurtis Malainey #define DRV_NAME "rt5677spi"
33b9960f6eSCurtis Malainey 
347d4d443eSBen Zhang #define RT5677_SPI_BURST_LEN	240
357d4d443eSBen Zhang #define RT5677_SPI_HEADER	5
367d4d443eSBen Zhang #define RT5677_SPI_FREQ		6000000
37af48f1d0SOder Chiou 
387d4d443eSBen Zhang /* The AddressPhase and DataPhase of SPI commands are MSB first on the wire.
397d4d443eSBen Zhang  * DataPhase word size of 16-bit commands is 2 bytes.
407d4d443eSBen Zhang  * DataPhase word size of 32-bit commands is 4 bytes.
417d4d443eSBen Zhang  * DataPhase word size of burst commands is 8 bytes.
427d4d443eSBen Zhang  * The DSP CPU is little-endian.
43af48f1d0SOder Chiou  */
447d4d443eSBen Zhang #define RT5677_SPI_WRITE_BURST	0x5
457d4d443eSBen Zhang #define RT5677_SPI_READ_BURST	0x4
467d4d443eSBen Zhang #define RT5677_SPI_WRITE_32	0x3
477d4d443eSBen Zhang #define RT5677_SPI_READ_32	0x2
487d4d443eSBen Zhang #define RT5677_SPI_WRITE_16	0x1
497d4d443eSBen Zhang #define RT5677_SPI_READ_16	0x0
507d4d443eSBen Zhang 
51a0e0d135SBen Zhang #define RT5677_BUF_BYTES_TOTAL		0x20000
52a0e0d135SBen Zhang #define RT5677_MIC_BUF_ADDR		0x60030000
53a0e0d135SBen Zhang #define RT5677_MODEL_ADDR		0x5FFC9800
54a0e0d135SBen Zhang #define RT5677_MIC_BUF_BYTES		((u32)(RT5677_BUF_BYTES_TOTAL - \
55a0e0d135SBen Zhang 					sizeof(u32)))
56a0e0d135SBen Zhang #define RT5677_MIC_BUF_FIRST_READ_SIZE	0x10000
57a0e0d135SBen Zhang 
587d4d443eSBen Zhang static struct spi_device *g_spi;
597d4d443eSBen Zhang static DEFINE_MUTEX(spi_mutex);
607d4d443eSBen Zhang 
61a0e0d135SBen Zhang struct rt5677_dsp {
62a0e0d135SBen Zhang 	struct device *dev;
63a0e0d135SBen Zhang 	struct delayed_work copy_work;
64a0e0d135SBen Zhang 	struct mutex dma_lock;
65a0e0d135SBen Zhang 	struct snd_pcm_substream *substream;
66a0e0d135SBen Zhang 	size_t dma_offset;	/* zero-based offset into runtime->dma_area */
67a0e0d135SBen Zhang 	size_t avail_bytes;	/* number of new bytes since last period */
68a0e0d135SBen Zhang 	u32 mic_read_offset;	/* zero-based offset into DSP's mic buffer */
69a0e0d135SBen Zhang 	bool new_hotword;	/* a new hotword is fired */
70a0e0d135SBen Zhang };
71a0e0d135SBen Zhang 
72a0e0d135SBen Zhang static const struct snd_pcm_hardware rt5677_spi_pcm_hardware = {
73a0e0d135SBen Zhang 	.info			= SNDRV_PCM_INFO_MMAP |
74a0e0d135SBen Zhang 				  SNDRV_PCM_INFO_MMAP_VALID |
75a0e0d135SBen Zhang 				  SNDRV_PCM_INFO_INTERLEAVED,
76a0e0d135SBen Zhang 	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
77a0e0d135SBen Zhang 	.period_bytes_min	= PAGE_SIZE,
78a0e0d135SBen Zhang 	.period_bytes_max	= RT5677_BUF_BYTES_TOTAL / 8,
79a0e0d135SBen Zhang 	.periods_min		= 8,
80a0e0d135SBen Zhang 	.periods_max		= 8,
81a0e0d135SBen Zhang 	.channels_min		= 1,
82a0e0d135SBen Zhang 	.channels_max		= 1,
83a0e0d135SBen Zhang 	.buffer_bytes_max	= RT5677_BUF_BYTES_TOTAL,
84a0e0d135SBen Zhang };
85a0e0d135SBen Zhang 
86a0e0d135SBen Zhang static struct snd_soc_dai_driver rt5677_spi_dai = {
87a0e0d135SBen Zhang 	/* The DAI name "rt5677-dsp-cpu-dai" is not used. The actual DAI name
88a0e0d135SBen Zhang 	 * registered with ASoC is the name of the device "spi-RT5677AA:00",
89a0e0d135SBen Zhang 	 * because we only have one DAI. See snd_soc_register_dais().
90a0e0d135SBen Zhang 	 */
91a0e0d135SBen Zhang 	.name = "rt5677-dsp-cpu-dai",
92a0e0d135SBen Zhang 	.id = 0,
93a0e0d135SBen Zhang 	.capture = {
94a0e0d135SBen Zhang 		.stream_name = "DSP Capture",
95a0e0d135SBen Zhang 		.channels_min = 1,
96a0e0d135SBen Zhang 		.channels_max = 1,
97a0e0d135SBen Zhang 		.rates = SNDRV_PCM_RATE_16000,
98a0e0d135SBen Zhang 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
99a0e0d135SBen Zhang 	},
100a0e0d135SBen Zhang };
101a0e0d135SBen Zhang 
102a0e0d135SBen Zhang /* PCM for streaming audio from the DSP buffer */
rt5677_spi_pcm_open(struct snd_soc_component * component,struct snd_pcm_substream * substream)103a0e0d135SBen Zhang static int rt5677_spi_pcm_open(
104a0e0d135SBen Zhang 		struct snd_soc_component *component,
105a0e0d135SBen Zhang 		struct snd_pcm_substream *substream)
106a0e0d135SBen Zhang {
107a0e0d135SBen Zhang 	snd_soc_set_runtime_hwparams(substream, &rt5677_spi_pcm_hardware);
108a0e0d135SBen Zhang 	return 0;
109a0e0d135SBen Zhang }
110a0e0d135SBen Zhang 
rt5677_spi_pcm_close(struct snd_soc_component * component,struct snd_pcm_substream * substream)111a0e0d135SBen Zhang static int rt5677_spi_pcm_close(
112a0e0d135SBen Zhang 		struct snd_soc_component *component,
113a0e0d135SBen Zhang 		struct snd_pcm_substream *substream)
114a0e0d135SBen Zhang {
1153e3b6295SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
116461c6232SBen Zhang 	struct snd_soc_component *codec_component =
117461c6232SBen Zhang 			snd_soc_rtdcom_lookup(rtd, "rt5677");
118461c6232SBen Zhang 	struct rt5677_priv *rt5677 =
119461c6232SBen Zhang 			snd_soc_component_get_drvdata(codec_component);
120a0e0d135SBen Zhang 	struct rt5677_dsp *rt5677_dsp =
121a0e0d135SBen Zhang 			snd_soc_component_get_drvdata(component);
122a0e0d135SBen Zhang 
123a0e0d135SBen Zhang 	cancel_delayed_work_sync(&rt5677_dsp->copy_work);
124461c6232SBen Zhang 	rt5677->set_dsp_vad(codec_component, false);
125a0e0d135SBen Zhang 	return 0;
126a0e0d135SBen Zhang }
127a0e0d135SBen Zhang 
rt5677_spi_hw_params(struct snd_soc_component * component,struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)128a0e0d135SBen Zhang static int rt5677_spi_hw_params(
129a0e0d135SBen Zhang 		struct snd_soc_component *component,
130a0e0d135SBen Zhang 		struct snd_pcm_substream *substream,
131a0e0d135SBen Zhang 		struct snd_pcm_hw_params *hw_params)
132a0e0d135SBen Zhang {
133a0e0d135SBen Zhang 	struct rt5677_dsp *rt5677_dsp =
134a0e0d135SBen Zhang 			snd_soc_component_get_drvdata(component);
135a0e0d135SBen Zhang 
136a0e0d135SBen Zhang 	mutex_lock(&rt5677_dsp->dma_lock);
137a0e0d135SBen Zhang 	rt5677_dsp->substream = substream;
138a0e0d135SBen Zhang 	mutex_unlock(&rt5677_dsp->dma_lock);
139a0e0d135SBen Zhang 
1409a560089STakashi Iwai 	return 0;
141a0e0d135SBen Zhang }
142a0e0d135SBen Zhang 
rt5677_spi_hw_free(struct snd_soc_component * component,struct snd_pcm_substream * substream)143a0e0d135SBen Zhang static int rt5677_spi_hw_free(
144a0e0d135SBen Zhang 		struct snd_soc_component *component,
145a0e0d135SBen Zhang 		struct snd_pcm_substream *substream)
146a0e0d135SBen Zhang {
147a0e0d135SBen Zhang 	struct rt5677_dsp *rt5677_dsp =
148a0e0d135SBen Zhang 			snd_soc_component_get_drvdata(component);
149a0e0d135SBen Zhang 
150a0e0d135SBen Zhang 	mutex_lock(&rt5677_dsp->dma_lock);
1516442793aSCurtis Malainey 	rt5677_dsp->substream = NULL;
152a0e0d135SBen Zhang 	mutex_unlock(&rt5677_dsp->dma_lock);
153a0e0d135SBen Zhang 
1549a560089STakashi Iwai 	return 0;
155a0e0d135SBen Zhang }
156a0e0d135SBen Zhang 
rt5677_spi_prepare(struct snd_soc_component * component,struct snd_pcm_substream * substream)157a0e0d135SBen Zhang static int rt5677_spi_prepare(
158a0e0d135SBen Zhang 		struct snd_soc_component *component,
159a0e0d135SBen Zhang 		struct snd_pcm_substream *substream)
160a0e0d135SBen Zhang {
1613e3b6295SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
162461c6232SBen Zhang 	struct snd_soc_component *rt5677_component =
163461c6232SBen Zhang 			snd_soc_rtdcom_lookup(rtd, "rt5677");
164461c6232SBen Zhang 	struct rt5677_priv *rt5677 =
165461c6232SBen Zhang 			snd_soc_component_get_drvdata(rt5677_component);
166a0e0d135SBen Zhang 	struct rt5677_dsp *rt5677_dsp =
167a0e0d135SBen Zhang 			snd_soc_component_get_drvdata(component);
168a0e0d135SBen Zhang 
169461c6232SBen Zhang 	rt5677->set_dsp_vad(rt5677_component, true);
170a0e0d135SBen Zhang 	rt5677_dsp->dma_offset = 0;
171a0e0d135SBen Zhang 	rt5677_dsp->avail_bytes = 0;
172a0e0d135SBen Zhang 	return 0;
173a0e0d135SBen Zhang }
174a0e0d135SBen Zhang 
rt5677_spi_pcm_pointer(struct snd_soc_component * component,struct snd_pcm_substream * substream)175a0e0d135SBen Zhang static snd_pcm_uframes_t rt5677_spi_pcm_pointer(
176a0e0d135SBen Zhang 		struct snd_soc_component *component,
177a0e0d135SBen Zhang 		struct snd_pcm_substream *substream)
178a0e0d135SBen Zhang {
179a0e0d135SBen Zhang 	struct snd_pcm_runtime *runtime = substream->runtime;
180a0e0d135SBen Zhang 	struct rt5677_dsp *rt5677_dsp =
181a0e0d135SBen Zhang 			snd_soc_component_get_drvdata(component);
182a0e0d135SBen Zhang 
183a0e0d135SBen Zhang 	return bytes_to_frames(runtime, rt5677_dsp->dma_offset);
184a0e0d135SBen Zhang }
185a0e0d135SBen Zhang 
rt5677_spi_mic_write_offset(u32 * mic_write_offset)186a0e0d135SBen Zhang static int rt5677_spi_mic_write_offset(u32 *mic_write_offset)
187a0e0d135SBen Zhang {
188a0e0d135SBen Zhang 	int ret;
189a0e0d135SBen Zhang 	/* Grab the first 4 bytes that hold the write pointer on the
190a0e0d135SBen Zhang 	 * dsp, and check to make sure that it points somewhere inside the
191a0e0d135SBen Zhang 	 * buffer.
192a0e0d135SBen Zhang 	 */
193a0e0d135SBen Zhang 	ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR, mic_write_offset,
194a0e0d135SBen Zhang 			sizeof(u32));
195a0e0d135SBen Zhang 	if (ret)
196a0e0d135SBen Zhang 		return ret;
197a0e0d135SBen Zhang 	/* Adjust the offset so that it's zero-based */
198a0e0d135SBen Zhang 	*mic_write_offset = *mic_write_offset - sizeof(u32);
199a0e0d135SBen Zhang 	return *mic_write_offset < RT5677_MIC_BUF_BYTES ? 0 : -EFAULT;
200a0e0d135SBen Zhang }
201a0e0d135SBen Zhang 
202a0e0d135SBen Zhang /*
203a0e0d135SBen Zhang  * Copy one contiguous block of audio samples from the DSP mic buffer to the
204a0e0d135SBen Zhang  * dma_area of the pcm runtime. The receiving buffer may wrap around.
205a0e0d135SBen Zhang  * @begin: start offset of the block to copy, in bytes.
206a0e0d135SBen Zhang  * @end:   offset of the first byte after the block to copy, must be greater
207a0e0d135SBen Zhang  *         than or equal to begin.
208a0e0d135SBen Zhang  *
209a0e0d135SBen Zhang  * Return: Zero if successful, or a negative error code on failure.
210a0e0d135SBen Zhang  */
rt5677_spi_copy_block(struct rt5677_dsp * rt5677_dsp,u32 begin,u32 end)211a0e0d135SBen Zhang static int rt5677_spi_copy_block(struct rt5677_dsp *rt5677_dsp,
212a0e0d135SBen Zhang 		u32 begin, u32 end)
213a0e0d135SBen Zhang {
214a0e0d135SBen Zhang 	struct snd_pcm_runtime *runtime = rt5677_dsp->substream->runtime;
215a0e0d135SBen Zhang 	size_t bytes_per_frame = frames_to_bytes(runtime, 1);
216a0e0d135SBen Zhang 	size_t first_chunk_len, second_chunk_len;
217a0e0d135SBen Zhang 	int ret;
218a0e0d135SBen Zhang 
219a0e0d135SBen Zhang 	if (begin > end || runtime->dma_bytes < 2 * bytes_per_frame) {
220a0e0d135SBen Zhang 		dev_err(rt5677_dsp->dev,
221a0e0d135SBen Zhang 			"Invalid copy from (%u, %u), dma_area size %zu\n",
222a0e0d135SBen Zhang 			begin, end, runtime->dma_bytes);
223a0e0d135SBen Zhang 		return -EINVAL;
224a0e0d135SBen Zhang 	}
225a0e0d135SBen Zhang 
226a0e0d135SBen Zhang 	/* The block to copy is empty */
227a0e0d135SBen Zhang 	if (begin == end)
228a0e0d135SBen Zhang 		return 0;
229a0e0d135SBen Zhang 
230a0e0d135SBen Zhang 	/* If the incoming chunk is too big for the receiving buffer, only the
231a0e0d135SBen Zhang 	 * last "receiving buffer size - one frame" bytes are copied.
232a0e0d135SBen Zhang 	 */
233a0e0d135SBen Zhang 	if (end - begin > runtime->dma_bytes - bytes_per_frame)
234a0e0d135SBen Zhang 		begin = end - (runtime->dma_bytes - bytes_per_frame);
235a0e0d135SBen Zhang 
236a0e0d135SBen Zhang 	/* May need to split to two chunks, calculate the size of each */
237a0e0d135SBen Zhang 	first_chunk_len = end - begin;
238a0e0d135SBen Zhang 	second_chunk_len = 0;
239a0e0d135SBen Zhang 	if (rt5677_dsp->dma_offset + first_chunk_len > runtime->dma_bytes) {
240a0e0d135SBen Zhang 		/* Receiving buffer wrapped around */
241a0e0d135SBen Zhang 		second_chunk_len = first_chunk_len;
242a0e0d135SBen Zhang 		first_chunk_len = runtime->dma_bytes - rt5677_dsp->dma_offset;
243a0e0d135SBen Zhang 		second_chunk_len -= first_chunk_len;
244a0e0d135SBen Zhang 	}
245a0e0d135SBen Zhang 
246a0e0d135SBen Zhang 	/* Copy first chunk */
247a0e0d135SBen Zhang 	ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) + begin,
248a0e0d135SBen Zhang 			runtime->dma_area + rt5677_dsp->dma_offset,
249a0e0d135SBen Zhang 			first_chunk_len);
250a0e0d135SBen Zhang 	if (ret)
251a0e0d135SBen Zhang 		return ret;
252a0e0d135SBen Zhang 	rt5677_dsp->dma_offset += first_chunk_len;
253a0e0d135SBen Zhang 	if (rt5677_dsp->dma_offset == runtime->dma_bytes)
254a0e0d135SBen Zhang 		rt5677_dsp->dma_offset = 0;
255a0e0d135SBen Zhang 
256a0e0d135SBen Zhang 	/* Copy second chunk */
257a0e0d135SBen Zhang 	if (second_chunk_len) {
258a0e0d135SBen Zhang 		ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) +
259a0e0d135SBen Zhang 				begin + first_chunk_len, runtime->dma_area,
260a0e0d135SBen Zhang 				second_chunk_len);
261a0e0d135SBen Zhang 		if (!ret)
262a0e0d135SBen Zhang 			rt5677_dsp->dma_offset = second_chunk_len;
263a0e0d135SBen Zhang 	}
264a0e0d135SBen Zhang 	return ret;
265a0e0d135SBen Zhang }
266a0e0d135SBen Zhang 
267a0e0d135SBen Zhang /*
268a0e0d135SBen Zhang  * Copy a given amount of audio samples from the DSP mic buffer starting at
269a0e0d135SBen Zhang  * mic_read_offset, to the dma_area of the pcm runtime. The source buffer may
270a0e0d135SBen Zhang  * wrap around. mic_read_offset is updated after successful copy.
271a0e0d135SBen Zhang  * @amount: amount of samples to copy, in bytes.
272a0e0d135SBen Zhang  *
273a0e0d135SBen Zhang  * Return: Zero if successful, or a negative error code on failure.
274a0e0d135SBen Zhang  */
rt5677_spi_copy(struct rt5677_dsp * rt5677_dsp,u32 amount)275a0e0d135SBen Zhang static int rt5677_spi_copy(struct rt5677_dsp *rt5677_dsp, u32 amount)
276a0e0d135SBen Zhang {
277a0e0d135SBen Zhang 	int ret = 0;
278a0e0d135SBen Zhang 	u32 target;
279a0e0d135SBen Zhang 
280a0e0d135SBen Zhang 	if (amount == 0)
281a0e0d135SBen Zhang 		return ret;
282a0e0d135SBen Zhang 
283a0e0d135SBen Zhang 	target = rt5677_dsp->mic_read_offset + amount;
284a0e0d135SBen Zhang 	/* Copy the first chunk in DSP's mic buffer */
285a0e0d135SBen Zhang 	ret |= rt5677_spi_copy_block(rt5677_dsp, rt5677_dsp->mic_read_offset,
286a0e0d135SBen Zhang 			min(target, RT5677_MIC_BUF_BYTES));
287a0e0d135SBen Zhang 
288a0e0d135SBen Zhang 	if (target >= RT5677_MIC_BUF_BYTES) {
289a0e0d135SBen Zhang 		/* Wrap around, copy the second chunk */
290a0e0d135SBen Zhang 		target -= RT5677_MIC_BUF_BYTES;
291a0e0d135SBen Zhang 		ret |= rt5677_spi_copy_block(rt5677_dsp, 0, target);
292a0e0d135SBen Zhang 	}
293a0e0d135SBen Zhang 
294a0e0d135SBen Zhang 	if (!ret)
295a0e0d135SBen Zhang 		rt5677_dsp->mic_read_offset = target;
296a0e0d135SBen Zhang 	return ret;
297a0e0d135SBen Zhang }
298a0e0d135SBen Zhang 
299a0e0d135SBen Zhang /*
300a0e0d135SBen Zhang  * A delayed work that streams audio samples from the DSP mic buffer to the
301a0e0d135SBen Zhang  * dma_area of the pcm runtime via SPI.
302a0e0d135SBen Zhang  */
rt5677_spi_copy_work(struct work_struct * work)303a0e0d135SBen Zhang static void rt5677_spi_copy_work(struct work_struct *work)
304a0e0d135SBen Zhang {
305a0e0d135SBen Zhang 	struct rt5677_dsp *rt5677_dsp =
306a0e0d135SBen Zhang 		container_of(work, struct rt5677_dsp, copy_work.work);
307a0e0d135SBen Zhang 	struct snd_pcm_runtime *runtime;
308a0e0d135SBen Zhang 	u32 mic_write_offset;
309a0e0d135SBen Zhang 	size_t new_bytes, copy_bytes, period_bytes;
310a0e0d135SBen Zhang 	unsigned int delay;
311a0e0d135SBen Zhang 	int ret = 0;
312a0e0d135SBen Zhang 
313a0e0d135SBen Zhang 	/* Ensure runtime->dma_area buffer does not go away while copying. */
314a0e0d135SBen Zhang 	mutex_lock(&rt5677_dsp->dma_lock);
315a0e0d135SBen Zhang 	if (!rt5677_dsp->substream) {
316a0e0d135SBen Zhang 		dev_err(rt5677_dsp->dev, "No pcm substream\n");
317a0e0d135SBen Zhang 		goto done;
318a0e0d135SBen Zhang 	}
319a0e0d135SBen Zhang 
320a0e0d135SBen Zhang 	runtime = rt5677_dsp->substream->runtime;
321a0e0d135SBen Zhang 
322a0e0d135SBen Zhang 	if (rt5677_spi_mic_write_offset(&mic_write_offset)) {
323a0e0d135SBen Zhang 		dev_err(rt5677_dsp->dev, "No mic_write_offset\n");
324a0e0d135SBen Zhang 		goto done;
325a0e0d135SBen Zhang 	}
326a0e0d135SBen Zhang 
327a0e0d135SBen Zhang 	/* If this is the first time that we've asked for streaming data after
328a0e0d135SBen Zhang 	 * a hotword is fired, we should start reading from the previous 2
329a0e0d135SBen Zhang 	 * seconds of audio from wherever the mic_write_offset is currently.
330a0e0d135SBen Zhang 	 */
331a0e0d135SBen Zhang 	if (rt5677_dsp->new_hotword) {
332a0e0d135SBen Zhang 		rt5677_dsp->new_hotword = false;
333a0e0d135SBen Zhang 		/* See if buffer wraparound happens */
334a0e0d135SBen Zhang 		if (mic_write_offset < RT5677_MIC_BUF_FIRST_READ_SIZE)
335a0e0d135SBen Zhang 			rt5677_dsp->mic_read_offset = RT5677_MIC_BUF_BYTES -
336a0e0d135SBen Zhang 					(RT5677_MIC_BUF_FIRST_READ_SIZE -
337a0e0d135SBen Zhang 					mic_write_offset);
338a0e0d135SBen Zhang 		else
339a0e0d135SBen Zhang 			rt5677_dsp->mic_read_offset = mic_write_offset -
340a0e0d135SBen Zhang 					RT5677_MIC_BUF_FIRST_READ_SIZE;
341a0e0d135SBen Zhang 	}
342a0e0d135SBen Zhang 
343a0e0d135SBen Zhang 	/* Calculate the amount of new samples in bytes */
344a0e0d135SBen Zhang 	if (rt5677_dsp->mic_read_offset <= mic_write_offset)
345a0e0d135SBen Zhang 		new_bytes = mic_write_offset - rt5677_dsp->mic_read_offset;
346a0e0d135SBen Zhang 	else
347a0e0d135SBen Zhang 		new_bytes = RT5677_MIC_BUF_BYTES + mic_write_offset
348a0e0d135SBen Zhang 				- rt5677_dsp->mic_read_offset;
349a0e0d135SBen Zhang 
350a0e0d135SBen Zhang 	/* Copy all new samples from DSP mic buffer, one period at a time */
351a0e0d135SBen Zhang 	period_bytes = snd_pcm_lib_period_bytes(rt5677_dsp->substream);
352a0e0d135SBen Zhang 	while (new_bytes) {
353a0e0d135SBen Zhang 		copy_bytes = min(new_bytes, period_bytes
354a0e0d135SBen Zhang 				- rt5677_dsp->avail_bytes);
355a0e0d135SBen Zhang 		ret = rt5677_spi_copy(rt5677_dsp, copy_bytes);
356a0e0d135SBen Zhang 		if (ret) {
357a0e0d135SBen Zhang 			dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret);
358a0e0d135SBen Zhang 			goto done;
359a0e0d135SBen Zhang 		}
360a0e0d135SBen Zhang 		rt5677_dsp->avail_bytes += copy_bytes;
361a0e0d135SBen Zhang 		if (rt5677_dsp->avail_bytes >= period_bytes) {
362a0e0d135SBen Zhang 			snd_pcm_period_elapsed(rt5677_dsp->substream);
363a0e0d135SBen Zhang 			rt5677_dsp->avail_bytes = 0;
364a0e0d135SBen Zhang 		}
365a0e0d135SBen Zhang 		new_bytes -= copy_bytes;
366a0e0d135SBen Zhang 	}
367a0e0d135SBen Zhang 
368a0e0d135SBen Zhang 	delay = bytes_to_frames(runtime, period_bytes) / (runtime->rate / 1000);
369a0e0d135SBen Zhang 	schedule_delayed_work(&rt5677_dsp->copy_work, msecs_to_jiffies(delay));
370a0e0d135SBen Zhang done:
371a0e0d135SBen Zhang 	mutex_unlock(&rt5677_dsp->dma_lock);
372a0e0d135SBen Zhang }
373a0e0d135SBen Zhang 
rt5677_spi_pcm_new(struct snd_soc_component * component,struct snd_soc_pcm_runtime * rtd)374f541220cSTakashi Iwai static int rt5677_spi_pcm_new(struct snd_soc_component *component,
375f541220cSTakashi Iwai 			      struct snd_soc_pcm_runtime *rtd)
376a0e0d135SBen Zhang {
3779a560089STakashi Iwai 	snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC,
378f541220cSTakashi Iwai 				       NULL, 0, 0);
379f541220cSTakashi Iwai 	return 0;
380a0e0d135SBen Zhang }
381a0e0d135SBen Zhang 
rt5677_spi_pcm_probe(struct snd_soc_component * component)382a0e0d135SBen Zhang static int rt5677_spi_pcm_probe(struct snd_soc_component *component)
383a0e0d135SBen Zhang {
384a0e0d135SBen Zhang 	struct rt5677_dsp *rt5677_dsp;
385a0e0d135SBen Zhang 
386a0e0d135SBen Zhang 	rt5677_dsp = devm_kzalloc(component->dev, sizeof(*rt5677_dsp),
387a0e0d135SBen Zhang 			GFP_KERNEL);
388f8a60435SColin Ian King 	if (!rt5677_dsp)
389f8a60435SColin Ian King 		return -ENOMEM;
390a0e0d135SBen Zhang 	rt5677_dsp->dev = &g_spi->dev;
391a0e0d135SBen Zhang 	mutex_init(&rt5677_dsp->dma_lock);
392a0e0d135SBen Zhang 	INIT_DELAYED_WORK(&rt5677_dsp->copy_work, rt5677_spi_copy_work);
393a0e0d135SBen Zhang 
394a0e0d135SBen Zhang 	snd_soc_component_set_drvdata(component, rt5677_dsp);
395a0e0d135SBen Zhang 	return 0;
396a0e0d135SBen Zhang }
397a0e0d135SBen Zhang 
398a0e0d135SBen Zhang static const struct snd_soc_component_driver rt5677_spi_dai_component = {
399a0e0d135SBen Zhang 	.name			= DRV_NAME,
400a0e0d135SBen Zhang 	.probe			= rt5677_spi_pcm_probe,
401a0e0d135SBen Zhang 	.open			= rt5677_spi_pcm_open,
402a0e0d135SBen Zhang 	.close			= rt5677_spi_pcm_close,
403a0e0d135SBen Zhang 	.hw_params		= rt5677_spi_hw_params,
404a0e0d135SBen Zhang 	.hw_free		= rt5677_spi_hw_free,
405a0e0d135SBen Zhang 	.prepare		= rt5677_spi_prepare,
406a0e0d135SBen Zhang 	.pointer		= rt5677_spi_pcm_pointer,
407f541220cSTakashi Iwai 	.pcm_construct		= rt5677_spi_pcm_new,
408*a1dca877SJason Montleon 	.legacy_dai_naming	= 1,
409a0e0d135SBen Zhang };
410a0e0d135SBen Zhang 
4117d4d443eSBen Zhang /* Select a suitable transfer command for the next transfer to ensure
4127d4d443eSBen Zhang  * the transfer address is always naturally aligned while minimizing
4137d4d443eSBen Zhang  * the total number of transfers required.
4147d4d443eSBen Zhang  *
4157d4d443eSBen Zhang  * 3 transfer commands are available:
4167d4d443eSBen Zhang  * RT5677_SPI_READ/WRITE_16:	Transfer 2 bytes
4177d4d443eSBen Zhang  * RT5677_SPI_READ/WRITE_32:	Transfer 4 bytes
4187d4d443eSBen Zhang  * RT5677_SPI_READ/WRITE_BURST:	Transfer any multiples of 8 bytes
4197d4d443eSBen Zhang  *
420a46eb523SCurtis Malainey  * Note:
421a46eb523SCurtis Malainey  * 16 Bit writes and reads are restricted to the address range
422a46eb523SCurtis Malainey  * 0x18020000 ~ 0x18021000
423a46eb523SCurtis Malainey  *
424a46eb523SCurtis Malainey  * For example, reading 256 bytes at 0x60030004 uses the following commands:
4257d4d443eSBen Zhang  * 0x60030004 RT5677_SPI_READ_32	4 bytes
4267d4d443eSBen Zhang  * 0x60030008 RT5677_SPI_READ_BURST	240 bytes
4277d4d443eSBen Zhang  * 0x600300F8 RT5677_SPI_READ_BURST	8 bytes
4287d4d443eSBen Zhang  * 0x60030100 RT5677_SPI_READ_32	4 bytes
4297d4d443eSBen Zhang  *
4307d4d443eSBen Zhang  * Input:
4317d4d443eSBen Zhang  * @read: true for read commands; false for write commands
4327d4d443eSBen Zhang  * @align: alignment of the next transfer address
4337d4d443eSBen Zhang  * @remain: number of bytes remaining to transfer
4347d4d443eSBen Zhang  *
4357d4d443eSBen Zhang  * Output:
4367d4d443eSBen Zhang  * @len: number of bytes to transfer with the selected command
4377d4d443eSBen Zhang  * Returns the selected command
4387d4d443eSBen Zhang  */
rt5677_spi_select_cmd(bool read,u32 align,u32 remain,u32 * len)4397d4d443eSBen Zhang static u8 rt5677_spi_select_cmd(bool read, u32 align, u32 remain, u32 *len)
440af48f1d0SOder Chiou {
4417d4d443eSBen Zhang 	u8 cmd;
442af48f1d0SOder Chiou 
443a46eb523SCurtis Malainey 	if (align == 4 || remain <= 4) {
4447d4d443eSBen Zhang 		cmd = RT5677_SPI_READ_32;
4457d4d443eSBen Zhang 		*len = 4;
4467d4d443eSBen Zhang 	} else {
4477d4d443eSBen Zhang 		cmd = RT5677_SPI_READ_BURST;
448a46eb523SCurtis Malainey 		*len = (((remain - 1) >> 3) + 1) << 3;
449a46eb523SCurtis Malainey 		*len = min_t(u32, *len, RT5677_SPI_BURST_LEN);
4507d4d443eSBen Zhang 	}
4517d4d443eSBen Zhang 	return read ? cmd : cmd + 1;
4527d4d443eSBen Zhang }
453af48f1d0SOder Chiou 
4547d4d443eSBen Zhang /* Copy dstlen bytes from src to dst, while reversing byte order for each word.
4557d4d443eSBen Zhang  * If srclen < dstlen, zeros are padded.
4567d4d443eSBen Zhang  */
rt5677_spi_reverse(u8 * dst,u32 dstlen,const u8 * src,u32 srclen)4577d4d443eSBen Zhang static void rt5677_spi_reverse(u8 *dst, u32 dstlen, const u8 *src, u32 srclen)
4587d4d443eSBen Zhang {
4597d4d443eSBen Zhang 	u32 w, i, si;
4607d4d443eSBen Zhang 	u32 word_size = min_t(u32, dstlen, 8);
461af48f1d0SOder Chiou 
4627d4d443eSBen Zhang 	for (w = 0; w < dstlen; w += word_size) {
4637b8164c1SCurtis Malainey 		for (i = 0; i < word_size && i + w < dstlen; i++) {
4647d4d443eSBen Zhang 			si = w + word_size - i - 1;
4657d4d443eSBen Zhang 			dst[w + i] = si < srclen ? src[si] : 0;
4667d4d443eSBen Zhang 		}
4677d4d443eSBen Zhang 	}
4687d4d443eSBen Zhang }
4697d4d443eSBen Zhang 
470a46eb523SCurtis Malainey /* Read DSP address space using SPI. addr and len have to be 4-byte aligned. */
rt5677_spi_read(u32 addr,void * rxbuf,size_t len)4717d4d443eSBen Zhang int rt5677_spi_read(u32 addr, void *rxbuf, size_t len)
4727d4d443eSBen Zhang {
4737d4d443eSBen Zhang 	u32 offset;
4747d4d443eSBen Zhang 	int status = 0;
4757d4d443eSBen Zhang 	struct spi_transfer t[2];
4767d4d443eSBen Zhang 	struct spi_message m;
4777d4d443eSBen Zhang 	/* +4 bytes is for the DummyPhase following the AddressPhase */
4787d4d443eSBen Zhang 	u8 header[RT5677_SPI_HEADER + 4];
4797d4d443eSBen Zhang 	u8 body[RT5677_SPI_BURST_LEN];
4807d4d443eSBen Zhang 	u8 spi_cmd;
4817d4d443eSBen Zhang 	u8 *cb = rxbuf;
4827d4d443eSBen Zhang 
4837d4d443eSBen Zhang 	if (!g_spi)
4847d4d443eSBen Zhang 		return -ENODEV;
4857d4d443eSBen Zhang 
486a46eb523SCurtis Malainey 	if ((addr & 3) || (len & 3)) {
4877d4d443eSBen Zhang 		dev_err(&g_spi->dev, "Bad read align 0x%x(%zu)\n", addr, len);
4887d4d443eSBen Zhang 		return -EACCES;
4897d4d443eSBen Zhang 	}
4907d4d443eSBen Zhang 
4917d4d443eSBen Zhang 	memset(t, 0, sizeof(t));
4927d4d443eSBen Zhang 	t[0].tx_buf = header;
4937d4d443eSBen Zhang 	t[0].len = sizeof(header);
4947d4d443eSBen Zhang 	t[0].speed_hz = RT5677_SPI_FREQ;
4957d4d443eSBen Zhang 	t[1].rx_buf = body;
4967d4d443eSBen Zhang 	t[1].speed_hz = RT5677_SPI_FREQ;
4977d4d443eSBen Zhang 	spi_message_init_with_transfers(&m, t, ARRAY_SIZE(t));
4987d4d443eSBen Zhang 
4997d4d443eSBen Zhang 	for (offset = 0; offset < len; offset += t[1].len) {
5007d4d443eSBen Zhang 		spi_cmd = rt5677_spi_select_cmd(true, (addr + offset) & 7,
5017d4d443eSBen Zhang 				len - offset, &t[1].len);
5027d4d443eSBen Zhang 
5037d4d443eSBen Zhang 		/* Construct SPI message header */
5047d4d443eSBen Zhang 		header[0] = spi_cmd;
5057d4d443eSBen Zhang 		header[1] = ((addr + offset) & 0xff000000) >> 24;
5067d4d443eSBen Zhang 		header[2] = ((addr + offset) & 0x00ff0000) >> 16;
5077d4d443eSBen Zhang 		header[3] = ((addr + offset) & 0x0000ff00) >> 8;
5087d4d443eSBen Zhang 		header[4] = ((addr + offset) & 0x000000ff) >> 0;
5097d4d443eSBen Zhang 
5107d4d443eSBen Zhang 		mutex_lock(&spi_mutex);
5117d4d443eSBen Zhang 		status |= spi_sync(g_spi, &m);
5127d4d443eSBen Zhang 		mutex_unlock(&spi_mutex);
5137d4d443eSBen Zhang 
5147b8164c1SCurtis Malainey 
5157d4d443eSBen Zhang 		/* Copy data back to caller buffer */
5167b8164c1SCurtis Malainey 		rt5677_spi_reverse(cb + offset, len - offset, body, t[1].len);
5177d4d443eSBen Zhang 	}
5187d4d443eSBen Zhang 	return status;
5197d4d443eSBen Zhang }
5207d4d443eSBen Zhang EXPORT_SYMBOL_GPL(rt5677_spi_read);
5217d4d443eSBen Zhang 
522a46eb523SCurtis Malainey /* Write DSP address space using SPI. addr has to be 4-byte aligned.
523a46eb523SCurtis Malainey  * If len is not 4-byte aligned, then extra zeros are written at the end
5247d4d443eSBen Zhang  * as padding.
5257d4d443eSBen Zhang  */
rt5677_spi_write(u32 addr,const void * txbuf,size_t len)5267d4d443eSBen Zhang int rt5677_spi_write(u32 addr, const void *txbuf, size_t len)
5277d4d443eSBen Zhang {
528a46eb523SCurtis Malainey 	u32 offset;
5297d4d443eSBen Zhang 	int status = 0;
5307d4d443eSBen Zhang 	struct spi_transfer t;
5317d4d443eSBen Zhang 	struct spi_message m;
5327d4d443eSBen Zhang 	/* +1 byte is for the DummyPhase following the DataPhase */
5337d4d443eSBen Zhang 	u8 buf[RT5677_SPI_HEADER + RT5677_SPI_BURST_LEN + 1];
5347d4d443eSBen Zhang 	u8 *body = buf + RT5677_SPI_HEADER;
5357d4d443eSBen Zhang 	u8 spi_cmd;
5367d4d443eSBen Zhang 	const u8 *cb = txbuf;
5377d4d443eSBen Zhang 
5387d4d443eSBen Zhang 	if (!g_spi)
5397d4d443eSBen Zhang 		return -ENODEV;
5407d4d443eSBen Zhang 
541a46eb523SCurtis Malainey 	if (addr & 3) {
5427d4d443eSBen Zhang 		dev_err(&g_spi->dev, "Bad write align 0x%x(%zu)\n", addr, len);
5437d4d443eSBen Zhang 		return -EACCES;
5447d4d443eSBen Zhang 	}
5457d4d443eSBen Zhang 
5467d4d443eSBen Zhang 	memset(&t, 0, sizeof(t));
5477d4d443eSBen Zhang 	t.tx_buf = buf;
5487d4d443eSBen Zhang 	t.speed_hz = RT5677_SPI_FREQ;
5497d4d443eSBen Zhang 	spi_message_init_with_transfers(&m, &t, 1);
5507d4d443eSBen Zhang 
551a46eb523SCurtis Malainey 	for (offset = 0; offset < len;) {
5527d4d443eSBen Zhang 		spi_cmd = rt5677_spi_select_cmd(false, (addr + offset) & 7,
553a46eb523SCurtis Malainey 				len - offset, &t.len);
5547d4d443eSBen Zhang 
5557d4d443eSBen Zhang 		/* Construct SPI message header */
5567d4d443eSBen Zhang 		buf[0] = spi_cmd;
5577d4d443eSBen Zhang 		buf[1] = ((addr + offset) & 0xff000000) >> 24;
5587d4d443eSBen Zhang 		buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
5597d4d443eSBen Zhang 		buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
5607d4d443eSBen Zhang 		buf[4] = ((addr + offset) & 0x000000ff) >> 0;
5617d4d443eSBen Zhang 
5627d4d443eSBen Zhang 		/* Fetch data from caller buffer */
5637d4d443eSBen Zhang 		rt5677_spi_reverse(body, t.len, cb + offset, len - offset);
5647d4d443eSBen Zhang 		offset += t.len;
5657d4d443eSBen Zhang 		t.len += RT5677_SPI_HEADER + 1;
5667d4d443eSBen Zhang 
5677d4d443eSBen Zhang 		mutex_lock(&spi_mutex);
5687d4d443eSBen Zhang 		status |= spi_sync(g_spi, &m);
5697d4d443eSBen Zhang 		mutex_unlock(&spi_mutex);
5707d4d443eSBen Zhang 	}
571af48f1d0SOder Chiou 	return status;
572af48f1d0SOder Chiou }
573e29bee09SBen Zhang EXPORT_SYMBOL_GPL(rt5677_spi_write);
574af48f1d0SOder Chiou 
rt5677_spi_write_firmware(u32 addr,const struct firmware * fw)5757d4d443eSBen Zhang int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw)
576af48f1d0SOder Chiou {
5777d4d443eSBen Zhang 	return rt5677_spi_write(addr, fw->data, fw->size);
578af48f1d0SOder Chiou }
5797d4d443eSBen Zhang EXPORT_SYMBOL_GPL(rt5677_spi_write_firmware);
580af48f1d0SOder Chiou 
rt5677_spi_hotword_detected(void)581a0e0d135SBen Zhang void rt5677_spi_hotword_detected(void)
582a0e0d135SBen Zhang {
583a0e0d135SBen Zhang 	struct rt5677_dsp *rt5677_dsp;
584a0e0d135SBen Zhang 
585a0e0d135SBen Zhang 	if (!g_spi)
586a0e0d135SBen Zhang 		return;
587a0e0d135SBen Zhang 
588a0e0d135SBen Zhang 	rt5677_dsp = dev_get_drvdata(&g_spi->dev);
589a0e0d135SBen Zhang 	if (!rt5677_dsp) {
590a0e0d135SBen Zhang 		dev_err(&g_spi->dev, "Can't get rt5677_dsp\n");
591a0e0d135SBen Zhang 		return;
592a0e0d135SBen Zhang 	}
593a0e0d135SBen Zhang 
594a0e0d135SBen Zhang 	mutex_lock(&rt5677_dsp->dma_lock);
595a0e0d135SBen Zhang 	dev_info(rt5677_dsp->dev, "Hotword detected\n");
596a0e0d135SBen Zhang 	rt5677_dsp->new_hotword = true;
597a0e0d135SBen Zhang 	mutex_unlock(&rt5677_dsp->dma_lock);
598a0e0d135SBen Zhang 
599a0e0d135SBen Zhang 	schedule_delayed_work(&rt5677_dsp->copy_work, 0);
600a0e0d135SBen Zhang }
601a0e0d135SBen Zhang EXPORT_SYMBOL_GPL(rt5677_spi_hotword_detected);
602a0e0d135SBen Zhang 
rt5677_spi_probe(struct spi_device * spi)603af48f1d0SOder Chiou static int rt5677_spi_probe(struct spi_device *spi)
604af48f1d0SOder Chiou {
605a0e0d135SBen Zhang 	int ret;
606a0e0d135SBen Zhang 
607af48f1d0SOder Chiou 	g_spi = spi;
608a0e0d135SBen Zhang 
6099558ad21SWei Yongjun 	ret = devm_snd_soc_register_component(&spi->dev,
6109558ad21SWei Yongjun 					      &rt5677_spi_dai_component,
611a0e0d135SBen Zhang 					      &rt5677_spi_dai, 1);
612a0e0d135SBen Zhang 	if (ret < 0)
613a0e0d135SBen Zhang 		dev_err(&spi->dev, "Failed to register component.\n");
614a0e0d135SBen Zhang 
615a0e0d135SBen Zhang 	return ret;
616a0e0d135SBen Zhang }
617a0e0d135SBen Zhang 
61856bbfbfdSPierre-Louis Bossart #ifdef CONFIG_ACPI
6192b070f67SOder Chiou static const struct acpi_device_id rt5677_spi_acpi_id[] = {
6202b070f67SOder Chiou 	{ "RT5677AA", 0 },
6212b070f67SOder Chiou 	{ }
6222b070f67SOder Chiou };
6232b070f67SOder Chiou MODULE_DEVICE_TABLE(acpi, rt5677_spi_acpi_id);
62456bbfbfdSPierre-Louis Bossart #endif
6252b070f67SOder Chiou 
626af48f1d0SOder Chiou static struct spi_driver rt5677_spi_driver = {
627af48f1d0SOder Chiou 	.driver = {
628b9960f6eSCurtis Malainey 		.name = DRV_NAME,
6292b070f67SOder Chiou 		.acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id),
630af48f1d0SOder Chiou 	},
631af48f1d0SOder Chiou 	.probe = rt5677_spi_probe,
632af48f1d0SOder Chiou };
633af48f1d0SOder Chiou module_spi_driver(rt5677_spi_driver);
634af48f1d0SOder Chiou 
635af48f1d0SOder Chiou MODULE_DESCRIPTION("ASoC RT5677 SPI driver");
636af48f1d0SOder Chiou MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
637af48f1d0SOder Chiou MODULE_LICENSE("GPL v2");
638