xref: /openbmc/linux/sound/soc/sunxi/sun8i-codec.c (revision c56f5f1c)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
236c68493SMylène Josserand /*
336c68493SMylène Josserand  * This driver supports the digital controls for the internal codec
436c68493SMylène Josserand  * found in Allwinner's A33 SoCs.
536c68493SMylène Josserand  *
636c68493SMylène Josserand  * (C) Copyright 2010-2016
736c68493SMylène Josserand  * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
836c68493SMylène Josserand  * huangxin <huangxin@Reuuimllatech.com>
936c68493SMylène Josserand  * Mylène Josserand <mylene.josserand@free-electrons.com>
1036c68493SMylène Josserand  */
1136c68493SMylène Josserand 
1236c68493SMylène Josserand #include <linux/module.h>
1336c68493SMylène Josserand #include <linux/delay.h>
1436c68493SMylène Josserand #include <linux/clk.h>
1536c68493SMylène Josserand #include <linux/io.h>
1690cac932SSamuel Holland #include <linux/of_device.h>
1736c68493SMylène Josserand #include <linux/pm_runtime.h>
1836c68493SMylène Josserand #include <linux/regmap.h>
1913c3bf17SVasily Khoruzhick #include <linux/log2.h>
2036c68493SMylène Josserand 
2136c68493SMylène Josserand #include <sound/pcm_params.h>
2236c68493SMylène Josserand #include <sound/soc.h>
2336c68493SMylène Josserand #include <sound/soc-dapm.h>
2436c68493SMylène Josserand 
2536c68493SMylène Josserand #define SUN8I_SYSCLK_CTL				0x00c
2636c68493SMylène Josserand #define SUN8I_SYSCLK_CTL_AIF1CLK_ENA			11
27d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL		(0x2 << 8)
28d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_AIF2CLK_ENA			7
29d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_PLL		(0x2 << 4)
3036c68493SMylène Josserand #define SUN8I_SYSCLK_CTL_SYSCLK_ENA			3
3136c68493SMylène Josserand #define SUN8I_SYSCLK_CTL_SYSCLK_SRC			0
32d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK		(0x0 << 0)
33d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF2CLK		(0x1 << 0)
3436c68493SMylène Josserand #define SUN8I_MOD_CLK_ENA				0x010
3536c68493SMylène Josserand #define SUN8I_MOD_CLK_ENA_AIF1				15
36eda85d1fSMylene JOSSERAND #define SUN8I_MOD_CLK_ENA_ADC				3
3736c68493SMylène Josserand #define SUN8I_MOD_CLK_ENA_DAC				2
3836c68493SMylène Josserand #define SUN8I_MOD_RST_CTL				0x014
3936c68493SMylène Josserand #define SUN8I_MOD_RST_CTL_AIF1				15
40eda85d1fSMylene JOSSERAND #define SUN8I_MOD_RST_CTL_ADC				3
4136c68493SMylène Josserand #define SUN8I_MOD_RST_CTL_DAC				2
4236c68493SMylène Josserand #define SUN8I_SYS_SR_CTRL				0x018
4336c68493SMylène Josserand #define SUN8I_SYS_SR_CTRL_AIF1_FS			12
4436c68493SMylène Josserand #define SUN8I_SYS_SR_CTRL_AIF2_FS			8
4536c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL				0x040
4636c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD		15
47*c56f5f1cSSamuel Holland #define SUN8I_AIF1CLK_CTRL_AIF1_CLK_INV			13
4836c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV		9
4936c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV		6
5036c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ		4
5136c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16		(1 << 4)
5236c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT		2
53eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_ADCDAT_CTRL				0x044
54fa5c0ca1SSamuel Holland #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA		15
55fa5c0ca1SSamuel Holland #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA		14
5618ebd62cSSamuel Holland #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_SRC		10
5718ebd62cSSamuel Holland #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_SRC		8
5836c68493SMylène Josserand #define SUN8I_AIF1_DACDAT_CTRL				0x048
5936c68493SMylène Josserand #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA		15
6036c68493SMylène Josserand #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA		14
6118ebd62cSSamuel Holland #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_SRC		10
6218ebd62cSSamuel Holland #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_SRC		8
63eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC				0x04c
640ba95493SSamuel Holland #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L	15
650ba95493SSamuel Holland #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL	14
660ba95493SSamuel Holland #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL		13
670ba95493SSamuel Holland #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR	12
68eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R	11
69eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR	10
70eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR		9
71eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL	8
72eda85d1fSMylene JOSSERAND #define SUN8I_ADC_DIG_CTRL				0x100
7330aff91eSSamuel Holland #define SUN8I_ADC_DIG_CTRL_ENAD				15
74eda85d1fSMylene JOSSERAND #define SUN8I_ADC_DIG_CTRL_ADOUT_DTS			2
75eda85d1fSMylene JOSSERAND #define SUN8I_ADC_DIG_CTRL_ADOUT_DLY			1
7636c68493SMylène Josserand #define SUN8I_DAC_DIG_CTRL				0x120
7736c68493SMylène Josserand #define SUN8I_DAC_DIG_CTRL_ENDA				15
7836c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC				0x130
7936c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L		15
8036c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L		14
8136c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL		13
8236c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL		12
8336c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R		11
8436c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R		10
8536c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR		9
8636c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR		8
8736c68493SMylène Josserand 
88d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK	GENMASK(9, 8)
89d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK	GENMASK(5, 4)
9036c68493SMylène Josserand #define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK		GENMASK(15, 12)
9136c68493SMylène Josserand #define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK		GENMASK(11, 8)
92*c56f5f1cSSamuel Holland #define SUN8I_AIF1CLK_CTRL_AIF1_CLK_INV_MASK	GENMASK(14, 13)
93316b7758SMaxime Ripard #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK	GENMASK(12, 9)
94f30ef55cSSamuel Holland #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK	GENMASK(8, 6)
95f30ef55cSSamuel Holland #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK	GENMASK(5, 4)
96f30ef55cSSamuel Holland #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK	GENMASK(3, 2)
9736c68493SMylène Josserand 
987826b8d1SSamuel Holland enum {
997826b8d1SSamuel Holland 	SUN8I_CODEC_AIF1,
1007826b8d1SSamuel Holland 	SUN8I_CODEC_NAIFS
1017826b8d1SSamuel Holland };
1027826b8d1SSamuel Holland 
10390cac932SSamuel Holland struct sun8i_codec_quirks {
10490cac932SSamuel Holland 	bool legacy_widgets	: 1;
1057518805fSSamuel Holland 	bool lrck_inversion	: 1;
10690cac932SSamuel Holland };
10790cac932SSamuel Holland 
10836c68493SMylène Josserand struct sun8i_codec {
10936c68493SMylène Josserand 	struct regmap			*regmap;
11036c68493SMylène Josserand 	struct clk			*clk_module;
11190cac932SSamuel Holland 	const struct sun8i_codec_quirks	*quirks;
11236c68493SMylène Josserand };
11336c68493SMylène Josserand 
11436c68493SMylène Josserand static int sun8i_codec_runtime_resume(struct device *dev)
11536c68493SMylène Josserand {
11636c68493SMylène Josserand 	struct sun8i_codec *scodec = dev_get_drvdata(dev);
11736c68493SMylène Josserand 	int ret;
11836c68493SMylène Josserand 
11936c68493SMylène Josserand 	regcache_cache_only(scodec->regmap, false);
12036c68493SMylène Josserand 
12136c68493SMylène Josserand 	ret = regcache_sync(scodec->regmap);
12236c68493SMylène Josserand 	if (ret) {
12336c68493SMylène Josserand 		dev_err(dev, "Failed to sync regmap cache\n");
1246b3bb3c8SSamuel Holland 		return ret;
12536c68493SMylène Josserand 	}
12636c68493SMylène Josserand 
12736c68493SMylène Josserand 	return 0;
12836c68493SMylène Josserand }
12936c68493SMylène Josserand 
13036c68493SMylène Josserand static int sun8i_codec_runtime_suspend(struct device *dev)
13136c68493SMylène Josserand {
13236c68493SMylène Josserand 	struct sun8i_codec *scodec = dev_get_drvdata(dev);
13336c68493SMylène Josserand 
13436c68493SMylène Josserand 	regcache_cache_only(scodec->regmap, true);
13536c68493SMylène Josserand 	regcache_mark_dirty(scodec->regmap);
13636c68493SMylène Josserand 
13736c68493SMylène Josserand 	return 0;
13836c68493SMylène Josserand }
13936c68493SMylène Josserand 
14036c68493SMylène Josserand static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
14136c68493SMylène Josserand {
14236c68493SMylène Josserand 	unsigned int rate = params_rate(params);
14336c68493SMylène Josserand 
14436c68493SMylène Josserand 	switch (rate) {
14536c68493SMylène Josserand 	case 8000:
14636c68493SMylène Josserand 	case 7350:
14736c68493SMylène Josserand 		return 0x0;
14836c68493SMylène Josserand 	case 11025:
14936c68493SMylène Josserand 		return 0x1;
15036c68493SMylène Josserand 	case 12000:
15136c68493SMylène Josserand 		return 0x2;
15236c68493SMylène Josserand 	case 16000:
15336c68493SMylène Josserand 		return 0x3;
15436c68493SMylène Josserand 	case 22050:
15536c68493SMylène Josserand 		return 0x4;
15636c68493SMylène Josserand 	case 24000:
15736c68493SMylène Josserand 		return 0x5;
15836c68493SMylène Josserand 	case 32000:
15936c68493SMylène Josserand 		return 0x6;
16036c68493SMylène Josserand 	case 44100:
16136c68493SMylène Josserand 		return 0x7;
16236c68493SMylène Josserand 	case 48000:
16336c68493SMylène Josserand 		return 0x8;
16436c68493SMylène Josserand 	case 96000:
16536c68493SMylène Josserand 		return 0x9;
16636c68493SMylène Josserand 	case 192000:
16736c68493SMylène Josserand 		return 0xa;
16836c68493SMylène Josserand 	default:
16936c68493SMylène Josserand 		return -EINVAL;
17036c68493SMylène Josserand 	}
17136c68493SMylène Josserand }
17236c68493SMylène Josserand 
1737826b8d1SSamuel Holland static int sun8i_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
17436c68493SMylène Josserand {
175a886990cSSamuel Holland 	struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
176*c56f5f1cSSamuel Holland 	u32 dsp_format, format, invert, value;
17736c68493SMylène Josserand 
17836c68493SMylène Josserand 	/* clock masters */
17936c68493SMylène Josserand 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
180560bfe77SMaxime Ripard 	case SND_SOC_DAIFMT_CBS_CFS: /* Codec slave, DAI master */
181560bfe77SMaxime Ripard 		value = 0x1;
18236c68493SMylène Josserand 		break;
183560bfe77SMaxime Ripard 	case SND_SOC_DAIFMT_CBM_CFM: /* Codec Master, DAI slave */
184560bfe77SMaxime Ripard 		value = 0x0;
18536c68493SMylène Josserand 		break;
18636c68493SMylène Josserand 	default:
18736c68493SMylène Josserand 		return -EINVAL;
18836c68493SMylène Josserand 	}
18936c68493SMylène Josserand 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
19036c68493SMylène Josserand 			   BIT(SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD),
19136c68493SMylène Josserand 			   value << SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD);
19236c68493SMylène Josserand 
193fd57ed2dSSamuel Holland 	/* DAI format */
194fd57ed2dSSamuel Holland 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
195fd57ed2dSSamuel Holland 	case SND_SOC_DAIFMT_I2S:
196fd57ed2dSSamuel Holland 		format = 0x0;
197fd57ed2dSSamuel Holland 		break;
198fd57ed2dSSamuel Holland 	case SND_SOC_DAIFMT_LEFT_J:
199fd57ed2dSSamuel Holland 		format = 0x1;
200fd57ed2dSSamuel Holland 		break;
201fd57ed2dSSamuel Holland 	case SND_SOC_DAIFMT_RIGHT_J:
202fd57ed2dSSamuel Holland 		format = 0x2;
203fd57ed2dSSamuel Holland 		break;
204fd57ed2dSSamuel Holland 	case SND_SOC_DAIFMT_DSP_A:
205*c56f5f1cSSamuel Holland 		format = 0x3;
206*c56f5f1cSSamuel Holland 		dsp_format = 0x0; /* Set LRCK_INV to 0 */
207*c56f5f1cSSamuel Holland 		break;
208fd57ed2dSSamuel Holland 	case SND_SOC_DAIFMT_DSP_B:
209fd57ed2dSSamuel Holland 		format = 0x3;
210*c56f5f1cSSamuel Holland 		dsp_format = 0x1; /* Set LRCK_INV to 1 */
211fd57ed2dSSamuel Holland 		break;
212fd57ed2dSSamuel Holland 	default:
213fd57ed2dSSamuel Holland 		return -EINVAL;
214fd57ed2dSSamuel Holland 	}
215fd57ed2dSSamuel Holland 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
216fd57ed2dSSamuel Holland 			   SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK,
217fd57ed2dSSamuel Holland 			   format << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT);
218fd57ed2dSSamuel Holland 
21936c68493SMylène Josserand 	/* clock inversion */
22036c68493SMylène Josserand 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
22136c68493SMylène Josserand 	case SND_SOC_DAIFMT_NB_NF: /* Normal */
222*c56f5f1cSSamuel Holland 		invert = 0x0;
22336c68493SMylène Josserand 		break;
224*c56f5f1cSSamuel Holland 	case SND_SOC_DAIFMT_NB_IF: /* Inverted LRCK */
225*c56f5f1cSSamuel Holland 		invert = 0x1;
226*c56f5f1cSSamuel Holland 		break;
227*c56f5f1cSSamuel Holland 	case SND_SOC_DAIFMT_IB_NF: /* Inverted BCLK */
228*c56f5f1cSSamuel Holland 		invert = 0x2;
229*c56f5f1cSSamuel Holland 		break;
230*c56f5f1cSSamuel Holland 	case SND_SOC_DAIFMT_IB_IF: /* Both inverted */
231*c56f5f1cSSamuel Holland 		invert = 0x3;
23236c68493SMylène Josserand 		break;
23336c68493SMylène Josserand 	default:
23436c68493SMylène Josserand 		return -EINVAL;
23536c68493SMylène Josserand 	}
236e7b8a6d3SMaxime Ripard 
237*c56f5f1cSSamuel Holland 	if (format == 0x3) {
238*c56f5f1cSSamuel Holland 		/* Inverted LRCK is not available in DSP mode. */
239*c56f5f1cSSamuel Holland 		if (invert & BIT(0))
240*c56f5f1cSSamuel Holland 			return -EINVAL;
241*c56f5f1cSSamuel Holland 
242*c56f5f1cSSamuel Holland 		/* Instead, the bit selects between DSP A/B formats. */
243*c56f5f1cSSamuel Holland 		invert |= dsp_format;
244*c56f5f1cSSamuel Holland 	} else {
245e7b8a6d3SMaxime Ripard 		/*
2467518805fSSamuel Holland 		 * It appears that the DAI and the codec in the A33 SoC don't
2477518805fSSamuel Holland 		 * share the same polarity for the LRCK signal when they mean
2487518805fSSamuel Holland 		 * 'normal' and 'inverted' in the datasheet.
249e7b8a6d3SMaxime Ripard 		 *
250e7b8a6d3SMaxime Ripard 		 * Since the DAI here is our regular i2s driver that have been
251e7b8a6d3SMaxime Ripard 		 * tested with way more codecs than just this one, it means
252e7b8a6d3SMaxime Ripard 		 * that the codec probably gets it backward, and we have to
253e7b8a6d3SMaxime Ripard 		 * invert the value here.
254e7b8a6d3SMaxime Ripard 		 */
255*c56f5f1cSSamuel Holland 		invert ^= scodec->quirks->lrck_inversion;
256*c56f5f1cSSamuel Holland 	}
257*c56f5f1cSSamuel Holland 
25836c68493SMylène Josserand 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
259*c56f5f1cSSamuel Holland 			   SUN8I_AIF1CLK_CTRL_AIF1_CLK_INV_MASK,
260*c56f5f1cSSamuel Holland 			   invert << SUN8I_AIF1CLK_CTRL_AIF1_CLK_INV);
26136c68493SMylène Josserand 
26236c68493SMylène Josserand 	return 0;
26336c68493SMylène Josserand }
26436c68493SMylène Josserand 
265316b7758SMaxime Ripard struct sun8i_codec_clk_div {
266316b7758SMaxime Ripard 	u8	div;
267316b7758SMaxime Ripard 	u8	val;
268316b7758SMaxime Ripard };
269316b7758SMaxime Ripard 
270316b7758SMaxime Ripard static const struct sun8i_codec_clk_div sun8i_codec_bclk_div[] = {
271316b7758SMaxime Ripard 	{ .div = 1,	.val = 0 },
272316b7758SMaxime Ripard 	{ .div = 2,	.val = 1 },
273316b7758SMaxime Ripard 	{ .div = 4,	.val = 2 },
274316b7758SMaxime Ripard 	{ .div = 6,	.val = 3 },
275316b7758SMaxime Ripard 	{ .div = 8,	.val = 4 },
276316b7758SMaxime Ripard 	{ .div = 12,	.val = 5 },
277316b7758SMaxime Ripard 	{ .div = 16,	.val = 6 },
278316b7758SMaxime Ripard 	{ .div = 24,	.val = 7 },
279316b7758SMaxime Ripard 	{ .div = 32,	.val = 8 },
280316b7758SMaxime Ripard 	{ .div = 48,	.val = 9 },
281316b7758SMaxime Ripard 	{ .div = 64,	.val = 10 },
282316b7758SMaxime Ripard 	{ .div = 96,	.val = 11 },
283316b7758SMaxime Ripard 	{ .div = 128,	.val = 12 },
284316b7758SMaxime Ripard 	{ .div = 192,	.val = 13 },
285316b7758SMaxime Ripard };
286316b7758SMaxime Ripard 
287316b7758SMaxime Ripard static u8 sun8i_codec_get_bclk_div(struct sun8i_codec *scodec,
288316b7758SMaxime Ripard 				   unsigned int rate,
289316b7758SMaxime Ripard 				   unsigned int word_size)
290316b7758SMaxime Ripard {
291316b7758SMaxime Ripard 	unsigned long clk_rate = clk_get_rate(scodec->clk_module);
292316b7758SMaxime Ripard 	unsigned int div = clk_rate / rate / word_size / 2;
293316b7758SMaxime Ripard 	unsigned int best_val = 0, best_diff = ~0;
294316b7758SMaxime Ripard 	int i;
295316b7758SMaxime Ripard 
296316b7758SMaxime Ripard 	for (i = 0; i < ARRAY_SIZE(sun8i_codec_bclk_div); i++) {
297316b7758SMaxime Ripard 		const struct sun8i_codec_clk_div *bdiv = &sun8i_codec_bclk_div[i];
298316b7758SMaxime Ripard 		unsigned int diff = abs(bdiv->div - div);
299316b7758SMaxime Ripard 
300316b7758SMaxime Ripard 		if (diff < best_diff) {
301316b7758SMaxime Ripard 			best_diff = diff;
302316b7758SMaxime Ripard 			best_val = bdiv->val;
303316b7758SMaxime Ripard 		}
304316b7758SMaxime Ripard 	}
305316b7758SMaxime Ripard 
306316b7758SMaxime Ripard 	return best_val;
307316b7758SMaxime Ripard }
308316b7758SMaxime Ripard 
30913c3bf17SVasily Khoruzhick static int sun8i_codec_get_lrck_div(unsigned int channels,
31013c3bf17SVasily Khoruzhick 				    unsigned int word_size)
31113c3bf17SVasily Khoruzhick {
31213c3bf17SVasily Khoruzhick 	unsigned int div = word_size * channels;
31313c3bf17SVasily Khoruzhick 
31413c3bf17SVasily Khoruzhick 	if (div < 16 || div > 256)
31513c3bf17SVasily Khoruzhick 		return -EINVAL;
31613c3bf17SVasily Khoruzhick 
31713c3bf17SVasily Khoruzhick 	return ilog2(div) - 4;
31813c3bf17SVasily Khoruzhick }
31913c3bf17SVasily Khoruzhick 
32036c68493SMylène Josserand static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
32136c68493SMylène Josserand 				 struct snd_pcm_hw_params *params,
32236c68493SMylène Josserand 				 struct snd_soc_dai *dai)
32336c68493SMylène Josserand {
324a886990cSSamuel Holland 	struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
32513c3bf17SVasily Khoruzhick 	int sample_rate, lrck_div;
326316b7758SMaxime Ripard 	u8 bclk_div;
32736c68493SMylène Josserand 
32836c68493SMylène Josserand 	/*
32936c68493SMylène Josserand 	 * The CPU DAI handles only a sample of 16 bits. Configure the
33036c68493SMylène Josserand 	 * codec to handle this type of sample resolution.
33136c68493SMylène Josserand 	 */
33236c68493SMylène Josserand 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
33336c68493SMylène Josserand 			   SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK,
33436c68493SMylène Josserand 			   SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16);
33536c68493SMylène Josserand 
336316b7758SMaxime Ripard 	bclk_div = sun8i_codec_get_bclk_div(scodec, params_rate(params), 16);
337316b7758SMaxime Ripard 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
338316b7758SMaxime Ripard 			   SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
339316b7758SMaxime Ripard 			   bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
340316b7758SMaxime Ripard 
34113c3bf17SVasily Khoruzhick 	lrck_div = sun8i_codec_get_lrck_div(params_channels(params),
34213c3bf17SVasily Khoruzhick 					    params_physical_width(params));
34313c3bf17SVasily Khoruzhick 	if (lrck_div < 0)
34413c3bf17SVasily Khoruzhick 		return lrck_div;
34513c3bf17SVasily Khoruzhick 
34636c68493SMylène Josserand 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
34736c68493SMylène Josserand 			   SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK,
34813c3bf17SVasily Khoruzhick 			   lrck_div << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);
34936c68493SMylène Josserand 
35036c68493SMylène Josserand 	sample_rate = sun8i_codec_get_hw_rate(params);
35136c68493SMylène Josserand 	if (sample_rate < 0)
35236c68493SMylène Josserand 		return sample_rate;
35336c68493SMylène Josserand 
35436c68493SMylène Josserand 	regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
35536c68493SMylène Josserand 			   SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
35636c68493SMylène Josserand 			   sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);
35736c68493SMylène Josserand 
35836c68493SMylène Josserand 	return 0;
35936c68493SMylène Josserand }
36036c68493SMylène Josserand 
3617826b8d1SSamuel Holland static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
3627826b8d1SSamuel Holland 	.set_fmt	= sun8i_codec_set_fmt,
3637826b8d1SSamuel Holland 	.hw_params	= sun8i_codec_hw_params,
3647826b8d1SSamuel Holland };
3657826b8d1SSamuel Holland 
3667826b8d1SSamuel Holland static struct snd_soc_dai_driver sun8i_codec_dais[] = {
3677826b8d1SSamuel Holland 	{
3687826b8d1SSamuel Holland 		.name	= "sun8i-codec-aif1",
3697826b8d1SSamuel Holland 		.id	= SUN8I_CODEC_AIF1,
3707826b8d1SSamuel Holland 		.ops	= &sun8i_codec_dai_ops,
3717826b8d1SSamuel Holland 		/* capture capabilities */
3727826b8d1SSamuel Holland 		.capture = {
3737826b8d1SSamuel Holland 			.stream_name	= "AIF1 Capture",
3747826b8d1SSamuel Holland 			.channels_min	= 1,
3757826b8d1SSamuel Holland 			.channels_max	= 2,
3767826b8d1SSamuel Holland 			.rates		= SNDRV_PCM_RATE_8000_192000,
3777826b8d1SSamuel Holland 			.formats	= SNDRV_PCM_FMTBIT_S16_LE,
3787826b8d1SSamuel Holland 			.sig_bits	= 24,
3797826b8d1SSamuel Holland 		},
3807826b8d1SSamuel Holland 		/* playback capabilities */
3817826b8d1SSamuel Holland 		.playback = {
3827826b8d1SSamuel Holland 			.stream_name	= "AIF1 Playback",
3837826b8d1SSamuel Holland 			.channels_min	= 1,
3847826b8d1SSamuel Holland 			.channels_max	= 2,
3857826b8d1SSamuel Holland 			.rates		= SNDRV_PCM_RATE_8000_192000,
3867826b8d1SSamuel Holland 			.formats	= SNDRV_PCM_FMTBIT_S16_LE,
3877826b8d1SSamuel Holland 		},
3887826b8d1SSamuel Holland 	},
3897826b8d1SSamuel Holland };
3907826b8d1SSamuel Holland 
39118ebd62cSSamuel Holland static const char *const sun8i_aif_stereo_mux_enum_values[] = {
39218ebd62cSSamuel Holland 	"Stereo", "Reverse Stereo", "Sum Mono", "Mix Mono"
39318ebd62cSSamuel Holland };
39418ebd62cSSamuel Holland 
39518ebd62cSSamuel Holland static SOC_ENUM_DOUBLE_DECL(sun8i_aif1_ad0_stereo_mux_enum,
39618ebd62cSSamuel Holland 			    SUN8I_AIF1_ADCDAT_CTRL,
39718ebd62cSSamuel Holland 			    SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_SRC,
39818ebd62cSSamuel Holland 			    SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_SRC,
39918ebd62cSSamuel Holland 			    sun8i_aif_stereo_mux_enum_values);
40018ebd62cSSamuel Holland 
40118ebd62cSSamuel Holland static const struct snd_kcontrol_new sun8i_aif1_ad0_stereo_mux_control =
40218ebd62cSSamuel Holland 	SOC_DAPM_ENUM("AIF1 AD0 Stereo Capture Route",
40318ebd62cSSamuel Holland 		      sun8i_aif1_ad0_stereo_mux_enum);
40418ebd62cSSamuel Holland 
405d58b7247SSamuel Holland static const struct snd_kcontrol_new sun8i_aif1_ad0_mixer_controls[] = {
406d58b7247SSamuel Holland 	SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch",
407d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC,
408d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L,
409d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0),
410d58b7247SSamuel Holland 	SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch",
411d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC,
412d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL,
413d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0),
414d58b7247SSamuel Holland 	SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch",
415d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC,
416d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL,
417d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0),
418d58b7247SSamuel Holland 	SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch",
419d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC,
420d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR,
421d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0),
422d58b7247SSamuel Holland };
423d58b7247SSamuel Holland 
42418ebd62cSSamuel Holland static SOC_ENUM_DOUBLE_DECL(sun8i_aif1_da0_stereo_mux_enum,
42518ebd62cSSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL,
42618ebd62cSSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_SRC,
42718ebd62cSSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_SRC,
42818ebd62cSSamuel Holland 			    sun8i_aif_stereo_mux_enum_values);
42918ebd62cSSamuel Holland 
43018ebd62cSSamuel Holland static const struct snd_kcontrol_new sun8i_aif1_da0_stereo_mux_control =
43118ebd62cSSamuel Holland 	SOC_DAPM_ENUM("AIF1 DA0 Stereo Playback Route",
43218ebd62cSSamuel Holland 		      sun8i_aif1_da0_stereo_mux_enum);
43318ebd62cSSamuel Holland 
434ca14da6eSMylène Josserand static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = {
435ca14da6eSMylène Josserand 	SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital DAC Playback Switch",
436ca14da6eSMylène Josserand 			SUN8I_DAC_MXR_SRC,
437ca14da6eSMylène Josserand 			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L,
43836c68493SMylène Josserand 			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R, 1, 0),
439ca14da6eSMylène Josserand 	SOC_DAPM_DOUBLE("AIF1 Slot 1 Digital DAC Playback Switch",
440ca14da6eSMylène Josserand 			SUN8I_DAC_MXR_SRC,
441ca14da6eSMylène Josserand 			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L,
44236c68493SMylène Josserand 			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R, 1, 0),
443ca14da6eSMylène Josserand 	SOC_DAPM_DOUBLE("AIF2 Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC,
444ca14da6eSMylène Josserand 			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL,
44536c68493SMylène Josserand 			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR, 1, 0),
446ca14da6eSMylène Josserand 	SOC_DAPM_DOUBLE("ADC Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC,
447ca14da6eSMylène Josserand 			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL,
44836c68493SMylène Josserand 			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0),
44936c68493SMylène Josserand };
45036c68493SMylène Josserand 
45136c68493SMylène Josserand static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
452d8f00682SSamuel Holland 	/* System Clocks */
4536b3bb3c8SSamuel Holland 	SND_SOC_DAPM_CLOCK_SUPPLY("mod"),
4546b3bb3c8SSamuel Holland 
455d8f00682SSamuel Holland 	SND_SOC_DAPM_SUPPLY("AIF1CLK",
456d8f00682SSamuel Holland 			    SUN8I_SYSCLK_CTL,
457d8f00682SSamuel Holland 			    SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0),
458d8f00682SSamuel Holland 	SND_SOC_DAPM_SUPPLY("SYSCLK",
459d8f00682SSamuel Holland 			    SUN8I_SYSCLK_CTL,
460d8f00682SSamuel Holland 			    SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0),
461d8f00682SSamuel Holland 
462ed3caa3bSSamuel Holland 	/* Module Clocks */
463ed3caa3bSSamuel Holland 	SND_SOC_DAPM_SUPPLY("CLK AIF1",
464ed3caa3bSSamuel Holland 			    SUN8I_MOD_CLK_ENA,
465ed3caa3bSSamuel Holland 			    SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0),
466ed3caa3bSSamuel Holland 	SND_SOC_DAPM_SUPPLY("CLK ADC",
467ed3caa3bSSamuel Holland 			    SUN8I_MOD_CLK_ENA,
468ed3caa3bSSamuel Holland 			    SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0),
469ed3caa3bSSamuel Holland 	SND_SOC_DAPM_SUPPLY("CLK DAC",
470ed3caa3bSSamuel Holland 			    SUN8I_MOD_CLK_ENA,
471ed3caa3bSSamuel Holland 			    SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0),
472ed3caa3bSSamuel Holland 
473ed3caa3bSSamuel Holland 	/* Module Resets */
474ed3caa3bSSamuel Holland 	SND_SOC_DAPM_SUPPLY("RST AIF1",
475ed3caa3bSSamuel Holland 			    SUN8I_MOD_RST_CTL,
476ed3caa3bSSamuel Holland 			    SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0),
477ed3caa3bSSamuel Holland 	SND_SOC_DAPM_SUPPLY("RST ADC",
478ed3caa3bSSamuel Holland 			    SUN8I_MOD_RST_CTL,
479ed3caa3bSSamuel Holland 			    SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0),
480ed3caa3bSSamuel Holland 	SND_SOC_DAPM_SUPPLY("RST DAC",
481ed3caa3bSSamuel Holland 			    SUN8I_MOD_RST_CTL,
482ed3caa3bSSamuel Holland 			    SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0),
483ed3caa3bSSamuel Holland 
484d58b7247SSamuel Holland 	/* Module Supplies */
485d58b7247SSamuel Holland 	SND_SOC_DAPM_SUPPLY("ADC",
486d58b7247SSamuel Holland 			    SUN8I_ADC_DIG_CTRL,
487d58b7247SSamuel Holland 			    SUN8I_ADC_DIG_CTRL_ENAD, 0, NULL, 0),
488d58b7247SSamuel Holland 	SND_SOC_DAPM_SUPPLY("DAC",
489d58b7247SSamuel Holland 			    SUN8I_DAC_DIG_CTRL,
490d58b7247SSamuel Holland 			    SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0),
49136c68493SMylène Josserand 
49290cac932SSamuel Holland 	/* AIF "ADC" Outputs */
4937826b8d1SSamuel Holland 	SND_SOC_DAPM_AIF_OUT("AIF1 AD0L", "AIF1 Capture", 0,
494eda85d1fSMylene JOSSERAND 			     SUN8I_AIF1_ADCDAT_CTRL,
495fa5c0ca1SSamuel Holland 			     SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0),
4967826b8d1SSamuel Holland 	SND_SOC_DAPM_AIF_OUT("AIF1 AD0R", "AIF1 Capture", 1,
497eda85d1fSMylene JOSSERAND 			     SUN8I_AIF1_ADCDAT_CTRL,
498fa5c0ca1SSamuel Holland 			     SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0),
499eda85d1fSMylene JOSSERAND 
50018ebd62cSSamuel Holland 	/* AIF "ADC" Mono/Stereo Muxes */
50118ebd62cSSamuel Holland 	SND_SOC_DAPM_MUX("AIF1 AD0L Stereo Mux", SND_SOC_NOPM, 0, 0,
50218ebd62cSSamuel Holland 			 &sun8i_aif1_ad0_stereo_mux_control),
50318ebd62cSSamuel Holland 	SND_SOC_DAPM_MUX("AIF1 AD0R Stereo Mux", SND_SOC_NOPM, 0, 0,
50418ebd62cSSamuel Holland 			 &sun8i_aif1_ad0_stereo_mux_control),
50518ebd62cSSamuel Holland 
506d58b7247SSamuel Holland 	/* AIF "ADC" Mixers */
5077b51f3c7SSamuel Holland 	SOC_MIXER_ARRAY("AIF1 AD0L Mixer", SND_SOC_NOPM, 0, 0,
508d58b7247SSamuel Holland 			sun8i_aif1_ad0_mixer_controls),
5097b51f3c7SSamuel Holland 	SOC_MIXER_ARRAY("AIF1 AD0R Mixer", SND_SOC_NOPM, 0, 0,
510d58b7247SSamuel Holland 			sun8i_aif1_ad0_mixer_controls),
511d58b7247SSamuel Holland 
51218ebd62cSSamuel Holland 	/* AIF "DAC" Mono/Stereo Muxes */
51318ebd62cSSamuel Holland 	SND_SOC_DAPM_MUX("AIF1 DA0L Stereo Mux", SND_SOC_NOPM, 0, 0,
51418ebd62cSSamuel Holland 			 &sun8i_aif1_da0_stereo_mux_control),
51518ebd62cSSamuel Holland 	SND_SOC_DAPM_MUX("AIF1 DA0R Stereo Mux", SND_SOC_NOPM, 0, 0,
51618ebd62cSSamuel Holland 			 &sun8i_aif1_da0_stereo_mux_control),
51718ebd62cSSamuel Holland 
518d58b7247SSamuel Holland 	/* AIF "DAC" Inputs */
5197826b8d1SSamuel Holland 	SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "AIF1 Playback", 0,
520d58b7247SSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL,
521d58b7247SSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0),
5227826b8d1SSamuel Holland 	SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "AIF1 Playback", 1,
523d58b7247SSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL,
524d58b7247SSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
525d58b7247SSamuel Holland 
52690cac932SSamuel Holland 	/* ADC Inputs (connected to analog codec DAPM context) */
52790cac932SSamuel Holland 	SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0),
52890cac932SSamuel Holland 	SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
52990cac932SSamuel Holland 
53090cac932SSamuel Holland 	/* DAC Outputs (connected to analog codec DAPM context) */
53190cac932SSamuel Holland 	SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
53290cac932SSamuel Holland 	SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
53390cac932SSamuel Holland 
534d58b7247SSamuel Holland 	/* DAC Mixers */
5357b51f3c7SSamuel Holland 	SOC_MIXER_ARRAY("DACL Mixer", SND_SOC_NOPM, 0, 0,
536fa22ca4fSMylène Josserand 			sun8i_dac_mixer_controls),
5377b51f3c7SSamuel Holland 	SOC_MIXER_ARRAY("DACR Mixer", SND_SOC_NOPM, 0, 0,
538fa22ca4fSMylène Josserand 			sun8i_dac_mixer_controls),
53936c68493SMylène Josserand };
54036c68493SMylène Josserand 
54136c68493SMylène Josserand static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
54236c68493SMylène Josserand 	/* Clock Routes */
543d8f00682SSamuel Holland 	{ "AIF1CLK", NULL, "mod" },
5446b3bb3c8SSamuel Holland 
545d8f00682SSamuel Holland 	{ "SYSCLK", NULL, "AIF1CLK" },
54690cac932SSamuel Holland 
547ed3caa3bSSamuel Holland 	{ "CLK AIF1", NULL, "AIF1CLK" },
548ed3caa3bSSamuel Holland 	{ "CLK AIF1", NULL, "SYSCLK" },
549ed3caa3bSSamuel Holland 	{ "RST AIF1", NULL, "CLK AIF1" },
550ed3caa3bSSamuel Holland 	{ "AIF1 AD0L", NULL, "RST AIF1" },
551ed3caa3bSSamuel Holland 	{ "AIF1 AD0R", NULL, "RST AIF1" },
552ed3caa3bSSamuel Holland 	{ "AIF1 DA0L", NULL, "RST AIF1" },
553ed3caa3bSSamuel Holland 	{ "AIF1 DA0R", NULL, "RST AIF1" },
55436c68493SMylène Josserand 
555ed3caa3bSSamuel Holland 	{ "CLK ADC", NULL, "SYSCLK" },
556ed3caa3bSSamuel Holland 	{ "RST ADC", NULL, "CLK ADC" },
557ed3caa3bSSamuel Holland 	{ "ADC", NULL, "RST ADC" },
55890cac932SSamuel Holland 	{ "ADCL", NULL, "ADC" },
55990cac932SSamuel Holland 	{ "ADCR", NULL, "ADC" },
560eda85d1fSMylene JOSSERAND 
561ed3caa3bSSamuel Holland 	{ "CLK DAC", NULL, "SYSCLK" },
562ed3caa3bSSamuel Holland 	{ "RST DAC", NULL, "CLK DAC" },
563ed3caa3bSSamuel Holland 	{ "DAC", NULL, "RST DAC" },
564ed3caa3bSSamuel Holland 	{ "DACL", NULL, "DAC" },
565ed3caa3bSSamuel Holland 	{ "DACR", NULL, "DAC" },
566ed3caa3bSSamuel Holland 
567d58b7247SSamuel Holland 	/* AIF "ADC" Output Routes */
56818ebd62cSSamuel Holland 	{ "AIF1 AD0L", NULL, "AIF1 AD0L Stereo Mux" },
56918ebd62cSSamuel Holland 	{ "AIF1 AD0R", NULL, "AIF1 AD0R Stereo Mux" },
57018ebd62cSSamuel Holland 
57118ebd62cSSamuel Holland 	/* AIF "ADC" Mono/Stereo Mux Routes */
57218ebd62cSSamuel Holland 	{ "AIF1 AD0L Stereo Mux", "Stereo", "AIF1 AD0L Mixer" },
57318ebd62cSSamuel Holland 	{ "AIF1 AD0L Stereo Mux", "Reverse Stereo", "AIF1 AD0R Mixer" },
57418ebd62cSSamuel Holland 	{ "AIF1 AD0L Stereo Mux", "Sum Mono", "AIF1 AD0L Mixer" },
57518ebd62cSSamuel Holland 	{ "AIF1 AD0L Stereo Mux", "Sum Mono", "AIF1 AD0R Mixer" },
57618ebd62cSSamuel Holland 	{ "AIF1 AD0L Stereo Mux", "Mix Mono", "AIF1 AD0L Mixer" },
57718ebd62cSSamuel Holland 	{ "AIF1 AD0L Stereo Mux", "Mix Mono", "AIF1 AD0R Mixer" },
57818ebd62cSSamuel Holland 
57918ebd62cSSamuel Holland 	{ "AIF1 AD0R Stereo Mux", "Stereo", "AIF1 AD0R Mixer" },
58018ebd62cSSamuel Holland 	{ "AIF1 AD0R Stereo Mux", "Reverse Stereo", "AIF1 AD0L Mixer" },
58118ebd62cSSamuel Holland 	{ "AIF1 AD0R Stereo Mux", "Sum Mono", "AIF1 AD0L Mixer" },
58218ebd62cSSamuel Holland 	{ "AIF1 AD0R Stereo Mux", "Sum Mono", "AIF1 AD0R Mixer" },
58318ebd62cSSamuel Holland 	{ "AIF1 AD0R Stereo Mux", "Mix Mono", "AIF1 AD0L Mixer" },
58418ebd62cSSamuel Holland 	{ "AIF1 AD0R Stereo Mux", "Mix Mono", "AIF1 AD0R Mixer" },
585d58b7247SSamuel Holland 
586d58b7247SSamuel Holland 	/* AIF "ADC" Mixer Routes */
58718ebd62cSSamuel Holland 	{ "AIF1 AD0L Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L Stereo Mux" },
5887b51f3c7SSamuel Holland 	{ "AIF1 AD0L Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" },
589d58b7247SSamuel Holland 
59018ebd62cSSamuel Holland 	{ "AIF1 AD0R Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R Stereo Mux" },
5917b51f3c7SSamuel Holland 	{ "AIF1 AD0R Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" },
592d58b7247SSamuel Holland 
59318ebd62cSSamuel Holland 	/* AIF "DAC" Mono/Stereo Mux Routes */
59418ebd62cSSamuel Holland 	{ "AIF1 DA0L Stereo Mux", "Stereo", "AIF1 DA0L" },
59518ebd62cSSamuel Holland 	{ "AIF1 DA0L Stereo Mux", "Reverse Stereo", "AIF1 DA0R" },
59618ebd62cSSamuel Holland 	{ "AIF1 DA0L Stereo Mux", "Sum Mono", "AIF1 DA0L" },
59718ebd62cSSamuel Holland 	{ "AIF1 DA0L Stereo Mux", "Sum Mono", "AIF1 DA0R" },
59818ebd62cSSamuel Holland 	{ "AIF1 DA0L Stereo Mux", "Mix Mono", "AIF1 DA0L" },
59918ebd62cSSamuel Holland 	{ "AIF1 DA0L Stereo Mux", "Mix Mono", "AIF1 DA0R" },
60018ebd62cSSamuel Holland 
60118ebd62cSSamuel Holland 	{ "AIF1 DA0R Stereo Mux", "Stereo", "AIF1 DA0R" },
60218ebd62cSSamuel Holland 	{ "AIF1 DA0R Stereo Mux", "Reverse Stereo", "AIF1 DA0L" },
60318ebd62cSSamuel Holland 	{ "AIF1 DA0R Stereo Mux", "Sum Mono", "AIF1 DA0L" },
60418ebd62cSSamuel Holland 	{ "AIF1 DA0R Stereo Mux", "Sum Mono", "AIF1 DA0R" },
60518ebd62cSSamuel Holland 	{ "AIF1 DA0R Stereo Mux", "Mix Mono", "AIF1 DA0L" },
60618ebd62cSSamuel Holland 	{ "AIF1 DA0R Stereo Mux", "Mix Mono", "AIF1 DA0R" },
60718ebd62cSSamuel Holland 
608d58b7247SSamuel Holland 	/* DAC Output Routes */
6097b51f3c7SSamuel Holland 	{ "DACL", NULL, "DACL Mixer" },
6107b51f3c7SSamuel Holland 	{ "DACR", NULL, "DACR Mixer" },
61136c68493SMylène Josserand 
61236c68493SMylène Josserand 	/* DAC Mixer Routes */
61318ebd62cSSamuel Holland 	{ "DACL Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L Stereo Mux" },
6147b51f3c7SSamuel Holland 	{ "DACL Mixer", "ADC Digital DAC Playback Switch", "ADCL" },
615e47d2dcdSSamuel Holland 
61618ebd62cSSamuel Holland 	{ "DACR Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R Stereo Mux" },
6177b51f3c7SSamuel Holland 	{ "DACR Mixer", "ADC Digital DAC Playback Switch", "ADCR" },
61836c68493SMylène Josserand };
61936c68493SMylène Josserand 
62090cac932SSamuel Holland static const struct snd_soc_dapm_widget sun8i_codec_legacy_widgets[] = {
62190cac932SSamuel Holland 	/* Legacy ADC Inputs (connected to analog codec DAPM context) */
62290cac932SSamuel Holland 	SND_SOC_DAPM_ADC("AIF1 Slot 0 Left ADC", NULL, SND_SOC_NOPM, 0, 0),
62390cac932SSamuel Holland 	SND_SOC_DAPM_ADC("AIF1 Slot 0 Right ADC", NULL, SND_SOC_NOPM, 0, 0),
62490cac932SSamuel Holland 
62590cac932SSamuel Holland 	/* Legacy DAC Outputs (connected to analog codec DAPM context) */
62690cac932SSamuel Holland 	SND_SOC_DAPM_DAC("AIF1 Slot 0 Left", NULL, SND_SOC_NOPM, 0, 0),
62790cac932SSamuel Holland 	SND_SOC_DAPM_DAC("AIF1 Slot 0 Right", NULL, SND_SOC_NOPM, 0, 0),
62890cac932SSamuel Holland };
62990cac932SSamuel Holland 
63090cac932SSamuel Holland static const struct snd_soc_dapm_route sun8i_codec_legacy_routes[] = {
63190cac932SSamuel Holland 	/* Legacy ADC Routes */
63290cac932SSamuel Holland 	{ "ADCL", NULL, "AIF1 Slot 0 Left ADC" },
63390cac932SSamuel Holland 	{ "ADCR", NULL, "AIF1 Slot 0 Right ADC" },
63490cac932SSamuel Holland 
63590cac932SSamuel Holland 	/* Legacy DAC Routes */
63690cac932SSamuel Holland 	{ "AIF1 Slot 0 Left", NULL, "DACL" },
63790cac932SSamuel Holland 	{ "AIF1 Slot 0 Right", NULL, "DACR" },
63890cac932SSamuel Holland };
63990cac932SSamuel Holland 
64090cac932SSamuel Holland static int sun8i_codec_component_probe(struct snd_soc_component *component)
64190cac932SSamuel Holland {
64290cac932SSamuel Holland 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
64390cac932SSamuel Holland 	struct sun8i_codec *scodec = snd_soc_component_get_drvdata(component);
64490cac932SSamuel Holland 	int ret;
64590cac932SSamuel Holland 
64690cac932SSamuel Holland 	/* Add widgets for backward compatibility with old device trees. */
64790cac932SSamuel Holland 	if (scodec->quirks->legacy_widgets) {
64890cac932SSamuel Holland 		ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_legacy_widgets,
64990cac932SSamuel Holland 						ARRAY_SIZE(sun8i_codec_legacy_widgets));
65090cac932SSamuel Holland 		if (ret)
65190cac932SSamuel Holland 			return ret;
65290cac932SSamuel Holland 
65390cac932SSamuel Holland 		ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_legacy_routes,
65490cac932SSamuel Holland 					      ARRAY_SIZE(sun8i_codec_legacy_routes));
65590cac932SSamuel Holland 		if (ret)
65690cac932SSamuel Holland 			return ret;
65790cac932SSamuel Holland 	}
65890cac932SSamuel Holland 
659d8f00682SSamuel Holland 	/*
660d8f00682SSamuel Holland 	 * AIF1CLK and AIF2CLK share a pair of clock parents: PLL_AUDIO ("mod")
661d8f00682SSamuel Holland 	 * and MCLK (from the CPU DAI connected to AIF1). MCLK's parent is also
662d8f00682SSamuel Holland 	 * PLL_AUDIO, so using it adds no additional flexibility. Use PLL_AUDIO
663d8f00682SSamuel Holland 	 * directly to simplify the clock tree.
664d8f00682SSamuel Holland 	 */
665d8f00682SSamuel Holland 	regmap_update_bits(scodec->regmap, SUN8I_SYSCLK_CTL,
666d8f00682SSamuel Holland 			   SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK |
667d8f00682SSamuel Holland 			   SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK,
668d8f00682SSamuel Holland 			   SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL |
669d8f00682SSamuel Holland 			   SUN8I_SYSCLK_CTL_AIF2CLK_SRC_PLL);
670d8f00682SSamuel Holland 
671d8f00682SSamuel Holland 	/* Use AIF1CLK as the SYSCLK parent since AIF1 is used most often. */
672d8f00682SSamuel Holland 	regmap_update_bits(scodec->regmap, SUN8I_SYSCLK_CTL,
673d8f00682SSamuel Holland 			   BIT(SUN8I_SYSCLK_CTL_SYSCLK_SRC),
674d8f00682SSamuel Holland 			   SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK);
675d8f00682SSamuel Holland 
67690cac932SSamuel Holland 	return 0;
67790cac932SSamuel Holland }
67890cac932SSamuel Holland 
6797ec9b872SKuninori Morimoto static const struct snd_soc_component_driver sun8i_soc_component = {
68036c68493SMylène Josserand 	.dapm_widgets		= sun8i_codec_dapm_widgets,
68136c68493SMylène Josserand 	.num_dapm_widgets	= ARRAY_SIZE(sun8i_codec_dapm_widgets),
68236c68493SMylène Josserand 	.dapm_routes		= sun8i_codec_dapm_routes,
68336c68493SMylène Josserand 	.num_dapm_routes	= ARRAY_SIZE(sun8i_codec_dapm_routes),
684a2f6d303SSamuel Holland 	.probe			= sun8i_codec_component_probe,
6857ec9b872SKuninori Morimoto 	.idle_bias_on		= 1,
6867ec9b872SKuninori Morimoto 	.use_pmdown_time	= 1,
6877ec9b872SKuninori Morimoto 	.endianness		= 1,
6887ec9b872SKuninori Morimoto 	.non_legacy_dai_naming	= 1,
68936c68493SMylène Josserand };
69036c68493SMylène Josserand 
69136c68493SMylène Josserand static const struct regmap_config sun8i_codec_regmap_config = {
69236c68493SMylène Josserand 	.reg_bits	= 32,
69336c68493SMylène Josserand 	.reg_stride	= 4,
69436c68493SMylène Josserand 	.val_bits	= 32,
69536c68493SMylène Josserand 	.max_register	= SUN8I_DAC_MXR_SRC,
69636c68493SMylène Josserand 
69736c68493SMylène Josserand 	.cache_type	= REGCACHE_FLAT,
69836c68493SMylène Josserand };
69936c68493SMylène Josserand 
70036c68493SMylène Josserand static int sun8i_codec_probe(struct platform_device *pdev)
70136c68493SMylène Josserand {
70236c68493SMylène Josserand 	struct sun8i_codec *scodec;
70336c68493SMylène Josserand 	void __iomem *base;
70436c68493SMylène Josserand 	int ret;
70536c68493SMylène Josserand 
70636c68493SMylène Josserand 	scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
70736c68493SMylène Josserand 	if (!scodec)
70836c68493SMylène Josserand 		return -ENOMEM;
70936c68493SMylène Josserand 
71036c68493SMylène Josserand 	scodec->clk_module = devm_clk_get(&pdev->dev, "mod");
71136c68493SMylène Josserand 	if (IS_ERR(scodec->clk_module)) {
71236c68493SMylène Josserand 		dev_err(&pdev->dev, "Failed to get the module clock\n");
71336c68493SMylène Josserand 		return PTR_ERR(scodec->clk_module);
71436c68493SMylène Josserand 	}
71536c68493SMylène Josserand 
716790b3657SYueHaibing 	base = devm_platform_ioremap_resource(pdev, 0);
71736c68493SMylène Josserand 	if (IS_ERR(base)) {
71836c68493SMylène Josserand 		dev_err(&pdev->dev, "Failed to map the registers\n");
71936c68493SMylène Josserand 		return PTR_ERR(base);
72036c68493SMylène Josserand 	}
72136c68493SMylène Josserand 
722efb736fbSSamuel Holland 	scodec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "bus", base,
72336c68493SMylène Josserand 						   &sun8i_codec_regmap_config);
72436c68493SMylène Josserand 	if (IS_ERR(scodec->regmap)) {
72536c68493SMylène Josserand 		dev_err(&pdev->dev, "Failed to create our regmap\n");
72636c68493SMylène Josserand 		return PTR_ERR(scodec->regmap);
72736c68493SMylène Josserand 	}
72836c68493SMylène Josserand 
72990cac932SSamuel Holland 	scodec->quirks = of_device_get_match_data(&pdev->dev);
73090cac932SSamuel Holland 
73136c68493SMylène Josserand 	platform_set_drvdata(pdev, scodec);
73236c68493SMylène Josserand 
73336c68493SMylène Josserand 	pm_runtime_enable(&pdev->dev);
73436c68493SMylène Josserand 	if (!pm_runtime_enabled(&pdev->dev)) {
73536c68493SMylène Josserand 		ret = sun8i_codec_runtime_resume(&pdev->dev);
73636c68493SMylène Josserand 		if (ret)
73736c68493SMylène Josserand 			goto err_pm_disable;
73836c68493SMylène Josserand 	}
73936c68493SMylène Josserand 
7407ec9b872SKuninori Morimoto 	ret = devm_snd_soc_register_component(&pdev->dev, &sun8i_soc_component,
7417826b8d1SSamuel Holland 					      sun8i_codec_dais,
7427826b8d1SSamuel Holland 					      ARRAY_SIZE(sun8i_codec_dais));
74336c68493SMylène Josserand 	if (ret) {
74436c68493SMylène Josserand 		dev_err(&pdev->dev, "Failed to register codec\n");
74536c68493SMylène Josserand 		goto err_suspend;
74636c68493SMylène Josserand 	}
74736c68493SMylène Josserand 
74836c68493SMylène Josserand 	return ret;
74936c68493SMylène Josserand 
75036c68493SMylène Josserand err_suspend:
75136c68493SMylène Josserand 	if (!pm_runtime_status_suspended(&pdev->dev))
75236c68493SMylène Josserand 		sun8i_codec_runtime_suspend(&pdev->dev);
75336c68493SMylène Josserand 
75436c68493SMylène Josserand err_pm_disable:
75536c68493SMylène Josserand 	pm_runtime_disable(&pdev->dev);
75636c68493SMylène Josserand 
75736c68493SMylène Josserand 	return ret;
75836c68493SMylène Josserand }
75936c68493SMylène Josserand 
76036c68493SMylène Josserand static int sun8i_codec_remove(struct platform_device *pdev)
76136c68493SMylène Josserand {
76236c68493SMylène Josserand 	pm_runtime_disable(&pdev->dev);
76336c68493SMylène Josserand 	if (!pm_runtime_status_suspended(&pdev->dev))
76436c68493SMylène Josserand 		sun8i_codec_runtime_suspend(&pdev->dev);
76536c68493SMylène Josserand 
76636c68493SMylène Josserand 	return 0;
76736c68493SMylène Josserand }
76836c68493SMylène Josserand 
76990cac932SSamuel Holland static const struct sun8i_codec_quirks sun8i_a33_quirks = {
77090cac932SSamuel Holland 	.legacy_widgets	= true,
7717518805fSSamuel Holland 	.lrck_inversion	= true,
77290cac932SSamuel Holland };
77390cac932SSamuel Holland 
77490cac932SSamuel Holland static const struct sun8i_codec_quirks sun50i_a64_quirks = {
77590cac932SSamuel Holland };
77690cac932SSamuel Holland 
77736c68493SMylène Josserand static const struct of_device_id sun8i_codec_of_match[] = {
77890cac932SSamuel Holland 	{ .compatible = "allwinner,sun8i-a33-codec", .data = &sun8i_a33_quirks },
77990cac932SSamuel Holland 	{ .compatible = "allwinner,sun50i-a64-codec", .data = &sun50i_a64_quirks },
78036c68493SMylène Josserand 	{}
78136c68493SMylène Josserand };
78236c68493SMylène Josserand MODULE_DEVICE_TABLE(of, sun8i_codec_of_match);
78336c68493SMylène Josserand 
78436c68493SMylène Josserand static const struct dev_pm_ops sun8i_codec_pm_ops = {
78536c68493SMylène Josserand 	SET_RUNTIME_PM_OPS(sun8i_codec_runtime_suspend,
78636c68493SMylène Josserand 			   sun8i_codec_runtime_resume, NULL)
78736c68493SMylène Josserand };
78836c68493SMylène Josserand 
78936c68493SMylène Josserand static struct platform_driver sun8i_codec_driver = {
79036c68493SMylène Josserand 	.driver = {
79136c68493SMylène Josserand 		.name = "sun8i-codec",
79236c68493SMylène Josserand 		.of_match_table = sun8i_codec_of_match,
79336c68493SMylène Josserand 		.pm = &sun8i_codec_pm_ops,
79436c68493SMylène Josserand 	},
79536c68493SMylène Josserand 	.probe = sun8i_codec_probe,
79636c68493SMylène Josserand 	.remove = sun8i_codec_remove,
79736c68493SMylène Josserand };
79836c68493SMylène Josserand module_platform_driver(sun8i_codec_driver);
79936c68493SMylène Josserand 
80036c68493SMylène Josserand MODULE_DESCRIPTION("Allwinner A33 (sun8i) codec driver");
80136c68493SMylène Josserand MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>");
80236c68493SMylène Josserand MODULE_LICENSE("GPL");
80336c68493SMylène Josserand MODULE_ALIAS("platform:sun8i-codec");
804