aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/sunxi/sun4i-codec.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2015-12-23 02:33:52 -0500
committerTakashi Iwai <tiwai@suse.de>2015-12-23 02:33:52 -0500
commitf80e39e0225c01ee68764ef7594c3a29ab5ebabb (patch)
treef5a85085741a173c93fc8f21938528b65ed95e42 /sound/soc/sunxi/sun4i-codec.c
parent59c8231089be96165735585694a801ae58ec6c95 (diff)
parent822ad70a2f5c420da5baa9f4354e6b7813ca6da9 (diff)
Merge tag 'asoc-v4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next
ASoC: Updates for v4.5 This is quite a busy release on the driver front with a lot of new drivers being added but comparatively quiet on the core side with only one big change going in and that a fairly straightforward refactoring. - Conversion of the array of DAI links to a list by Mengdong Lin, supporting dynamically adding and removing DAI links. - Some more fixes for the topology code, though it is still not final and ready for enabling in production. We really need to get to the point where that can be done. - A pile of changes for Intel SkyLake drivers which hopefully deliver some useful initial functionality for systems with this chipset, though there is more work still to come. - New drivers for a number of Imagination Technologies IPs. - Lots of new features and cleanups for the Renesas drivers. - ANC support for WM5110. - New driver for Atmel class D speaker drivers. - New drivers for Cirrus CS47L24 and WM1831. - New driver for Dialog DA7128. - New drivers for Realtek RT5659 and RT56156. - New driver for Rockchip RK3036. - New driver for TI PC3168A
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) {