Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * This driver supports the analog controls for the internal codec
  4 * found in Allwinner's A64 SoC.
  5 *
  6 * Copyright (C) 2016 Chen-Yu Tsai <wens@csie.org>
  7 * Copyright (C) 2017 Marcus Cooper <codekipper@gmail.com>
  8 * Copyright (C) 2018 Vasily Khoruzhick <anarsoul@gmail.com>
  9 *
 10 * Based on sun8i-codec-analog.c
 11 *
 12 */
 13
 14#include <linux/io.h>
 15#include <linux/kernel.h>
 16#include <linux/mod_devicetable.h>
 17#include <linux/module.h>
 18#include <linux/platform_device.h>
 19#include <linux/regmap.h>
 20
 21#include <sound/soc.h>
 22#include <sound/soc-dapm.h>
 23#include <sound/tlv.h>
 24
 25#include "sun8i-adda-pr-regmap.h"
 26
 27/* Codec analog control register offsets and bit fields */
 28#define SUN50I_ADDA_HP_CTRL		0x00
 29#define SUN50I_ADDA_HP_CTRL_PA_CLK_GATE		7
 30#define SUN50I_ADDA_HP_CTRL_HPPA_EN		6
 31#define SUN50I_ADDA_HP_CTRL_HPVOL		0
 32
 33#define SUN50I_ADDA_OL_MIX_CTRL		0x01
 34#define SUN50I_ADDA_OL_MIX_CTRL_MIC1		6
 35#define SUN50I_ADDA_OL_MIX_CTRL_MIC2		5
 36#define SUN50I_ADDA_OL_MIX_CTRL_PHONE		4
 37#define SUN50I_ADDA_OL_MIX_CTRL_PHONEN		3
 38#define SUN50I_ADDA_OL_MIX_CTRL_LINEINL		2
 39#define SUN50I_ADDA_OL_MIX_CTRL_DACL		1
 40#define SUN50I_ADDA_OL_MIX_CTRL_DACR		0
 41
 42#define SUN50I_ADDA_OR_MIX_CTRL		0x02
 43#define SUN50I_ADDA_OR_MIX_CTRL_MIC1		6
 44#define SUN50I_ADDA_OR_MIX_CTRL_MIC2		5
 45#define SUN50I_ADDA_OR_MIX_CTRL_PHONE		4
 46#define SUN50I_ADDA_OR_MIX_CTRL_PHONEP		3
 47#define SUN50I_ADDA_OR_MIX_CTRL_LINEINR		2
 48#define SUN50I_ADDA_OR_MIX_CTRL_DACR		1
 49#define SUN50I_ADDA_OR_MIX_CTRL_DACL		0
 50
 51#define SUN50I_ADDA_EARPIECE_CTRL0	0x03
 52#define SUN50I_ADDA_EARPIECE_CTRL0_EAR_RAMP_TIME	4
 53#define SUN50I_ADDA_EARPIECE_CTRL0_ESPSR		0
 54
 55#define SUN50I_ADDA_EARPIECE_CTRL1	0x04
 56#define SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_EN	7
 57#define SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE	6
 58#define SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL	0
 59
 60#define SUN50I_ADDA_LINEOUT_CTRL0	0x05
 61#define SUN50I_ADDA_LINEOUT_CTRL0_LEN		7
 62#define SUN50I_ADDA_LINEOUT_CTRL0_REN		6
 63#define SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL	5
 64#define SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL	4
 65
 66#define SUN50I_ADDA_LINEOUT_CTRL1	0x06
 67#define SUN50I_ADDA_LINEOUT_CTRL1_VOL		0
 68
 69#define SUN50I_ADDA_MIC1_CTRL		0x07
 70#define SUN50I_ADDA_MIC1_CTRL_MIC1G		4
 71#define SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN		3
 72#define SUN50I_ADDA_MIC1_CTRL_MIC1BOOST		0
 73
 74#define SUN50I_ADDA_MIC2_CTRL		0x08
 75#define SUN50I_ADDA_MIC2_CTRL_MIC2G		4
 76#define SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN		3
 77#define SUN50I_ADDA_MIC2_CTRL_MIC2BOOST		0
 78
 79#define SUN50I_ADDA_LINEIN_CTRL		0x09
 80#define SUN50I_ADDA_LINEIN_CTRL_LINEING		0
 81
 82#define SUN50I_ADDA_MIX_DAC_CTRL	0x0a
 83#define SUN50I_ADDA_MIX_DAC_CTRL_DACAREN	7
 84#define SUN50I_ADDA_MIX_DAC_CTRL_DACALEN	6
 85#define SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN		5
 86#define SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN		4
 87#define SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE	3
 88#define SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE	2
 89#define SUN50I_ADDA_MIX_DAC_CTRL_RHPIS		1
 90#define SUN50I_ADDA_MIX_DAC_CTRL_LHPIS		0
 91
 92#define SUN50I_ADDA_L_ADCMIX_SRC	0x0b
 93#define SUN50I_ADDA_L_ADCMIX_SRC_MIC1		6
 94#define SUN50I_ADDA_L_ADCMIX_SRC_MIC2		5
 95#define SUN50I_ADDA_L_ADCMIX_SRC_PHONE		4
 96#define SUN50I_ADDA_L_ADCMIX_SRC_PHONEN		3
 97#define SUN50I_ADDA_L_ADCMIX_SRC_LINEINL	2
 98#define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL		1
 99#define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR		0
