aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/sunxi/sun4i-codec.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sunxi/sun4i-codec.c')
-rw-r--r--sound/soc/sunxi/sun4i-codec.c279
1 files changed, 230 insertions, 49 deletions
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index 1bb896d78d09..44f170c73b06 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -28,6 +28,7 @@
28#include <linux/of_address.h> 28#include <linux/of_address.h>
29#include <linux/clk.h> 29#include <linux/clk.h>
30#include <linux/regmap.h> 30#include <linux/regmap.h>
31#include <linux/gpio/consumer.h>
31 32
32#include <sound/core.h> 33#include <sound/core.h>
33#include <sound/pcm.h> 34#include <sound/pcm.h>
@@ -70,6 +71,7 @@
70 71
71/* Codec ADC register offsets and bit fields */ 72/* Codec ADC register offsets and bit fields */
72#define SUN4I_CODEC_ADC_FIFOC (0x1c) 73#define SUN4I_CODEC_ADC_FIFOC (0x1c)
74#define SUN4I_CODEC_ADC_FIFOC_ADC_FS (29)
73#define SUN4I_CODEC_ADC_FIFOC_EN_AD (28) 75#define SUN4I_CODEC_ADC_FIFOC_EN_AD (28)
74#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24) 76#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24)
75#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8) 77#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8)
@@ -102,17 +104,14 @@ struct sun4i_codec {
102 struct regmap *regmap; 104 struct regmap *regmap;
103 struct clk *clk_apb; 105 struct clk *clk_apb;
104 struct clk *clk_module; 106 struct clk *clk_module;
107 struct gpio_desc *gpio_pa;
105 108
109 struct snd_dmaengine_dai_dma_data capture_dma_data;
106 struct snd_dmaengine_dai_dma_data playback_dma_data; 110 struct snd_dmaengine_dai_dma_data playback_dma_data;
107}; 111};
108 112
109static void sun4i_codec_start_playback(struct sun4i_codec *scodec) 113static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
110{ 114{
111 /*
112 * FIXME: according to the BSP, we might need to drive a PA
113 * GPIO high here on some boards
114 */
115
116 /* Flush TX FIFO */ 115 /* Flush TX FIFO */
117 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 116 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
118 BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH), 117 BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
@@ -126,37 +125,50 @@ static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
126 125
127static void sun4i_codec_stop_playback(struct sun4i_codec *scodec) 126static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
128{ 127{
129 /*
130 * FIXME: according to the BSP, we might need to drive a PA
131 * GPIO low here on some boards
132 */
133
134 /* Disable DAC DRQ */ 128 /* Disable DAC DRQ */
135 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 129 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
136 BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN), 130 BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
137 0); 131 0);
138} 132}
139 133
134static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
135{
136 /* Enable ADC DRQ */
137 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
138 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN),
139 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
140}
141
142static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
143{
144 /* Disable ADC DRQ */
145 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
146 BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0);
147}
148
140static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd, 149static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
141 struct snd_soc_dai *dai) 150 struct snd_soc_dai *dai)
142{ 151{
143 struct snd_soc_pcm_runtime *rtd = substream->private_data; 152 struct snd_soc_pcm_runtime *rtd = substream->private_data;
144 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); 153 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
145 154
146 if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
147 return -ENOTSUPP;
148
149 switch (cmd) { 155 switch (cmd) {
150 case SNDRV_PCM_TRIGGER_START: 156 case SNDRV_PCM_TRIGGER_START:
151 case SNDRV_PCM_TRIGGER_RESUME: 157 case SNDRV_PCM_TRIGGER_RESUME:
152 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 158 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
153 sun4i_codec_start_playback(scodec); 159 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
160 sun4i_codec_start_playback(scodec);
161 else
162 sun4i_codec_start_capture(scodec);
154 break; 163 break;
155 164
156 case SNDRV_PCM_TRIGGER_STOP: 165 case SNDRV_PCM_TRIGGER_STOP:
157 case SNDRV_PCM_TRIGGER_SUSPEND: 166 case SNDRV_PCM_TRIGGER_SUSPEND:
158 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 167 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
159 sun4i_codec_stop_playback(scodec); 168 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
169 sun4i_codec_stop_playback(scodec);
170 else
171 sun4i_codec_stop_capture(scodec);
160 break; 172 break;
161 173
162 default: 174 default:
@@ -166,15 +178,54 @@ static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
166 return 0; 178 return 0;
167} 179}
168 180
169static int sun4i_codec_prepare(struct snd_pcm_substream *substream, 181static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
170 struct snd_soc_dai *dai) 182 struct snd_soc_dai *dai)
171{ 183{
172 struct snd_soc_pcm_runtime *rtd = substream->private_data; 184 struct snd_soc_pcm_runtime *rtd = substream->private_data;
173 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); 185 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
174 u32 val;
175 186
176 if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) 187
177 return -ENOTSUPP; 188 /* Flush RX FIFO */
189 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
190 BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH),
191 BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));
192
193
194 /* Set RX FIFO trigger level */
195 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
196 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
197 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
198
199 /*
200 * FIXME: Undocumented in the datasheet, but
201 * Allwinner's code mentions that it is related
202 * related to microphone gain
203 */
204 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
205 0x3 << 25,
206 0x1 << 25);
207
208 if (of_device_is_compatible(scodec->dev->of_node,
209 "allwinner,sun7i-a20-codec"))
210 /* FIXME: Undocumented bits */
211 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_TUNE,
212 0x3 << 8,
213 0x1 << 8);
214
215 /* Fill most significant bits with valid data MSB */
216 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
217 BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
218 BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
219
220 return 0;
221}
222
223static int sun4i_codec_prepare_playback(struct snd_pcm_substream *substream,
224 struct snd_soc_dai *dai)
225{
226 struct snd_soc_pcm_runtime *rtd = substream->private_data;
227 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
228 u32 val;
178 229
179 /* Flush the TX FIFO */ 230 /* Flush the TX FIFO */
180 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 231 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
@@ -203,6 +254,15 @@ static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
203 0); 254 0);
204 255
205 return 0; 256 return 0;
257};
258
259static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
260 struct snd_soc_dai *dai)
261{
262 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
263 return sun4i_codec_prepare_playback(substream, dai);
264
265 return sun4i_codec_prepare_capture(substream, dai);
206} 266}
207 267
208static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params) 268static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
@@ -277,30 +337,32 @@ static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
277 } 337 }
278} 338}
279 339
280static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, 340static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
281 struct snd_pcm_hw_params *params, 341 struct snd_pcm_hw_params *params,
282 struct snd_soc_dai *dai) 342 unsigned int hwrate)
283{ 343{
284 struct snd_soc_pcm_runtime *rtd = substream->private_data; 344 /* Set ADC sample rate */
285 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); 345 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
286 unsigned long clk_freq; 346 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
287 int ret, hwrate; 347 hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
288 u32 val;
289
290 if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
291 return -ENOTSUPP;
292 348
293 clk_freq = sun4i_codec_get_mod_freq(params); 349 /* Set the number of channels we want to use */
294 if (!clk_freq) 350 if (params_channels(params) == 1)
295 return -EINVAL; 351 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
352 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
353 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
354 else
355 regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
356 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0);
296 357
297 ret = clk_set_rate(scodec->clk_module, clk_freq); 358 return 0;
298 if (ret) 359}
299 return ret;
300 360
301 hwrate = sun4i_codec_get_hw_rate(params); 361static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
302 if (hwrate < 0) 362 struct snd_pcm_hw_params *params,
303 return hwrate; 363 unsigned int hwrate)
364{
365 u32 val;
304 366
305 /* Set DAC sample rate */ 367 /* Set DAC sample rate */
306 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, 368 regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
@@ -345,6 +407,35 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
345 return 0; 407 return 0;
346} 408}
347 409
410static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
411 struct snd_pcm_hw_params *params,
412 struct snd_soc_dai *dai)
413{
414 struct snd_soc_pcm_runtime *rtd = substream->private_data;
415 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
416 unsigned long clk_freq;
417 int ret, hwrate;
418
419 clk_freq = sun4i_codec_get_mod_freq(params);
420 if (!clk_freq)
421 return -EINVAL;
422
423 ret = clk_set_rate(scodec->clk_module, clk_freq);
424 if (ret)
425 return ret;
426
427 hwrate = sun4i_codec_get_hw_rate(params);
428 if (hwrate < 0)
429 return hwrate;
430
431 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
432 return sun4i_codec_hw_params_playback(scodec, params,
433 hwrate);
434
435 return sun4i_codec_hw_params_capture(scodec, params,
436 hwrate);
437}
438
348static int sun4i_codec_startup(struct snd_pcm_substream *substream, 439static int sun4i_codec_startup(struct snd_pcm_substream *substream,
349 struct snd_soc_dai *dai) 440 struct snd_soc_dai *dai)
350{ 441{
@@ -395,6 +486,20 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
395 SNDRV_PCM_FMTBIT_S32_LE, 486 SNDRV_PCM_FMTBIT_S32_LE,
396 .sig_bits = 24, 487 .sig_bits = 24,
397 }, 488 },
489 .capture = {
490 .stream_name = "Codec Capture",
491 .channels_min = 1,
492 .channels_max = 2,
493 .rate_min = 8000,
494 .rate_max = 192000,
495 .rates = SNDRV_PCM_RATE_8000_48000 |
496 SNDRV_PCM_RATE_96000 |
497 SNDRV_PCM_RATE_192000 |
498 SNDRV_PCM_RATE_KNOT,
499 .formats = SNDRV_PCM_FMTBIT_S16_LE |
500 SNDRV_PCM_FMTBIT_S32_LE,
501 .sig_bits = 24,
502 },
398}; 503};
399 504
400/*** Codec ***/ 505/*** Codec ***/
@@ -429,12 +534,23 @@ static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
429 SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0), 534 SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),
430}; 535};
431 536
432static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = { 537static const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = {
538 /* Digital parts of the ADCs */
539 SND_SOC_DAPM_SUPPLY("ADC", SUN4I_CODEC_ADC_FIFOC,
540 SUN4I_CODEC_ADC_FIFOC_EN_AD, 0,
541 NULL, 0),
542
433 /* Digital parts of the DACs */ 543 /* Digital parts of the DACs */
434 SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC, 544 SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
435 SUN4I_CODEC_DAC_DPC_EN_DA, 0, 545 SUN4I_CODEC_DAC_DPC_EN_DA, 0,
436 NULL, 0), 546 NULL, 0),
437 547
548 /* Analog parts of the ADCs */
549 SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
550 SUN4I_CODEC_ADC_ACTL_ADC_L_EN, 0),
551 SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
552 SUN4I_CODEC_ADC_ACTL_ADC_R_EN, 0),
553
438 /* Analog parts of the DACs */ 554 /* Analog parts of the DACs */
439 SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL, 555 SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
440 SUN4I_CODEC_DAC_ACTL_DACAENL, 0), 556 SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
@@ -453,6 +569,14 @@ static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
453 SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL, 569 SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
454 SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0), 570 SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),
455 571
572 /* VMIC */
573 SND_SOC_DAPM_SUPPLY("VMIC", SUN4I_CODEC_ADC_ACTL,
574 SUN4I_CODEC_ADC_ACTL_VMICEN, 0, NULL, 0),
575
576 /* Mic Pre-Amplifiers */
577 SND_SOC_DAPM_PGA("MIC1 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
578 SUN4I_CODEC_ADC_ACTL_PREG1EN, 0, NULL, 0),
579
456 /* Power Amplifier */ 580 /* Power Amplifier */
457 SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL, 581 SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL,
458 SUN4I_CODEC_ADC_ACTL_PA_EN, 0, 582 SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
@@ -461,15 +585,19 @@ static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
461 SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0, 585 SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0,
462 &sun4i_codec_pa_mute), 586 &sun4i_codec_pa_mute),
463 587
588 SND_SOC_DAPM_INPUT("Mic1"),
589
464 SND_SOC_DAPM_OUTPUT("HP Right"), 590 SND_SOC_DAPM_OUTPUT("HP Right"),
465 SND_SOC_DAPM_OUTPUT("HP Left"), 591 SND_SOC_DAPM_OUTPUT("HP Left"),
466}; 592};
467 593
468static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = { 594static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = {
469 /* Left DAC Routes */ 595 /* Left ADC / DAC Routes */
596 { "Left ADC", NULL, "ADC" },
470 { "Left DAC", NULL, "DAC" }, 597 { "Left DAC", NULL, "DAC" },
471 598
472 /* Right DAC Routes */ 599 /* Right ADC / DAC Routes */
600 { "Right ADC", NULL, "ADC" },
473 { "Right DAC", NULL, "DAC" }, 601 { "Right DAC", NULL, "DAC" },
474 602
475 /* Right Mixer Routes */ 603 /* Right Mixer Routes */
@@ -491,15 +619,21 @@ static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = {
491 { "Power Amplifier Mute", "Switch", "Power Amplifier" }, 619 { "Power Amplifier Mute", "Switch", "Power Amplifier" },
492 { "HP Right", NULL, "Power Amplifier Mute" }, 620 { "HP Right", NULL, "Power Amplifier Mute" },
493 { "HP Left", NULL, "Power Amplifier Mute" }, 621 { "HP Left", NULL, "Power Amplifier Mute" },
622
623 /* Mic1 Routes */
624 { "Left ADC", NULL, "MIC1 Pre-Amplifier" },
625 { "Right ADC", NULL, "MIC1 Pre-Amplifier" },
626 { "MIC1 Pre-Amplifier", NULL, "Mic1"},
627 { "Mic1", NULL, "VMIC" },
494}; 628};
495 629
496static struct snd_soc_codec_driver sun4i_codec_codec = { 630static struct snd_soc_codec_driver sun4i_codec_codec = {
497 .controls = sun4i_codec_widgets, 631 .controls = sun4i_codec_widgets,
498 .num_controls = ARRAY_SIZE(sun4i_codec_widgets), 632 .num_controls = ARRAY_SIZE(sun4i_codec_widgets),
499 .dapm_widgets = sun4i_codec_dapm_widgets, 633 .dapm_widgets = sun4i_codec_codec_dapm_widgets,
500 .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_dapm_widgets), 634 .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
501 .dapm_routes = sun4i_codec_dapm_routes, 635 .dapm_routes = sun4i_codec_codec_dapm_routes,
502 .num_dapm_routes = ARRAY_SIZE(sun4i_codec_dapm_routes), 636 .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes),
503}; 637};
504 638
505static const struct snd_soc_component_driver sun4i_codec_component = { 639static const struct snd_soc_component_driver sun4i_codec_component = {
@@ -516,7 +650,7 @@ static int sun4i_codec_dai_probe(struct snd_soc_dai *dai)
516 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card); 650 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
517 651
518 snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data, 652 snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
519 NULL); 653 &scodec->capture_dma_data);
520 654
521 return 0; 655 return 0;
522} 656}
@@ -532,6 +666,14 @@ static struct snd_soc_dai_driver dummy_cpu_dai = {
532 .formats = SUN4I_CODEC_FORMATS, 666 .formats = SUN4I_CODEC_FORMATS,
533 .sig_bits = 24, 667 .sig_bits = 24,
534 }, 668 },
669 .capture = {
670 .stream_name = "Capture",
671 .channels_min = 1,
672 .channels_max = 2,
673 .rates = SUN4I_CODEC_RATES,
674 .formats = SUN4I_CODEC_FORMATS,
675 .sig_bits = 24,
676 },
535}; 677};
536 678
537static const struct regmap_config sun4i_codec_regmap_config = { 679static const struct regmap_config sun4i_codec_regmap_config = {
@@ -569,6 +711,27 @@ static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
569 return link; 711 return link;
570}; 712};
571 713
714static int sun4i_codec_spk_event(struct snd_soc_dapm_widget *w,
715 struct snd_kcontrol *k, int event)
716{
717 struct sun4i_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card);
718
719 if (scodec->gpio_pa)
720 gpiod_set_value_cansleep(scodec->gpio_pa,
721 !!SND_SOC_DAPM_EVENT_ON(event));
722
723 return 0;
724}
725
726static const struct snd_soc_dapm_widget sun4i_codec_card_dapm_widgets[] = {
727 SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
728};
729
730static const struct snd_soc_dapm_route sun4i_codec_card_dapm_routes[] = {
731 { "Speaker", NULL, "HP Right" },
732 { "Speaker", NULL, "HP Left" },
733};
734
572static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) 735static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
573{ 736{
574 struct snd_soc_card *card; 737 struct snd_soc_card *card;
@@ -583,6 +746,10 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
583 746
584 card->dev = dev; 747 card->dev = dev;
585 card->name = "sun4i-codec"; 748 card->name = "sun4i-codec";
749 card->dapm_widgets = sun4i_codec_card_dapm_widgets;
750 card->num_dapm_widgets = ARRAY_SIZE(sun4i_codec_card_dapm_widgets);
751 card->dapm_routes = sun4i_codec_card_dapm_routes;
752 card->num_dapm_routes = ARRAY_SIZE(sun4i_codec_card_dapm_routes);
586 753
587 return card; 754 return card;
588}; 755};
@@ -634,11 +801,25 @@ static int sun4i_codec_probe(struct platform_device *pdev)
634 return -EINVAL; 801 return -EINVAL;
635 } 802 }
636 803
804 scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa",
805 GPIOD_OUT_LOW);
806 if (IS_ERR(scodec->gpio_pa)) {
807 ret = PTR_ERR(scodec->gpio_pa);
808 if (ret != -EPROBE_DEFER)
809 dev_err(&pdev->dev, "Failed to get pa gpio: %d\n", ret);
810 return ret;
811 }
812
637 /* DMA configuration for TX FIFO */ 813 /* DMA configuration for TX FIFO */
638 scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA; 814 scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA;
639 scodec->playback_dma_data.maxburst = 4; 815 scodec->playback_dma_data.maxburst = 4;
640 scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 816 scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
641 817
818 /* DMA configuration for RX FIFO */
819 scodec->capture_dma_data.addr = res->start + SUN4I_CODEC_ADC_RXDATA;
820 scodec->capture_dma_data.maxburst = 4;
821 scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
822
642 ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec, 823 ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec,
643 &sun4i_codec_dai, 1); 824 &sun4i_codec_dai, 1);
644 if (ret) { 825 if (ret) {