diff options
Diffstat (limited to 'sound/soc/sunxi/sun4i-codec.c')
-rw-r--r-- | sound/soc/sunxi/sun4i-codec.c | 279 |
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 | ||
109 | static void sun4i_codec_start_playback(struct sun4i_codec *scodec) | 113 | static 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 | ||
127 | static void sun4i_codec_stop_playback(struct sun4i_codec *scodec) | 126 | static 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 | ||
134 | static 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 | |||
142 | static 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 | |||
140 | static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd, | 149 | static 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 | ||
169 | static int sun4i_codec_prepare(struct snd_pcm_substream *substream, | 181 | static 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 | |||
223 | static 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 | |||
259 | static 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 | ||
208 | static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params) | 268 | static 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 | ||
280 | static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, | 340 | static 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); | 361 | static 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 | ||
410 | static 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 | |||
348 | static int sun4i_codec_startup(struct snd_pcm_substream *substream, | 439 | static 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 | ||
432 | static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = { | 537 | static 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 | ||
468 | static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = { | 594 | static 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 | ||
496 | static struct snd_soc_codec_driver sun4i_codec_codec = { | 630 | static 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 | ||
505 | static const struct snd_soc_component_driver sun4i_codec_component = { | 639 | static 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 | ||
537 | static const struct regmap_config sun4i_codec_regmap_config = { | 679 | static 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 | ||
714 | static 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 | |||
726 | static const struct snd_soc_dapm_widget sun4i_codec_card_dapm_widgets[] = { | ||
727 | SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event), | ||
728 | }; | ||
729 | |||
730 | static const struct snd_soc_dapm_route sun4i_codec_card_dapm_routes[] = { | ||
731 | { "Speaker", NULL, "HP Right" }, | ||
732 | { "Speaker", NULL, "HP Left" }, | ||
733 | }; | ||
734 | |||
572 | static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) | 735 | static 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) { |