diff options
-rw-r--r-- | sound/soc/codecs/Kconfig | 3 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/rt5514-spi.c | 459 | ||||
-rw-r--r-- | sound/soc/codecs/rt5514-spi.h | 38 | ||||
-rw-r--r-- | sound/soc/codecs/rt5514.c | 136 | ||||
-rw-r--r-- | sound/soc/codecs/rt5514.h | 4 |
6 files changed, 640 insertions, 2 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 4d82a58ff6b0..7d5afd1ea09b 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -643,6 +643,9 @@ config SND_SOC_RT298 | |||
643 | config SND_SOC_RT5514 | 643 | config SND_SOC_RT5514 |
644 | tristate | 644 | tristate |
645 | 645 | ||
646 | config SND_SOC_RT5514_SPI | ||
647 | tristate | ||
648 | |||
646 | config SND_SOC_RT5616 | 649 | config SND_SOC_RT5616 |
647 | tristate "Realtek RT5616 CODEC" | 650 | tristate "Realtek RT5616 CODEC" |
648 | depends on I2C | 651 | depends on I2C |
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 0f548fd34ca3..734b68de246c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -100,6 +100,7 @@ snd-soc-rl6347a-objs := rl6347a.o | |||
100 | snd-soc-rt286-objs := rt286.o | 100 | snd-soc-rt286-objs := rt286.o |
101 | snd-soc-rt298-objs := rt298.o | 101 | snd-soc-rt298-objs := rt298.o |
102 | snd-soc-rt5514-objs := rt5514.o | 102 | snd-soc-rt5514-objs := rt5514.o |
103 | snd-soc-rt5514-spi-objs := rt5514-spi.o | ||
103 | snd-soc-rt5616-objs := rt5616.o | 104 | snd-soc-rt5616-objs := rt5616.o |
104 | snd-soc-rt5631-objs := rt5631.o | 105 | snd-soc-rt5631-objs := rt5631.o |
105 | snd-soc-rt5640-objs := rt5640.o | 106 | snd-soc-rt5640-objs := rt5640.o |
@@ -314,6 +315,7 @@ obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o | |||
314 | obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o | 315 | obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o |
315 | obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o | 316 | obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o |
316 | obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o | 317 | obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o |
318 | obj-$(CONFIG_SND_SOC_RT5514_SPI) += snd-soc-rt5514-spi.o | ||
317 | obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o | 319 | obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o |
318 | obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o | 320 | obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o |
319 | obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o | 321 | obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o |
diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c new file mode 100644 index 000000000000..8a9382e9787a --- /dev/null +++ b/sound/soc/codecs/rt5514-spi.c | |||
@@ -0,0 +1,459 @@ | |||
1 | /* | ||
2 | * rt5514-spi.c -- RT5514 SPI driver | ||
3 | * | ||
4 | * Copyright 2015 Realtek Semiconductor Corp. | ||
5 | * 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. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/input.h> | ||
14 | #include <linux/spi/spi.h> | ||
15 | #include <linux/device.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/irq.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/gpio.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/kthread.h> | ||
24 | #include <linux/uaccess.h> | ||
25 | #include <linux/miscdevice.h> | ||
26 | #include <linux/regulator/consumer.h> | ||
27 | #include <linux/pm_qos.h> | ||
28 | #include <linux/sysfs.h> | ||
29 | #include <linux/clk.h> | ||
30 | #include <sound/core.h> | ||
31 | #include <sound/pcm.h> | ||
32 | #include <sound/pcm_params.h> | ||
33 | #include <sound/soc.h> | ||
34 | #include <sound/soc-dapm.h> | ||
35 | #include <sound/initval.h> | ||
36 | #include <sound/tlv.h> | ||
37 | |||
38 | #include "rt5514-spi.h" | ||
39 | |||
40 | static struct spi_device *rt5514_spi; | ||
41 | |||
42 | struct rt5514_dsp { | ||
43 | struct device *dev; | ||
44 | struct delayed_work copy_work; | ||
45 | struct mutex dma_lock; | ||
46 | struct snd_pcm_substream *substream; | ||
47 | unsigned int buf_base, buf_limit, buf_rp; | ||
48 | size_t buf_size; | ||
49 | size_t dma_offset; | ||
50 | size_t dsp_offset; | ||
51 | }; | ||
52 | |||
53 | static const struct snd_pcm_hardware rt5514_spi_pcm_hardware = { | ||
54 | .info = SNDRV_PCM_INFO_MMAP | | ||
55 | SNDRV_PCM_INFO_MMAP_VALID | | ||
56 | SNDRV_PCM_INFO_INTERLEAVED, | ||
57 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
58 | .period_bytes_min = PAGE_SIZE, | ||
59 | .period_bytes_max = 0x20000 / 8, | ||
60 | .periods_min = 8, | ||
61 | .periods_max = 8, | ||
62 | .channels_min = 1, | ||
63 | .channels_max = 1, | ||
64 | .buffer_bytes_max = 0x20000, | ||
65 | }; | ||
66 | |||
67 | static struct snd_soc_dai_driver rt5514_spi_dai = { | ||
68 | .name = "rt5514-dsp-cpu-dai", | ||
69 | .id = 0, | ||
70 | .capture = { | ||
71 | .stream_name = "DSP Capture", | ||
72 | .channels_min = 1, | ||
73 | .channels_max = 1, | ||
74 | .rates = SNDRV_PCM_RATE_16000, | ||
75 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
76 | }, | ||
77 | }; | ||
78 | |||
79 | static void rt5514_spi_copy_work(struct work_struct *work) | ||
80 | { | ||
81 | struct rt5514_dsp *rt5514_dsp = | ||
82 | container_of(work, struct rt5514_dsp, copy_work.work); | ||
83 | struct snd_pcm_runtime *runtime = rt5514_dsp->substream->runtime; | ||
84 | size_t period_bytes, truncated_bytes = 0; | ||
85 | |||
86 | mutex_lock(&rt5514_dsp->dma_lock); | ||
87 | if (!rt5514_dsp->substream) { | ||
88 | dev_err(rt5514_dsp->dev, "No pcm substream\n"); | ||
89 | goto done; | ||
90 | } | ||
91 | |||
92 | period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream); | ||
93 | |||
94 | if (rt5514_dsp->buf_size - rt5514_dsp->dsp_offset < period_bytes) | ||
95 | period_bytes = rt5514_dsp->buf_size - rt5514_dsp->dsp_offset; | ||
96 | |||
97 | if (rt5514_dsp->buf_rp + period_bytes <= rt5514_dsp->buf_limit) { | ||
98 | rt5514_spi_burst_read(rt5514_dsp->buf_rp, | ||
99 | runtime->dma_area + rt5514_dsp->dma_offset, | ||
100 | period_bytes); | ||
101 | |||
102 | if (rt5514_dsp->buf_rp + period_bytes == rt5514_dsp->buf_limit) | ||
103 | rt5514_dsp->buf_rp = rt5514_dsp->buf_base; | ||
104 | else | ||
105 | rt5514_dsp->buf_rp += period_bytes; | ||
106 | } else { | ||
107 | truncated_bytes = rt5514_dsp->buf_limit - rt5514_dsp->buf_rp; | ||
108 | rt5514_spi_burst_read(rt5514_dsp->buf_rp, | ||
109 | runtime->dma_area + rt5514_dsp->dma_offset, | ||
110 | truncated_bytes); | ||
111 | |||
112 | rt5514_spi_burst_read(rt5514_dsp->buf_base, | ||
113 | runtime->dma_area + rt5514_dsp->dma_offset + | ||
114 | truncated_bytes, period_bytes - truncated_bytes); | ||
115 | |||
116 | rt5514_dsp->buf_rp = rt5514_dsp->buf_base + | ||
117 | period_bytes - truncated_bytes; | ||
118 | } | ||
119 | |||
120 | rt5514_dsp->dma_offset += period_bytes; | ||
121 | if (rt5514_dsp->dma_offset >= runtime->dma_bytes) | ||
122 | rt5514_dsp->dma_offset = 0; | ||
123 | |||
124 | rt5514_dsp->dsp_offset += period_bytes; | ||
125 | |||
126 | snd_pcm_period_elapsed(rt5514_dsp->substream); | ||
127 | |||
128 | if (rt5514_dsp->dsp_offset < rt5514_dsp->buf_size) | ||
129 | schedule_delayed_work(&rt5514_dsp->copy_work, 5); | ||
130 | done: | ||
131 | mutex_unlock(&rt5514_dsp->dma_lock); | ||
132 | } | ||
133 | |||
134 | /* PCM for streaming audio from the DSP buffer */ | ||
135 | static int rt5514_spi_pcm_open(struct snd_pcm_substream *substream) | ||
136 | { | ||
137 | snd_soc_set_runtime_hwparams(substream, &rt5514_spi_pcm_hardware); | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static int rt5514_spi_hw_params(struct snd_pcm_substream *substream, | ||
143 | struct snd_pcm_hw_params *hw_params) | ||
144 | { | ||
145 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
146 | struct rt5514_dsp *rt5514_dsp = | ||
147 | snd_soc_platform_get_drvdata(rtd->platform); | ||
148 | int ret; | ||
149 | |||
150 | mutex_lock(&rt5514_dsp->dma_lock); | ||
151 | ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, | ||
152 | params_buffer_bytes(hw_params)); | ||
153 | rt5514_dsp->substream = substream; | ||
154 | mutex_unlock(&rt5514_dsp->dma_lock); | ||
155 | |||
156 | return ret; | ||
157 | } | ||
158 | |||
159 | static int rt5514_spi_hw_free(struct snd_pcm_substream *substream) | ||
160 | { | ||
161 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
162 | struct rt5514_dsp *rt5514_dsp = | ||
163 | snd_soc_platform_get_drvdata(rtd->platform); | ||
164 | |||
165 | mutex_lock(&rt5514_dsp->dma_lock); | ||
166 | rt5514_dsp->substream = NULL; | ||
167 | mutex_unlock(&rt5514_dsp->dma_lock); | ||
168 | |||
169 | cancel_delayed_work_sync(&rt5514_dsp->copy_work); | ||
170 | |||
171 | return snd_pcm_lib_free_vmalloc_buffer(substream); | ||
172 | } | ||
173 | |||
174 | static int rt5514_spi_prepare(struct snd_pcm_substream *substream) | ||
175 | { | ||
176 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
177 | struct rt5514_dsp *rt5514_dsp = | ||
178 | snd_soc_platform_get_drvdata(rtd->platform); | ||
179 | u8 buf[8]; | ||
180 | |||
181 | rt5514_dsp->dma_offset = 0; | ||
182 | rt5514_dsp->dsp_offset = 0; | ||
183 | |||
184 | /** | ||
185 | * The address area x1800XXXX is the register address, and it cannot | ||
186 | * support spi burst read perfectly. So we use the spi burst read | ||
187 | * individually to make sure the data correctly. | ||
188 | */ | ||
189 | rt5514_spi_burst_read(RT5514_BUFFER_VOICE_BASE, (u8 *)&buf, | ||
190 | sizeof(buf)); | ||
191 | rt5514_dsp->buf_base = buf[0] | buf[1] << 8 | buf[2] << 16 | | ||
192 | buf[3] << 24; | ||
193 | |||
194 | rt5514_spi_burst_read(RT5514_BUFFER_VOICE_LIMIT, (u8 *)&buf, | ||
195 | sizeof(buf)); | ||
196 | rt5514_dsp->buf_limit = buf[0] | buf[1] << 8 | buf[2] << 16 | | ||
197 | buf[3] << 24; | ||
198 | |||
199 | rt5514_spi_burst_read(RT5514_BUFFER_VOICE_RP, (u8 *)&buf, | ||
200 | sizeof(buf)); | ||
201 | rt5514_dsp->buf_rp = buf[0] | buf[1] << 8 | buf[2] << 16 | | ||
202 | buf[3] << 24; | ||
203 | |||
204 | rt5514_spi_burst_read(RT5514_BUFFER_VOICE_SIZE, (u8 *)&buf, | ||
205 | sizeof(buf)); | ||
206 | rt5514_dsp->buf_size = buf[0] | buf[1] << 8 | buf[2] << 16 | | ||
207 | buf[3] << 24; | ||
208 | |||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static int rt5514_spi_trigger(struct snd_pcm_substream *substream, int cmd) | ||
213 | { | ||
214 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
215 | struct rt5514_dsp *rt5514_dsp = | ||
216 | snd_soc_platform_get_drvdata(rtd->platform); | ||
217 | |||
218 | if (cmd == SNDRV_PCM_TRIGGER_START) { | ||
219 | if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit && | ||
220 | rt5514_dsp->buf_rp && rt5514_dsp->buf_size) | ||
221 | schedule_delayed_work(&rt5514_dsp->copy_work, 0); | ||
222 | } | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static snd_pcm_uframes_t rt5514_spi_pcm_pointer( | ||
228 | struct snd_pcm_substream *substream) | ||
229 | { | ||
230 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
231 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
232 | struct rt5514_dsp *rt5514_dsp = | ||
233 | snd_soc_platform_get_drvdata(rtd->platform); | ||
234 | |||
235 | return bytes_to_frames(runtime, rt5514_dsp->dma_offset); | ||
236 | } | ||
237 | |||
238 | static struct snd_pcm_ops rt5514_spi_pcm_ops = { | ||
239 | .open = rt5514_spi_pcm_open, | ||
240 | .hw_params = rt5514_spi_hw_params, | ||
241 | .hw_free = rt5514_spi_hw_free, | ||
242 | .trigger = rt5514_spi_trigger, | ||
243 | .prepare = rt5514_spi_prepare, | ||
244 | .pointer = rt5514_spi_pcm_pointer, | ||
245 | .mmap = snd_pcm_lib_mmap_vmalloc, | ||
246 | .page = snd_pcm_lib_get_vmalloc_page, | ||
247 | }; | ||
248 | |||
249 | static int rt5514_spi_pcm_probe(struct snd_soc_platform *platform) | ||
250 | { | ||
251 | struct rt5514_dsp *rt5514_dsp; | ||
252 | |||
253 | rt5514_dsp = devm_kzalloc(platform->dev, sizeof(*rt5514_dsp), | ||
254 | GFP_KERNEL); | ||
255 | |||
256 | rt5514_dsp->dev = &rt5514_spi->dev; | ||
257 | mutex_init(&rt5514_dsp->dma_lock); | ||
258 | INIT_DELAYED_WORK(&rt5514_dsp->copy_work, rt5514_spi_copy_work); | ||
259 | snd_soc_platform_set_drvdata(platform, rt5514_dsp); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static struct snd_soc_platform_driver rt5514_spi_platform = { | ||
265 | .probe = rt5514_spi_pcm_probe, | ||
266 | .ops = &rt5514_spi_pcm_ops, | ||
267 | }; | ||
268 | |||
269 | static const struct snd_soc_component_driver rt5514_spi_dai_component = { | ||
270 | .name = "rt5514-spi-dai", | ||
271 | }; | ||
272 | |||
273 | /** | ||
274 | * rt5514_spi_burst_read - Read data from SPI by rt5514 address. | ||
275 | * @addr: Start address. | ||
276 | * @rxbuf: Data Buffer for reading. | ||
277 | * @len: Data length, it must be a multiple of 8. | ||
278 | * | ||
279 | * | ||
280 | * Returns true for success. | ||
281 | */ | ||
282 | int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len) | ||
283 | { | ||
284 | u8 spi_cmd = RT5514_SPI_CMD_BURST_READ; | ||
285 | int status; | ||
286 | u8 write_buf[8]; | ||
287 | unsigned int i, end, offset = 0; | ||
288 | |||
289 | struct spi_message message; | ||
290 | struct spi_transfer x[3]; | ||
291 | |||
292 | while (offset < len) { | ||
293 | if (offset + RT5514_SPI_BUF_LEN <= len) | ||
294 | end = RT5514_SPI_BUF_LEN; | ||
295 | else | ||
296 | end = len % RT5514_SPI_BUF_LEN; | ||
297 | |||
298 | write_buf[0] = spi_cmd; | ||
299 | write_buf[1] = ((addr + offset) & 0xff000000) >> 24; | ||
300 | write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16; | ||
301 | write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8; | ||
302 | write_buf[4] = ((addr + offset) & 0x000000ff) >> 0; | ||
303 | |||
304 | spi_message_init(&message); | ||
305 | memset(x, 0, sizeof(x)); | ||
306 | |||
307 | x[0].len = 5; | ||
308 | x[0].tx_buf = write_buf; | ||
309 | spi_message_add_tail(&x[0], &message); | ||
310 | |||
311 | x[1].len = 4; | ||
312 | x[1].tx_buf = write_buf; | ||
313 | spi_message_add_tail(&x[1], &message); | ||
314 | |||
315 | x[2].len = end; | ||
316 | x[2].rx_buf = rxbuf + offset; | ||
317 | spi_message_add_tail(&x[2], &message); | ||
318 | |||
319 | status = spi_sync(rt5514_spi, &message); | ||
320 | |||
321 | if (status) | ||
322 | return false; | ||
323 | |||
324 | offset += RT5514_SPI_BUF_LEN; | ||
325 | } | ||
326 | |||
327 | for (i = 0; i < len; i += 8) { | ||
328 | write_buf[0] = rxbuf[i + 0]; | ||
329 | write_buf[1] = rxbuf[i + 1]; | ||
330 | write_buf[2] = rxbuf[i + 2]; | ||
331 | write_buf[3] = rxbuf[i + 3]; | ||
332 | write_buf[4] = rxbuf[i + 4]; | ||
333 | write_buf[5] = rxbuf[i + 5]; | ||
334 | write_buf[6] = rxbuf[i + 6]; | ||
335 | write_buf[7] = rxbuf[i + 7]; | ||
336 | |||
337 | rxbuf[i + 0] = write_buf[7]; | ||
338 | rxbuf[i + 1] = write_buf[6]; | ||
339 | rxbuf[i + 2] = write_buf[5]; | ||
340 | rxbuf[i + 3] = write_buf[4]; | ||
341 | rxbuf[i + 4] = write_buf[3]; | ||
342 | rxbuf[i + 5] = write_buf[2]; | ||
343 | rxbuf[i + 6] = write_buf[1]; | ||
344 | rxbuf[i + 7] = write_buf[0]; | ||
345 | } | ||
346 | |||
347 | return true; | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * rt5514_spi_burst_write - Write data to SPI by rt5514 address. | ||
352 | * @addr: Start address. | ||
353 | * @txbuf: Data Buffer for writng. | ||
354 | * @len: Data length, it must be a multiple of 8. | ||
355 | * | ||
356 | * | ||
357 | * Returns true for success. | ||
358 | */ | ||
359 | int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len) | ||
360 | { | ||
361 | u8 spi_cmd = RT5514_SPI_CMD_BURST_WRITE; | ||
362 | u8 *write_buf; | ||
363 | unsigned int i, end, offset = 0; | ||
364 | |||
365 | write_buf = kmalloc(RT5514_SPI_BUF_LEN + 6, GFP_KERNEL); | ||
366 | |||
367 | if (write_buf == NULL) | ||
368 | return -ENOMEM; | ||
369 | |||
370 | while (offset < len) { | ||
371 | if (offset + RT5514_SPI_BUF_LEN <= len) | ||
372 | end = RT5514_SPI_BUF_LEN; | ||
373 | else | ||
374 | end = len % RT5514_SPI_BUF_LEN; | ||
375 | |||
376 | write_buf[0] = spi_cmd; | ||
377 | write_buf[1] = ((addr + offset) & 0xff000000) >> 24; | ||
378 | write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16; | ||
379 | write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8; | ||
380 | write_buf[4] = ((addr + offset) & 0x000000ff) >> 0; | ||
381 | |||
382 | for (i = 0; i < end; i += 8) { | ||
383 | write_buf[i + 12] = txbuf[offset + i + 0]; | ||
384 | write_buf[i + 11] = txbuf[offset + i + 1]; | ||
385 | write_buf[i + 10] = txbuf[offset + i + 2]; | ||
386 | write_buf[i + 9] = txbuf[offset + i + 3]; | ||
387 | write_buf[i + 8] = txbuf[offset + i + 4]; | ||
388 | write_buf[i + 7] = txbuf[offset + i + 5]; | ||
389 | write_buf[i + 6] = txbuf[offset + i + 6]; | ||
390 | write_buf[i + 5] = txbuf[offset + i + 7]; | ||
391 | } | ||
392 | |||
393 | write_buf[end + 5] = spi_cmd; | ||
394 | |||
395 | spi_write(rt5514_spi, write_buf, end + 6); | ||
396 | |||
397 | offset += RT5514_SPI_BUF_LEN; | ||
398 | } | ||
399 | |||
400 | kfree(write_buf); | ||
401 | |||
402 | return 0; | ||
403 | } | ||
404 | EXPORT_SYMBOL_GPL(rt5514_spi_burst_write); | ||
405 | |||
406 | static int rt5514_spi_probe(struct spi_device *spi) | ||
407 | { | ||
408 | int ret; | ||
409 | |||
410 | rt5514_spi = spi; | ||
411 | |||
412 | ret = snd_soc_register_platform(&spi->dev, &rt5514_spi_platform); | ||
413 | if (ret < 0) { | ||
414 | dev_err(&spi->dev, "Failed to register platform.\n"); | ||
415 | goto err_plat; | ||
416 | } | ||
417 | |||
418 | ret = snd_soc_register_component(&spi->dev, &rt5514_spi_dai_component, | ||
419 | &rt5514_spi_dai, 1); | ||
420 | if (ret < 0) { | ||
421 | dev_err(&spi->dev, "Failed to register component.\n"); | ||
422 | goto err_comp; | ||
423 | } | ||
424 | |||
425 | return 0; | ||
426 | err_comp: | ||
427 | snd_soc_unregister_platform(&spi->dev); | ||
428 | err_plat: | ||
429 | |||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | static int rt5514_spi_remove(struct spi_device *spi) | ||
434 | { | ||
435 | snd_soc_unregister_component(&spi->dev); | ||
436 | snd_soc_unregister_platform(&spi->dev); | ||
437 | |||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | static const struct of_device_id rt5514_of_match[] = { | ||
442 | { .compatible = "realtek,rt5514", }, | ||
443 | {}, | ||
444 | }; | ||
445 | MODULE_DEVICE_TABLE(of, rt5514_of_match); | ||
446 | |||
447 | static struct spi_driver rt5514_spi_driver = { | ||
448 | .driver = { | ||
449 | .name = "rt5514", | ||
450 | .of_match_table = of_match_ptr(rt5514_of_match), | ||
451 | }, | ||
452 | .probe = rt5514_spi_probe, | ||
453 | .remove = rt5514_spi_remove, | ||
454 | }; | ||
455 | module_spi_driver(rt5514_spi_driver); | ||
456 | |||
457 | MODULE_DESCRIPTION("RT5514 SPI driver"); | ||
458 | MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>"); | ||
459 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/codecs/rt5514-spi.h b/sound/soc/codecs/rt5514-spi.h new file mode 100644 index 000000000000..f69b1cdf2f9b --- /dev/null +++ b/sound/soc/codecs/rt5514-spi.h | |||
@@ -0,0 +1,38 @@ | |||
1 | /* | ||
2 | * rt5514-spi.h -- RT5514 driver | ||
3 | * | ||
4 | * Copyright 2015 Realtek Semiconductor Corp. | ||
5 | * 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. | ||
10 | */ | ||
11 | |||
12 | #ifndef __RT5514_SPI_H__ | ||
13 | #define __RT5514_SPI_H__ | ||
14 | |||
15 | /** | ||
16 | * RT5514_SPI_BUF_LEN is the buffer size of SPI master controller. | ||
17 | */ | ||
18 | #define RT5514_SPI_BUF_LEN 240 | ||
19 | |||
20 | #define RT5514_BUFFER_VOICE_BASE 0x18001034 | ||
21 | #define RT5514_BUFFER_VOICE_LIMIT 0x18001038 | ||
22 | #define RT5514_BUFFER_VOICE_RP 0x1800103c | ||
23 | #define RT5514_BUFFER_VOICE_SIZE 0x18001040 | ||
24 | |||
25 | /* SPI Command */ | ||
26 | enum { | ||
27 | RT5514_SPI_CMD_16_READ = 0, | ||
28 | RT5514_SPI_CMD_16_WRITE, | ||
29 | RT5514_SPI_CMD_32_READ, | ||
30 | RT5514_SPI_CMD_32_WRITE, | ||
31 | RT5514_SPI_CMD_BURST_READ, | ||
32 | RT5514_SPI_CMD_BURST_WRITE, | ||
33 | }; | ||
34 | |||
35 | int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len); | ||
36 | int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len); | ||
37 | |||
38 | #endif /* __RT5514_SPI_H__ */ | ||
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 879bf60f4965..ecb09891b662 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c | |||
@@ -30,6 +30,9 @@ | |||
30 | 30 | ||
31 | #include "rl6231.h" | 31 | #include "rl6231.h" |
32 | #include "rt5514.h" | 32 | #include "rt5514.h" |
33 | #if defined(CONFIG_SND_SOC_RT5514_SPI) | ||
34 | #include "rt5514-spi.h" | ||
35 | #endif | ||
33 | 36 | ||
34 | static const struct reg_sequence rt5514_i2c_patch[] = { | 37 | static const struct reg_sequence rt5514_i2c_patch[] = { |
35 | {0x1800101c, 0x00000000}, | 38 | {0x1800101c, 0x00000000}, |
@@ -110,6 +113,35 @@ static const struct reg_default rt5514_reg[] = { | |||
110 | {RT5514_VENDOR_ID2, 0x10ec5514}, | 113 | {RT5514_VENDOR_ID2, 0x10ec5514}, |
111 | }; | 114 | }; |
112 | 115 | ||
116 | static void rt5514_enable_dsp_prepare(struct rt5514_priv *rt5514) | ||
117 | { | ||
118 | /* Reset */ | ||
119 | regmap_write(rt5514->i2c_regmap, 0x18002000, 0x000010ec); | ||
120 | /* LDO_I_limit */ | ||
121 | regmap_write(rt5514->i2c_regmap, 0x18002200, 0x00028604); | ||
122 | /* I2C bypass enable */ | ||
123 | regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000001); | ||
124 | /* mini-core reset */ | ||
125 | regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x0005514b); | ||
126 | regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x00055149); | ||
127 | /* I2C bypass disable */ | ||
128 | regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000000); | ||
129 | /* PIN config */ | ||
130 | regmap_write(rt5514->i2c_regmap, 0x18002070, 0x00000040); | ||
131 | /* PLL3(QN)=RCOSC*(10+2) */ | ||
132 | regmap_write(rt5514->i2c_regmap, 0x18002240, 0x0000000a); | ||
133 | /* PLL3 source=RCOSC, fsi=rt_clk */ | ||
134 | regmap_write(rt5514->i2c_regmap, 0x18002100, 0x0000000b); | ||
135 | /* Power on RCOSC, pll3 */ | ||
136 | regmap_write(rt5514->i2c_regmap, 0x18002004, 0x00808b81); | ||
137 | /* DSP clk source = pll3, ENABLE DSP clk */ | ||
138 | regmap_write(rt5514->i2c_regmap, 0x18002f08, 0x00000005); | ||
139 | /* Enable DSP clk auto switch */ | ||
140 | regmap_write(rt5514->i2c_regmap, 0x18001114, 0x00000001); | ||
141 | /* Reduce DSP power */ | ||
142 | regmap_write(rt5514->i2c_regmap, 0x18001118, 0x00000001); | ||
143 | } | ||
144 | |||
113 | static bool rt5514_volatile_register(struct device *dev, unsigned int reg) | 145 | static bool rt5514_volatile_register(struct device *dev, unsigned int reg) |
114 | { | 146 | { |
115 | switch (reg) { | 147 | switch (reg) { |
@@ -248,6 +280,74 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv, | |||
248 | 280 | ||
249 | static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); | 281 | static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); |
250 | 282 | ||
283 | static int rt5514_dsp_voice_wake_up_get(struct snd_kcontrol *kcontrol, | ||
284 | struct snd_ctl_elem_value *ucontrol) | ||
285 | { | ||
286 | struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); | ||
287 | struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); | ||
288 | |||
289 | ucontrol->value.integer.value[0] = rt5514->dsp_enabled; | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol, | ||
295 | struct snd_ctl_elem_value *ucontrol) | ||
296 | { | ||
297 | struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); | ||
298 | struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); | ||
299 | struct snd_soc_codec *codec = rt5514->codec; | ||
300 | const struct firmware *fw = NULL; | ||
301 | |||
302 | if (ucontrol->value.integer.value[0] == rt5514->dsp_enabled) | ||
303 | return 0; | ||
304 | |||
305 | if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { | ||
306 | rt5514->dsp_enabled = ucontrol->value.integer.value[0]; | ||
307 | |||
308 | if (rt5514->dsp_enabled) { | ||
309 | rt5514_enable_dsp_prepare(rt5514); | ||
310 | |||
311 | request_firmware(&fw, RT5514_FIRMWARE1, codec->dev); | ||
312 | if (fw) { | ||
313 | #if defined(CONFIG_SND_SOC_RT5514_SPI) | ||
314 | rt5514_spi_burst_write(0x4ff60000, fw->data, | ||
315 | ((fw->size/8)+1)*8); | ||
316 | #else | ||
317 | dev_err(codec->dev, "There is no SPI driver for" | ||
318 | " loading the firmware\n"); | ||
319 | #endif | ||
320 | release_firmware(fw); | ||
321 | fw = NULL; | ||
322 | } | ||
323 | |||
324 | request_firmware(&fw, RT5514_FIRMWARE2, codec->dev); | ||
325 | if (fw) { | ||
326 | #if defined(CONFIG_SND_SOC_RT5514_SPI) | ||
327 | rt5514_spi_burst_write(0x4ffc0000, fw->data, | ||
328 | ((fw->size/8)+1)*8); | ||
329 | #else | ||
330 | dev_err(codec->dev, "There is no SPI driver for" | ||
331 | " loading the firmware\n"); | ||
332 | #endif | ||
333 | release_firmware(fw); | ||
334 | fw = NULL; | ||
335 | } | ||
336 | |||
337 | /* DSP run */ | ||
338 | regmap_write(rt5514->i2c_regmap, 0x18002f00, | ||
339 | 0x00055148); | ||
340 | } else { | ||
341 | regmap_multi_reg_write(rt5514->i2c_regmap, | ||
342 | rt5514_i2c_patch, ARRAY_SIZE(rt5514_i2c_patch)); | ||
343 | regcache_mark_dirty(rt5514->regmap); | ||
344 | regcache_sync(rt5514->regmap); | ||
345 | } | ||
346 | } | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
251 | static const struct snd_kcontrol_new rt5514_snd_controls[] = { | 351 | static const struct snd_kcontrol_new rt5514_snd_controls[] = { |
252 | SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST, | 352 | SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST, |
253 | RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv), | 353 | RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv), |
@@ -257,6 +357,8 @@ static const struct snd_kcontrol_new rt5514_snd_controls[] = { | |||
257 | SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1, | 357 | SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1, |
258 | RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0, | 358 | RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0, |
259 | adc_vol_tlv), | 359 | adc_vol_tlv), |
360 | SOC_SINGLE_EXT("DSP Voice Wake Up", SND_SOC_NOPM, 0, 1, 0, | ||
361 | rt5514_dsp_voice_wake_up_get, rt5514_dsp_voice_wake_up_put), | ||
260 | }; | 362 | }; |
261 | 363 | ||
262 | /* ADC Mixer*/ | 364 | /* ADC Mixer*/ |
@@ -365,6 +467,35 @@ static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, | |||
365 | return 0; | 467 | return 0; |
366 | } | 468 | } |
367 | 469 | ||
470 | static int rt5514_pre_event(struct snd_soc_dapm_widget *w, | ||
471 | struct snd_kcontrol *kcontrol, int event) | ||
472 | { | ||
473 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); | ||
474 | struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); | ||
475 | |||
476 | switch (event) { | ||
477 | case SND_SOC_DAPM_PRE_PMU: | ||
478 | /** | ||
479 | * If the DSP is enabled in start of recording, the DSP | ||
480 | * should be disabled, and sync back to normal recording | ||
481 | * settings to make sure recording properly. | ||
482 | */ | ||
483 | if (rt5514->dsp_enabled) { | ||
484 | rt5514->dsp_enabled = 0; | ||
485 | regmap_multi_reg_write(rt5514->i2c_regmap, | ||
486 | rt5514_i2c_patch, ARRAY_SIZE(rt5514_i2c_patch)); | ||
487 | regcache_mark_dirty(rt5514->regmap); | ||
488 | regcache_sync(rt5514->regmap); | ||
489 | } | ||
490 | break; | ||
491 | |||
492 | default: | ||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | |||
368 | static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = { | 499 | static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = { |
369 | /* Input Lines */ | 500 | /* Input Lines */ |
370 | SND_SOC_DAPM_INPUT("DMIC1L"), | 501 | SND_SOC_DAPM_INPUT("DMIC1L"), |
@@ -472,6 +603,8 @@ static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = { | |||
472 | 603 | ||
473 | /* Audio Interface */ | 604 | /* Audio Interface */ |
474 | SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), | 605 | SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), |
606 | |||
607 | SND_SOC_DAPM_PRE("DAPM Pre", rt5514_pre_event), | ||
475 | }; | 608 | }; |
476 | 609 | ||
477 | static const struct snd_soc_dapm_route rt5514_dapm_routes[] = { | 610 | static const struct snd_soc_dapm_route rt5514_dapm_routes[] = { |
@@ -871,7 +1004,6 @@ static const struct regmap_config rt5514_i2c_regmap = { | |||
871 | .reg_bits = 32, | 1004 | .reg_bits = 32, |
872 | .val_bits = 32, | 1005 | .val_bits = 32, |
873 | 1006 | ||
874 | .max_register = RT5514_DSP_MAPPING | RT5514_VENDOR_ID2, | ||
875 | .readable_reg = rt5514_i2c_readable_register, | 1007 | .readable_reg = rt5514_i2c_readable_register, |
876 | 1008 | ||
877 | .cache_type = REGCACHE_NONE, | 1009 | .cache_type = REGCACHE_NONE, |
@@ -944,7 +1076,7 @@ static int rt5514_i2c_probe(struct i2c_client *i2c, | |||
944 | return -ENODEV; | 1076 | return -ENODEV; |
945 | } | 1077 | } |
946 | 1078 | ||
947 | ret = regmap_register_patch(rt5514->i2c_regmap, rt5514_i2c_patch, | 1079 | ret = regmap_multi_reg_write(rt5514->i2c_regmap, rt5514_i2c_patch, |
948 | ARRAY_SIZE(rt5514_i2c_patch)); | 1080 | ARRAY_SIZE(rt5514_i2c_patch)); |
949 | if (ret != 0) | 1081 | if (ret != 0) |
950 | dev_warn(&i2c->dev, "Failed to apply i2c_regmap patch: %d\n", | 1082 | dev_warn(&i2c->dev, "Failed to apply i2c_regmap patch: %d\n", |
diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h index 6ad8a612f659..6e89e7d46a10 100644 --- a/sound/soc/codecs/rt5514.h +++ b/sound/soc/codecs/rt5514.h | |||
@@ -225,6 +225,9 @@ | |||
225 | #define RT5514_PLL_INP_MAX 40000000 | 225 | #define RT5514_PLL_INP_MAX 40000000 |
226 | #define RT5514_PLL_INP_MIN 256000 | 226 | #define RT5514_PLL_INP_MIN 256000 |
227 | 227 | ||
228 | #define RT5514_FIRMWARE1 "rt5514_dsp_fw1.bin" | ||
229 | #define RT5514_FIRMWARE2 "rt5514_dsp_fw2.bin" | ||
230 | |||
228 | /* System Clock Source */ | 231 | /* System Clock Source */ |
229 | enum { | 232 | enum { |
230 | RT5514_SCLK_S_MCLK, | 233 | RT5514_SCLK_S_MCLK, |
@@ -247,6 +250,7 @@ struct rt5514_priv { | |||
247 | int pll_src; | 250 | int pll_src; |
248 | int pll_in; | 251 | int pll_in; |
249 | int pll_out; | 252 | int pll_out; |
253 | int dsp_enabled; | ||
250 | }; | 254 | }; |
251 | 255 | ||
252 | #endif /* __RT5514_H__ */ | 256 | #endif /* __RT5514_H__ */ |