diff options
author | Takashi Iwai <tiwai@suse.de> | 2015-12-23 02:33:52 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2015-12-23 02:33:52 -0500 |
commit | f80e39e0225c01ee68764ef7594c3a29ab5ebabb (patch) | |
tree | f5a85085741a173c93fc8f21938528b65ed95e42 /sound/soc/sunxi/sun4i-codec.c | |
parent | 59c8231089be96165735585694a801ae58ec6c95 (diff) | |
parent | 822ad70a2f5c420da5baa9f4354e6b7813ca6da9 (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.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) { |