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