xref: /openbmc/linux/sound/soc/sunxi/sun8i-codec.c (revision a886990c)
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
4736c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV		14
4836c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV		13
4936c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV		9
5036c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV		6
5136c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ		4
5236c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16		(1 << 4)
5336c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT		2
54eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_ADCDAT_CTRL				0x044
55fa5c0ca1SSamuel Holland #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA		15
56fa5c0ca1SSamuel Holland #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA		14
5718ebd62cSSamuel Holland #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_SRC		10
5818ebd62cSSamuel Holland #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_SRC		8
5936c68493SMylène Josserand #define SUN8I_AIF1_DACDAT_CTRL				0x048
6036c68493SMylène Josserand #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA		15
6136c68493SMylène Josserand #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA		14
6218ebd62cSSamuel Holland #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_SRC		10
6318ebd62cSSamuel Holland #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_SRC		8
64eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC				0x04c
650ba95493SSamuel Holland #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L	15
660ba95493SSamuel Holland #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL	14
670ba95493SSamuel Holland #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL		13
680ba95493SSamuel Holland #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR	12
69eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R	11
70eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR	10
71eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR		9
72eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL	8
73eda85d1fSMylene JOSSERAND #define SUN8I_ADC_DIG_CTRL				0x100
7430aff91eSSamuel Holland #define SUN8I_ADC_DIG_CTRL_ENAD				15
75eda85d1fSMylene JOSSERAND #define SUN8I_ADC_DIG_CTRL_ADOUT_DTS			2
76eda85d1fSMylene JOSSERAND #define SUN8I_ADC_DIG_CTRL_ADOUT_DLY			1
7736c68493SMylène Josserand #define SUN8I_DAC_DIG_CTRL				0x120
7836c68493SMylène Josserand #define SUN8I_DAC_DIG_CTRL_ENDA				15
7936c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC				0x130
8036c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L		15
8136c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L		14
8236c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL		13
8336c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL		12
8436c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R		11
8536c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R		10
8636c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR		9
8736c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR		8
8836c68493SMylène Josserand 
89d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK	GENMASK(9, 8)
90d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK	GENMASK(5, 4)
9136c68493SMylène Josserand #define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK		GENMASK(15, 12)
9236c68493SMylène Josserand #define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK		GENMASK(11, 8)
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 
9890cac932SSamuel Holland struct sun8i_codec_quirks {
9990cac932SSamuel Holland 	bool legacy_widgets	: 1;
1007518805fSSamuel Holland 	bool lrck_inversion	: 1;
10190cac932SSamuel Holland };
10290cac932SSamuel Holland 
10336c68493SMylène Josserand struct sun8i_codec {
10436c68493SMylène Josserand 	struct regmap			*regmap;
10536c68493SMylène Josserand 	struct clk			*clk_module;
10690cac932SSamuel Holland 	const struct sun8i_codec_quirks	*quirks;
10736c68493SMylène Josserand };
10836c68493SMylène Josserand 
10936c68493SMylène Josserand static int sun8i_codec_runtime_resume(struct device *dev)
11036c68493SMylène Josserand {
11136c68493SMylène Josserand 	struct sun8i_codec *scodec = dev_get_drvdata(dev);
11236c68493SMylène Josserand 	int ret;
11336c68493SMylène Josserand 
11436c68493SMylène Josserand 	regcache_cache_only(scodec->regmap, false);
11536c68493SMylène Josserand 
11636c68493SMylène Josserand 	ret = regcache_sync(scodec->regmap);
11736c68493SMylène Josserand 	if (ret) {
11836c68493SMylène Josserand 		dev_err(dev, "Failed to sync regmap cache\n");
1196b3bb3c8SSamuel Holland 		return ret;
12036c68493SMylène Josserand 	}
12136c68493SMylène Josserand 
12236c68493SMylène Josserand 	return 0;
12336c68493SMylène Josserand }
12436c68493SMylène Josserand 
12536c68493SMylène Josserand static int sun8i_codec_runtime_suspend(struct device *dev)
12636c68493SMylène Josserand {
12736c68493SMylène Josserand 	struct sun8i_codec *scodec = dev_get_drvdata(dev);
12836c68493SMylène Josserand 
12936c68493SMylène Josserand 	regcache_cache_only(scodec->regmap, true);
13036c68493SMylène Josserand 	regcache_mark_dirty(scodec->regmap);
13136c68493SMylène Josserand 
13236c68493SMylène Josserand 	return 0;
13336c68493SMylène Josserand }
13436c68493SMylène Josserand 
13536c68493SMylène Josserand static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
13636c68493SMylène Josserand {
13736c68493SMylène Josserand 	unsigned int rate = params_rate(params);
13836c68493SMylène Josserand 
13936c68493SMylène Josserand 	switch (rate) {
14036c68493SMylène Josserand 	case 8000:
14136c68493SMylène Josserand 	case 7350:
14236c68493SMylène Josserand 		return 0x0;
14336c68493SMylène Josserand 	case 11025:
14436c68493SMylène Josserand 		return 0x1;
14536c68493SMylène Josserand 	case 12000:
14636c68493SMylène Josserand 		return 0x2;
14736c68493SMylène Josserand 	case 16000:
14836c68493SMylène Josserand 		return 0x3;
14936c68493SMylène Josserand 	case 22050:
15036c68493SMylène Josserand 		return 0x4;
15136c68493SMylène Josserand 	case 24000:
15236c68493SMylène Josserand 		return 0x5;
15336c68493SMylène Josserand 	case 32000:
15436c68493SMylène Josserand 		return 0x6;
15536c68493SMylène Josserand 	case 44100:
15636c68493SMylène Josserand 		return 0x7;
15736c68493SMylène Josserand 	case 48000:
15836c68493SMylène Josserand 		return 0x8;
15936c68493SMylène Josserand 	case 96000:
16036c68493SMylène Josserand 		return 0x9;
16136c68493SMylène Josserand 	case 192000:
16236c68493SMylène Josserand 		return 0xa;
16336c68493SMylène Josserand 	default:
16436c68493SMylène Josserand 		return -EINVAL;
16536c68493SMylène Josserand 	}
16636c68493SMylène Josserand }
16736c68493SMylène Josserand 
16836c68493SMylène Josserand static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
16936c68493SMylène Josserand {
170a886990cSSamuel Holland 	struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
17136c68493SMylène Josserand 	u32 value;
17236c68493SMylène Josserand 
17336c68493SMylène Josserand 	/* clock masters */
17436c68493SMylène Josserand 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
175560bfe77SMaxime Ripard 	case SND_SOC_DAIFMT_CBS_CFS: /* Codec slave, DAI master */
176560bfe77SMaxime Ripard 		value = 0x1;
17736c68493SMylène Josserand 		break;
178560bfe77SMaxime Ripard 	case SND_SOC_DAIFMT_CBM_CFM: /* Codec Master, DAI slave */
179560bfe77SMaxime Ripard 		value = 0x0;
18036c68493SMylène Josserand 		break;
18136c68493SMylène Josserand 	default:
18236c68493SMylène Josserand 		return -EINVAL;
18336c68493SMylène Josserand 	}
18436c68493SMylène Josserand 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
18536c68493SMylène Josserand 			   BIT(SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD),
18636c68493SMylène Josserand 			   value << SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD);
18736c68493SMylène Josserand 
18836c68493SMylène Josserand 	/* clock inversion */
18936c68493SMylène Josserand 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
19036c68493SMylène Josserand 	case SND_SOC_DAIFMT_NB_NF: /* Normal */
19136c68493SMylène Josserand 		value = 0x0;
19236c68493SMylène Josserand 		break;
19336c68493SMylène Josserand 	case SND_SOC_DAIFMT_IB_IF: /* Inversion */
19436c68493SMylène Josserand 		value = 0x1;
19536c68493SMylène Josserand 		break;
19636c68493SMylène Josserand 	default:
19736c68493SMylène Josserand 		return -EINVAL;
19836c68493SMylène Josserand 	}
19936c68493SMylène Josserand 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
20036c68493SMylène Josserand 			   BIT(SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV),
20136c68493SMylène Josserand 			   value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV);
202e7b8a6d3SMaxime Ripard 
203e7b8a6d3SMaxime Ripard 	/*
2047518805fSSamuel Holland 	 * It appears that the DAI and the codec in the A33 SoC don't
2057518805fSSamuel Holland 	 * share the same polarity for the LRCK signal when they mean
2067518805fSSamuel Holland 	 * 'normal' and 'inverted' in the datasheet.
207e7b8a6d3SMaxime Ripard 	 *
208e7b8a6d3SMaxime Ripard 	 * Since the DAI here is our regular i2s driver that have been
209e7b8a6d3SMaxime Ripard 	 * tested with way more codecs than just this one, it means
210e7b8a6d3SMaxime Ripard 	 * that the codec probably gets it backward, and we have to
211e7b8a6d3SMaxime Ripard 	 * invert the value here.
212e7b8a6d3SMaxime Ripard 	 */
2137518805fSSamuel Holland 	value ^= scodec->quirks->lrck_inversion;
21436c68493SMylène Josserand 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
21536c68493SMylène Josserand 			   BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV),
2167518805fSSamuel Holland 			   value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV);
21736c68493SMylène Josserand 
21836c68493SMylène Josserand 	/* DAI format */
21936c68493SMylène Josserand 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
22036c68493SMylène Josserand 	case SND_SOC_DAIFMT_I2S:
22136c68493SMylène Josserand 		value = 0x0;
22236c68493SMylène Josserand 		break;
22336c68493SMylène Josserand 	case SND_SOC_DAIFMT_LEFT_J:
22436c68493SMylène Josserand 		value = 0x1;
22536c68493SMylène Josserand 		break;
22636c68493SMylène Josserand 	case SND_SOC_DAIFMT_RIGHT_J:
22736c68493SMylène Josserand 		value = 0x2;
22836c68493SMylène Josserand 		break;
22936c68493SMylène Josserand 	case SND_SOC_DAIFMT_DSP_A:
23036c68493SMylène Josserand 	case SND_SOC_DAIFMT_DSP_B:
23136c68493SMylène Josserand 		value = 0x3;
23236c68493SMylène Josserand 		break;
23336c68493SMylène Josserand 	default:
23436c68493SMylène Josserand 		return -EINVAL;
23536c68493SMylène Josserand 	}
23636c68493SMylène Josserand 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
23796781fd9SSamuel Holland 			   SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK,
23836c68493SMylène Josserand 			   value << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT);
23936c68493SMylène Josserand 
24036c68493SMylène Josserand 	return 0;
24136c68493SMylène Josserand }
24236c68493SMylène Josserand 
243316b7758SMaxime Ripard struct sun8i_codec_clk_div {
244316b7758SMaxime Ripard 	u8	div;
245316b7758SMaxime Ripard 	u8	val;
246316b7758SMaxime Ripard };
247316b7758SMaxime Ripard 
248316b7758SMaxime Ripard static const struct sun8i_codec_clk_div sun8i_codec_bclk_div[] = {
249316b7758SMaxime Ripard 	{ .div = 1,	.val = 0 },
250316b7758SMaxime Ripard 	{ .div = 2,	.val = 1 },
251316b7758SMaxime Ripard 	{ .div = 4,	.val = 2 },
252316b7758SMaxime Ripard 	{ .div = 6,	.val = 3 },
253316b7758SMaxime Ripard 	{ .div = 8,	.val = 4 },
254316b7758SMaxime Ripard 	{ .div = 12,	.val = 5 },
255316b7758SMaxime Ripard 	{ .div = 16,	.val = 6 },
256316b7758SMaxime Ripard 	{ .div = 24,	.val = 7 },
257316b7758SMaxime Ripard 	{ .div = 32,	.val = 8 },
258316b7758SMaxime Ripard 	{ .div = 48,	.val = 9 },
259316b7758SMaxime Ripard 	{ .div = 64,	.val = 10 },
260316b7758SMaxime Ripard 	{ .div = 96,	.val = 11 },
261316b7758SMaxime Ripard 	{ .div = 128,	.val = 12 },
262316b7758SMaxime Ripard 	{ .div = 192,	.val = 13 },
263316b7758SMaxime Ripard };
264316b7758SMaxime Ripard 
265316b7758SMaxime Ripard static u8 sun8i_codec_get_bclk_div(struct sun8i_codec *scodec,
266316b7758SMaxime Ripard 				   unsigned int rate,
267316b7758SMaxime Ripard 				   unsigned int word_size)
268316b7758SMaxime Ripard {
269316b7758SMaxime Ripard 	unsigned long clk_rate = clk_get_rate(scodec->clk_module);
270316b7758SMaxime Ripard 	unsigned int div = clk_rate / rate / word_size / 2;
271316b7758SMaxime Ripard 	unsigned int best_val = 0, best_diff = ~0;
272316b7758SMaxime Ripard 	int i;
273316b7758SMaxime Ripard 
274316b7758SMaxime Ripard 	for (i = 0; i < ARRAY_SIZE(sun8i_codec_bclk_div); i++) {
275316b7758SMaxime Ripard 		const struct sun8i_codec_clk_div *bdiv = &sun8i_codec_bclk_div[i];
276316b7758SMaxime Ripard 		unsigned int diff = abs(bdiv->div - div);
277316b7758SMaxime Ripard 
278316b7758SMaxime Ripard 		if (diff < best_diff) {
279316b7758SMaxime Ripard 			best_diff = diff;
280316b7758SMaxime Ripard 			best_val = bdiv->val;
281316b7758SMaxime Ripard 		}
282316b7758SMaxime Ripard 	}
283316b7758SMaxime Ripard 
284316b7758SMaxime Ripard 	return best_val;
285316b7758SMaxime Ripard }
286316b7758SMaxime Ripard 
28713c3bf17SVasily Khoruzhick static int sun8i_codec_get_lrck_div(unsigned int channels,
28813c3bf17SVasily Khoruzhick 				    unsigned int word_size)
28913c3bf17SVasily Khoruzhick {
29013c3bf17SVasily Khoruzhick 	unsigned int div = word_size * channels;
29113c3bf17SVasily Khoruzhick 
29213c3bf17SVasily Khoruzhick 	if (div < 16 || div > 256)
29313c3bf17SVasily Khoruzhick 		return -EINVAL;
29413c3bf17SVasily Khoruzhick 
29513c3bf17SVasily Khoruzhick 	return ilog2(div) - 4;
29613c3bf17SVasily Khoruzhick }
29713c3bf17SVasily Khoruzhick 
29836c68493SMylène Josserand static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
29936c68493SMylène Josserand 				 struct snd_pcm_hw_params *params,
30036c68493SMylène Josserand 				 struct snd_soc_dai *dai)
30136c68493SMylène Josserand {
302a886990cSSamuel Holland 	struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
30313c3bf17SVasily Khoruzhick 	int sample_rate, lrck_div;
304316b7758SMaxime Ripard 	u8 bclk_div;
30536c68493SMylène Josserand 
30636c68493SMylène Josserand 	/*
30736c68493SMylène Josserand 	 * The CPU DAI handles only a sample of 16 bits. Configure the
30836c68493SMylène Josserand 	 * codec to handle this type of sample resolution.
30936c68493SMylène Josserand 	 */
31036c68493SMylène Josserand 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
31136c68493SMylène Josserand 			   SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK,
31236c68493SMylène Josserand 			   SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16);
31336c68493SMylène Josserand 
314316b7758SMaxime Ripard 	bclk_div = sun8i_codec_get_bclk_div(scodec, params_rate(params), 16);
315316b7758SMaxime Ripard 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
316316b7758SMaxime Ripard 			   SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
317316b7758SMaxime Ripard 			   bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
318316b7758SMaxime Ripard 
31913c3bf17SVasily Khoruzhick 	lrck_div = sun8i_codec_get_lrck_div(params_channels(params),
32013c3bf17SVasily Khoruzhick 					    params_physical_width(params));
32113c3bf17SVasily Khoruzhick 	if (lrck_div < 0)
32213c3bf17SVasily Khoruzhick 		return lrck_div;
32313c3bf17SVasily Khoruzhick 
32436c68493SMylène Josserand 	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
32536c68493SMylène Josserand 			   SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK,
32613c3bf17SVasily Khoruzhick 			   lrck_div << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);
32736c68493SMylène Josserand 
32836c68493SMylène Josserand 	sample_rate = sun8i_codec_get_hw_rate(params);
32936c68493SMylène Josserand 	if (sample_rate < 0)
33036c68493SMylène Josserand 		return sample_rate;
33136c68493SMylène Josserand 
33236c68493SMylène Josserand 	regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
33336c68493SMylène Josserand 			   SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
33436c68493SMylène Josserand 			   sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);
33536c68493SMylène Josserand 
33636c68493SMylène Josserand 	return 0;
33736c68493SMylène Josserand }
33836c68493SMylène Josserand 
33918ebd62cSSamuel Holland static const char *const sun8i_aif_stereo_mux_enum_values[] = {
34018ebd62cSSamuel Holland 	"Stereo", "Reverse Stereo", "Sum Mono", "Mix Mono"
34118ebd62cSSamuel Holland };
34218ebd62cSSamuel Holland 
34318ebd62cSSamuel Holland static SOC_ENUM_DOUBLE_DECL(sun8i_aif1_ad0_stereo_mux_enum,
34418ebd62cSSamuel Holland 			    SUN8I_AIF1_ADCDAT_CTRL,
34518ebd62cSSamuel Holland 			    SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_SRC,
34618ebd62cSSamuel Holland 			    SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_SRC,
34718ebd62cSSamuel Holland 			    sun8i_aif_stereo_mux_enum_values);
34818ebd62cSSamuel Holland 
34918ebd62cSSamuel Holland static const struct snd_kcontrol_new sun8i_aif1_ad0_stereo_mux_control =
35018ebd62cSSamuel Holland 	SOC_DAPM_ENUM("AIF1 AD0 Stereo Capture Route",
35118ebd62cSSamuel Holland 		      sun8i_aif1_ad0_stereo_mux_enum);
35218ebd62cSSamuel Holland 
353d58b7247SSamuel Holland static const struct snd_kcontrol_new sun8i_aif1_ad0_mixer_controls[] = {
354d58b7247SSamuel Holland 	SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch",
355d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC,
356d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L,
357d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0),
358d58b7247SSamuel Holland 	SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch",
359d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC,
360d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL,
361d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0),
362d58b7247SSamuel Holland 	SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch",
363d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC,
364d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL,
365d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0),
366d58b7247SSamuel Holland 	SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch",
367d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC,
368d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR,
369d58b7247SSamuel Holland 			SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0),
370d58b7247SSamuel Holland };
371d58b7247SSamuel Holland 
37218ebd62cSSamuel Holland static SOC_ENUM_DOUBLE_DECL(sun8i_aif1_da0_stereo_mux_enum,
37318ebd62cSSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL,
37418ebd62cSSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_SRC,
37518ebd62cSSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_SRC,
37618ebd62cSSamuel Holland 			    sun8i_aif_stereo_mux_enum_values);
37718ebd62cSSamuel Holland 
37818ebd62cSSamuel Holland static const struct snd_kcontrol_new sun8i_aif1_da0_stereo_mux_control =
37918ebd62cSSamuel Holland 	SOC_DAPM_ENUM("AIF1 DA0 Stereo Playback Route",
38018ebd62cSSamuel Holland 		      sun8i_aif1_da0_stereo_mux_enum);
38118ebd62cSSamuel Holland 
382ca14da6eSMylène Josserand static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = {
383ca14da6eSMylène Josserand 	SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital DAC Playback Switch",
384ca14da6eSMylène Josserand 			SUN8I_DAC_MXR_SRC,
385ca14da6eSMylène Josserand 			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L,
38636c68493SMylène Josserand 			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R, 1, 0),
387ca14da6eSMylène Josserand 	SOC_DAPM_DOUBLE("AIF1 Slot 1 Digital DAC Playback Switch",
388ca14da6eSMylène Josserand 			SUN8I_DAC_MXR_SRC,
389ca14da6eSMylène Josserand 			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L,
39036c68493SMylène Josserand 			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R, 1, 0),
391ca14da6eSMylène Josserand 	SOC_DAPM_DOUBLE("AIF2 Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC,
392ca14da6eSMylène Josserand 			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL,
39336c68493SMylène Josserand 			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR, 1, 0),
394ca14da6eSMylène Josserand 	SOC_DAPM_DOUBLE("ADC Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC,
395ca14da6eSMylène Josserand 			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL,
39636c68493SMylène Josserand 			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0),
39736c68493SMylène Josserand };
39836c68493SMylène Josserand 
39936c68493SMylène Josserand static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
400d8f00682SSamuel Holland 	/* System Clocks */
4016b3bb3c8SSamuel Holland 	SND_SOC_DAPM_CLOCK_SUPPLY("mod"),
4026b3bb3c8SSamuel Holland 
403d8f00682SSamuel Holland 	SND_SOC_DAPM_SUPPLY("AIF1CLK",
404d8f00682SSamuel Holland 			    SUN8I_SYSCLK_CTL,
405d8f00682SSamuel Holland 			    SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0),
406d8f00682SSamuel Holland 	SND_SOC_DAPM_SUPPLY("SYSCLK",
407d8f00682SSamuel Holland 			    SUN8I_SYSCLK_CTL,
408d8f00682SSamuel Holland 			    SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0),
409d8f00682SSamuel Holland 
410ed3caa3bSSamuel Holland 	/* Module Clocks */
411ed3caa3bSSamuel Holland 	SND_SOC_DAPM_SUPPLY("CLK AIF1",
412ed3caa3bSSamuel Holland 			    SUN8I_MOD_CLK_ENA,
413ed3caa3bSSamuel Holland 			    SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0),
414ed3caa3bSSamuel Holland 	SND_SOC_DAPM_SUPPLY("CLK ADC",
415ed3caa3bSSamuel Holland 			    SUN8I_MOD_CLK_ENA,
416ed3caa3bSSamuel Holland 			    SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0),
417ed3caa3bSSamuel Holland 	SND_SOC_DAPM_SUPPLY("CLK DAC",
418ed3caa3bSSamuel Holland 			    SUN8I_MOD_CLK_ENA,
419ed3caa3bSSamuel Holland 			    SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0),
420ed3caa3bSSamuel Holland 
421ed3caa3bSSamuel Holland 	/* Module Resets */
422ed3caa3bSSamuel Holland 	SND_SOC_DAPM_SUPPLY("RST AIF1",
423ed3caa3bSSamuel Holland 			    SUN8I_MOD_RST_CTL,
424ed3caa3bSSamuel Holland 			    SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0),
425ed3caa3bSSamuel Holland 	SND_SOC_DAPM_SUPPLY("RST ADC",
426ed3caa3bSSamuel Holland 			    SUN8I_MOD_RST_CTL,
427ed3caa3bSSamuel Holland 			    SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0),
428ed3caa3bSSamuel Holland 	SND_SOC_DAPM_SUPPLY("RST DAC",
429ed3caa3bSSamuel Holland 			    SUN8I_MOD_RST_CTL,
430ed3caa3bSSamuel Holland 			    SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0),
431ed3caa3bSSamuel Holland 
432d58b7247SSamuel Holland 	/* Module Supplies */
433d58b7247SSamuel Holland 	SND_SOC_DAPM_SUPPLY("ADC",
434d58b7247SSamuel Holland 			    SUN8I_ADC_DIG_CTRL,
435d58b7247SSamuel Holland 			    SUN8I_ADC_DIG_CTRL_ENAD, 0, NULL, 0),
436d58b7247SSamuel Holland 	SND_SOC_DAPM_SUPPLY("DAC",
437d58b7247SSamuel Holland 			    SUN8I_DAC_DIG_CTRL,
438d58b7247SSamuel Holland 			    SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0),
43936c68493SMylène Josserand 
44090cac932SSamuel Holland 	/* AIF "ADC" Outputs */
441fc5668f6SSamuel Holland 	SND_SOC_DAPM_AIF_OUT("AIF1 AD0L", "Capture", 0,
442eda85d1fSMylene JOSSERAND 			     SUN8I_AIF1_ADCDAT_CTRL,
443fa5c0ca1SSamuel Holland 			     SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0),
4444ab60cefSSamuel Holland 	SND_SOC_DAPM_AIF_OUT("AIF1 AD0R", "Capture", 1,
445eda85d1fSMylene JOSSERAND 			     SUN8I_AIF1_ADCDAT_CTRL,
446fa5c0ca1SSamuel Holland 			     SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0),
447eda85d1fSMylene JOSSERAND 
44818ebd62cSSamuel Holland 	/* AIF "ADC" Mono/Stereo Muxes */
44918ebd62cSSamuel Holland 	SND_SOC_DAPM_MUX("AIF1 AD0L Stereo Mux", SND_SOC_NOPM, 0, 0,
45018ebd62cSSamuel Holland 			 &sun8i_aif1_ad0_stereo_mux_control),
45118ebd62cSSamuel Holland 	SND_SOC_DAPM_MUX("AIF1 AD0R Stereo Mux", SND_SOC_NOPM, 0, 0,
45218ebd62cSSamuel Holland 			 &sun8i_aif1_ad0_stereo_mux_control),
45318ebd62cSSamuel Holland 
454d58b7247SSamuel Holland 	/* AIF "ADC" Mixers */
4557b51f3c7SSamuel Holland 	SOC_MIXER_ARRAY("AIF1 AD0L Mixer", SND_SOC_NOPM, 0, 0,
456d58b7247SSamuel Holland 			sun8i_aif1_ad0_mixer_controls),
4577b51f3c7SSamuel Holland 	SOC_MIXER_ARRAY("AIF1 AD0R Mixer", SND_SOC_NOPM, 0, 0,
458d58b7247SSamuel Holland 			sun8i_aif1_ad0_mixer_controls),
459d58b7247SSamuel Holland 
46018ebd62cSSamuel Holland 	/* AIF "DAC" Mono/Stereo Muxes */
46118ebd62cSSamuel Holland 	SND_SOC_DAPM_MUX("AIF1 DA0L Stereo Mux", SND_SOC_NOPM, 0, 0,
46218ebd62cSSamuel Holland 			 &sun8i_aif1_da0_stereo_mux_control),
46318ebd62cSSamuel Holland 	SND_SOC_DAPM_MUX("AIF1 DA0R Stereo Mux", SND_SOC_NOPM, 0, 0,
46418ebd62cSSamuel Holland 			 &sun8i_aif1_da0_stereo_mux_control),
46518ebd62cSSamuel Holland 
466d58b7247SSamuel Holland 	/* AIF "DAC" Inputs */
467d58b7247SSamuel Holland 	SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0,
468d58b7247SSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL,
469d58b7247SSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0),
4704ab60cefSSamuel Holland 	SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 1,
471d58b7247SSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL,
472d58b7247SSamuel Holland 			    SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
473d58b7247SSamuel Holland 
47490cac932SSamuel Holland 	/* ADC Inputs (connected to analog codec DAPM context) */
47590cac932SSamuel Holland 	SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0),
47690cac932SSamuel Holland 	SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
47790cac932SSamuel Holland 
47890cac932SSamuel Holland 	/* DAC Outputs (connected to analog codec DAPM context) */
47990cac932SSamuel Holland 	SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
48090cac932SSamuel Holland 	SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
48190cac932SSamuel Holland 
482d58b7247SSamuel Holland 	/* DAC Mixers */
4837b51f3c7SSamuel Holland 	SOC_MIXER_ARRAY("DACL Mixer", SND_SOC_NOPM, 0, 0,
484fa22ca4fSMylène Josserand 			sun8i_dac_mixer_controls),
4857b51f3c7SSamuel Holland 	SOC_MIXER_ARRAY("DACR Mixer", SND_SOC_NOPM, 0, 0,
486fa22ca4fSMylène Josserand 			sun8i_dac_mixer_controls),
48736c68493SMylène Josserand };
48836c68493SMylène Josserand 
48936c68493SMylène Josserand static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
49036c68493SMylène Josserand 	/* Clock Routes */
491d8f00682SSamuel Holland 	{ "AIF1CLK", NULL, "mod" },
4926b3bb3c8SSamuel Holland 
493d8f00682SSamuel Holland 	{ "SYSCLK", NULL, "AIF1CLK" },
49490cac932SSamuel Holland 
495ed3caa3bSSamuel Holland 	{ "CLK AIF1", NULL, "AIF1CLK" },
496ed3caa3bSSamuel Holland 	{ "CLK AIF1", NULL, "SYSCLK" },
497ed3caa3bSSamuel Holland 	{ "RST AIF1", NULL, "CLK AIF1" },
498ed3caa3bSSamuel Holland 	{ "AIF1 AD0L", NULL, "RST AIF1" },
499ed3caa3bSSamuel Holland 	{ "AIF1 AD0R", NULL, "RST AIF1" },
500ed3caa3bSSamuel Holland 	{ "AIF1 DA0L", NULL, "RST AIF1" },
501ed3caa3bSSamuel Holland 	{ "AIF1 DA0R", NULL, "RST AIF1" },
50236c68493SMylène Josserand 
503ed3caa3bSSamuel Holland 	{ "CLK ADC", NULL, "SYSCLK" },
504ed3caa3bSSamuel Holland 	{ "RST ADC", NULL, "CLK ADC" },
505ed3caa3bSSamuel Holland 	{ "ADC", NULL, "RST ADC" },
50690cac932SSamuel Holland 	{ "ADCL", NULL, "ADC" },
50790cac932SSamuel Holland 	{ "ADCR", NULL, "ADC" },
508eda85d1fSMylene JOSSERAND 
509ed3caa3bSSamuel Holland 	{ "CLK DAC", NULL, "SYSCLK" },
510ed3caa3bSSamuel Holland 	{ "RST DAC", NULL, "CLK DAC" },
511ed3caa3bSSamuel Holland 	{ "DAC", NULL, "RST DAC" },
512ed3caa3bSSamuel Holland 	{ "DACL", NULL, "DAC" },
513ed3caa3bSSamuel Holland 	{ "DACR", NULL, "DAC" },
514ed3caa3bSSamuel Holland 
515d58b7247SSamuel Holland 	/* AIF "ADC" Output Routes */
51618ebd62cSSamuel Holland 	{ "AIF1 AD0L", NULL, "AIF1 AD0L Stereo Mux" },
51718ebd62cSSamuel Holland 	{ "AIF1 AD0R", NULL, "AIF1 AD0R Stereo Mux" },
51818ebd62cSSamuel Holland 
51918ebd62cSSamuel Holland 	/* AIF "ADC" Mono/Stereo Mux Routes */
52018ebd62cSSamuel Holland 	{ "AIF1 AD0L Stereo Mux", "Stereo", "AIF1 AD0L Mixer" },
52118ebd62cSSamuel Holland 	{ "AIF1 AD0L Stereo Mux", "Reverse Stereo", "AIF1 AD0R Mixer" },
52218ebd62cSSamuel Holland 	{ "AIF1 AD0L Stereo Mux", "Sum Mono", "AIF1 AD0L Mixer" },
52318ebd62cSSamuel Holland 	{ "AIF1 AD0L Stereo Mux", "Sum Mono", "AIF1 AD0R Mixer" },
52418ebd62cSSamuel Holland 	{ "AIF1 AD0L Stereo Mux", "Mix Mono", "AIF1 AD0L Mixer" },
52518ebd62cSSamuel Holland 	{ "AIF1 AD0L Stereo Mux", "Mix Mono", "AIF1 AD0R Mixer" },
52618ebd62cSSamuel Holland 
52718ebd62cSSamuel Holland 	{ "AIF1 AD0R Stereo Mux", "Stereo", "AIF1 AD0R Mixer" },
52818ebd62cSSamuel Holland 	{ "AIF1 AD0R Stereo Mux", "Reverse Stereo", "AIF1 AD0L Mixer" },
52918ebd62cSSamuel Holland 	{ "AIF1 AD0R Stereo Mux", "Sum Mono", "AIF1 AD0L Mixer" },
53018ebd62cSSamuel Holland 	{ "AIF1 AD0R Stereo Mux", "Sum Mono", "AIF1 AD0R Mixer" },
53118ebd62cSSamuel Holland 	{ "AIF1 AD0R Stereo Mux", "Mix Mono", "AIF1 AD0L Mixer" },
53218ebd62cSSamuel Holland 	{ "AIF1 AD0R Stereo Mux", "Mix Mono", "AIF1 AD0R Mixer" },
533d58b7247SSamuel Holland 
534d58b7247SSamuel Holland 	/* AIF "ADC" Mixer Routes */
53518ebd62cSSamuel Holland 	{ "AIF1 AD0L Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L Stereo Mux" },
5367b51f3c7SSamuel Holland 	{ "AIF1 AD0L Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" },
537d58b7247SSamuel Holland 
53818ebd62cSSamuel Holland 	{ "AIF1 AD0R Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R Stereo Mux" },
5397b51f3c7SSamuel Holland 	{ "AIF1 AD0R Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" },
540d58b7247SSamuel Holland 
54118ebd62cSSamuel Holland 	/* AIF "DAC" Mono/Stereo Mux Routes */
54218ebd62cSSamuel Holland 	{ "AIF1 DA0L Stereo Mux", "Stereo", "AIF1 DA0L" },
54318ebd62cSSamuel Holland 	{ "AIF1 DA0L Stereo Mux", "Reverse Stereo", "AIF1 DA0R" },
54418ebd62cSSamuel Holland 	{ "AIF1 DA0L Stereo Mux", "Sum Mono", "AIF1 DA0L" },
54518ebd62cSSamuel Holland 	{ "AIF1 DA0L Stereo Mux", "Sum Mono", "AIF1 DA0R" },
54618ebd62cSSamuel Holland 	{ "AIF1 DA0L Stereo Mux", "Mix Mono", "AIF1 DA0L" },
54718ebd62cSSamuel Holland 	{ "AIF1 DA0L Stereo Mux", "Mix Mono", "AIF1 DA0R" },
54818ebd62cSSamuel Holland 
54918ebd62cSSamuel Holland 	{ "AIF1 DA0R Stereo Mux", "Stereo", "AIF1 DA0R" },
55018ebd62cSSamuel Holland 	{ "AIF1 DA0R Stereo Mux", "Reverse Stereo", "AIF1 DA0L" },
55118ebd62cSSamuel Holland 	{ "AIF1 DA0R Stereo Mux", "Sum Mono", "AIF1 DA0L" },
55218ebd62cSSamuel Holland 	{ "AIF1 DA0R Stereo Mux", "Sum Mono", "AIF1 DA0R" },
55318ebd62cSSamuel Holland 	{ "AIF1 DA0R Stereo Mux", "Mix Mono", "AIF1 DA0L" },
55418ebd62cSSamuel Holland 	{ "AIF1 DA0R Stereo Mux", "Mix Mono", "AIF1 DA0R" },
55518ebd62cSSamuel Holland 
556d58b7247SSamuel Holland 	/* DAC Output Routes */
5577b51f3c7SSamuel Holland 	{ "DACL", NULL, "DACL Mixer" },
5587b51f3c7SSamuel Holland 	{ "DACR", NULL, "DACR Mixer" },
55936c68493SMylène Josserand 
56036c68493SMylène Josserand 	/* DAC Mixer Routes */
56118ebd62cSSamuel Holland 	{ "DACL Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L Stereo Mux" },
5627b51f3c7SSamuel Holland 	{ "DACL Mixer", "ADC Digital DAC Playback Switch", "ADCL" },
563e47d2dcdSSamuel Holland 
56418ebd62cSSamuel Holland 	{ "DACR Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R Stereo Mux" },
5657b51f3c7SSamuel Holland 	{ "DACR Mixer", "ADC Digital DAC Playback Switch", "ADCR" },
56636c68493SMylène Josserand };
56736c68493SMylène Josserand 
56890cac932SSamuel Holland static const struct snd_soc_dapm_widget sun8i_codec_legacy_widgets[] = {
56990cac932SSamuel Holland 	/* Legacy ADC Inputs (connected to analog codec DAPM context) */
57090cac932SSamuel Holland 	SND_SOC_DAPM_ADC("AIF1 Slot 0 Left ADC", NULL, SND_SOC_NOPM, 0, 0),
57190cac932SSamuel Holland 	SND_SOC_DAPM_ADC("AIF1 Slot 0 Right ADC", NULL, SND_SOC_NOPM, 0, 0),
57290cac932SSamuel Holland 
57390cac932SSamuel Holland 	/* Legacy DAC Outputs (connected to analog codec DAPM context) */
57490cac932SSamuel Holland 	SND_SOC_DAPM_DAC("AIF1 Slot 0 Left", NULL, SND_SOC_NOPM, 0, 0),
57590cac932SSamuel Holland 	SND_SOC_DAPM_DAC("AIF1 Slot 0 Right", NULL, SND_SOC_NOPM, 0, 0),
57690cac932SSamuel Holland };
57790cac932SSamuel Holland 
57890cac932SSamuel Holland static const struct snd_soc_dapm_route sun8i_codec_legacy_routes[] = {
57990cac932SSamuel Holland 	/* Legacy ADC Routes */
58090cac932SSamuel Holland 	{ "ADCL", NULL, "AIF1 Slot 0 Left ADC" },
58190cac932SSamuel Holland 	{ "ADCR", NULL, "AIF1 Slot 0 Right ADC" },
58290cac932SSamuel Holland 
58390cac932SSamuel Holland 	/* Legacy DAC Routes */
58490cac932SSamuel Holland 	{ "AIF1 Slot 0 Left", NULL, "DACL" },
58590cac932SSamuel Holland 	{ "AIF1 Slot 0 Right", NULL, "DACR" },
58690cac932SSamuel Holland };
58790cac932SSamuel Holland 
58890cac932SSamuel Holland static int sun8i_codec_component_probe(struct snd_soc_component *component)
58990cac932SSamuel Holland {
59090cac932SSamuel Holland 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
59190cac932SSamuel Holland 	struct sun8i_codec *scodec = snd_soc_component_get_drvdata(component);
59290cac932SSamuel Holland 	int ret;
59390cac932SSamuel Holland 
59490cac932SSamuel Holland 	/* Add widgets for backward compatibility with old device trees. */
59590cac932SSamuel Holland 	if (scodec->quirks->legacy_widgets) {
59690cac932SSamuel Holland 		ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_legacy_widgets,
59790cac932SSamuel Holland 						ARRAY_SIZE(sun8i_codec_legacy_widgets));
59890cac932SSamuel Holland 		if (ret)
59990cac932SSamuel Holland 			return ret;
60090cac932SSamuel Holland 
60190cac932SSamuel Holland 		ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_legacy_routes,
60290cac932SSamuel Holland 					      ARRAY_SIZE(sun8i_codec_legacy_routes));
60390cac932SSamuel Holland 		if (ret)
60490cac932SSamuel Holland 			return ret;
60590cac932SSamuel Holland 	}
60690cac932SSamuel Holland 
607d8f00682SSamuel Holland 	/*
608d8f00682SSamuel Holland 	 * AIF1CLK and AIF2CLK share a pair of clock parents: PLL_AUDIO ("mod")
609d8f00682SSamuel Holland 	 * and MCLK (from the CPU DAI connected to AIF1). MCLK's parent is also
610d8f00682SSamuel Holland 	 * PLL_AUDIO, so using it adds no additional flexibility. Use PLL_AUDIO
611d8f00682SSamuel Holland 	 * directly to simplify the clock tree.
612d8f00682SSamuel Holland 	 */
613d8f00682SSamuel Holland 	regmap_update_bits(scodec->regmap, SUN8I_SYSCLK_CTL,
614d8f00682SSamuel Holland 			   SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK |
615d8f00682SSamuel Holland 			   SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK,
616d8f00682SSamuel Holland 			   SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL |
617d8f00682SSamuel Holland 			   SUN8I_SYSCLK_CTL_AIF2CLK_SRC_PLL);
618d8f00682SSamuel Holland 
619d8f00682SSamuel Holland 	/* Use AIF1CLK as the SYSCLK parent since AIF1 is used most often. */
620d8f00682SSamuel Holland 	regmap_update_bits(scodec->regmap, SUN8I_SYSCLK_CTL,
621d8f00682SSamuel Holland 			   BIT(SUN8I_SYSCLK_CTL_SYSCLK_SRC),
622d8f00682SSamuel Holland 			   SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK);
623d8f00682SSamuel Holland 
62490cac932SSamuel Holland 	return 0;
62590cac932SSamuel Holland }
62690cac932SSamuel Holland 
627fe49cd98SGustavo A. R. Silva static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
62836c68493SMylène Josserand 	.hw_params = sun8i_codec_hw_params,
62936c68493SMylène Josserand 	.set_fmt = sun8i_set_fmt,
63036c68493SMylène Josserand };
63136c68493SMylène Josserand 
63236c68493SMylène Josserand static struct snd_soc_dai_driver sun8i_codec_dai = {
63336c68493SMylène Josserand 	.name = "sun8i",
63436c68493SMylène Josserand 	/* playback capabilities */
63536c68493SMylène Josserand 	.playback = {
63636c68493SMylène Josserand 		.stream_name = "Playback",
63736c68493SMylène Josserand 		.channels_min = 1,
63836c68493SMylène Josserand 		.channels_max = 2,
63936c68493SMylène Josserand 		.rates = SNDRV_PCM_RATE_8000_192000,
64036c68493SMylène Josserand 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
64136c68493SMylène Josserand 	},
642eda85d1fSMylene JOSSERAND 	/* capture capabilities */
643eda85d1fSMylene JOSSERAND 	.capture = {
644eda85d1fSMylene JOSSERAND 		.stream_name = "Capture",
645eda85d1fSMylene JOSSERAND 		.channels_min = 1,
646eda85d1fSMylene JOSSERAND 		.channels_max = 2,
647eda85d1fSMylene JOSSERAND 		.rates = SNDRV_PCM_RATE_8000_192000,
648eda85d1fSMylene JOSSERAND 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
649eda85d1fSMylene JOSSERAND 		.sig_bits = 24,
650eda85d1fSMylene JOSSERAND 	},
65136c68493SMylène Josserand 	/* pcm operations */
65236c68493SMylène Josserand 	.ops = &sun8i_codec_dai_ops,
65336c68493SMylène Josserand };
65436c68493SMylène Josserand 
6557ec9b872SKuninori Morimoto static const struct snd_soc_component_driver sun8i_soc_component = {
65636c68493SMylène Josserand 	.dapm_widgets		= sun8i_codec_dapm_widgets,
65736c68493SMylène Josserand 	.num_dapm_widgets	= ARRAY_SIZE(sun8i_codec_dapm_widgets),
65836c68493SMylène Josserand 	.dapm_routes		= sun8i_codec_dapm_routes,
65936c68493SMylène Josserand 	.num_dapm_routes	= ARRAY_SIZE(sun8i_codec_dapm_routes),
660a2f6d303SSamuel Holland 	.probe			= sun8i_codec_component_probe,
6617ec9b872SKuninori Morimoto 	.idle_bias_on		= 1,
6627ec9b872SKuninori Morimoto 	.use_pmdown_time	= 1,
6637ec9b872SKuninori Morimoto 	.endianness		= 1,
6647ec9b872SKuninori Morimoto 	.non_legacy_dai_naming	= 1,
66536c68493SMylène Josserand };
66636c68493SMylène Josserand 
66736c68493SMylène Josserand static const struct regmap_config sun8i_codec_regmap_config = {
66836c68493SMylène Josserand 	.reg_bits	= 32,
66936c68493SMylène Josserand 	.reg_stride	= 4,
67036c68493SMylène Josserand 	.val_bits	= 32,
67136c68493SMylène Josserand 	.max_register	= SUN8I_DAC_MXR_SRC,
67236c68493SMylène Josserand 
67336c68493SMylène Josserand 	.cache_type	= REGCACHE_FLAT,
67436c68493SMylène Josserand };
67536c68493SMylène Josserand 
67636c68493SMylène Josserand static int sun8i_codec_probe(struct platform_device *pdev)
67736c68493SMylène Josserand {
67836c68493SMylène Josserand 	struct sun8i_codec *scodec;
67936c68493SMylène Josserand 	void __iomem *base;
68036c68493SMylène Josserand 	int ret;
68136c68493SMylène Josserand 
68236c68493SMylène Josserand 	scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
68336c68493SMylène Josserand 	if (!scodec)
68436c68493SMylène Josserand 		return -ENOMEM;
68536c68493SMylène Josserand 
68636c68493SMylène Josserand 	scodec->clk_module = devm_clk_get(&pdev->dev, "mod");
68736c68493SMylène Josserand 	if (IS_ERR(scodec->clk_module)) {
68836c68493SMylène Josserand 		dev_err(&pdev->dev, "Failed to get the module clock\n");
68936c68493SMylène Josserand 		return PTR_ERR(scodec->clk_module);
69036c68493SMylène Josserand 	}
69136c68493SMylène Josserand 
692790b3657SYueHaibing 	base = devm_platform_ioremap_resource(pdev, 0);
69336c68493SMylène Josserand 	if (IS_ERR(base)) {
69436c68493SMylène Josserand 		dev_err(&pdev->dev, "Failed to map the registers\n");
69536c68493SMylène Josserand 		return PTR_ERR(base);
69636c68493SMylène Josserand 	}
69736c68493SMylène Josserand 
698efb736fbSSamuel Holland 	scodec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "bus", base,
69936c68493SMylène Josserand 						   &sun8i_codec_regmap_config);
70036c68493SMylène Josserand 	if (IS_ERR(scodec->regmap)) {
70136c68493SMylène Josserand 		dev_err(&pdev->dev, "Failed to create our regmap\n");
70236c68493SMylène Josserand 		return PTR_ERR(scodec->regmap);
70336c68493SMylène Josserand 	}
70436c68493SMylène Josserand 
70590cac932SSamuel Holland 	scodec->quirks = of_device_get_match_data(&pdev->dev);
70690cac932SSamuel Holland 
70736c68493SMylène Josserand 	platform_set_drvdata(pdev, scodec);
70836c68493SMylène Josserand 
70936c68493SMylène Josserand 	pm_runtime_enable(&pdev->dev);
71036c68493SMylène Josserand 	if (!pm_runtime_enabled(&pdev->dev)) {
71136c68493SMylène Josserand 		ret = sun8i_codec_runtime_resume(&pdev->dev);
71236c68493SMylène Josserand 		if (ret)
71336c68493SMylène Josserand 			goto err_pm_disable;
71436c68493SMylène Josserand 	}
71536c68493SMylène Josserand 
7167ec9b872SKuninori Morimoto 	ret = devm_snd_soc_register_component(&pdev->dev, &sun8i_soc_component,
71736c68493SMylène Josserand 				     &sun8i_codec_dai, 1);
71836c68493SMylène Josserand 	if (ret) {
71936c68493SMylène Josserand 		dev_err(&pdev->dev, "Failed to register codec\n");
72036c68493SMylène Josserand 		goto err_suspend;
72136c68493SMylène Josserand 	}
72236c68493SMylène Josserand 
72336c68493SMylène Josserand 	return ret;
72436c68493SMylène Josserand 
72536c68493SMylène Josserand err_suspend:
72636c68493SMylène Josserand 	if (!pm_runtime_status_suspended(&pdev->dev))
72736c68493SMylène Josserand 		sun8i_codec_runtime_suspend(&pdev->dev);
72836c68493SMylène Josserand 
72936c68493SMylène Josserand err_pm_disable:
73036c68493SMylène Josserand 	pm_runtime_disable(&pdev->dev);
73136c68493SMylène Josserand 
73236c68493SMylène Josserand 	return ret;
73336c68493SMylène Josserand }
73436c68493SMylène Josserand 
73536c68493SMylène Josserand static int sun8i_codec_remove(struct platform_device *pdev)
73636c68493SMylène Josserand {
73736c68493SMylène Josserand 	pm_runtime_disable(&pdev->dev);
73836c68493SMylène Josserand 	if (!pm_runtime_status_suspended(&pdev->dev))
73936c68493SMylène Josserand 		sun8i_codec_runtime_suspend(&pdev->dev);
74036c68493SMylène Josserand 
74136c68493SMylène Josserand 	return 0;
74236c68493SMylène Josserand }
74336c68493SMylène Josserand 
74490cac932SSamuel Holland static const struct sun8i_codec_quirks sun8i_a33_quirks = {
74590cac932SSamuel Holland 	.legacy_widgets	= true,
7467518805fSSamuel Holland 	.lrck_inversion	= true,
74790cac932SSamuel Holland };
74890cac932SSamuel Holland 
74990cac932SSamuel Holland static const struct sun8i_codec_quirks sun50i_a64_quirks = {
75090cac932SSamuel Holland };
75190cac932SSamuel Holland 
75236c68493SMylène Josserand static const struct of_device_id sun8i_codec_of_match[] = {
75390cac932SSamuel Holland 	{ .compatible = "allwinner,sun8i-a33-codec", .data = &sun8i_a33_quirks },
75490cac932SSamuel Holland 	{ .compatible = "allwinner,sun50i-a64-codec", .data = &sun50i_a64_quirks },
75536c68493SMylène Josserand 	{}
75636c68493SMylène Josserand };
75736c68493SMylène Josserand MODULE_DEVICE_TABLE(of, sun8i_codec_of_match);
75836c68493SMylène Josserand 
75936c68493SMylène Josserand static const struct dev_pm_ops sun8i_codec_pm_ops = {
76036c68493SMylène Josserand 	SET_RUNTIME_PM_OPS(sun8i_codec_runtime_suspend,
76136c68493SMylène Josserand 			   sun8i_codec_runtime_resume, NULL)
76236c68493SMylène Josserand };
76336c68493SMylène Josserand 
76436c68493SMylène Josserand static struct platform_driver sun8i_codec_driver = {
76536c68493SMylène Josserand 	.driver = {
76636c68493SMylène Josserand 		.name = "sun8i-codec",
76736c68493SMylène Josserand 		.of_match_table = sun8i_codec_of_match,
76836c68493SMylène Josserand 		.pm = &sun8i_codec_pm_ops,
76936c68493SMylène Josserand 	},
77036c68493SMylène Josserand 	.probe = sun8i_codec_probe,
77136c68493SMylène Josserand 	.remove = sun8i_codec_remove,
77236c68493SMylène Josserand };
77336c68493SMylène Josserand module_platform_driver(sun8i_codec_driver);
77436c68493SMylène Josserand 
77536c68493SMylène Josserand MODULE_DESCRIPTION("Allwinner A33 (sun8i) codec driver");
77636c68493SMylène Josserand MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>");
77736c68493SMylène Josserand MODULE_LICENSE("GPL");
77836c68493SMylène Josserand MODULE_ALIAS("platform:sun8i-codec");
779