xref: /OK3568_Linux_fs/kernel/sound/soc/sunxi/sun8i-codec.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * This driver supports the digital controls for the internal codec
4*4882a593Smuzhiyun  * found in Allwinner's A33 SoCs.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * (C) Copyright 2010-2016
7*4882a593Smuzhiyun  * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
8*4882a593Smuzhiyun  * huangxin <huangxin@Reuuimllatech.com>
9*4882a593Smuzhiyun  * Mylène Josserand <mylene.josserand@free-electrons.com>
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/delay.h>
14*4882a593Smuzhiyun #include <linux/clk.h>
15*4882a593Smuzhiyun #include <linux/io.h>
16*4882a593Smuzhiyun #include <linux/of_device.h>
17*4882a593Smuzhiyun #include <linux/pm_runtime.h>
18*4882a593Smuzhiyun #include <linux/regmap.h>
19*4882a593Smuzhiyun #include <linux/log2.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #include <sound/pcm_params.h>
22*4882a593Smuzhiyun #include <sound/soc.h>
23*4882a593Smuzhiyun #include <sound/soc-dapm.h>
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #define SUN8I_SYSCLK_CTL				0x00c
26*4882a593Smuzhiyun #define SUN8I_SYSCLK_CTL_AIF1CLK_ENA			11
27*4882a593Smuzhiyun #define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL		(0x2 << 8)
28*4882a593Smuzhiyun #define SUN8I_SYSCLK_CTL_AIF2CLK_ENA			7
29*4882a593Smuzhiyun #define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_PLL		(0x2 << 4)
30*4882a593Smuzhiyun #define SUN8I_SYSCLK_CTL_SYSCLK_ENA			3
31*4882a593Smuzhiyun #define SUN8I_SYSCLK_CTL_SYSCLK_SRC			0
32*4882a593Smuzhiyun #define SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK		(0x0 << 0)
33*4882a593Smuzhiyun #define SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF2CLK		(0x1 << 0)
34*4882a593Smuzhiyun #define SUN8I_MOD_CLK_ENA				0x010
35*4882a593Smuzhiyun #define SUN8I_MOD_CLK_ENA_AIF1				15
36*4882a593Smuzhiyun #define SUN8I_MOD_CLK_ENA_ADC				3
37*4882a593Smuzhiyun #define SUN8I_MOD_CLK_ENA_DAC				2
38*4882a593Smuzhiyun #define SUN8I_MOD_RST_CTL				0x014
39*4882a593Smuzhiyun #define SUN8I_MOD_RST_CTL_AIF1				15
40*4882a593Smuzhiyun #define SUN8I_MOD_RST_CTL_ADC				3
41*4882a593Smuzhiyun #define SUN8I_MOD_RST_CTL_DAC				2
42*4882a593Smuzhiyun #define SUN8I_SYS_SR_CTRL				0x018
43*4882a593Smuzhiyun #define SUN8I_SYS_SR_CTRL_AIF1_FS			12
44*4882a593Smuzhiyun #define SUN8I_SYS_SR_CTRL_AIF2_FS			8
45*4882a593Smuzhiyun #define SUN8I_AIF1CLK_CTRL				0x040
46*4882a593Smuzhiyun #define SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD		15
47*4882a593Smuzhiyun #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV		14
48*4882a593Smuzhiyun #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV		13
49*4882a593Smuzhiyun #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV		9
50*4882a593Smuzhiyun #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV		6
51*4882a593Smuzhiyun #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ		4
52*4882a593Smuzhiyun #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16		(1 << 4)
53*4882a593Smuzhiyun #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT		2
54*4882a593Smuzhiyun #define SUN8I_AIF1_ADCDAT_CTRL				0x044
55*4882a593Smuzhiyun #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA		15
56*4882a593Smuzhiyun #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA		14
57*4882a593Smuzhiyun #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_SRC		10
58*4882a593Smuzhiyun #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_SRC		8
59*4882a593Smuzhiyun #define SUN8I_AIF1_DACDAT_CTRL				0x048
60*4882a593Smuzhiyun #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA		15
61*4882a593Smuzhiyun #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA		14
62*4882a593Smuzhiyun #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_SRC		10
63*4882a593Smuzhiyun #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_SRC		8
64*4882a593Smuzhiyun #define SUN8I_AIF1_MXR_SRC				0x04c
65*4882a593Smuzhiyun #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L	15
66*4882a593Smuzhiyun #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL	14
67*4882a593Smuzhiyun #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL		13
68*4882a593Smuzhiyun #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR	12
69*4882a593Smuzhiyun #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R	11
70*4882a593Smuzhiyun #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR	10
71*4882a593Smuzhiyun #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR		9
72*4882a593Smuzhiyun #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL	8
73*4882a593Smuzhiyun #define SUN8I_ADC_DIG_CTRL				0x100
74*4882a593Smuzhiyun #define SUN8I_ADC_DIG_CTRL_ENAD				15
75*4882a593Smuzhiyun #define SUN8I_ADC_DIG_CTRL_ADOUT_DTS			2
76*4882a593Smuzhiyun #define SUN8I_ADC_DIG_CTRL_ADOUT_DLY			1
77*4882a593Smuzhiyun #define SUN8I_DAC_DIG_CTRL				0x120
78*4882a593Smuzhiyun #define SUN8I_DAC_DIG_CTRL_ENDA				15
79*4882a593Smuzhiyun #define SUN8I_DAC_MXR_SRC				0x130
80*4882a593Smuzhiyun #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L		15
81*4882a593Smuzhiyun #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L		14
82*4882a593Smuzhiyun #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL		13
83*4882a593Smuzhiyun #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL		12
84*4882a593Smuzhiyun #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R		11
85*4882a593Smuzhiyun #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R		10
86*4882a593Smuzhiyun #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR		9
87*4882a593Smuzhiyun #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR		8
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun #define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK	GENMASK(9, 8)
90*4882a593Smuzhiyun #define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK	GENMASK(5, 4)
91*4882a593Smuzhiyun #define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK		GENMASK(15, 12)
92*4882a593Smuzhiyun #define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK		GENMASK(11, 8)
93*4882a593Smuzhiyun #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK	GENMASK(12, 9)
94*4882a593Smuzhiyun #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK	GENMASK(8, 6)
95*4882a593Smuzhiyun #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK	GENMASK(5, 4)
96*4882a593Smuzhiyun #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK	GENMASK(3, 2)
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun struct sun8i_codec_quirks {
99*4882a593Smuzhiyun 	bool legacy_widgets	: 1;
100*4882a593Smuzhiyun 	bool lrck_inversion	: 1;
101*4882a593Smuzhiyun };
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun struct sun8i_codec {
104*4882a593Smuzhiyun 	struct regmap			*regmap;
105*4882a593Smuzhiyun 	struct clk			*clk_module;
106*4882a593Smuzhiyun 	const struct sun8i_codec_quirks	*quirks;
107*4882a593Smuzhiyun };
108*4882a593Smuzhiyun 
sun8i_codec_runtime_resume(struct device * dev)109*4882a593Smuzhiyun static int sun8i_codec_runtime_resume(struct device *dev)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun 	struct sun8i_codec *scodec = dev_get_drvdata(dev);
112*4882a593Smuzhiyun 	int ret;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	regcache_cache_only(scodec->regmap, false);
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	ret = regcache_sync(scodec->regmap);
117*4882a593Smuzhiyun 	if (ret) {
118*4882a593Smuzhiyun 		dev_err(dev, "Failed to sync regmap cache\n");
119*4882a593Smuzhiyun 		return ret;
120*4882a593Smuzhiyun 	}
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	return 0;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun 
sun8i_codec_runtime_suspend(struct device * dev)125*4882a593Smuzhiyun static int sun8i_codec_runtime_suspend(struct device *dev)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun 	struct sun8i_codec *scodec = dev_get_drvdata(dev);
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	regcache_cache_only(scodec->regmap, true);
130*4882a593Smuzhiyun 	regcache_mark_dirty(scodec->regmap);
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	return 0;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun 
sun8i_codec_get_hw_rate(struct snd_pcm_hw_params * params)135*4882a593Smuzhiyun static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun 	unsigned int rate = params_rate(params);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	switch (rate) {
140*4882a593Smuzhiyun 	case 8000:
141*4882a593Smuzhiyun 	case 7350:
142*4882a593Smuzhiyun 		return 0x0;
143*4882a593Smuzhiyun 	case 11025:
144*4882a593Smuzhiyun 		return 0x1;
145*4882a593Smuzhiyun 	case 12000:
146*4882a593Smuzhiyun 		return 0x2;
147*4882a593Smuzhiyun 	case 16000:
148*4882a593Smuzhiyun 		return 0x3;
149*4882a593Smuzhiyun 	case 22050:
150*4882a593Smuzhiyun 		return 0x4;
151*4882a593Smuzhiyun 	case 24000:
152*4882a593Smuzhiyun 		return 0x5;
153*4882a593Smuzhiyun 	case 32000:
154*4882a593Smuzhiyun 		return 0x6;
155*4882a593Smuzhiyun 	case 44100:
156*4882a593Smuzhiyun 		return 0x7;
157*4882a593Smuzhiyun 	case 48000:
158*4882a593Smuzhiyun 		return 0x8;
159*4882a593Smuzhiyun 	case 96000:
160*4882a593Smuzhiyun 		return 0x9;
161*4882a593Smuzhiyun 	case 192000:
162*4882a593Smuzhiyun 		return 0xa;
163*4882a593Smuzhiyun 	default:
164*4882a593Smuzhiyun 		return -EINVAL;
165*4882a593Smuzhiyun 	}
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun 
sun8i_set_fmt(struct snd_soc_dai * dai,unsigned int fmt)168*4882a593Smuzhiyun static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
169*4882a593Smuzhiyun {
170*4882a593Smuzhiyun 	struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
171*4882a593Smuzhiyun 	u32 value;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	/* clock masters */
174*4882a593Smuzhiyun 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
175*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_CBS_CFS: /* Codec slave, DAI master */
176*4882a593Smuzhiyun 		value = 0x1;
177*4882a593Smuzhiyun 		break;
178*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_CBM_CFM: /* Codec Master, DAI slave */
179*4882a593Smuzhiyun 		value = 0x0;
180*4882a593Smuzhiyun 		break;
181*4882a593Smuzhiyun 	default:
182*4882a593Smuzhiyun 		return -EINVAL;
183*4882a593Smuzhiyun 	}
184*4882a593Smuzhiyun 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
185*4882a593Smuzhiyun 			   BIT(SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD),
186*4882a593Smuzhiyun 			   value << SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD);
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	/* clock inversion */
189*4882a593Smuzhiyun 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
190*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_NB_NF: /* Normal */
191*4882a593Smuzhiyun 		value = 0x0;
192*4882a593Smuzhiyun 		break;
193*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_IB_IF: /* Inversion */
194*4882a593Smuzhiyun 		value = 0x1;
195*4882a593Smuzhiyun 		break;
196*4882a593Smuzhiyun 	default:
197*4882a593Smuzhiyun 		return -EINVAL;
198*4882a593Smuzhiyun 	}
199*4882a593Smuzhiyun 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
200*4882a593Smuzhiyun 			   BIT(SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV),
201*4882a593Smuzhiyun 			   value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV);
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	/*
204*4882a593Smuzhiyun 	 * It appears that the DAI and the codec in the A33 SoC don't
205*4882a593Smuzhiyun 	 * share the same polarity for the LRCK signal when they mean
206*4882a593Smuzhiyun 	 * 'normal' and 'inverted' in the datasheet.
207*4882a593Smuzhiyun 	 *
208*4882a593Smuzhiyun 	 * Since the DAI here is our regular i2s driver that have been
209*4882a593Smuzhiyun 	 * tested with way more codecs than just this one, it means
210*4882a593Smuzhiyun 	 * that the codec probably gets it backward, and we have to
211*4882a593Smuzhiyun 	 * invert the value here.
212*4882a593Smuzhiyun 	 */
213*4882a593Smuzhiyun 	value ^= scodec->quirks->lrck_inversion;
214*4882a593Smuzhiyun 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
215*4882a593Smuzhiyun 			   BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV),
216*4882a593Smuzhiyun 			   value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV);
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	/* DAI format */
219*4882a593Smuzhiyun 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
220*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_I2S:
221*4882a593Smuzhiyun 		value = 0x0;
222*4882a593Smuzhiyun 		break;
223*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_LEFT_J:
224*4882a593Smuzhiyun 		value = 0x1;
225*4882a593Smuzhiyun 		break;
226*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_RIGHT_J:
227*4882a593Smuzhiyun 		value = 0x2;
228*4882a593Smuzhiyun 		break;
229*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_DSP_A:
230*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_DSP_B:
231*4882a593Smuzhiyun 		value = 0x3;
232*4882a593Smuzhiyun 		break;
233*4882a593Smuzhiyun 	default:
234*4882a593Smuzhiyun 		return -EINVAL;
235*4882a593Smuzhiyun 	}
236*4882a593Smuzhiyun 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
237*4882a593Smuzhiyun 			   SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK,
238*4882a593Smuzhiyun 			   value << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT);
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	return 0;
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun struct sun8i_codec_clk_div {
244*4882a593Smuzhiyun 	u8	div;
245*4882a593Smuzhiyun 	u8	val;
246*4882a593Smuzhiyun };
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun static const struct sun8i_codec_clk_div sun8i_codec_bclk_div[] = {
249*4882a593Smuzhiyun 	{ .div = 1,	.val = 0 },
250*4882a593Smuzhiyun 	{ .div = 2,	.val = 1 },
251*4882a593Smuzhiyun 	{ .div = 4,	.val = 2 },
252*4882a593Smuzhiyun 	{ .div = 6,	.val = 3 },
253*4882a593Smuzhiyun 	{ .div = 8,	.val = 4 },
254*4882a593Smuzhiyun 	{ .div = 12,	.val = 5 },
255*4882a593Smuzhiyun 	{ .div = 16,	.val = 6 },
256*4882a593Smuzhiyun 	{ .div = 24,	.val = 7 },
257*4882a593Smuzhiyun 	{ .div = 32,	.val = 8 },
258*4882a593Smuzhiyun 	{ .div = 48,	.val = 9 },
259*4882a593Smuzhiyun 	{ .div = 64,	.val = 10 },
260*4882a593Smuzhiyun 	{ .div = 96,	.val = 11 },
261*4882a593Smuzhiyun 	{ .div = 128,	.val = 12 },
262*4882a593Smuzhiyun 	{ .div = 192,	.val = 13 },
263*4882a593Smuzhiyun };
264*4882a593Smuzhiyun 
sun8i_codec_get_bclk_div(struct sun8i_codec * scodec,unsigned int rate,unsigned int word_size)265*4882a593Smuzhiyun static u8 sun8i_codec_get_bclk_div(struct sun8i_codec *scodec,
266*4882a593Smuzhiyun 				   unsigned int rate,
267*4882a593Smuzhiyun 				   unsigned int word_size)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun 	unsigned long clk_rate = clk_get_rate(scodec->clk_module);
270*4882a593Smuzhiyun 	unsigned int div = clk_rate / rate / word_size / 2;
271*4882a593Smuzhiyun 	unsigned int best_val = 0, best_diff = ~0;
272*4882a593Smuzhiyun 	int i;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(sun8i_codec_bclk_div); i++) {
275*4882a593Smuzhiyun 		const struct sun8i_codec_clk_div *bdiv = &sun8i_codec_bclk_div[i];
276*4882a593Smuzhiyun 		unsigned int diff = abs(bdiv->div - div);
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 		if (diff < best_diff) {
279*4882a593Smuzhiyun 			best_diff = diff;
280*4882a593Smuzhiyun 			best_val = bdiv->val;
281*4882a593Smuzhiyun 		}
282*4882a593Smuzhiyun 	}
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	return best_val;
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun 
sun8i_codec_get_lrck_div(unsigned int channels,unsigned int word_size)287*4882a593Smuzhiyun static int sun8i_codec_get_lrck_div(unsigned int channels,
288*4882a593Smuzhiyun 				    unsigned int word_size)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun 	unsigned int div = word_size * channels;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	if (div < 16 || div > 256)
293*4882a593Smuzhiyun 		return -EINVAL;
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	return ilog2(div) - 4;
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun 
sun8i_codec_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)298*4882a593Smuzhiyun static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
299*4882a593Smuzhiyun 				 struct snd_pcm_hw_params *params,
300*4882a593Smuzhiyun 				 struct snd_soc_dai *dai)
301*4882a593Smuzhiyun {
302*4882a593Smuzhiyun 	struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
303*4882a593Smuzhiyun 	int sample_rate, lrck_div;
304*4882a593Smuzhiyun 	u8 bclk_div;
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	/*
307*4882a593Smuzhiyun 	 * The CPU DAI handles only a sample of 16 bits. Configure the
308*4882a593Smuzhiyun 	 * codec to handle this type of sample resolution.
309*4882a593Smuzhiyun 	 */
310*4882a593Smuzhiyun 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
311*4882a593Smuzhiyun 			   SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK,
312*4882a593Smuzhiyun 			   SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16);
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	bclk_div = sun8i_codec_get_bclk_div(scodec, params_rate(params), 16);
315*4882a593Smuzhiyun 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
316*4882a593Smuzhiyun 			   SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
317*4882a593Smuzhiyun 			   bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	lrck_div = sun8i_codec_get_lrck_div(params_channels(params),
320*4882a593Smuzhiyun 					    params_physical_width(params));
321*4882a593Smuzhiyun 	if (lrck_div < 0)
322*4882a593Smuzhiyun 		return lrck_div;
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
325*4882a593Smuzhiyun 			   SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK,
326*4882a593Smuzhiyun 			   lrck_div << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	sample_rate = sun8i_codec_get_hw_rate(params);
329*4882a593Smuzhiyun 	if (sample_rate < 0)
330*4882a593Smuzhiyun 		return sample_rate;
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
333*4882a593Smuzhiyun 			   SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
334*4882a593Smuzhiyun 			   sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	return 0;
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun static const char *const sun8i_aif_stereo_mux_enum_values[] = {
340*4882a593Smuzhiyun 	"Stereo", "Reverse Stereo", "Sum Mono", "Mix Mono"
341*4882a593Smuzhiyun };
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun static SOC_ENUM_DOUBLE_DECL(sun8i_aif1_ad0_stereo_mux_enum,
344*4882a593Smuzhiyun 			    SUN8I_AIF1_ADCDAT_CTRL,
345*4882a593Smuzhiyun 			    SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_SRC,
346*4882a593Smuzhiyun 			    SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_SRC,
347*4882a593Smuzhiyun 			    sun8i_aif_stereo_mux_enum_values);
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun static const struct snd_kcontrol_new sun8i_aif1_ad0_stereo_mux_control =
350*4882a593Smuzhiyun 	SOC_DAPM_ENUM("AIF1 AD0 Stereo Capture Route",
351*4882a593Smuzhiyun 		      sun8i_aif1_ad0_stereo_mux_enum);
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun static const struct snd_kcontrol_new sun8i_aif1_ad0_mixer_controls[] = {
354*4882a593Smuzhiyun 	SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch",
355*4882a593Smuzhiyun 			SUN8I_AIF1_MXR_SRC,
356*4882a593Smuzhiyun 			SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L,
357*4882a593Smuzhiyun 			SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0),
358*4882a593Smuzhiyun 	SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch",
359*4882a593Smuzhiyun 			SUN8I_AIF1_MXR_SRC,
360*4882a593Smuzhiyun 			SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL,
361*4882a593Smuzhiyun 			SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0),
362*4882a593Smuzhiyun 	SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch",
363*4882a593Smuzhiyun 			SUN8I_AIF1_MXR_SRC,
364*4882a593Smuzhiyun 			SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL,
365*4882a593Smuzhiyun 			SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0),
366*4882a593Smuzhiyun 	SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch",
367*4882a593Smuzhiyun 			SUN8I_AIF1_MXR_SRC,
368*4882a593Smuzhiyun 			SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR,
369*4882a593Smuzhiyun 			SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0),
370*4882a593Smuzhiyun };
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun static SOC_ENUM_DOUBLE_DECL(sun8i_aif1_da0_stereo_mux_enum,
373*4882a593Smuzhiyun 			    SUN8I_AIF1_DACDAT_CTRL,
374*4882a593Smuzhiyun 			    SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_SRC,
375*4882a593Smuzhiyun 			    SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_SRC,
376*4882a593Smuzhiyun 			    sun8i_aif_stereo_mux_enum_values);
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun static const struct snd_kcontrol_new sun8i_aif1_da0_stereo_mux_control =
379*4882a593Smuzhiyun 	SOC_DAPM_ENUM("AIF1 DA0 Stereo Playback Route",
380*4882a593Smuzhiyun 		      sun8i_aif1_da0_stereo_mux_enum);
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = {
383*4882a593Smuzhiyun 	SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital DAC Playback Switch",
384*4882a593Smuzhiyun 			SUN8I_DAC_MXR_SRC,
385*4882a593Smuzhiyun 			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L,
386*4882a593Smuzhiyun 			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R, 1, 0),
387*4882a593Smuzhiyun 	SOC_DAPM_DOUBLE("AIF1 Slot 1 Digital DAC Playback Switch",
388*4882a593Smuzhiyun 			SUN8I_DAC_MXR_SRC,
389*4882a593Smuzhiyun 			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L,
390*4882a593Smuzhiyun 			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R, 1, 0),
391*4882a593Smuzhiyun 	SOC_DAPM_DOUBLE("AIF2 Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC,
392*4882a593Smuzhiyun 			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL,
393*4882a593Smuzhiyun 			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR, 1, 0),
394*4882a593Smuzhiyun 	SOC_DAPM_DOUBLE("ADC Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC,
395*4882a593Smuzhiyun 			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL,
396*4882a593Smuzhiyun 			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0),
397*4882a593Smuzhiyun };
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
400*4882a593Smuzhiyun 	/* System Clocks */
401*4882a593Smuzhiyun 	SND_SOC_DAPM_CLOCK_SUPPLY("mod"),
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	SND_SOC_DAPM_SUPPLY("AIF1CLK",
404*4882a593Smuzhiyun 			    SUN8I_SYSCLK_CTL,
405*4882a593Smuzhiyun 			    SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0),
406*4882a593Smuzhiyun 	SND_SOC_DAPM_SUPPLY("SYSCLK",
407*4882a593Smuzhiyun 			    SUN8I_SYSCLK_CTL,
408*4882a593Smuzhiyun 			    SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0),
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	/* Module Clocks */
411*4882a593Smuzhiyun 	SND_SOC_DAPM_SUPPLY("CLK AIF1",
412*4882a593Smuzhiyun 			    SUN8I_MOD_CLK_ENA,
413*4882a593Smuzhiyun 			    SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0),
414*4882a593Smuzhiyun 	SND_SOC_DAPM_SUPPLY("CLK ADC",
415*4882a593Smuzhiyun 			    SUN8I_MOD_CLK_ENA,
416*4882a593Smuzhiyun 			    SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0),
417*4882a593Smuzhiyun 	SND_SOC_DAPM_SUPPLY("CLK DAC",
418*4882a593Smuzhiyun 			    SUN8I_MOD_CLK_ENA,
419*4882a593Smuzhiyun 			    SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0),
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 	/* Module Resets */
422*4882a593Smuzhiyun 	SND_SOC_DAPM_SUPPLY("RST AIF1",
423*4882a593Smuzhiyun 			    SUN8I_MOD_RST_CTL,
424*4882a593Smuzhiyun 			    SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0),
425*4882a593Smuzhiyun 	SND_SOC_DAPM_SUPPLY("RST ADC",
426*4882a593Smuzhiyun 			    SUN8I_MOD_RST_CTL,
427*4882a593Smuzhiyun 			    SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0),
428*4882a593Smuzhiyun 	SND_SOC_DAPM_SUPPLY("RST DAC",
429*4882a593Smuzhiyun 			    SUN8I_MOD_RST_CTL,
430*4882a593Smuzhiyun 			    SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0),
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	/* Module Supplies */
433*4882a593Smuzhiyun 	SND_SOC_DAPM_SUPPLY("ADC",
434*4882a593Smuzhiyun 			    SUN8I_ADC_DIG_CTRL,
435*4882a593Smuzhiyun 			    SUN8I_ADC_DIG_CTRL_ENAD, 0, NULL, 0),
436*4882a593Smuzhiyun 	SND_SOC_DAPM_SUPPLY("DAC",
437*4882a593Smuzhiyun 			    SUN8I_DAC_DIG_CTRL,
438*4882a593Smuzhiyun 			    SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0),
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	/* AIF "ADC" Outputs */
441*4882a593Smuzhiyun 	SND_SOC_DAPM_AIF_OUT("AIF1 AD0L", "Capture", 0,
442*4882a593Smuzhiyun 			     SUN8I_AIF1_ADCDAT_CTRL,
443*4882a593Smuzhiyun 			     SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0),
444*4882a593Smuzhiyun 	SND_SOC_DAPM_AIF_OUT("AIF1 AD0R", "Capture", 1,
445*4882a593Smuzhiyun 			     SUN8I_AIF1_ADCDAT_CTRL,
446*4882a593Smuzhiyun 			     SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0),
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	/* AIF "ADC" Mono/Stereo Muxes */
449*4882a593Smuzhiyun 	SND_SOC_DAPM_MUX("AIF1 AD0L Stereo Mux", SND_SOC_NOPM, 0, 0,
450*4882a593Smuzhiyun 			 &sun8i_aif1_ad0_stereo_mux_control),
451*4882a593Smuzhiyun 	SND_SOC_DAPM_MUX("AIF1 AD0R Stereo Mux", SND_SOC_NOPM, 0, 0,
452*4882a593Smuzhiyun 			 &sun8i_aif1_ad0_stereo_mux_control),
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	/* AIF "ADC" Mixers */
455*4882a593Smuzhiyun 	SOC_MIXER_ARRAY("AIF1 AD0L Mixer", SND_SOC_NOPM, 0, 0,
456*4882a593Smuzhiyun 			sun8i_aif1_ad0_mixer_controls),
457*4882a593Smuzhiyun 	SOC_MIXER_ARRAY("AIF1 AD0R Mixer", SND_SOC_NOPM, 0, 0,
458*4882a593Smuzhiyun 			sun8i_aif1_ad0_mixer_controls),
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	/* AIF "DAC" Mono/Stereo Muxes */
461*4882a593Smuzhiyun 	SND_SOC_DAPM_MUX("AIF1 DA0L Stereo Mux", SND_SOC_NOPM, 0, 0,
462*4882a593Smuzhiyun 			 &sun8i_aif1_da0_stereo_mux_control),
463*4882a593Smuzhiyun 	SND_SOC_DAPM_MUX("AIF1 DA0R Stereo Mux", SND_SOC_NOPM, 0, 0,
464*4882a593Smuzhiyun 			 &sun8i_aif1_da0_stereo_mux_control),
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	/* AIF "DAC" Inputs */
467*4882a593Smuzhiyun 	SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0,
468*4882a593Smuzhiyun 			    SUN8I_AIF1_DACDAT_CTRL,
469*4882a593Smuzhiyun 			    SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0),
470*4882a593Smuzhiyun 	SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 1,
471*4882a593Smuzhiyun 			    SUN8I_AIF1_DACDAT_CTRL,
472*4882a593Smuzhiyun 			    SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	/* ADC Inputs (connected to analog codec DAPM context) */
475*4882a593Smuzhiyun 	SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0),
476*4882a593Smuzhiyun 	SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 	/* DAC Outputs (connected to analog codec DAPM context) */
479*4882a593Smuzhiyun 	SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
480*4882a593Smuzhiyun 	SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	/* DAC Mixers */
483*4882a593Smuzhiyun 	SOC_MIXER_ARRAY("DACL Mixer", SND_SOC_NOPM, 0, 0,
484*4882a593Smuzhiyun 			sun8i_dac_mixer_controls),
485*4882a593Smuzhiyun 	SOC_MIXER_ARRAY("DACR Mixer", SND_SOC_NOPM, 0, 0,
486*4882a593Smuzhiyun 			sun8i_dac_mixer_controls),
487*4882a593Smuzhiyun };
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
490*4882a593Smuzhiyun 	/* Clock Routes */
491*4882a593Smuzhiyun 	{ "AIF1CLK", NULL, "mod" },
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 	{ "SYSCLK", NULL, "AIF1CLK" },
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun 	{ "CLK AIF1", NULL, "AIF1CLK" },
496*4882a593Smuzhiyun 	{ "CLK AIF1", NULL, "SYSCLK" },
497*4882a593Smuzhiyun 	{ "RST AIF1", NULL, "CLK AIF1" },
498*4882a593Smuzhiyun 	{ "AIF1 AD0L", NULL, "RST AIF1" },
499*4882a593Smuzhiyun 	{ "AIF1 AD0R", NULL, "RST AIF1" },
500*4882a593Smuzhiyun 	{ "AIF1 DA0L", NULL, "RST AIF1" },
501*4882a593Smuzhiyun 	{ "AIF1 DA0R", NULL, "RST AIF1" },
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun 	{ "CLK ADC", NULL, "SYSCLK" },
504*4882a593Smuzhiyun 	{ "RST ADC", NULL, "CLK ADC" },
505*4882a593Smuzhiyun 	{ "ADC", NULL, "RST ADC" },
506*4882a593Smuzhiyun 	{ "ADCL", NULL, "ADC" },
507*4882a593Smuzhiyun 	{ "ADCR", NULL, "ADC" },
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	{ "CLK DAC", NULL, "SYSCLK" },
510*4882a593Smuzhiyun 	{ "RST DAC", NULL, "CLK DAC" },
511*4882a593Smuzhiyun 	{ "DAC", NULL, "RST DAC" },
512*4882a593Smuzhiyun 	{ "DACL", NULL, "DAC" },
513*4882a593Smuzhiyun 	{ "DACR", NULL, "DAC" },
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 	/* AIF "ADC" Output Routes */
516*4882a593Smuzhiyun 	{ "AIF1 AD0L", NULL, "AIF1 AD0L Stereo Mux" },
517*4882a593Smuzhiyun 	{ "AIF1 AD0R", NULL, "AIF1 AD0R Stereo Mux" },
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	/* AIF "ADC" Mono/Stereo Mux Routes */
520*4882a593Smuzhiyun 	{ "AIF1 AD0L Stereo Mux", "Stereo", "AIF1 AD0L Mixer" },
521*4882a593Smuzhiyun 	{ "AIF1 AD0L Stereo Mux", "Reverse Stereo", "AIF1 AD0R Mixer" },
522*4882a593Smuzhiyun 	{ "AIF1 AD0L Stereo Mux", "Sum Mono", "AIF1 AD0L Mixer" },
523*4882a593Smuzhiyun 	{ "AIF1 AD0L Stereo Mux", "Sum Mono", "AIF1 AD0R Mixer" },
524*4882a593Smuzhiyun 	{ "AIF1 AD0L Stereo Mux", "Mix Mono", "AIF1 AD0L Mixer" },
525*4882a593Smuzhiyun 	{ "AIF1 AD0L Stereo Mux", "Mix Mono", "AIF1 AD0R Mixer" },
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun 	{ "AIF1 AD0R Stereo Mux", "Stereo", "AIF1 AD0R Mixer" },
528*4882a593Smuzhiyun 	{ "AIF1 AD0R Stereo Mux", "Reverse Stereo", "AIF1 AD0L Mixer" },
529*4882a593Smuzhiyun 	{ "AIF1 AD0R Stereo Mux", "Sum Mono", "AIF1 AD0L Mixer" },
530*4882a593Smuzhiyun 	{ "AIF1 AD0R Stereo Mux", "Sum Mono", "AIF1 AD0R Mixer" },
531*4882a593Smuzhiyun 	{ "AIF1 AD0R Stereo Mux", "Mix Mono", "AIF1 AD0L Mixer" },
532*4882a593Smuzhiyun 	{ "AIF1 AD0R Stereo Mux", "Mix Mono", "AIF1 AD0R Mixer" },
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	/* AIF "ADC" Mixer Routes */
535*4882a593Smuzhiyun 	{ "AIF1 AD0L Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L Stereo Mux" },
536*4882a593Smuzhiyun 	{ "AIF1 AD0L Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" },
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	{ "AIF1 AD0R Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R Stereo Mux" },
539*4882a593Smuzhiyun 	{ "AIF1 AD0R Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" },
540*4882a593Smuzhiyun 
541*4882a593Smuzhiyun 	/* AIF "DAC" Mono/Stereo Mux Routes */
542*4882a593Smuzhiyun 	{ "AIF1 DA0L Stereo Mux", "Stereo", "AIF1 DA0L" },
543*4882a593Smuzhiyun 	{ "AIF1 DA0L Stereo Mux", "Reverse Stereo", "AIF1 DA0R" },
544*4882a593Smuzhiyun 	{ "AIF1 DA0L Stereo Mux", "Sum Mono", "AIF1 DA0L" },
545*4882a593Smuzhiyun 	{ "AIF1 DA0L Stereo Mux", "Sum Mono", "AIF1 DA0R" },
546*4882a593Smuzhiyun 	{ "AIF1 DA0L Stereo Mux", "Mix Mono", "AIF1 DA0L" },
547*4882a593Smuzhiyun 	{ "AIF1 DA0L Stereo Mux", "Mix Mono", "AIF1 DA0R" },
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun 	{ "AIF1 DA0R Stereo Mux", "Stereo", "AIF1 DA0R" },
550*4882a593Smuzhiyun 	{ "AIF1 DA0R Stereo Mux", "Reverse Stereo", "AIF1 DA0L" },
551*4882a593Smuzhiyun 	{ "AIF1 DA0R Stereo Mux", "Sum Mono", "AIF1 DA0L" },
552*4882a593Smuzhiyun 	{ "AIF1 DA0R Stereo Mux", "Sum Mono", "AIF1 DA0R" },
553*4882a593Smuzhiyun 	{ "AIF1 DA0R Stereo Mux", "Mix Mono", "AIF1 DA0L" },
554*4882a593Smuzhiyun 	{ "AIF1 DA0R Stereo Mux", "Mix Mono", "AIF1 DA0R" },
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 	/* DAC Output Routes */
557*4882a593Smuzhiyun 	{ "DACL", NULL, "DACL Mixer" },
558*4882a593Smuzhiyun 	{ "DACR", NULL, "DACR Mixer" },
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun 	/* DAC Mixer Routes */
561*4882a593Smuzhiyun 	{ "DACL Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L Stereo Mux" },
562*4882a593Smuzhiyun 	{ "DACL Mixer", "ADC Digital DAC Playback Switch", "ADCL" },
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	{ "DACR Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R Stereo Mux" },
565*4882a593Smuzhiyun 	{ "DACR Mixer", "ADC Digital DAC Playback Switch", "ADCR" },
566*4882a593Smuzhiyun };
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun static const struct snd_soc_dapm_widget sun8i_codec_legacy_widgets[] = {
569*4882a593Smuzhiyun 	/* Legacy ADC Inputs (connected to analog codec DAPM context) */
570*4882a593Smuzhiyun 	SND_SOC_DAPM_ADC("AIF1 Slot 0 Left ADC", NULL, SND_SOC_NOPM, 0, 0),
571*4882a593Smuzhiyun 	SND_SOC_DAPM_ADC("AIF1 Slot 0 Right ADC", NULL, SND_SOC_NOPM, 0, 0),
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	/* Legacy DAC Outputs (connected to analog codec DAPM context) */
574*4882a593Smuzhiyun 	SND_SOC_DAPM_DAC("AIF1 Slot 0 Left", NULL, SND_SOC_NOPM, 0, 0),
575*4882a593Smuzhiyun 	SND_SOC_DAPM_DAC("AIF1 Slot 0 Right", NULL, SND_SOC_NOPM, 0, 0),
576*4882a593Smuzhiyun };
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun static const struct snd_soc_dapm_route sun8i_codec_legacy_routes[] = {
579*4882a593Smuzhiyun 	/* Legacy ADC Routes */
580*4882a593Smuzhiyun 	{ "ADCL", NULL, "AIF1 Slot 0 Left ADC" },
581*4882a593Smuzhiyun 	{ "ADCR", NULL, "AIF1 Slot 0 Right ADC" },
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	/* Legacy DAC Routes */
584*4882a593Smuzhiyun 	{ "AIF1 Slot 0 Left", NULL, "DACL" },
585*4882a593Smuzhiyun 	{ "AIF1 Slot 0 Right", NULL, "DACR" },
586*4882a593Smuzhiyun };
587*4882a593Smuzhiyun 
sun8i_codec_component_probe(struct snd_soc_component * component)588*4882a593Smuzhiyun static int sun8i_codec_component_probe(struct snd_soc_component *component)
589*4882a593Smuzhiyun {
590*4882a593Smuzhiyun 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
591*4882a593Smuzhiyun 	struct sun8i_codec *scodec = snd_soc_component_get_drvdata(component);
592*4882a593Smuzhiyun 	int ret;
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 	/* Add widgets for backward compatibility with old device trees. */
595*4882a593Smuzhiyun 	if (scodec->quirks->legacy_widgets) {
596*4882a593Smuzhiyun 		ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_legacy_widgets,
597*4882a593Smuzhiyun 						ARRAY_SIZE(sun8i_codec_legacy_widgets));
598*4882a593Smuzhiyun 		if (ret)
599*4882a593Smuzhiyun 			return ret;
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 		ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_legacy_routes,
602*4882a593Smuzhiyun 					      ARRAY_SIZE(sun8i_codec_legacy_routes));
603*4882a593Smuzhiyun 		if (ret)
604*4882a593Smuzhiyun 			return ret;
605*4882a593Smuzhiyun 	}
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	/*
608*4882a593Smuzhiyun 	 * AIF1CLK and AIF2CLK share a pair of clock parents: PLL_AUDIO ("mod")
609*4882a593Smuzhiyun 	 * and MCLK (from the CPU DAI connected to AIF1). MCLK's parent is also
610*4882a593Smuzhiyun 	 * PLL_AUDIO, so using it adds no additional flexibility. Use PLL_AUDIO
611*4882a593Smuzhiyun 	 * directly to simplify the clock tree.
612*4882a593Smuzhiyun 	 */
613*4882a593Smuzhiyun 	regmap_update_bits(scodec->regmap, SUN8I_SYSCLK_CTL,
614*4882a593Smuzhiyun 			   SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK |
615*4882a593Smuzhiyun 			   SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK,
616*4882a593Smuzhiyun 			   SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL |
617*4882a593Smuzhiyun 			   SUN8I_SYSCLK_CTL_AIF2CLK_SRC_PLL);
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	/* Use AIF1CLK as the SYSCLK parent since AIF1 is used most often. */
620*4882a593Smuzhiyun 	regmap_update_bits(scodec->regmap, SUN8I_SYSCLK_CTL,
621*4882a593Smuzhiyun 			   BIT(SUN8I_SYSCLK_CTL_SYSCLK_SRC),
622*4882a593Smuzhiyun 			   SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK);
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun 	return 0;
625*4882a593Smuzhiyun }
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
628*4882a593Smuzhiyun 	.hw_params = sun8i_codec_hw_params,
629*4882a593Smuzhiyun 	.set_fmt = sun8i_set_fmt,
630*4882a593Smuzhiyun };
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun static struct snd_soc_dai_driver sun8i_codec_dai = {
633*4882a593Smuzhiyun 	.name = "sun8i",
634*4882a593Smuzhiyun 	/* playback capabilities */
635*4882a593Smuzhiyun 	.playback = {
636*4882a593Smuzhiyun 		.stream_name = "Playback",
637*4882a593Smuzhiyun 		.channels_min = 1,
638*4882a593Smuzhiyun 		.channels_max = 2,
639*4882a593Smuzhiyun 		.rates = SNDRV_PCM_RATE_8000_192000,
640*4882a593Smuzhiyun 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
641*4882a593Smuzhiyun 	},
642*4882a593Smuzhiyun 	/* capture capabilities */
643*4882a593Smuzhiyun 	.capture = {
644*4882a593Smuzhiyun 		.stream_name = "Capture",
645*4882a593Smuzhiyun 		.channels_min = 1,
646*4882a593Smuzhiyun 		.channels_max = 2,
647*4882a593Smuzhiyun 		.rates = SNDRV_PCM_RATE_8000_192000,
648*4882a593Smuzhiyun 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
649*4882a593Smuzhiyun 		.sig_bits = 24,
650*4882a593Smuzhiyun 	},
651*4882a593Smuzhiyun 	/* pcm operations */
652*4882a593Smuzhiyun 	.ops = &sun8i_codec_dai_ops,
653*4882a593Smuzhiyun };
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun static const struct snd_soc_component_driver sun8i_soc_component = {
656*4882a593Smuzhiyun 	.dapm_widgets		= sun8i_codec_dapm_widgets,
657*4882a593Smuzhiyun 	.num_dapm_widgets	= ARRAY_SIZE(sun8i_codec_dapm_widgets),
658*4882a593Smuzhiyun 	.dapm_routes		= sun8i_codec_dapm_routes,
659*4882a593Smuzhiyun 	.num_dapm_routes	= ARRAY_SIZE(sun8i_codec_dapm_routes),
660*4882a593Smuzhiyun 	.probe			= sun8i_codec_component_probe,
661*4882a593Smuzhiyun 	.idle_bias_on		= 1,
662*4882a593Smuzhiyun 	.use_pmdown_time	= 1,
663*4882a593Smuzhiyun 	.endianness		= 1,
664*4882a593Smuzhiyun 	.non_legacy_dai_naming	= 1,
665*4882a593Smuzhiyun };
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun static const struct regmap_config sun8i_codec_regmap_config = {
668*4882a593Smuzhiyun 	.reg_bits	= 32,
669*4882a593Smuzhiyun 	.reg_stride	= 4,
670*4882a593Smuzhiyun 	.val_bits	= 32,
671*4882a593Smuzhiyun 	.max_register	= SUN8I_DAC_MXR_SRC,
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun 	.cache_type	= REGCACHE_FLAT,
674*4882a593Smuzhiyun };
675*4882a593Smuzhiyun 
sun8i_codec_probe(struct platform_device * pdev)676*4882a593Smuzhiyun static int sun8i_codec_probe(struct platform_device *pdev)
677*4882a593Smuzhiyun {
678*4882a593Smuzhiyun 	struct sun8i_codec *scodec;
679*4882a593Smuzhiyun 	void __iomem *base;
680*4882a593Smuzhiyun 	int ret;
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun 	scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
683*4882a593Smuzhiyun 	if (!scodec)
684*4882a593Smuzhiyun 		return -ENOMEM;
685*4882a593Smuzhiyun 
686*4882a593Smuzhiyun 	scodec->clk_module = devm_clk_get(&pdev->dev, "mod");
687*4882a593Smuzhiyun 	if (IS_ERR(scodec->clk_module)) {
688*4882a593Smuzhiyun 		dev_err(&pdev->dev, "Failed to get the module clock\n");
689*4882a593Smuzhiyun 		return PTR_ERR(scodec->clk_module);
690*4882a593Smuzhiyun 	}
691*4882a593Smuzhiyun 
692*4882a593Smuzhiyun 	base = devm_platform_ioremap_resource(pdev, 0);
693*4882a593Smuzhiyun 	if (IS_ERR(base)) {
694*4882a593Smuzhiyun 		dev_err(&pdev->dev, "Failed to map the registers\n");
695*4882a593Smuzhiyun 		return PTR_ERR(base);
696*4882a593Smuzhiyun 	}
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 	scodec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "bus", base,
699*4882a593Smuzhiyun 						   &sun8i_codec_regmap_config);
700*4882a593Smuzhiyun 	if (IS_ERR(scodec->regmap)) {
701*4882a593Smuzhiyun 		dev_err(&pdev->dev, "Failed to create our regmap\n");
702*4882a593Smuzhiyun 		return PTR_ERR(scodec->regmap);
703*4882a593Smuzhiyun 	}
704*4882a593Smuzhiyun 
705*4882a593Smuzhiyun 	scodec->quirks = of_device_get_match_data(&pdev->dev);
706*4882a593Smuzhiyun 
707*4882a593Smuzhiyun 	platform_set_drvdata(pdev, scodec);
708*4882a593Smuzhiyun 
709*4882a593Smuzhiyun 	pm_runtime_enable(&pdev->dev);
710*4882a593Smuzhiyun 	if (!pm_runtime_enabled(&pdev->dev)) {
711*4882a593Smuzhiyun 		ret = sun8i_codec_runtime_resume(&pdev->dev);
712*4882a593Smuzhiyun 		if (ret)
713*4882a593Smuzhiyun 			goto err_pm_disable;
714*4882a593Smuzhiyun 	}
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 	ret = devm_snd_soc_register_component(&pdev->dev, &sun8i_soc_component,
717*4882a593Smuzhiyun 				     &sun8i_codec_dai, 1);
718*4882a593Smuzhiyun 	if (ret) {
719*4882a593Smuzhiyun 		dev_err(&pdev->dev, "Failed to register codec\n");
720*4882a593Smuzhiyun 		goto err_suspend;
721*4882a593Smuzhiyun 	}
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun 	return ret;
724*4882a593Smuzhiyun 
725*4882a593Smuzhiyun err_suspend:
726*4882a593Smuzhiyun 	if (!pm_runtime_status_suspended(&pdev->dev))
727*4882a593Smuzhiyun 		sun8i_codec_runtime_suspend(&pdev->dev);
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun err_pm_disable:
730*4882a593Smuzhiyun 	pm_runtime_disable(&pdev->dev);
731*4882a593Smuzhiyun 
732*4882a593Smuzhiyun 	return ret;
733*4882a593Smuzhiyun }
734*4882a593Smuzhiyun 
sun8i_codec_remove(struct platform_device * pdev)735*4882a593Smuzhiyun static int sun8i_codec_remove(struct platform_device *pdev)
736*4882a593Smuzhiyun {
737*4882a593Smuzhiyun 	pm_runtime_disable(&pdev->dev);
738*4882a593Smuzhiyun 	if (!pm_runtime_status_suspended(&pdev->dev))
739*4882a593Smuzhiyun 		sun8i_codec_runtime_suspend(&pdev->dev);
740*4882a593Smuzhiyun 
741*4882a593Smuzhiyun 	return 0;
742*4882a593Smuzhiyun }
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun static const struct sun8i_codec_quirks sun8i_a33_quirks = {
745*4882a593Smuzhiyun 	.legacy_widgets	= true,
746*4882a593Smuzhiyun 	.lrck_inversion	= true,
747*4882a593Smuzhiyun };
748*4882a593Smuzhiyun 
749*4882a593Smuzhiyun static const struct sun8i_codec_quirks sun50i_a64_quirks = {
750*4882a593Smuzhiyun };
751*4882a593Smuzhiyun 
752*4882a593Smuzhiyun static const struct of_device_id sun8i_codec_of_match[] = {
753*4882a593Smuzhiyun 	{ .compatible = "allwinner,sun8i-a33-codec", .data = &sun8i_a33_quirks },
754*4882a593Smuzhiyun 	{ .compatible = "allwinner,sun50i-a64-codec", .data = &sun50i_a64_quirks },
755*4882a593Smuzhiyun 	{}
756*4882a593Smuzhiyun };
757*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, sun8i_codec_of_match);
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun static const struct dev_pm_ops sun8i_codec_pm_ops = {
760*4882a593Smuzhiyun 	SET_RUNTIME_PM_OPS(sun8i_codec_runtime_suspend,
761*4882a593Smuzhiyun 			   sun8i_codec_runtime_resume, NULL)
762*4882a593Smuzhiyun };
763*4882a593Smuzhiyun 
764*4882a593Smuzhiyun static struct platform_driver sun8i_codec_driver = {
765*4882a593Smuzhiyun 	.driver = {
766*4882a593Smuzhiyun 		.name = "sun8i-codec",
767*4882a593Smuzhiyun 		.of_match_table = sun8i_codec_of_match,
768*4882a593Smuzhiyun 		.pm = &sun8i_codec_pm_ops,
769*4882a593Smuzhiyun 	},
770*4882a593Smuzhiyun 	.probe = sun8i_codec_probe,
771*4882a593Smuzhiyun 	.remove = sun8i_codec_remove,
772*4882a593Smuzhiyun };
773*4882a593Smuzhiyun module_platform_driver(sun8i_codec_driver);
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun MODULE_DESCRIPTION("Allwinner A33 (sun8i) codec driver");
776*4882a593Smuzhiyun MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>");
777*4882a593Smuzhiyun MODULE_LICENSE("GPL");
778*4882a593Smuzhiyun MODULE_ALIAS("platform:sun8i-codec");
779