100
101#define SUN50I_ADDA_R_ADCMIX_SRC	0x0c
102#define SUN50I_ADDA_R_ADCMIX_SRC_MIC1		6
103#define SUN50I_ADDA_R_ADCMIX_SRC_MIC2		5
104#define SUN50I_ADDA_R_ADCMIX_SRC_PHONE		4
105#define SUN50I_ADDA_R_ADCMIX_SRC_PHONEP		3
106#define SUN50I_ADDA_R_ADCMIX_SRC_LINEINR	2
107#define SUN50I_ADDA_R_ADCMIX_SRC_OMIXR		1
108#define SUN50I_ADDA_R_ADCMIX_SRC_OMIXL		0
109
110#define SUN50I_ADDA_ADC_CTRL		0x0d
111#define SUN50I_ADDA_ADC_CTRL_ADCREN		7
112#define SUN50I_ADDA_ADC_CTRL_ADCLEN		6
113#define SUN50I_ADDA_ADC_CTRL_ADCG		0
114
115#define SUN50I_ADDA_HS_MBIAS_CTRL	0x0e
116#define SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN	7
117
118#define SUN50I_ADDA_MDET_CTRL		0x1c
119#define SUN50I_ADDA_MDET_CTRL_SELDETADC_FS	4
120#define SUN50I_ADDA_MDET_CTRL_SELDETADC_DB	2
121#define SUN50I_ADDA_MDET_CTRL_SELDETADC_BF	0
122
123#define SUN50I_ADDA_JACK_MIC_CTRL	0x1d
124#define SUN50I_ADDA_JACK_MIC_CTRL_JACKDETEN	7
125#define SUN50I_ADDA_JACK_MIC_CTRL_INNERRESEN	6
126#define SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN	5
127#define SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN	4
128
129/* mixer controls */
130static const struct snd_kcontrol_new sun50i_a64_codec_mixer_controls[] = {
131	SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
132			  SUN50I_ADDA_OL_MIX_CTRL,
133			  SUN50I_ADDA_OR_MIX_CTRL,
134			  SUN50I_ADDA_OL_MIX_CTRL_MIC1, 1, 0),
135	SOC_DAPM_DOUBLE_R("Mic2 Playback Switch",
136			  SUN50I_ADDA_OL_MIX_CTRL,
137			  SUN50I_ADDA_OR_MIX_CTRL,
138			  SUN50I_ADDA_OL_MIX_CTRL_MIC2, 1, 0),
139	SOC_DAPM_DOUBLE_R("Line In Playback Switch",
140			  SUN50I_ADDA_OL_MIX_CTRL,
141			  SUN50I_ADDA_OR_MIX_CTRL,
142			  SUN50I_ADDA_OL_MIX_CTRL_LINEINL, 1, 0),
143	SOC_DAPM_DOUBLE_R("DAC Playback Switch",
144			  SUN50I_ADDA_OL_MIX_CTRL,
145			  SUN50I_ADDA_OR_MIX_CTRL,
146			  SUN50I_ADDA_OL_MIX_CTRL_DACL, 1, 0),
147	SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
148			  SUN50I_ADDA_OL_MIX_CTRL,
149			  SUN50I_ADDA_OR_MIX_CTRL,
150			  SUN50I_ADDA_OL_MIX_CTRL_DACR, 1, 0),
151};
152
153/* ADC mixer controls */
154static const struct snd_kcontrol_new sun50i_codec_adc_mixer_controls[] = {
155	SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
156			  SUN50I_ADDA_L_ADCMIX_SRC,
157			  SUN50I_ADDA_R_ADCMIX_SRC,
158			  SUN50I_ADDA_L_ADCMIX_SRC_MIC1, 1, 0),
159	SOC_DAPM_DOUBLE_R("Mic2 Capture Switch",
160			  SUN50I_ADDA_L_ADCMIX_SRC,
161			  SUN50I_ADDA_R_ADCMIX_SRC,
162			  SUN50I_ADDA_L_ADCMIX_SRC_MIC2, 1, 0),
163	SOC_DAPM_DOUBLE_R("Line In Capture Switch",
164			  SUN50I_ADDA_L_ADCMIX_SRC,
165			  SUN50I_ADDA_R_ADCMIX_SRC,
166			  SUN50I_ADDA_L_ADCMIX_SRC_LINEINL, 1, 0),
167	SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
168			  SUN50I_ADDA_L_ADCMIX_SRC,
169			  SUN50I_ADDA_R_ADCMIX_SRC,
170			  SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL, 1, 0),
171	SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
172			  SUN50I_ADDA_L_ADCMIX_SRC,
173			  SUN50I_ADDA_R_ADCMIX_SRC,
174			  SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR, 1, 0),
175};
176
177static const DECLARE_TLV_DB_SCALE(sun50i_codec_out_mixer_pregain_scale,
178				  -450, 150, 0);
179static const DECLARE_TLV_DB_RANGE(sun50i_codec_mic_gain_scale,
180	0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
181	1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
182);
183
184static const DECLARE_TLV_DB_SCALE(sun50i_codec_hp_vol_scale, -6300, 100, 1);
185
186static const DECLARE_TLV_DB_RANGE(sun50i_codec_lineout_vol_scale,
187	0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
188	2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
189);
190
191static const DECLARE_TLV_DB_RANGE(sun50i_codec_earpiece_vol_scale,
192	0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
193	2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
194);
195
196/* volume / mute controls */
197static const struct snd_kcontrol_new sun50i_a64_codec_controls[] = {
198	SOC_SINGLE_TLV("Headphone Playback Volume",
199		       SUN50I_ADDA_HP_CTRL,
200		       SUN50I_ADDA_HP_CTRL_HPVOL, 0x3f, 0,
201		       sun50i_codec_hp_vol_scale),
202
203	/* Mixer pre-gain */
204	SOC_SINGLE_TLV("Mic1 Playback Volume", SUN50I_ADDA_MIC1_CTRL,
205		       SUN50I_ADDA_MIC1_CTRL_MIC1G,
206		       0x7, 0, sun50i_codec_out_mixer_pregain_scale),
207
208	/* Microphone Amp boost gain */
209	SOC_SINGLE_TLV("Mic1 Boost Volume", SUN50I_ADDA_MIC1_CTRL,
210		       SUN50I_ADDA_MIC1_CTRL_MIC1BOOST, 0x7, 0,
211		       sun50i_codec_mic_gain_scale),
212
213	/* Mixer pre-gain */
214	SOC_SINGLE_TLV("Mic2 Playback Volume",
215		       SUN50I_ADDA_MIC2_CTRL, SUN50I_ADDA_MIC2_CTRL_MIC2G,
216		       0x7, 0, sun50i_codec_out_mixer_pregain_scale),
217
218	/* Microphone Amp boost gain */
219	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN50I_ADDA_MIC2_CTRL,
220		       SUN50I_ADDA_MIC2_CTRL_MIC2BOOST, 0x7, 0,
221		       sun50i_codec_mic_gain_scale),
222
223	/* ADC */
224	SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN50I_ADDA_ADC_CTRL,
225		       SUN50I_ADDA_ADC_CTRL_ADCG, 0x7, 0,
226		       sun50i_codec_out_mixer_pregain_scale),
227
228	/* Mixer pre-gain */
229	SOC_SINGLE_TLV("Line In Playback Volume", SUN50I_ADDA_LINEIN_CTRL,
230		       SUN50I_ADDA_LINEIN_CTRL_LINEING,
231		       0x7, 0, sun50i_codec_out_mixer_pregain_scale),
232
233	SOC_SINGLE_TLV("Line Out Playback Volume",
234		       SUN50I_ADDA_LINEOUT_CTRL1,
235		       SUN50I_ADDA_LINEOUT_CTRL1_VOL, 0x1f, 0,
236		       sun50i_codec_lineout_vol_scale),
237
238	SOC_SINGLE_TLV("Earpiece Playback Volume",
239		       SUN50I_ADDA_EARPIECE_CTRL1,
240		       SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL, 0x1f, 0,
241		       sun50i_codec_earpiece_vol_scale),
242};
243
244static const char * const sun50i_codec_hp_src_enum_text[] = {
245	"DAC", "Mixer",
246};
247
248static SOC_ENUM_DOUBLE_DECL(sun50i_codec_hp_src_enum,
249			    SUN50I_ADDA_MIX_DAC_CTRL,
250			    SUN50I_ADDA_MIX_DAC_CTRL_LHPIS,
251			    SUN50I_ADDA_MIX_DAC_CTRL_RHPIS,
252			    sun50i_codec_hp_src_enum_text);
253
254static const struct snd_kcontrol_new sun50i_codec_hp_src[] = {
255	SOC_DAPM_ENUM("Headphone Source Playback Route",
256		      sun50i_codec_hp_src_enum),
257};
258
259static const struct snd_kcontrol_new sun50i_codec_hp_switch =
260	SOC_DAPM_DOUBLE("Headphone Playback Switch",
261			SUN50I_ADDA_MIX_DAC_CTRL,
262			SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE,
263			SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE, 1, 0);
264
265static const char * const sun50i_codec_lineout_src_enum_text[] = {
266	"Stereo", "Mono Differential",
267};
268
269static SOC_ENUM_DOUBLE_DECL(sun50i_codec_lineout_src_enum,
270			    SUN50I_ADDA_LINEOUT_CTRL0,
271			    SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL,
272			    SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL,
273			    sun50i_codec_lineout_src_enum_text);
274
275static const struct snd_kcontrol_new sun50i_codec_lineout_src[] = {
276	SOC_DAPM_ENUM("Line Out Source Playback Route",
277		      sun50i_codec_lineout_src_enum),
278};
279
280static const struct snd_kcontrol_new sun50i_codec_lineout_switch =
281	SOC_DAPM_DOUBLE("Line Out Playback Switch",
282			SUN50I_ADDA_LINEOUT_CTRL0,
283			SUN50I_ADDA_LINEOUT_CTRL0_LEN,
284			SUN50I_ADDA_LINEOUT_CTRL0_REN, 1, 0);
285
286static const char * const sun50i_codec_earpiece_src_enum_text[] = {
287	"DACR", "DACL", "Right Mixer", "Left Mixer",
288};
289
290static SOC_ENUM_SINGLE_DECL(sun50i_codec_earpiece_src_enum,
291			    SUN50I_ADDA_EARPIECE_CTRL0,
292			    SUN50I_ADDA_EARPIECE_CTRL0_ESPSR,
293			    sun50i_codec_earpiece_src_enum_text);
294
295static const struct snd_kcontrol_new sun50i_codec_earpiece_src[] = {
296	SOC_DAPM_ENUM("Earpiece Source Playback Route",
297		      sun50i_codec_earpiece_src_enum),
298};
299
300static const struct snd_kcontrol_new sun50i_codec_earpiece_switch[] = {
301	SOC_DAPM_SINGLE("Earpiece Playback Switch",
302			SUN50I_ADDA_EARPIECE_CTRL1,
303			SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE, 1, 0),
304};
305
306static int sun50i_codec_hbias_event(struct snd_soc_dapm_widget *w,
307				    struct snd_kcontrol *kcontrol, int event)
308{
309	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
310	u32 value = !!SND_SOC_DAPM_EVENT_ON(event);
311
312	regmap_update_bits(component->regmap, SUN50I_ADDA_JACK_MIC_CTRL,
313			   BIT(SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN),
314			   value << SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN);
315
316	return 0;
317}
318
319static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = {
320	/* DAC */
321	SND_SOC_DAPM_DAC("Left DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL,
322			 SUN50I_ADDA_MIX_DAC_CTRL_DACALEN, 0),
323	SND_SOC_DAPM_DAC("Right DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL,
324			 SUN50I_ADDA_MIX_DAC_CTRL_DACAREN, 0),
325	/* ADC */
326	SND_SOC_DAPM_ADC("Left ADC", NULL, SUN50I_ADDA_ADC_CTRL,
327			 SUN50I_ADDA_ADC_CTRL_ADCLEN, 0),
328	SND_SOC_DAPM_ADC("Right ADC", NULL, SUN50I_ADDA_ADC_CTRL,
329			 SUN50I_ADDA_ADC_CTRL_ADCREN, 0),
330	/*
331	 * Due to this component and the codec belonging to separate DAPM
332	 * contexts, we need to manually link the above widgets to their
333	 * stream widgets at the card level.
334	 */
335
336	SND_SOC_DAPM_REGULATOR_SUPPLY("cpvdd", 0, 0),
337	SND_SOC_DAPM_MUX("Left Headphone Source",
338			 SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src),
339	SND_SOC_DAPM_MUX("Right Headphone Source",
340			 SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src),
341	SND_SOC_DAPM_SWITCH("Left Headphone Switch",
342			    SND_SOC_NOPM, 0, 0, &sun50i_codec_hp_switch),
343	SND_SOC_DAPM_SWITCH("Right Headphone Switch",
344			    SND_SOC_NOPM, 0, 0, &sun50i_codec_hp_switch),
345	SND_SOC_DAPM_OUT_DRV("Left Headphone Amp",
346			     SND_SOC_NOPM, 0, 0, NULL, 0),
347	SND_SOC_DAPM_OUT_DRV("Right Headphone Amp",
348			     SND_SOC_NOPM, 0, 0, NULL, 0),
349	SND_SOC_DAPM_SUPPLY("Headphone Amp", SUN50I_ADDA_HP_CTRL,
350			     SUN50I_ADDA_HP_CTRL_HPPA_EN, 0, NULL, 0),
351	SND_SOC_DAPM_OUTPUT("HP"),
352
353	SND_SOC_DAPM_MUX("Left Line Out Source",
354			 SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src),
355	SND_SOC_DAPM_MUX("Right Line Out Source",
356			 SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src),
357	SND_SOC_DAPM_SWITCH("Left Line Out Switch",
358			    SND_SOC_NOPM, 0, 0, &sun50i_codec_lineout_switch),
359	SND_SOC_DAPM_SWITCH("Right Line Out Switch",
360			    SND_SOC_NOPM, 0, 0, &sun50i_codec_lineout_switch),
361	SND_SOC_DAPM_OUTPUT("LINEOUT"),
362
363	SND_SOC_DAPM_MUX("Earpiece Source Playback Route",
364			 SND_SOC_NOPM, 0, 0, sun50i_codec_earpiece_src),
365	SOC_MIXER_NAMED_CTL_ARRAY("Earpiece Switch",
366				  SND_SOC_NOPM, 0, 0,
367				  sun50i_codec_earpiece_switch),
368	SND_SOC_DAPM_OUT_DRV("Earpiece Amp", SUN50I_ADDA_EARPIECE_CTRL1,
369			     SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_EN, 0, NULL, 0),
370	SND_SOC_DAPM_OUTPUT("EARPIECE"),
371
372	/* Microphone inputs */
373	SND_SOC_DAPM_INPUT("MIC1"),
374
375	/* Microphone Bias */
376	SND_SOC_DAPM_SUPPLY("MBIAS", SUN50I_ADDA_HS_MBIAS_CTRL,
377			    SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN,
378			    0, NULL, 0),
379
380	/* Mic input path */
381	SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN50I_ADDA_MIC1_CTRL,
382			 SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN, 0, NULL, 0),
383
384	/* Microphone input */
385	SND_SOC_DAPM_INPUT("MIC2"),
386
387	/* Microphone Bias */
388	SND_SOC_DAPM_SUPPLY("HBIAS", SUN50I_ADDA_JACK_MIC_CTRL,
389			    SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN,
390			    0, sun50i_codec_hbias_event,
391			    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
392
393	/* Mic input path */
394	SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN50I_ADDA_MIC2_CTRL,
395			 SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN, 0, NULL, 0),
396
397	/* Line input */
398	SND_SOC_DAPM_INPUT("LINEIN"),
399
400	/* Mixers */
401	SND_SOC_DAPM_MIXER("Left Mixer", SUN50I_ADDA_MIX_DAC_CTRL,
402			   SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN, 0,
403			   sun50i_a64_codec_mixer_controls,
404			   ARRAY_SIZE(sun50i_a64_codec_mixer_controls)),
405	SND_SOC_DAPM_MIXER("Right Mixer", SUN50I_ADDA_MIX_DAC_CTRL,
406			   SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN, 0,
407			   sun50i_a64_codec_mixer_controls,
408			   ARRAY_SIZE(sun50i_a64_codec_mixer_controls)),
409	SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
410			   sun50i_codec_adc_mixer_controls,
411			   ARRAY_SIZE(sun50i_codec_adc_mixer_controls)),
412	SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
413			   sun50i_codec_adc_mixer_controls,
414			   ARRAY_SIZE(sun50i_codec_adc_mixer_controls)),
415};
416
417static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = {
418	/* Left Mixer Routes */
419	{ "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
420	{ "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
421	{ "Left Mixer", "Line In Playback Switch", "LINEIN" },
422	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
423	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
424
425	/* Right Mixer Routes */
426	{ "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
427	{ "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
428	{ "Right Mixer", "Line In Playback Switch", "LINEIN" },
429	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
430	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
431
432	/* Left ADC Mixer Routes */
433	{ "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
434	{ "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
435	{ "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
436	{ "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
437	{ "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
438
439	/* Right ADC Mixer Routes */
440	{ "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
441	{ "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
442	{ "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
443	{ "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
444	{ "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
445
446	/* ADC Routes */
447	{ "Left ADC", NULL, "Left ADC Mixer" },
448	{ "Right ADC", NULL, "Right ADC Mixer" },
449
450	/* Headphone Routes */
451	{ "Left Headphone Source", "DAC", "Left DAC" },
452	{ "Left Headphone Source", "Mixer", "Left Mixer" },
453	{ "Left Headphone Switch", "Headphone Playback Switch", "Left Headphone Source" },
454	{ "Left Headphone Amp", NULL, "Left Headphone Switch" },
455	{ "Left Headphone Amp", NULL, "Headphone Amp" },
456	{ "HP", NULL, "Left Headphone Amp" },
457
458	{ "Right Headphone Source", "DAC", "Right DAC" },
459	{ "Right Headphone Source", "Mixer", "Right Mixer" },
460	{ "Right Headphone Switch", "Headphone Playback Switch", "Right Headphone Source" },
461	{ "Right Headphone Amp", NULL, "Right Headphone Switch" },
462	{ "Right Headphone Amp", NULL, "Headphone Amp" },
463	{ "HP", NULL, "Right Headphone Amp" },
464
465	{ "Headphone Amp", NULL, "cpvdd" },
466
467	/* Microphone Routes */
468	{ "Mic1 Amplifier", NULL, "MIC1"},
469
470	/* Microphone Routes */
471	{ "Mic2 Amplifier", NULL, "MIC2"},
472
473	/* Line-out Routes */
474	{ "Left Line Out Source", "Stereo", "Left Mixer" },
475	{ "Left Line Out Source", "Mono Differential", "Left Mixer" },
476	{ "Left Line Out Source", "Mono Differential", "Right Mixer" },
477	{ "Left Line Out Switch", "Line Out Playback Switch", "Left Line Out Source" },
478	{ "LINEOUT", NULL, "Left Line Out Switch" },
479
480	{ "Right Line Out Switch", "Line Out Playback Switch", "Right Mixer" },
481	{ "Right Line Out Source", "Stereo", "Right Line Out Switch" },
482	{ "Right Line Out Source", "Mono Differential", "Left Line Out Switch" },
483	{ "LINEOUT", NULL, "Right Line Out Source" },
484
485	/* Earpiece Routes */
486	{ "Earpiece Source Playback Route", "DACL", "Left DAC" },
487	{ "Earpiece Source Playback Route", "DACR", "Right DAC" },
488	{ "Earpiece Source Playback Route", "Left Mixer", "Left Mixer" },
489	{ "Earpiece Source Playback Route", "Right Mixer", "Right Mixer" },
490	{ "Earpiece Switch", "Earpiece Playback Switch", "Earpiece Source Playback Route" },
491	{ "Earpiece Amp", NULL, "Earpiece Switch" },
492	{ "EARPIECE", NULL, "Earpiece Amp" },
493};
494
495static int sun50i_a64_codec_set_bias_level(struct snd_soc_component *component,
496					   enum snd_soc_bias_level level)
497{
498	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
499	int hbias;
500
501	switch (level) {
502	case SND_SOC_BIAS_OFF:
503		regmap_clear_bits(component->regmap, SUN50I_ADDA_JACK_MIC_CTRL,
504				   BIT(SUN50I_ADDA_JACK_MIC_CTRL_JACKDETEN) |
505				   BIT(SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN));
506
507		regmap_set_bits(component->regmap, SUN50I_ADDA_HP_CTRL,
508				BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE));
509		break;
510	case SND_SOC_BIAS_STANDBY:
511		regmap_clear_bits(component->regmap, SUN50I_ADDA_HP_CTRL,
512				   BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE));
513
514		hbias = snd_soc_dapm_get_pin_status(dapm, "HBIAS");
515		regmap_update_bits(component->regmap, SUN50I_ADDA_JACK_MIC_CTRL,
516				   BIT(SUN50I_ADDA_JACK_MIC_CTRL_JACKDETEN) |
517				   BIT(SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN),
518				   BIT(SUN50I_ADDA_JACK_MIC_CTRL_JACKDETEN) |
519				   hbias << SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN);
520		break;
521	default:
522		break;
523	}
524
525	return 0;
526}
527
528static const struct snd_soc_component_driver sun50i_codec_analog_cmpnt_drv = {
529	.controls		= sun50i_a64_codec_controls,
530	.num_controls		= ARRAY_SIZE(sun50i_a64_codec_controls),
531	.dapm_widgets		= sun50i_a64_codec_widgets,
532	.num_dapm_widgets	= ARRAY_SIZE(sun50i_a64_codec_widgets),
533	.dapm_routes		= sun50i_a64_codec_routes,
534	.num_dapm_routes	= ARRAY_SIZE(sun50i_a64_codec_routes),
535	.set_bias_level		= sun50i_a64_codec_set_bias_level,
536	.idle_bias_on		= true,
537	.suspend_bias_off	= true,
538};
539
540static const struct of_device_id sun50i_codec_analog_of_match[] = {
541	{
542		.compatible = "allwinner,sun50i-a64-codec-analog",
543	},
544	{}
545};
546MODULE_DEVICE_TABLE(of, sun50i_codec_analog_of_match);
547
548static int sun50i_codec_analog_probe(struct platform_device *pdev)
549{
550	struct regmap *regmap;
551	void __iomem *base;
552	bool enable;
553
554	base = devm_platform_ioremap_resource(pdev, 0);
555	if (IS_ERR(base)) {
556		dev_err(&pdev->dev, "Failed to map the registers\n");
557		return PTR_ERR(base);
558	}
559
560	regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base);
561	if (IS_ERR(regmap)) {
562		dev_err(&pdev->dev, "Failed to create regmap\n");
563		return PTR_ERR(regmap);
564	}
565
566	enable = device_property_read_bool(&pdev->dev,
567					   "allwinner,internal-bias-resistor");
568	regmap_update_bits(regmap, SUN50I_ADDA_JACK_MIC_CTRL,
569			   BIT(SUN50I_ADDA_JACK_MIC_CTRL_INNERRESEN),
570			   enable << SUN50I_ADDA_JACK_MIC_CTRL_INNERRESEN);
571
572	/* Select sample interval of the ADC sample to 16ms */
573	regmap_update_bits(regmap, SUN50I_ADDA_MDET_CTRL,
574			   0x7 << SUN50I_ADDA_MDET_CTRL_SELDETADC_FS |
575			   0x3 << SUN50I_ADDA_MDET_CTRL_SELDETADC_BF,
576			   0x3 << SUN50I_ADDA_MDET_CTRL_SELDETADC_FS |
577			   0x3 << SUN50I_ADDA_MDET_CTRL_SELDETADC_BF);
578
579	return devm_snd_soc_register_component(&pdev->dev,
580					       &sun50i_codec_analog_cmpnt_drv,
581					       NULL, 0);
582}
583
584static struct platform_driver sun50i_codec_analog_driver = {
585	.driver = {
586		.name = "sun50i-codec-analog",
587		.of_match_table = sun50i_codec_analog_of_match,
588	},
589	.probe = sun50i_codec_analog_probe,
590};
591module_platform_driver(sun50i_codec_analog_driver);
592
593MODULE_DESCRIPTION("Allwinner internal codec analog controls driver for A64");
594MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
595MODULE_LICENSE("GPL");
596MODULE_ALIAS("platform:sun50i-codec-analog");