blob: 519ccb3444e8d56b1de69545c65faa7f1b83de93 [file] [log] [blame]
Emilio López45fb6b62015-09-12 15:26:24 +02001/*
2 * Copyright 2014 Emilio López <emilio@elopez.com.ar>
3 * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
4 * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
Adam Sampson474d1472015-10-27 21:00:45 +00005 * Copyright 2015 Adam Sampson <ats@offog.org>
Emilio López45fb6b62015-09-12 15:26:24 +02006 *
7 * Based on the Allwinner SDK driver, released under the GPL.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19
20#include <linux/init.h>
21#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/platform_device.h>
24#include <linux/delay.h>
25#include <linux/slab.h>
26#include <linux/of.h>
27#include <linux/of_platform.h>
28#include <linux/of_address.h>
29#include <linux/clk.h>
30#include <linux/regmap.h>
31
32#include <sound/core.h>
33#include <sound/pcm.h>
34#include <sound/pcm_params.h>
35#include <sound/soc.h>
36#include <sound/tlv.h>
37#include <sound/initval.h>
38#include <sound/dmaengine_pcm.h>
39
40/* Codec DAC register offsets and bit fields */
41#define SUN4I_CODEC_DAC_DPC (0x00)
42#define SUN4I_CODEC_DAC_DPC_EN_DA (31)
43#define SUN4I_CODEC_DAC_DPC_DVOL (12)
44#define SUN4I_CODEC_DAC_FIFOC (0x04)
45#define SUN4I_CODEC_DAC_FIFOC_DAC_FS (29)
46#define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION (28)
47#define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT (26)
48#define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE (24)
49#define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT (21)
50#define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL (8)
51#define SUN4I_CODEC_DAC_FIFOC_MONO_EN (6)
52#define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS (5)
53#define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN (4)
54#define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0)
55#define SUN4I_CODEC_DAC_FIFOS (0x08)
56#define SUN4I_CODEC_DAC_TXDATA (0x0c)
57#define SUN4I_CODEC_DAC_ACTL (0x10)
58#define SUN4I_CODEC_DAC_ACTL_DACAENR (31)
59#define SUN4I_CODEC_DAC_ACTL_DACAENL (30)
60#define SUN4I_CODEC_DAC_ACTL_MIXEN (29)
61#define SUN4I_CODEC_DAC_ACTL_LDACLMIXS (15)
62#define SUN4I_CODEC_DAC_ACTL_RDACRMIXS (14)
63#define SUN4I_CODEC_DAC_ACTL_LDACRMIXS (13)
64#define SUN4I_CODEC_DAC_ACTL_DACPAS (8)
65#define SUN4I_CODEC_DAC_ACTL_MIXPAS (7)
66#define SUN4I_CODEC_DAC_ACTL_PA_MUTE (6)
67#define SUN4I_CODEC_DAC_ACTL_PA_VOL (0)
68#define SUN4I_CODEC_DAC_TUNE (0x14)
69#define SUN4I_CODEC_DAC_DEBUG (0x18)
70
71/* Codec ADC register offsets and bit fields */
72#define SUN4I_CODEC_ADC_FIFOC (0x1c)
Maxime Ripard1fb34b42015-11-30 16:37:47 +010073#define SUN4I_CODEC_ADC_FIFOC_ADC_FS (29)
Emilio López45fb6b62015-09-12 15:26:24 +020074#define SUN4I_CODEC_ADC_FIFOC_EN_AD (28)
75#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24)
76#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8)
77#define SUN4I_CODEC_ADC_FIFOC_MONO_EN (7)
78#define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS (6)
79#define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN (4)
80#define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0)
81#define SUN4I_CODEC_ADC_FIFOS (0x20)
82#define SUN4I_CODEC_ADC_RXDATA (0x24)
83#define SUN4I_CODEC_ADC_ACTL (0x28)
84#define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31)
85#define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30)
86#define SUN4I_CODEC_ADC_ACTL_PREG1EN (29)
87#define SUN4I_CODEC_ADC_ACTL_PREG2EN (28)
88#define SUN4I_CODEC_ADC_ACTL_VMICEN (27)
89#define SUN4I_CODEC_ADC_ACTL_VADCG (20)
90#define SUN4I_CODEC_ADC_ACTL_ADCIS (17)
91#define SUN4I_CODEC_ADC_ACTL_PA_EN (4)
92#define SUN4I_CODEC_ADC_ACTL_DDE (3)
93#define SUN4I_CODEC_ADC_DEBUG (0x2c)
94
95/* Other various ADC registers */
96#define SUN4I_CODEC_DAC_TXCNT (0x30)
97#define SUN4I_CODEC_ADC_RXCNT (0x34)
98#define SUN4I_CODEC_AC_SYS_VERI (0x38)
99#define SUN4I_CODEC_AC_MIC_PHONE_CAL (0x3c)
100
101struct sun4i_codec {
102 struct device *dev;
103 struct regmap *regmap;
104 struct clk *clk_apb;
105 struct clk *clk_module;
106
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100107 struct snd_dmaengine_dai_dma_data capture_dma_data;
Emilio López45fb6b62015-09-12 15:26:24 +0200108 struct snd_dmaengine_dai_dma_data playback_dma_data;
109};
110
111static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
112{
Emilio López45fb6b62015-09-12 15:26:24 +0200113 /* Flush TX FIFO */
114 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
115 BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
116 BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
117
118 /* Enable DAC DRQ */
119 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
120 BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
121 BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
122}
123
124static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
125{
Emilio López45fb6b62015-09-12 15:26:24 +0200126 /* Disable DAC DRQ */
127 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
128 BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
129 0);
130}
131
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100132static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
133{
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100134 /* Enable ADC DRQ */
135 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
136 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN),
137 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
138}
139
140static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
141{
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100142 /* Disable ADC DRQ */
143 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
144 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0);
145}
146
Emilio López45fb6b62015-09-12 15:26:24 +0200147static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
148 struct snd_soc_dai *dai)
149{
150 struct snd_soc_pcm_runtime *rtd = substream->private_data;
151 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
152
Emilio López45fb6b62015-09-12 15:26:24 +0200153 switch (cmd) {
154 case SNDRV_PCM_TRIGGER_START:
155 case SNDRV_PCM_TRIGGER_RESUME:
156 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100157 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
158 sun4i_codec_start_playback(scodec);
159 else
160 sun4i_codec_start_capture(scodec);
Emilio López45fb6b62015-09-12 15:26:24 +0200161 break;
162
163 case SNDRV_PCM_TRIGGER_STOP:
164 case SNDRV_PCM_TRIGGER_SUSPEND:
165 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100166 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
167 sun4i_codec_stop_playback(scodec);
168 else
169 sun4i_codec_stop_capture(scodec);
Emilio López45fb6b62015-09-12 15:26:24 +0200170 break;
171
172 default:
173 return -EINVAL;
174 }
175
176 return 0;
177}
178
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100179static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
180 struct snd_soc_dai *dai)
181{
182 struct snd_soc_pcm_runtime *rtd = substream->private_data;
183 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
184
185
186 /* Flush RX FIFO */
187 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
188 BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH),
189 BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));
190
191
192 /* Set RX FIFO trigger level */
193 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
194 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
195 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
196
197 /*
198 * FIXME: Undocumented in the datasheet, but
199 * Allwinner's code mentions that it is related
200 * related to microphone gain
201 */
202 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
203 0x3 << 25,
204 0x1 << 25);
205
206 if (of_device_is_compatible(scodec->dev->of_node,
207 "allwinner,sun7i-a20-codec"))
208 /* FIXME: Undocumented bits */
209 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_TUNE,
210 0x3 << 8,
211 0x1 << 8);
212
213 /* Fill most significant bits with valid data MSB */
214 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
215 BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
216 BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
217
218 return 0;
219}
220
221static int sun4i_codec_prepare_playback(struct snd_pcm_substream *substream,
222 struct snd_soc_dai *dai)
Emilio López45fb6b62015-09-12 15:26:24 +0200223{
224 struct snd_soc_pcm_runtime *rtd = substream->private_data;
225 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
226 u32 val;
227
Emilio López45fb6b62015-09-12 15:26:24 +0200228 /* Flush the TX FIFO */
229 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
230 BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
231 BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
232
233 /* Set TX FIFO Empty Trigger Level */
234 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
235 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
236 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
237
238 if (substream->runtime->rate > 32000)
239 /* Use 64 bits FIR filter */
240 val = 0;
241 else
242 /* Use 32 bits FIR filter */
243 val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION);
244
245 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
246 BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),
247 val);
248
249 /* Send zeros when we have an underrun */
250 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
251 BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT),
252 0);
253
254 return 0;
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100255};
256
257static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
258 struct snd_soc_dai *dai)
259{
260 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
261 return sun4i_codec_prepare_playback(substream, dai);
262
263 return sun4i_codec_prepare_capture(substream, dai);
Emilio López45fb6b62015-09-12 15:26:24 +0200264}
265
266static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
267{
268 unsigned int rate = params_rate(params);
269
270 switch (rate) {
271 case 176400:
272 case 88200:
273 case 44100:
274 case 33075:
275 case 22050:
276 case 14700:
277 case 11025:
278 case 7350:
279 return 22579200;
280
281 case 192000:
282 case 96000:
283 case 48000:
284 case 32000:
285 case 24000:
286 case 16000:
287 case 12000:
288 case 8000:
289 return 24576000;
290
291 default:
292 return 0;
293 }
294}
295
296static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
297{
298 unsigned int rate = params_rate(params);
299
300 switch (rate) {
301 case 192000:
302 case 176400:
303 return 6;
304
305 case 96000:
306 case 88200:
307 return 7;
308
309 case 48000:
310 case 44100:
311 return 0;
312
313 case 32000:
314 case 33075:
315 return 1;
316
317 case 24000:
318 case 22050:
319 return 2;
320
321 case 16000:
322 case 14700:
323 return 3;
324
325 case 12000:
326 case 11025:
327 return 4;
328
329 case 8000:
330 case 7350:
331 return 5;
332
333 default:
334 return -EINVAL;
335 }
336}
337
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100338static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
339 struct snd_pcm_hw_params *params,
340 unsigned int hwrate)
Emilio López45fb6b62015-09-12 15:26:24 +0200341{
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100342 /* Set ADC sample rate */
343 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
344 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
345 hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
Emilio López45fb6b62015-09-12 15:26:24 +0200346
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100347 /* Set the number of channels we want to use */
348 if (params_channels(params) == 1)
349 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
350 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
351 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
352 else
353 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
354 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0);
Emilio López45fb6b62015-09-12 15:26:24 +0200355
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100356 return 0;
357}
Emilio López45fb6b62015-09-12 15:26:24 +0200358
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100359static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
360 struct snd_pcm_hw_params *params,
361 unsigned int hwrate)
362{
363 u32 val;
Emilio López45fb6b62015-09-12 15:26:24 +0200364
365 /* Set DAC sample rate */
366 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
367 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
368 hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
369
370 /* Set the number of channels we want to use */
371 if (params_channels(params) == 1)
372 val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN);
373 else
374 val = 0;
375
376 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
377 BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),
378 val);
379
380 /* Set the number of sample bits to either 16 or 24 bits */
381 if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
382 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
383 BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
384 BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
385
386 /* Set TX FIFO mode to padding the LSBs with 0 */
387 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
388 BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
389 0);
390
391 scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
392 } else {
393 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
394 BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
395 0);
396
397 /* Set TX FIFO mode to repeat the MSB */
398 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
399 BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
400 BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
401
402 scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
403 }
404
405 return 0;
406}
407
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100408static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
409 struct snd_pcm_hw_params *params,
410 struct snd_soc_dai *dai)
411{
412 struct snd_soc_pcm_runtime *rtd = substream->private_data;
413 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
414 unsigned long clk_freq;
Maxime Ripard8400ddf2015-12-01 12:06:47 +0100415 int ret, hwrate;
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100416
417 clk_freq = sun4i_codec_get_mod_freq(params);
418 if (!clk_freq)
419 return -EINVAL;
420
Maxime Ripard8400ddf2015-12-01 12:06:47 +0100421 ret = clk_set_rate(scodec->clk_module, clk_freq);
422 if (ret)
423 return ret;
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100424
425 hwrate = sun4i_codec_get_hw_rate(params);
426 if (hwrate < 0)
427 return hwrate;
428
429 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
430 return sun4i_codec_hw_params_playback(scodec, params,
431 hwrate);
432
433 return sun4i_codec_hw_params_capture(scodec, params,
434 hwrate);
435}
436
Emilio López45fb6b62015-09-12 15:26:24 +0200437static int sun4i_codec_startup(struct snd_pcm_substream *substream,
438 struct snd_soc_dai *dai)
439{
440 struct snd_soc_pcm_runtime *rtd = substream->private_data;
441 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
442
443 /*
444 * Stop issuing DRQ when we have room for less than 16 samples
445 * in our TX FIFO
446 */
447 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
448 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT,
449 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
450
451 return clk_prepare_enable(scodec->clk_module);
452}
453
454static void sun4i_codec_shutdown(struct snd_pcm_substream *substream,
455 struct snd_soc_dai *dai)
456{
457 struct snd_soc_pcm_runtime *rtd = substream->private_data;
458 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
459
460 clk_disable_unprepare(scodec->clk_module);
461}
462
463static const struct snd_soc_dai_ops sun4i_codec_dai_ops = {
464 .startup = sun4i_codec_startup,
465 .shutdown = sun4i_codec_shutdown,
466 .trigger = sun4i_codec_trigger,
467 .hw_params = sun4i_codec_hw_params,
468 .prepare = sun4i_codec_prepare,
469};
470
471static struct snd_soc_dai_driver sun4i_codec_dai = {
472 .name = "Codec",
473 .ops = &sun4i_codec_dai_ops,
474 .playback = {
475 .stream_name = "Codec Playback",
476 .channels_min = 1,
477 .channels_max = 2,
478 .rate_min = 8000,
479 .rate_max = 192000,
480 .rates = SNDRV_PCM_RATE_8000_48000 |
481 SNDRV_PCM_RATE_96000 |
Maxime Riparddebb9722015-09-29 21:43:18 +0200482 SNDRV_PCM_RATE_192000,
Emilio López45fb6b62015-09-12 15:26:24 +0200483 .formats = SNDRV_PCM_FMTBIT_S16_LE |
484 SNDRV_PCM_FMTBIT_S32_LE,
485 .sig_bits = 24,
486 },
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100487 .capture = {
488 .stream_name = "Codec Capture",
489 .channels_min = 1,
490 .channels_max = 2,
491 .rate_min = 8000,
492 .rate_max = 192000,
493 .rates = SNDRV_PCM_RATE_8000_48000 |
494 SNDRV_PCM_RATE_96000 |
495 SNDRV_PCM_RATE_192000 |
496 SNDRV_PCM_RATE_KNOT,
497 .formats = SNDRV_PCM_FMTBIT_S16_LE |
498 SNDRV_PCM_FMTBIT_S32_LE,
499 .sig_bits = 24,
500 },
Emilio López45fb6b62015-09-12 15:26:24 +0200501};
502
503/*** Codec ***/
504static const struct snd_kcontrol_new sun4i_codec_pa_mute =
505 SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
506 SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
507
508static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);
509
510static const struct snd_kcontrol_new sun4i_codec_widgets[] = {
Adam Sampson474d1472015-10-27 21:00:45 +0000511 SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,
Emilio López45fb6b62015-09-12 15:26:24 +0200512 SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
513 sun4i_codec_pa_volume_scale),
514};
515
516static const struct snd_kcontrol_new sun4i_codec_left_mixer_controls[] = {
517 SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
518 SUN4I_CODEC_DAC_ACTL_LDACLMIXS, 1, 0),
519};
520
521static const struct snd_kcontrol_new sun4i_codec_right_mixer_controls[] = {
522 SOC_DAPM_SINGLE("Right DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
523 SUN4I_CODEC_DAC_ACTL_RDACRMIXS, 1, 0),
524 SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
525 SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0),
526};
527
528static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
529 SOC_DAPM_SINGLE("DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
530 SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0),
531 SOC_DAPM_SINGLE("Mixer Playback Switch", SUN4I_CODEC_DAC_ACTL,
532 SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),
533};
534
Hans de Goedee6415b42015-12-11 19:43:56 +0100535static const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = {
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100536 /* Digital parts of the ADCs */
537 SND_SOC_DAPM_SUPPLY("ADC", SUN4I_CODEC_ADC_FIFOC,
538 SUN4I_CODEC_ADC_FIFOC_EN_AD, 0,
539 NULL, 0),
540
Emilio López45fb6b62015-09-12 15:26:24 +0200541 /* Digital parts of the DACs */
542 SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
543 SUN4I_CODEC_DAC_DPC_EN_DA, 0,
544 NULL, 0),
545
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100546 /* Analog parts of the ADCs */
547 SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
548 SUN4I_CODEC_ADC_ACTL_ADC_L_EN, 0),
549 SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
550 SUN4I_CODEC_ADC_ACTL_ADC_R_EN, 0),
551
Emilio López45fb6b62015-09-12 15:26:24 +0200552 /* Analog parts of the DACs */
553 SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
554 SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
555 SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
556 SUN4I_CODEC_DAC_ACTL_DACAENR, 0),
557
558 /* Mixers */
559 SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
560 sun4i_codec_left_mixer_controls,
561 ARRAY_SIZE(sun4i_codec_left_mixer_controls)),
562 SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
563 sun4i_codec_right_mixer_controls,
564 ARRAY_SIZE(sun4i_codec_right_mixer_controls)),
565
566 /* Global Mixer Enable */
567 SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
568 SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),
569
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100570 /* VMIC */
571 SND_SOC_DAPM_SUPPLY("VMIC", SUN4I_CODEC_ADC_ACTL,
572 SUN4I_CODEC_ADC_ACTL_VMICEN, 0, NULL, 0),
573
574 /* Mic Pre-Amplifiers */
575 SND_SOC_DAPM_PGA("MIC1 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
576 SUN4I_CODEC_ADC_ACTL_PREG1EN, 0, NULL, 0),
577
Adam Sampson474d1472015-10-27 21:00:45 +0000578 /* Power Amplifier */
579 SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL,
Emilio López45fb6b62015-09-12 15:26:24 +0200580 SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
581 sun4i_codec_pa_mixer_controls,
582 ARRAY_SIZE(sun4i_codec_pa_mixer_controls)),
Adam Sampson474d1472015-10-27 21:00:45 +0000583 SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0,
Emilio López45fb6b62015-09-12 15:26:24 +0200584 &sun4i_codec_pa_mute),
585
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100586 SND_SOC_DAPM_INPUT("Mic1"),
587
Emilio López45fb6b62015-09-12 15:26:24 +0200588 SND_SOC_DAPM_OUTPUT("HP Right"),
589 SND_SOC_DAPM_OUTPUT("HP Left"),
590};
591
Hans de Goedee6415b42015-12-11 19:43:56 +0100592static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = {
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100593 /* Left ADC / DAC Routes */
594 { "Left ADC", NULL, "ADC" },
Emilio López45fb6b62015-09-12 15:26:24 +0200595 { "Left DAC", NULL, "DAC" },
596
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100597 /* Right ADC / DAC Routes */
598 { "Right ADC", NULL, "ADC" },
Emilio López45fb6b62015-09-12 15:26:24 +0200599 { "Right DAC", NULL, "DAC" },
600
601 /* Right Mixer Routes */
602 { "Right Mixer", NULL, "Mixer Enable" },
603 { "Right Mixer", "Left DAC Playback Switch", "Left DAC" },
604 { "Right Mixer", "Right DAC Playback Switch", "Right DAC" },
605
606 /* Left Mixer Routes */
607 { "Left Mixer", NULL, "Mixer Enable" },
608 { "Left Mixer", "Left DAC Playback Switch", "Left DAC" },
609
Adam Sampson474d1472015-10-27 21:00:45 +0000610 /* Power Amplifier Routes */
611 { "Power Amplifier", "Mixer Playback Switch", "Left Mixer" },
612 { "Power Amplifier", "Mixer Playback Switch", "Right Mixer" },
613 { "Power Amplifier", "DAC Playback Switch", "Left DAC" },
614 { "Power Amplifier", "DAC Playback Switch", "Right DAC" },
Emilio López45fb6b62015-09-12 15:26:24 +0200615
Adam Sampson474d1472015-10-27 21:00:45 +0000616 /* Headphone Output Routes */
617 { "Power Amplifier Mute", "Switch", "Power Amplifier" },
618 { "HP Right", NULL, "Power Amplifier Mute" },
619 { "HP Left", NULL, "Power Amplifier Mute" },
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100620
621 /* Mic1 Routes */
622 { "Left ADC", NULL, "MIC1 Pre-Amplifier" },
623 { "Right ADC", NULL, "MIC1 Pre-Amplifier" },
624 { "MIC1 Pre-Amplifier", NULL, "Mic1"},
625 { "Mic1", NULL, "VMIC" },
Emilio López45fb6b62015-09-12 15:26:24 +0200626};
627
628static struct snd_soc_codec_driver sun4i_codec_codec = {
629 .controls = sun4i_codec_widgets,
630 .num_controls = ARRAY_SIZE(sun4i_codec_widgets),
Hans de Goedee6415b42015-12-11 19:43:56 +0100631 .dapm_widgets = sun4i_codec_codec_dapm_widgets,
632 .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
633 .dapm_routes = sun4i_codec_codec_dapm_routes,
634 .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes),
Emilio López45fb6b62015-09-12 15:26:24 +0200635};
636
637static const struct snd_soc_component_driver sun4i_codec_component = {
638 .name = "sun4i-codec",
639};
640
641#define SUN4I_CODEC_RATES SNDRV_PCM_RATE_8000_192000
642#define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
643 SNDRV_PCM_FMTBIT_S32_LE)
644
645static int sun4i_codec_dai_probe(struct snd_soc_dai *dai)
646{
647 struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
648 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
649
650 snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100651 &scodec->capture_dma_data);
Emilio López45fb6b62015-09-12 15:26:24 +0200652
653 return 0;
654}
655
656static struct snd_soc_dai_driver dummy_cpu_dai = {
657 .name = "sun4i-codec-cpu-dai",
658 .probe = sun4i_codec_dai_probe,
659 .playback = {
660 .stream_name = "Playback",
661 .channels_min = 1,
662 .channels_max = 2,
663 .rates = SUN4I_CODEC_RATES,
664 .formats = SUN4I_CODEC_FORMATS,
665 .sig_bits = 24,
666 },
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100667 .capture = {
668 .stream_name = "Capture",
669 .channels_min = 1,
670 .channels_max = 2,
671 .rates = SUN4I_CODEC_RATES,
672 .formats = SUN4I_CODEC_FORMATS,
673 .sig_bits = 24,
674 },
Emilio López45fb6b62015-09-12 15:26:24 +0200675};
676
677static const struct regmap_config sun4i_codec_regmap_config = {
678 .reg_bits = 32,
679 .reg_stride = 4,
680 .val_bits = 32,
681 .max_register = SUN4I_CODEC_AC_MIC_PHONE_CAL,
682};
683
684static const struct of_device_id sun4i_codec_of_match[] = {
685 { .compatible = "allwinner,sun4i-a10-codec" },
686 { .compatible = "allwinner,sun7i-a20-codec" },
687 {}
688};
689MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
690
691static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
692 int *num_links)
693{
694 struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link),
695 GFP_KERNEL);
696 if (!link)
697 return NULL;
698
699 link->name = "cdc";
700 link->stream_name = "CDC PCM";
701 link->codec_dai_name = "Codec";
702 link->cpu_dai_name = dev_name(dev);
703 link->codec_name = dev_name(dev);
704 link->platform_name = dev_name(dev);
705 link->dai_fmt = SND_SOC_DAIFMT_I2S;
706
707 *num_links = 1;
708
709 return link;
710};
711
712static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
713{
714 struct snd_soc_card *card;
Emilio López45fb6b62015-09-12 15:26:24 +0200715
716 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
717 if (!card)
718 return NULL;
719
720 card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
721 if (!card->dai_link)
722 return NULL;
723
724 card->dev = dev;
725 card->name = "sun4i-codec";
726
Emilio López45fb6b62015-09-12 15:26:24 +0200727 return card;
728};
729
730static int sun4i_codec_probe(struct platform_device *pdev)
731{
732 struct snd_soc_card *card;
733 struct sun4i_codec *scodec;
734 struct resource *res;
735 void __iomem *base;
736 int ret;
737
738 scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
739 if (!scodec)
740 return -ENOMEM;
741
742 scodec->dev = &pdev->dev;
743
744 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
745 base = devm_ioremap_resource(&pdev->dev, res);
746 if (IS_ERR(base)) {
747 dev_err(&pdev->dev, "Failed to map the registers\n");
748 return PTR_ERR(base);
749 }
750
751 scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
752 &sun4i_codec_regmap_config);
753 if (IS_ERR(scodec->regmap)) {
754 dev_err(&pdev->dev, "Failed to create our regmap\n");
755 return PTR_ERR(scodec->regmap);
756 }
757
758 /* Get the clocks from the DT */
759 scodec->clk_apb = devm_clk_get(&pdev->dev, "apb");
760 if (IS_ERR(scodec->clk_apb)) {
761 dev_err(&pdev->dev, "Failed to get the APB clock\n");
762 return PTR_ERR(scodec->clk_apb);
763 }
764
765 scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
766 if (IS_ERR(scodec->clk_module)) {
767 dev_err(&pdev->dev, "Failed to get the module clock\n");
768 return PTR_ERR(scodec->clk_module);
769 }
770
771 /* Enable the bus clock */
772 if (clk_prepare_enable(scodec->clk_apb)) {
773 dev_err(&pdev->dev, "Failed to enable the APB clock\n");
774 return -EINVAL;
775 }
776
777 /* DMA configuration for TX FIFO */
778 scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA;
779 scodec->playback_dma_data.maxburst = 4;
780 scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
781
Maxime Ripard1fb34b42015-11-30 16:37:47 +0100782 /* DMA configuration for RX FIFO */
783 scodec->capture_dma_data.addr = res->start + SUN4I_CODEC_ADC_RXDATA;
784 scodec->capture_dma_data.maxburst = 4;
785 scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
786
Emilio López45fb6b62015-09-12 15:26:24 +0200787 ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec,
788 &sun4i_codec_dai, 1);
789 if (ret) {
790 dev_err(&pdev->dev, "Failed to register our codec\n");
791 goto err_clk_disable;
792 }
793
794 ret = devm_snd_soc_register_component(&pdev->dev,
795 &sun4i_codec_component,
796 &dummy_cpu_dai, 1);
797 if (ret) {
798 dev_err(&pdev->dev, "Failed to register our DAI\n");
799 goto err_unregister_codec;
800 }
801
802 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
803 if (ret) {
804 dev_err(&pdev->dev, "Failed to register against DMAEngine\n");
805 goto err_unregister_codec;
806 }
807
808 card = sun4i_codec_create_card(&pdev->dev);
809 if (!card) {
810 dev_err(&pdev->dev, "Failed to create our card\n");
811 goto err_unregister_codec;
812 }
813
814 platform_set_drvdata(pdev, card);
815 snd_soc_card_set_drvdata(card, scodec);
816
817 ret = snd_soc_register_card(card);
818 if (ret) {
819 dev_err(&pdev->dev, "Failed to register our card\n");
820 goto err_unregister_codec;
821 }
822
823 return 0;
824
825err_unregister_codec:
826 snd_soc_unregister_codec(&pdev->dev);
827err_clk_disable:
828 clk_disable_unprepare(scodec->clk_apb);
829 return ret;
830}
831
832static int sun4i_codec_remove(struct platform_device *pdev)
833{
834 struct snd_soc_card *card = platform_get_drvdata(pdev);
835 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
836
837 snd_soc_unregister_card(card);
838 snd_soc_unregister_codec(&pdev->dev);
839 clk_disable_unprepare(scodec->clk_apb);
840
841 return 0;
842}
843
844static struct platform_driver sun4i_codec_driver = {
845 .driver = {
846 .name = "sun4i-codec",
Emilio López45fb6b62015-09-12 15:26:24 +0200847 .of_match_table = sun4i_codec_of_match,
848 },
849 .probe = sun4i_codec_probe,
850 .remove = sun4i_codec_remove,
851};
852module_platform_driver(sun4i_codec_driver);
853
854MODULE_DESCRIPTION("Allwinner A10 codec driver");
855MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");
856MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
857MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
858MODULE_LICENSE("GPL");