xref: /openbmc/linux/sound/soc/codecs/mc13783.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
116216333SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28b908b86SPhilippe Rétornaz /*
38b908b86SPhilippe Rétornaz  * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
48b908b86SPhilippe Rétornaz  * Copyright 2009 Sascha Hauer, s.hauer@pengutronix.de
58b908b86SPhilippe Rétornaz  * Copyright 2012 Philippe Retornaz, philippe.retornaz@epfl.ch
68b908b86SPhilippe Rétornaz  *
78b908b86SPhilippe Rétornaz  * Initial development of this code was funded by
85856d8bdSAlexander A. Klimov  * Phytec Messtechnik GmbH, https://www.phytec.de
98b908b86SPhilippe Rétornaz  */
108b908b86SPhilippe Rétornaz #include <linux/module.h>
118b908b86SPhilippe Rétornaz #include <linux/device.h>
12780aaeffSAlexander Shiyan #include <linux/of.h>
138b908b86SPhilippe Rétornaz #include <linux/mfd/mc13xxx.h>
148b908b86SPhilippe Rétornaz #include <linux/slab.h>
158b908b86SPhilippe Rétornaz #include <sound/core.h>
168b908b86SPhilippe Rétornaz #include <sound/control.h>
178b908b86SPhilippe Rétornaz #include <sound/pcm.h>
188b908b86SPhilippe Rétornaz #include <sound/soc.h>
198b908b86SPhilippe Rétornaz #include <sound/initval.h>
208b908b86SPhilippe Rétornaz #include <sound/soc-dapm.h>
212d9215c1SMark Brown #include <linux/regmap.h>
228b908b86SPhilippe Rétornaz 
238b908b86SPhilippe Rétornaz #include "mc13783.h"
248b908b86SPhilippe Rétornaz 
258b908b86SPhilippe Rétornaz #define AUDIO_RX0_ALSPEN		(1 << 5)
268b908b86SPhilippe Rétornaz #define AUDIO_RX0_ALSPSEL		(1 << 7)
278b908b86SPhilippe Rétornaz #define AUDIO_RX0_ADDCDC		(1 << 21)
288b908b86SPhilippe Rétornaz #define AUDIO_RX0_ADDSTDC		(1 << 22)
298b908b86SPhilippe Rétornaz #define AUDIO_RX0_ADDRXIN		(1 << 23)
308b908b86SPhilippe Rétornaz 
318b908b86SPhilippe Rétornaz #define AUDIO_RX1_PGARXEN		(1 << 0);
328b908b86SPhilippe Rétornaz #define AUDIO_RX1_PGASTEN		(1 << 5)
338b908b86SPhilippe Rétornaz #define AUDIO_RX1_ARXINEN		(1 << 10)
348b908b86SPhilippe Rétornaz 
358b908b86SPhilippe Rétornaz #define AUDIO_TX_AMC1REN		(1 << 5)
368b908b86SPhilippe Rétornaz #define AUDIO_TX_AMC1LEN		(1 << 7)
378b908b86SPhilippe Rétornaz #define AUDIO_TX_AMC2EN			(1 << 9)
388b908b86SPhilippe Rétornaz #define AUDIO_TX_ATXINEN		(1 << 11)
398b908b86SPhilippe Rétornaz #define AUDIO_TX_RXINREC		(1 << 13)
408b908b86SPhilippe Rétornaz 
418b908b86SPhilippe Rétornaz #define SSI_NETWORK_CDCTXRXSLOT(x)	(((x) & 0x3) << 2)
428b908b86SPhilippe Rétornaz #define SSI_NETWORK_CDCTXSECSLOT(x)	(((x) & 0x3) << 4)
438b908b86SPhilippe Rétornaz #define SSI_NETWORK_CDCRXSECSLOT(x)	(((x) & 0x3) << 6)
448b908b86SPhilippe Rétornaz #define SSI_NETWORK_CDCRXSECGAIN(x)	(((x) & 0x3) << 8)
458b908b86SPhilippe Rétornaz #define SSI_NETWORK_CDCSUMGAIN(x)	(1 << 10)
468b908b86SPhilippe Rétornaz #define SSI_NETWORK_CDCFSDLY(x)		(1 << 11)
478b908b86SPhilippe Rétornaz #define SSI_NETWORK_DAC_SLOTS_8		(1 << 12)
488b908b86SPhilippe Rétornaz #define SSI_NETWORK_DAC_SLOTS_4		(2 << 12)
498b908b86SPhilippe Rétornaz #define SSI_NETWORK_DAC_SLOTS_2		(3 << 12)
508b908b86SPhilippe Rétornaz #define SSI_NETWORK_DAC_SLOT_MASK	(3 << 12)
518b908b86SPhilippe Rétornaz #define SSI_NETWORK_DAC_RXSLOT_0_1	(0 << 14)
528b908b86SPhilippe Rétornaz #define SSI_NETWORK_DAC_RXSLOT_2_3	(1 << 14)
538b908b86SPhilippe Rétornaz #define SSI_NETWORK_DAC_RXSLOT_4_5	(2 << 14)
548b908b86SPhilippe Rétornaz #define SSI_NETWORK_DAC_RXSLOT_6_7	(3 << 14)
558b908b86SPhilippe Rétornaz #define SSI_NETWORK_DAC_RXSLOT_MASK	(3 << 14)
568b908b86SPhilippe Rétornaz #define SSI_NETWORK_STDCRXSECSLOT(x)	(((x) & 0x3) << 16)
578b908b86SPhilippe Rétornaz #define SSI_NETWORK_STDCRXSECGAIN(x)	(((x) & 0x3) << 18)
588b908b86SPhilippe Rétornaz #define SSI_NETWORK_STDCSUMGAIN		(1 << 20)
598b908b86SPhilippe Rétornaz 
608b908b86SPhilippe Rétornaz /*
618b908b86SPhilippe Rétornaz  * MC13783_AUDIO_CODEC and MC13783_AUDIO_DAC mostly share the same
628b908b86SPhilippe Rétornaz  * register layout
638b908b86SPhilippe Rétornaz  */
648b908b86SPhilippe Rétornaz #define AUDIO_SSI_SEL			(1 << 0)
658b908b86SPhilippe Rétornaz #define AUDIO_CLK_SEL			(1 << 1)
668b908b86SPhilippe Rétornaz #define AUDIO_CSM			(1 << 2)
678b908b86SPhilippe Rétornaz #define AUDIO_BCL_INV			(1 << 3)
688b908b86SPhilippe Rétornaz #define AUDIO_CFS_INV			(1 << 4)
698b908b86SPhilippe Rétornaz #define AUDIO_CFS(x)			(((x) & 0x3) << 5)
708b908b86SPhilippe Rétornaz #define AUDIO_CLK(x)			(((x) & 0x7) << 7)
718b908b86SPhilippe Rétornaz #define AUDIO_C_EN			(1 << 11)
728b908b86SPhilippe Rétornaz #define AUDIO_C_CLK_EN			(1 << 12)
738b908b86SPhilippe Rétornaz #define AUDIO_C_RESET			(1 << 15)
748b908b86SPhilippe Rétornaz 
758b908b86SPhilippe Rétornaz #define AUDIO_CODEC_CDCFS8K16K		(1 << 10)
768b908b86SPhilippe Rétornaz #define AUDIO_DAC_CFS_DLY_B		(1 << 10)
778b908b86SPhilippe Rétornaz 
788b908b86SPhilippe Rétornaz struct mc13783_priv {
798b908b86SPhilippe Rétornaz 	struct mc13xxx *mc13xxx;
802d9215c1SMark Brown 	struct regmap *regmap;
818b908b86SPhilippe Rétornaz 
828b908b86SPhilippe Rétornaz 	enum mc13783_ssi_port adc_ssi_port;
838b908b86SPhilippe Rétornaz 	enum mc13783_ssi_port dac_ssi_port;
848b908b86SPhilippe Rétornaz };
858b908b86SPhilippe Rétornaz 
868b908b86SPhilippe Rétornaz /* Mapping between sample rates and register value */
878b908b86SPhilippe Rétornaz static unsigned int mc13783_rates[] = {
888b908b86SPhilippe Rétornaz 	8000, 11025, 12000, 16000,
898b908b86SPhilippe Rétornaz 	22050, 24000, 32000, 44100,
908b908b86SPhilippe Rétornaz 	48000, 64000, 96000
918b908b86SPhilippe Rétornaz };
928b908b86SPhilippe Rétornaz 
mc13783_pcm_hw_params_dac(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)938b908b86SPhilippe Rétornaz static int mc13783_pcm_hw_params_dac(struct snd_pcm_substream *substream,
948b908b86SPhilippe Rétornaz 				struct snd_pcm_hw_params *params,
958b908b86SPhilippe Rétornaz 				struct snd_soc_dai *dai)
968b908b86SPhilippe Rétornaz {
9778c97ec0SKuninori Morimoto 	struct snd_soc_component *component = dai->component;
988b908b86SPhilippe Rétornaz 	unsigned int rate = params_rate(params);
998b908b86SPhilippe Rétornaz 	int i;
1008b908b86SPhilippe Rétornaz 
1018b908b86SPhilippe Rétornaz 	for (i = 0; i < ARRAY_SIZE(mc13783_rates); i++) {
1028b908b86SPhilippe Rétornaz 		if (rate == mc13783_rates[i]) {
10378c97ec0SKuninori Morimoto 			snd_soc_component_update_bits(component, MC13783_AUDIO_DAC,
1048b908b86SPhilippe Rétornaz 					0xf << 17, i << 17);
1058b908b86SPhilippe Rétornaz 			return 0;
1068b908b86SPhilippe Rétornaz 		}
1078b908b86SPhilippe Rétornaz 	}
1088b908b86SPhilippe Rétornaz 
1098b908b86SPhilippe Rétornaz 	return -EINVAL;
1108b908b86SPhilippe Rétornaz }
1118b908b86SPhilippe Rétornaz 
mc13783_pcm_hw_params_codec(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)1128b908b86SPhilippe Rétornaz static int mc13783_pcm_hw_params_codec(struct snd_pcm_substream *substream,
1138b908b86SPhilippe Rétornaz 				struct snd_pcm_hw_params *params,
1148b908b86SPhilippe Rétornaz 				struct snd_soc_dai *dai)
1158b908b86SPhilippe Rétornaz {
11678c97ec0SKuninori Morimoto 	struct snd_soc_component *component = dai->component;
1178b908b86SPhilippe Rétornaz 	unsigned int rate = params_rate(params);
1188b908b86SPhilippe Rétornaz 	unsigned int val;
1198b908b86SPhilippe Rétornaz 
1208b908b86SPhilippe Rétornaz 	switch (rate) {
1218b908b86SPhilippe Rétornaz 	case 8000:
1228b908b86SPhilippe Rétornaz 		val = 0;
1238b908b86SPhilippe Rétornaz 		break;
1248b908b86SPhilippe Rétornaz 	case 16000:
1258b908b86SPhilippe Rétornaz 		val = AUDIO_CODEC_CDCFS8K16K;
1268b908b86SPhilippe Rétornaz 		break;
1278b908b86SPhilippe Rétornaz 	default:
1288b908b86SPhilippe Rétornaz 		return -EINVAL;
1298b908b86SPhilippe Rétornaz 	}
1308b908b86SPhilippe Rétornaz 
13178c97ec0SKuninori Morimoto 	snd_soc_component_update_bits(component, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K,
1328b908b86SPhilippe Rétornaz 			val);
1338b908b86SPhilippe Rétornaz 
1348b908b86SPhilippe Rétornaz 	return 0;
1358b908b86SPhilippe Rétornaz }
1368b908b86SPhilippe Rétornaz 
mc13783_pcm_hw_params_sync(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)1378b908b86SPhilippe Rétornaz static int mc13783_pcm_hw_params_sync(struct snd_pcm_substream *substream,
1388b908b86SPhilippe Rétornaz 				struct snd_pcm_hw_params *params,
1398b908b86SPhilippe Rétornaz 				struct snd_soc_dai *dai)
1408b908b86SPhilippe Rétornaz {
1418b908b86SPhilippe Rétornaz 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
1428b908b86SPhilippe Rétornaz 		return mc13783_pcm_hw_params_dac(substream, params, dai);
1438b908b86SPhilippe Rétornaz 	else
1448b908b86SPhilippe Rétornaz 		return mc13783_pcm_hw_params_codec(substream, params, dai);
1458b908b86SPhilippe Rétornaz }
1468b908b86SPhilippe Rétornaz 
mc13783_set_fmt(struct snd_soc_dai * dai,unsigned int fmt,unsigned int reg)1478b908b86SPhilippe Rétornaz static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt,
1488b908b86SPhilippe Rétornaz 			unsigned int reg)
1498b908b86SPhilippe Rétornaz {
15078c97ec0SKuninori Morimoto 	struct snd_soc_component *component = dai->component;
1518b908b86SPhilippe Rétornaz 	unsigned int val = 0;
1528b908b86SPhilippe Rétornaz 	unsigned int mask = AUDIO_CFS(3) | AUDIO_BCL_INV | AUDIO_CFS_INV |
1538b908b86SPhilippe Rétornaz 				AUDIO_CSM | AUDIO_C_CLK_EN | AUDIO_C_RESET;
1548b908b86SPhilippe Rétornaz 
1558b908b86SPhilippe Rétornaz 
1568b908b86SPhilippe Rétornaz 	/* DAI mode */
1578b908b86SPhilippe Rétornaz 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
1588b908b86SPhilippe Rétornaz 	case SND_SOC_DAIFMT_I2S:
1598b908b86SPhilippe Rétornaz 		val |= AUDIO_CFS(2);
1608b908b86SPhilippe Rétornaz 		break;
1618b908b86SPhilippe Rétornaz 	case SND_SOC_DAIFMT_DSP_A:
1628b908b86SPhilippe Rétornaz 		val |= AUDIO_CFS(1);
1638b908b86SPhilippe Rétornaz 		break;
1648b908b86SPhilippe Rétornaz 	default:
1658b908b86SPhilippe Rétornaz 		return -EINVAL;
1668b908b86SPhilippe Rétornaz 	}
1678b908b86SPhilippe Rétornaz 
1688b908b86SPhilippe Rétornaz 	/* DAI clock inversion */
1698b908b86SPhilippe Rétornaz 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
1708b908b86SPhilippe Rétornaz 	case SND_SOC_DAIFMT_NB_NF:
1718b908b86SPhilippe Rétornaz 		val |= AUDIO_BCL_INV;
1728b908b86SPhilippe Rétornaz 		break;
1738b908b86SPhilippe Rétornaz 	case SND_SOC_DAIFMT_NB_IF:
1748b908b86SPhilippe Rétornaz 		val |= AUDIO_BCL_INV | AUDIO_CFS_INV;
1758b908b86SPhilippe Rétornaz 		break;
1768b908b86SPhilippe Rétornaz 	case SND_SOC_DAIFMT_IB_NF:
1778b908b86SPhilippe Rétornaz 		break;
1788b908b86SPhilippe Rétornaz 	case SND_SOC_DAIFMT_IB_IF:
1798b908b86SPhilippe Rétornaz 		val |= AUDIO_CFS_INV;
1808b908b86SPhilippe Rétornaz 		break;
1818b908b86SPhilippe Rétornaz 	}
1828b908b86SPhilippe Rétornaz 
1838b908b86SPhilippe Rétornaz 	/* DAI clock master masks */
184*f9482022SMark Brown 	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
185*f9482022SMark Brown 	case SND_SOC_DAIFMT_CBP_CFP:
1868b908b86SPhilippe Rétornaz 		val |= AUDIO_C_CLK_EN;
1878b908b86SPhilippe Rétornaz 		break;
188*f9482022SMark Brown 	case SND_SOC_DAIFMT_CBC_CFC:
1898b908b86SPhilippe Rétornaz 		val |= AUDIO_CSM;
1908b908b86SPhilippe Rétornaz 		break;
191*f9482022SMark Brown 	default:
1928b908b86SPhilippe Rétornaz 		return -EINVAL;
1938b908b86SPhilippe Rétornaz 	}
1948b908b86SPhilippe Rétornaz 
1958b908b86SPhilippe Rétornaz 	val |= AUDIO_C_RESET;
1968b908b86SPhilippe Rétornaz 
19778c97ec0SKuninori Morimoto 	snd_soc_component_update_bits(component, reg, mask, val);
1988b908b86SPhilippe Rétornaz 
1998b908b86SPhilippe Rétornaz 	return 0;
2008b908b86SPhilippe Rétornaz }
2018b908b86SPhilippe Rétornaz 
mc13783_set_fmt_async(struct snd_soc_dai * dai,unsigned int fmt)2028b908b86SPhilippe Rétornaz static int mc13783_set_fmt_async(struct snd_soc_dai *dai, unsigned int fmt)
2038b908b86SPhilippe Rétornaz {
2048b908b86SPhilippe Rétornaz 	if (dai->id == MC13783_ID_STEREO_DAC)
2058b908b86SPhilippe Rétornaz 		return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC);
2068b908b86SPhilippe Rétornaz 	else
2078b908b86SPhilippe Rétornaz 		return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC);
2088b908b86SPhilippe Rétornaz }
2098b908b86SPhilippe Rétornaz 
mc13783_set_fmt_sync(struct snd_soc_dai * dai,unsigned int fmt)2108b908b86SPhilippe Rétornaz static int mc13783_set_fmt_sync(struct snd_soc_dai *dai, unsigned int fmt)
2118b908b86SPhilippe Rétornaz {
2128b908b86SPhilippe Rétornaz 	int ret;
2138b908b86SPhilippe Rétornaz 
2148b908b86SPhilippe Rétornaz 	ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC);
2158b908b86SPhilippe Rétornaz 	if (ret)
2168b908b86SPhilippe Rétornaz 		return ret;
2178b908b86SPhilippe Rétornaz 
2188b908b86SPhilippe Rétornaz 	/*
219*f9482022SMark Brown 	 * In synchronous mode force the voice codec into consumer mode
2208b908b86SPhilippe Rétornaz 	 * so that the clock / framesync from the stereo DAC is used
2218b908b86SPhilippe Rétornaz 	 */
222*f9482022SMark Brown 	fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
223*f9482022SMark Brown 	fmt |= SND_SOC_DAIFMT_CBC_CFC;
2248b908b86SPhilippe Rétornaz 	ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC);
2258b908b86SPhilippe Rétornaz 
2268b908b86SPhilippe Rétornaz 	return ret;
2278b908b86SPhilippe Rétornaz }
2288b908b86SPhilippe Rétornaz 
2298b908b86SPhilippe Rétornaz static int mc13783_sysclk[] = {
2308b908b86SPhilippe Rétornaz 	13000000,
2318b908b86SPhilippe Rétornaz 	15360000,
2328b908b86SPhilippe Rétornaz 	16800000,
2338b908b86SPhilippe Rétornaz 	-1,
2348b908b86SPhilippe Rétornaz 	26000000,
2358b908b86SPhilippe Rétornaz 	-1, /* 12000000, invalid for voice codec */
2368b908b86SPhilippe Rétornaz 	-1, /* 3686400, invalid for voice codec */
2378b908b86SPhilippe Rétornaz 	33600000,
2388b908b86SPhilippe Rétornaz };
2398b908b86SPhilippe Rétornaz 
mc13783_set_sysclk(struct snd_soc_dai * dai,int clk_id,unsigned int freq,int dir,unsigned int reg)2408b908b86SPhilippe Rétornaz static int mc13783_set_sysclk(struct snd_soc_dai *dai,
2418b908b86SPhilippe Rétornaz 				  int clk_id, unsigned int freq, int dir,
2428b908b86SPhilippe Rétornaz 				  unsigned int reg)
2438b908b86SPhilippe Rétornaz {
24478c97ec0SKuninori Morimoto 	struct snd_soc_component *component = dai->component;
2458b908b86SPhilippe Rétornaz 	int clk;
2468b908b86SPhilippe Rétornaz 	unsigned int val = 0;
2478b908b86SPhilippe Rétornaz 	unsigned int mask = AUDIO_CLK(0x7) | AUDIO_CLK_SEL;
2488b908b86SPhilippe Rétornaz 
2498b908b86SPhilippe Rétornaz 	for (clk = 0; clk < ARRAY_SIZE(mc13783_sysclk); clk++) {
2508b908b86SPhilippe Rétornaz 		if (mc13783_sysclk[clk] < 0)
2518b908b86SPhilippe Rétornaz 			continue;
2528b908b86SPhilippe Rétornaz 		if (mc13783_sysclk[clk] == freq)
2538b908b86SPhilippe Rétornaz 			break;
2548b908b86SPhilippe Rétornaz 	}
2558b908b86SPhilippe Rétornaz 
2568b908b86SPhilippe Rétornaz 	if (clk == ARRAY_SIZE(mc13783_sysclk))
2578b908b86SPhilippe Rétornaz 		return -EINVAL;
2588b908b86SPhilippe Rétornaz 
2598b908b86SPhilippe Rétornaz 	if (clk_id == MC13783_CLK_CLIB)
2608b908b86SPhilippe Rétornaz 		val |= AUDIO_CLK_SEL;
2618b908b86SPhilippe Rétornaz 
2628b908b86SPhilippe Rétornaz 	val |= AUDIO_CLK(clk);
2638b908b86SPhilippe Rétornaz 
26478c97ec0SKuninori Morimoto 	snd_soc_component_update_bits(component, reg, mask, val);
2658b908b86SPhilippe Rétornaz 
2668b908b86SPhilippe Rétornaz 	return 0;
2678b908b86SPhilippe Rétornaz }
2688b908b86SPhilippe Rétornaz 
mc13783_set_sysclk_dac(struct snd_soc_dai * dai,int clk_id,unsigned int freq,int dir)2698b908b86SPhilippe Rétornaz static int mc13783_set_sysclk_dac(struct snd_soc_dai *dai,
2708b908b86SPhilippe Rétornaz 				  int clk_id, unsigned int freq, int dir)
2718b908b86SPhilippe Rétornaz {
2728b908b86SPhilippe Rétornaz 	return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC);
2738b908b86SPhilippe Rétornaz }
2748b908b86SPhilippe Rétornaz 
mc13783_set_sysclk_codec(struct snd_soc_dai * dai,int clk_id,unsigned int freq,int dir)2758b908b86SPhilippe Rétornaz static int mc13783_set_sysclk_codec(struct snd_soc_dai *dai,
2768b908b86SPhilippe Rétornaz 				  int clk_id, unsigned int freq, int dir)
2778b908b86SPhilippe Rétornaz {
2788b908b86SPhilippe Rétornaz 	return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC);
2798b908b86SPhilippe Rétornaz }
2808b908b86SPhilippe Rétornaz 
mc13783_set_sysclk_sync(struct snd_soc_dai * dai,int clk_id,unsigned int freq,int dir)2818b908b86SPhilippe Rétornaz static int mc13783_set_sysclk_sync(struct snd_soc_dai *dai,
2828b908b86SPhilippe Rétornaz 				  int clk_id, unsigned int freq, int dir)
2838b908b86SPhilippe Rétornaz {
2848b908b86SPhilippe Rétornaz 	int ret;
2858b908b86SPhilippe Rétornaz 
2868b908b86SPhilippe Rétornaz 	ret = mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC);
2878b908b86SPhilippe Rétornaz 	if (ret)
2888b908b86SPhilippe Rétornaz 		return ret;
2898b908b86SPhilippe Rétornaz 
2908b908b86SPhilippe Rétornaz 	return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC);
2918b908b86SPhilippe Rétornaz }
2928b908b86SPhilippe Rétornaz 
mc13783_set_tdm_slot_dac(struct snd_soc_dai * dai,unsigned int tx_mask,unsigned int rx_mask,int slots,int slot_width)2938b908b86SPhilippe Rétornaz static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai,
2948b908b86SPhilippe Rétornaz 	unsigned int tx_mask, unsigned int rx_mask, int slots,
2958b908b86SPhilippe Rétornaz 	int slot_width)
2968b908b86SPhilippe Rétornaz {
29778c97ec0SKuninori Morimoto 	struct snd_soc_component *component = dai->component;
2988b908b86SPhilippe Rétornaz 	unsigned int val = 0;
2998b908b86SPhilippe Rétornaz 	unsigned int mask = SSI_NETWORK_DAC_SLOT_MASK |
3008b908b86SPhilippe Rétornaz 				SSI_NETWORK_DAC_RXSLOT_MASK;
3018b908b86SPhilippe Rétornaz 
3028b908b86SPhilippe Rétornaz 	switch (slots) {
3038b908b86SPhilippe Rétornaz 	case 2:
3048b908b86SPhilippe Rétornaz 		val |= SSI_NETWORK_DAC_SLOTS_2;
3058b908b86SPhilippe Rétornaz 		break;
3068b908b86SPhilippe Rétornaz 	case 4:
3078b908b86SPhilippe Rétornaz 		val |= SSI_NETWORK_DAC_SLOTS_4;
3088b908b86SPhilippe Rétornaz 		break;
3098b908b86SPhilippe Rétornaz 	case 8:
3108b908b86SPhilippe Rétornaz 		val |= SSI_NETWORK_DAC_SLOTS_8;
3118b908b86SPhilippe Rétornaz 		break;
3128b908b86SPhilippe Rétornaz 	default:
3138b908b86SPhilippe Rétornaz 		return -EINVAL;
3148b908b86SPhilippe Rétornaz 	}
3158b908b86SPhilippe Rétornaz 
3168b908b86SPhilippe Rétornaz 	switch (rx_mask) {
317bec78c5fSLars-Peter Clausen 	case 0x03:
3188b908b86SPhilippe Rétornaz 		val |= SSI_NETWORK_DAC_RXSLOT_0_1;
3198b908b86SPhilippe Rétornaz 		break;
320bec78c5fSLars-Peter Clausen 	case 0x0c:
3218b908b86SPhilippe Rétornaz 		val |= SSI_NETWORK_DAC_RXSLOT_2_3;
3228b908b86SPhilippe Rétornaz 		break;
323bec78c5fSLars-Peter Clausen 	case 0x30:
3248b908b86SPhilippe Rétornaz 		val |= SSI_NETWORK_DAC_RXSLOT_4_5;
3258b908b86SPhilippe Rétornaz 		break;
326bec78c5fSLars-Peter Clausen 	case 0xc0:
3278b908b86SPhilippe Rétornaz 		val |= SSI_NETWORK_DAC_RXSLOT_6_7;
3288b908b86SPhilippe Rétornaz 		break;
3298b908b86SPhilippe Rétornaz 	default:
3308b908b86SPhilippe Rétornaz 		return -EINVAL;
3311d198f26SJoe Perches 	}
3328b908b86SPhilippe Rétornaz 
33378c97ec0SKuninori Morimoto 	snd_soc_component_update_bits(component, MC13783_SSI_NETWORK, mask, val);
3348b908b86SPhilippe Rétornaz 
3358b908b86SPhilippe Rétornaz 	return 0;
3368b908b86SPhilippe Rétornaz }
3378b908b86SPhilippe Rétornaz 
mc13783_set_tdm_slot_codec(struct snd_soc_dai * dai,unsigned int tx_mask,unsigned int rx_mask,int slots,int slot_width)3388b908b86SPhilippe Rétornaz static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai,
3398b908b86SPhilippe Rétornaz 	unsigned int tx_mask, unsigned int rx_mask, int slots,
3408b908b86SPhilippe Rétornaz 	int slot_width)
3418b908b86SPhilippe Rétornaz {
34278c97ec0SKuninori Morimoto 	struct snd_soc_component *component = dai->component;
3438b908b86SPhilippe Rétornaz 	unsigned int val = 0;
3448b908b86SPhilippe Rétornaz 	unsigned int mask = 0x3f;
3458b908b86SPhilippe Rétornaz 
3468b908b86SPhilippe Rétornaz 	if (slots != 4)
3478b908b86SPhilippe Rétornaz 		return -EINVAL;
3488b908b86SPhilippe Rétornaz 
349bec78c5fSLars-Peter Clausen 	if (tx_mask != 0x3)
3508b908b86SPhilippe Rétornaz 		return -EINVAL;
3518b908b86SPhilippe Rétornaz 
3528b908b86SPhilippe Rétornaz 	val |= (0x00 << 2);	/* primary timeslot RX/TX(?) is 0 */
3538b908b86SPhilippe Rétornaz 	val |= (0x01 << 4);	/* secondary timeslot TX is 1 */
3548b908b86SPhilippe Rétornaz 
35578c97ec0SKuninori Morimoto 	snd_soc_component_update_bits(component, MC13783_SSI_NETWORK, mask, val);
3568b908b86SPhilippe Rétornaz 
3578b908b86SPhilippe Rétornaz 	return 0;
3588b908b86SPhilippe Rétornaz }
3598b908b86SPhilippe Rétornaz 
mc13783_set_tdm_slot_sync(struct snd_soc_dai * dai,unsigned int tx_mask,unsigned int rx_mask,int slots,int slot_width)3608b908b86SPhilippe Rétornaz static int mc13783_set_tdm_slot_sync(struct snd_soc_dai *dai,
3618b908b86SPhilippe Rétornaz 	unsigned int tx_mask, unsigned int rx_mask, int slots,
3628b908b86SPhilippe Rétornaz 	int slot_width)
3638b908b86SPhilippe Rétornaz {
3648b908b86SPhilippe Rétornaz 	int ret;
3658b908b86SPhilippe Rétornaz 
3668b908b86SPhilippe Rétornaz 	ret = mc13783_set_tdm_slot_dac(dai, tx_mask, rx_mask, slots,
3678b908b86SPhilippe Rétornaz 			slot_width);
3688b908b86SPhilippe Rétornaz 	if (ret)
3698b908b86SPhilippe Rétornaz 		return ret;
3708b908b86SPhilippe Rétornaz 
3718b908b86SPhilippe Rétornaz 	ret = mc13783_set_tdm_slot_codec(dai, tx_mask, rx_mask, slots,
3728b908b86SPhilippe Rétornaz 			slot_width);
3738b908b86SPhilippe Rétornaz 
3748b908b86SPhilippe Rétornaz 	return ret;
3758b908b86SPhilippe Rétornaz }
3768b908b86SPhilippe Rétornaz 
3778b908b86SPhilippe Rétornaz static const struct snd_kcontrol_new mc1l_amp_ctl =
3786d97c09cSGaëtan Carlier 	SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 7, 1, 0);
3798b908b86SPhilippe Rétornaz 
3808b908b86SPhilippe Rétornaz static const struct snd_kcontrol_new mc1r_amp_ctl =
3816d97c09cSGaëtan Carlier 	SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 5, 1, 0);
3828b908b86SPhilippe Rétornaz 
3838b908b86SPhilippe Rétornaz static const struct snd_kcontrol_new mc2_amp_ctl =
3846d97c09cSGaëtan Carlier 	SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 9, 1, 0);
3858b908b86SPhilippe Rétornaz 
3868b908b86SPhilippe Rétornaz static const struct snd_kcontrol_new atx_amp_ctl =
3876d97c09cSGaëtan Carlier 	SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 11, 1, 0);
3888b908b86SPhilippe Rétornaz 
3898b908b86SPhilippe Rétornaz 
3908b908b86SPhilippe Rétornaz /* Virtual mux. The chip does the input selection automatically
3918b908b86SPhilippe Rétornaz  * as soon as we enable one input. */
3928b908b86SPhilippe Rétornaz static const char * const adcl_enum_text[] = {
3938b908b86SPhilippe Rétornaz 	"MC1L", "RXINL",
3948b908b86SPhilippe Rétornaz };
3958b908b86SPhilippe Rétornaz 
39615ab40a9SLars-Peter Clausen static SOC_ENUM_SINGLE_VIRT_DECL(adcl_enum, adcl_enum_text);
3978b908b86SPhilippe Rétornaz 
3988b908b86SPhilippe Rétornaz static const struct snd_kcontrol_new left_input_mux =
39936bc38a7SLars-Peter Clausen 	SOC_DAPM_ENUM("Route", adcl_enum);
4008b908b86SPhilippe Rétornaz 
4018b908b86SPhilippe Rétornaz static const char * const adcr_enum_text[] = {
4028b908b86SPhilippe Rétornaz 	"MC1R", "MC2", "RXINR", "TXIN",
4038b908b86SPhilippe Rétornaz };
4048b908b86SPhilippe Rétornaz 
40515ab40a9SLars-Peter Clausen static SOC_ENUM_SINGLE_VIRT_DECL(adcr_enum, adcr_enum_text);
4068b908b86SPhilippe Rétornaz 
4078b908b86SPhilippe Rétornaz static const struct snd_kcontrol_new right_input_mux =
40836bc38a7SLars-Peter Clausen 	SOC_DAPM_ENUM("Route", adcr_enum);
4098b908b86SPhilippe Rétornaz 
4108b908b86SPhilippe Rétornaz static const struct snd_kcontrol_new samp_ctl =
4116d97c09cSGaëtan Carlier 	SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 3, 1, 0);
4128b908b86SPhilippe Rétornaz 
413bb7838d4SSteffen Trumtrar static const char * const speaker_amp_source_text[] = {
414bb7838d4SSteffen Trumtrar 	"CODEC", "Right"
415bb7838d4SSteffen Trumtrar };
416a7509874STakashi Iwai static SOC_ENUM_SINGLE_DECL(speaker_amp_source, MC13783_AUDIO_RX0, 4,
417bb7838d4SSteffen Trumtrar 			    speaker_amp_source_text);
418bb7838d4SSteffen Trumtrar static const struct snd_kcontrol_new speaker_amp_source_mux =
419bb7838d4SSteffen Trumtrar 	SOC_DAPM_ENUM("Speaker Amp Source MUX", speaker_amp_source);
420bb7838d4SSteffen Trumtrar 
421bb7838d4SSteffen Trumtrar static const char * const headset_amp_source_text[] = {
422bb7838d4SSteffen Trumtrar 	"CODEC", "Mixer"
423bb7838d4SSteffen Trumtrar };
424bb7838d4SSteffen Trumtrar 
425a7509874STakashi Iwai static SOC_ENUM_SINGLE_DECL(headset_amp_source, MC13783_AUDIO_RX0, 11,
426bb7838d4SSteffen Trumtrar 			    headset_amp_source_text);
427bb7838d4SSteffen Trumtrar static const struct snd_kcontrol_new headset_amp_source_mux =
428bb7838d4SSteffen Trumtrar 	SOC_DAPM_ENUM("Headset Amp Source MUX", headset_amp_source);
429bb7838d4SSteffen Trumtrar 
430bb7838d4SSteffen Trumtrar static const struct snd_kcontrol_new cdcout_ctl =
431bb7838d4SSteffen Trumtrar 	SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 18, 1, 0);
432bb7838d4SSteffen Trumtrar 
433bb7838d4SSteffen Trumtrar static const struct snd_kcontrol_new adc_bypass_ctl =
434bb7838d4SSteffen Trumtrar 	SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_CODEC, 16, 1, 0);
435bb7838d4SSteffen Trumtrar 
4368b908b86SPhilippe Rétornaz static const struct snd_kcontrol_new lamp_ctl =
4376d97c09cSGaëtan Carlier 	SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 5, 1, 0);
4388b908b86SPhilippe Rétornaz 
4398b908b86SPhilippe Rétornaz static const struct snd_kcontrol_new hlamp_ctl =
4406d97c09cSGaëtan Carlier 	SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 10, 1, 0);
4418b908b86SPhilippe Rétornaz 
4428b908b86SPhilippe Rétornaz static const struct snd_kcontrol_new hramp_ctl =
4436d97c09cSGaëtan Carlier 	SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 9, 1, 0);
4448b908b86SPhilippe Rétornaz 
4458b908b86SPhilippe Rétornaz static const struct snd_kcontrol_new llamp_ctl =
4466d97c09cSGaëtan Carlier 	SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 16, 1, 0);
4478b908b86SPhilippe Rétornaz 
4488b908b86SPhilippe Rétornaz static const struct snd_kcontrol_new lramp_ctl =
4496d97c09cSGaëtan Carlier 	SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 15, 1, 0);
4508b908b86SPhilippe Rétornaz 
4518b908b86SPhilippe Rétornaz static const struct snd_soc_dapm_widget mc13783_dapm_widgets[] = {
4528b908b86SPhilippe Rétornaz /* Input */
4538b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_INPUT("MC1LIN"),
4548b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_INPUT("MC1RIN"),
4558b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_INPUT("MC2IN"),
4568b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_INPUT("RXINR"),
4578b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_INPUT("RXINL"),
4588b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_INPUT("TXIN"),
4598b908b86SPhilippe Rétornaz 
4606d97c09cSGaëtan Carlier 	SND_SOC_DAPM_SUPPLY("MC1 Bias", MC13783_AUDIO_TX, 0, 0, NULL, 0),
4616d97c09cSGaëtan Carlier 	SND_SOC_DAPM_SUPPLY("MC2 Bias", MC13783_AUDIO_TX, 1, 0, NULL, 0),
4628b908b86SPhilippe Rétornaz 
4636d97c09cSGaëtan Carlier 	SND_SOC_DAPM_SWITCH("MC1L Amp", MC13783_AUDIO_TX, 7, 0, &mc1l_amp_ctl),
4646d97c09cSGaëtan Carlier 	SND_SOC_DAPM_SWITCH("MC1R Amp", MC13783_AUDIO_TX, 5, 0, &mc1r_amp_ctl),
4656d97c09cSGaëtan Carlier 	SND_SOC_DAPM_SWITCH("MC2 Amp", MC13783_AUDIO_TX, 9, 0, &mc2_amp_ctl),
4666d97c09cSGaëtan Carlier 	SND_SOC_DAPM_SWITCH("TXIN Amp", MC13783_AUDIO_TX, 11, 0, &atx_amp_ctl),
4678b908b86SPhilippe Rétornaz 
46836bc38a7SLars-Peter Clausen 	SND_SOC_DAPM_MUX("PGA Left Input Mux", SND_SOC_NOPM, 0, 0,
4698b908b86SPhilippe Rétornaz 			      &left_input_mux),
47036bc38a7SLars-Peter Clausen 	SND_SOC_DAPM_MUX("PGA Right Input Mux", SND_SOC_NOPM, 0, 0,
4718b908b86SPhilippe Rétornaz 			      &right_input_mux),
4728b908b86SPhilippe Rétornaz 
473bb7838d4SSteffen Trumtrar 	SND_SOC_DAPM_MUX("Speaker Amp Source MUX", SND_SOC_NOPM, 0, 0,
474bb7838d4SSteffen Trumtrar 			 &speaker_amp_source_mux),
475bb7838d4SSteffen Trumtrar 
476bb7838d4SSteffen Trumtrar 	SND_SOC_DAPM_MUX("Headset Amp Source MUX", SND_SOC_NOPM, 0, 0,
477bb7838d4SSteffen Trumtrar 			 &headset_amp_source_mux),
478bb7838d4SSteffen Trumtrar 
4798b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_PGA("PGA Left Input", SND_SOC_NOPM, 0, 0, NULL, 0),
4808b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_PGA("PGA Right Input", SND_SOC_NOPM, 0, 0, NULL, 0),
4818b908b86SPhilippe Rétornaz 
4826d97c09cSGaëtan Carlier 	SND_SOC_DAPM_ADC("ADC", "Capture", MC13783_AUDIO_CODEC, 11, 0),
4836d97c09cSGaëtan Carlier 	SND_SOC_DAPM_SUPPLY("ADC_Reset", MC13783_AUDIO_CODEC, 15, 0, NULL, 0),
4848b908b86SPhilippe Rétornaz 
485bb7838d4SSteffen Trumtrar 	SND_SOC_DAPM_PGA("Voice CODEC PGA", MC13783_AUDIO_RX1, 0, 0, NULL, 0),
486bb7838d4SSteffen Trumtrar 	SND_SOC_DAPM_SWITCH("Voice CODEC Bypass", MC13783_AUDIO_CODEC, 16, 0,
487bb7838d4SSteffen Trumtrar 			&adc_bypass_ctl),
488bb7838d4SSteffen Trumtrar 
4898b908b86SPhilippe Rétornaz /* Output */
4906d97c09cSGaëtan Carlier 	SND_SOC_DAPM_SUPPLY("DAC_E", MC13783_AUDIO_DAC, 11, 0, NULL, 0),
4916d97c09cSGaëtan Carlier 	SND_SOC_DAPM_SUPPLY("DAC_Reset", MC13783_AUDIO_DAC, 15, 0, NULL, 0),
4928b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_OUTPUT("RXOUTL"),
4938b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_OUTPUT("RXOUTR"),
4948b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_OUTPUT("HSL"),
4958b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_OUTPUT("HSR"),
496bb7838d4SSteffen Trumtrar 	SND_SOC_DAPM_OUTPUT("LSPL"),
4978b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_OUTPUT("LSP"),
4988b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_OUTPUT("SP"),
499bb7838d4SSteffen Trumtrar 	SND_SOC_DAPM_OUTPUT("CDCOUT"),
5008b908b86SPhilippe Rétornaz 
501bb7838d4SSteffen Trumtrar 	SND_SOC_DAPM_SWITCH("CDCOUT Switch", MC13783_AUDIO_RX0, 18, 0,
502bb7838d4SSteffen Trumtrar 			&cdcout_ctl),
503bb7838d4SSteffen Trumtrar 	SND_SOC_DAPM_SWITCH("Speaker Amp Switch", MC13783_AUDIO_RX0, 3, 0,
504bb7838d4SSteffen Trumtrar 			&samp_ctl),
5058b908b86SPhilippe Rétornaz 	SND_SOC_DAPM_SWITCH("Loudspeaker Amp", SND_SOC_NOPM, 0, 0, &lamp_ctl),
5066d97c09cSGaëtan Carlier 	SND_SOC_DAPM_SWITCH("Headset Amp Left", MC13783_AUDIO_RX0, 10, 0,
5076d97c09cSGaëtan Carlier 			&hlamp_ctl),
5086d97c09cSGaëtan Carlier 	SND_SOC_DAPM_SWITCH("Headset Amp Right", MC13783_AUDIO_RX0, 9, 0,
5096d97c09cSGaëtan Carlier 			&hramp_ctl),
5106d97c09cSGaëtan Carlier 	SND_SOC_DAPM_SWITCH("Line out Amp Left", MC13783_AUDIO_RX0, 16, 0,
5116d97c09cSGaëtan Carlier 			&llamp_ctl),
5126d97c09cSGaëtan Carlier 	SND_SOC_DAPM_SWITCH("Line out Amp Right", MC13783_AUDIO_RX0, 15, 0,
5136d97c09cSGaëtan Carlier 			&lramp_ctl),
5146d97c09cSGaëtan Carlier 	SND_SOC_DAPM_DAC("DAC", "Playback", MC13783_AUDIO_RX0, 22, 0),
5156d97c09cSGaëtan Carlier 	SND_SOC_DAPM_PGA("DAC PGA", MC13783_AUDIO_RX1, 5, 0, NULL, 0),
5168b908b86SPhilippe Rétornaz };
5178b908b86SPhilippe Rétornaz 
5188b908b86SPhilippe Rétornaz static struct snd_soc_dapm_route mc13783_routes[] = {
5198b908b86SPhilippe Rétornaz /* Input */
5208b908b86SPhilippe Rétornaz 	{ "MC1L Amp", NULL, "MC1LIN"},
5218b908b86SPhilippe Rétornaz 	{ "MC1R Amp", NULL, "MC1RIN" },
5228b908b86SPhilippe Rétornaz 	{ "MC2 Amp", NULL, "MC2IN" },
5238b908b86SPhilippe Rétornaz 	{ "TXIN Amp", NULL, "TXIN"},
5248b908b86SPhilippe Rétornaz 
5258b908b86SPhilippe Rétornaz 	{ "PGA Left Input Mux", "MC1L", "MC1L Amp" },
5268b908b86SPhilippe Rétornaz 	{ "PGA Left Input Mux", "RXINL", "RXINL"},
5278b908b86SPhilippe Rétornaz 	{ "PGA Right Input Mux", "MC1R", "MC1R Amp" },
5288b908b86SPhilippe Rétornaz 	{ "PGA Right Input Mux", "MC2",  "MC2 Amp"},
5298b908b86SPhilippe Rétornaz 	{ "PGA Right Input Mux", "TXIN", "TXIN Amp"},
5308b908b86SPhilippe Rétornaz 	{ "PGA Right Input Mux", "RXINR", "RXINR"},
5318b908b86SPhilippe Rétornaz 
5328b908b86SPhilippe Rétornaz 	{ "PGA Left Input", NULL, "PGA Left Input Mux"},
5338b908b86SPhilippe Rétornaz 	{ "PGA Right Input", NULL, "PGA Right Input Mux"},
5348b908b86SPhilippe Rétornaz 
5358b908b86SPhilippe Rétornaz 	{ "ADC", NULL, "PGA Left Input"},
5368b908b86SPhilippe Rétornaz 	{ "ADC", NULL, "PGA Right Input"},
5378b908b86SPhilippe Rétornaz 	{ "ADC", NULL, "ADC_Reset"},
5388b908b86SPhilippe Rétornaz 
539bb7838d4SSteffen Trumtrar 	{ "Voice CODEC PGA", "Voice CODEC Bypass", "ADC" },
540bb7838d4SSteffen Trumtrar 
541bb7838d4SSteffen Trumtrar 	{ "Speaker Amp Source MUX", "CODEC", "Voice CODEC PGA"},
542bb7838d4SSteffen Trumtrar 	{ "Speaker Amp Source MUX", "Right", "DAC PGA"},
543bb7838d4SSteffen Trumtrar 
544bb7838d4SSteffen Trumtrar 	{ "Headset Amp Source MUX", "CODEC", "Voice CODEC PGA"},
545bb7838d4SSteffen Trumtrar 	{ "Headset Amp Source MUX", "Mixer", "DAC PGA"},
546bb7838d4SSteffen Trumtrar 
5478b908b86SPhilippe Rétornaz /* Output */
5488b908b86SPhilippe Rétornaz 	{ "HSL", NULL, "Headset Amp Left" },
5498b908b86SPhilippe Rétornaz 	{ "HSR", NULL, "Headset Amp Right"},
5508b908b86SPhilippe Rétornaz 	{ "RXOUTL", NULL, "Line out Amp Left"},
5518b908b86SPhilippe Rétornaz 	{ "RXOUTR", NULL, "Line out Amp Right"},
552bb7838d4SSteffen Trumtrar 	{ "SP", "Speaker Amp Switch", "Speaker Amp Source MUX"},
553bb7838d4SSteffen Trumtrar 	{ "LSP", "Loudspeaker Amp", "Speaker Amp Source MUX"},
554bb7838d4SSteffen Trumtrar 	{ "HSL", "Headset Amp Left", "Headset Amp Source MUX"},
555bb7838d4SSteffen Trumtrar 	{ "HSR", "Headset Amp Right", "Headset Amp Source MUX"},
5568b908b86SPhilippe Rétornaz 	{ "Line out Amp Left", NULL, "DAC PGA"},
5578b908b86SPhilippe Rétornaz 	{ "Line out Amp Right", NULL, "DAC PGA"},
5588b908b86SPhilippe Rétornaz 	{ "DAC PGA", NULL, "DAC"},
5598b908b86SPhilippe Rétornaz 	{ "DAC", NULL, "DAC_E"},
560bb7838d4SSteffen Trumtrar 	{ "CDCOUT", "CDCOUT Switch", "Voice CODEC PGA"},
5618b908b86SPhilippe Rétornaz };
5628b908b86SPhilippe Rétornaz 
5638b908b86SPhilippe Rétornaz static const char * const mc13783_3d_mixer[] = {"Stereo", "Phase Mix",
5648b908b86SPhilippe Rétornaz 						"Mono", "Mono Mix"};
5658b908b86SPhilippe Rétornaz 
566d1755bb7STakashi Iwai static SOC_ENUM_SINGLE_DECL(mc13783_enum_3d_mixer,
567d1755bb7STakashi Iwai 			    MC13783_AUDIO_RX1, 16,
5688b908b86SPhilippe Rétornaz 			    mc13783_3d_mixer);
5698b908b86SPhilippe Rétornaz 
5708b908b86SPhilippe Rétornaz static struct snd_kcontrol_new mc13783_control_list[] = {
5718b908b86SPhilippe Rétornaz 	SOC_SINGLE("Loudspeaker enable", MC13783_AUDIO_RX0, 5, 1, 0),
5728b908b86SPhilippe Rétornaz 	SOC_SINGLE("PCM Playback Volume", MC13783_AUDIO_RX1, 6, 15, 0),
573c6452e39SSteffen Trumtrar 	SOC_SINGLE("PCM Playback Switch", MC13783_AUDIO_RX1, 5, 1, 0),
5748b908b86SPhilippe Rétornaz 	SOC_DOUBLE("PCM Capture Volume", MC13783_AUDIO_TX, 19, 14, 31, 0),
5758b908b86SPhilippe Rétornaz 	SOC_ENUM("3D Control", mc13783_enum_3d_mixer),
576c6452e39SSteffen Trumtrar 
577c6452e39SSteffen Trumtrar 	SOC_SINGLE("CDCOUT Switch", MC13783_AUDIO_RX0, 18, 1, 0),
578c6452e39SSteffen Trumtrar 	SOC_SINGLE("Earpiece Amp Switch", MC13783_AUDIO_RX0, 3, 1, 0),
579c6452e39SSteffen Trumtrar 	SOC_DOUBLE("Headset Amp Switch", MC13783_AUDIO_RX0, 10, 9, 1, 0),
580c6452e39SSteffen Trumtrar 	SOC_DOUBLE("Line out Amp Switch", MC13783_AUDIO_RX0, 16, 15, 1, 0),
581c6452e39SSteffen Trumtrar 
582c6452e39SSteffen Trumtrar 	SOC_SINGLE("PCM Capture Mixin Switch", MC13783_AUDIO_RX0, 22, 1, 0),
583c6452e39SSteffen Trumtrar 	SOC_SINGLE("Line in Capture Mixin Switch", MC13783_AUDIO_RX0, 23, 1, 0),
584c6452e39SSteffen Trumtrar 
585c6452e39SSteffen Trumtrar 	SOC_SINGLE("CODEC Capture Volume", MC13783_AUDIO_RX1, 1, 15, 0),
586c6452e39SSteffen Trumtrar 	SOC_SINGLE("CODEC Capture Mixin Switch", MC13783_AUDIO_RX0, 21, 1, 0),
587c6452e39SSteffen Trumtrar 
588c6452e39SSteffen Trumtrar 	SOC_SINGLE("Line in Capture Volume", MC13783_AUDIO_RX1, 12, 15, 0),
589c6452e39SSteffen Trumtrar 	SOC_SINGLE("Line in Capture Switch", MC13783_AUDIO_RX1, 10, 1, 0),
590c6452e39SSteffen Trumtrar 
591c6452e39SSteffen Trumtrar 	SOC_SINGLE("MC1 Capture Bias Switch", MC13783_AUDIO_TX, 0, 1, 0),
592c6452e39SSteffen Trumtrar 	SOC_SINGLE("MC2 Capture Bias Switch", MC13783_AUDIO_TX, 1, 1, 0),
5938b908b86SPhilippe Rétornaz };
5948b908b86SPhilippe Rétornaz 
mc13783_probe(struct snd_soc_component * component)59578c97ec0SKuninori Morimoto static int mc13783_probe(struct snd_soc_component *component)
5968b908b86SPhilippe Rétornaz {
59778c97ec0SKuninori Morimoto 	struct mc13783_priv *priv = snd_soc_component_get_drvdata(component);
5988b908b86SPhilippe Rétornaz 
59978c97ec0SKuninori Morimoto 	snd_soc_component_init_regmap(component,
60078c97ec0SKuninori Morimoto 				  dev_get_regmap(component->dev->parent, NULL));
60133953d85SKuninori Morimoto 
6028b908b86SPhilippe Rétornaz 	/* these are the reset values */
6038b908b86SPhilippe Rétornaz 	mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX0, 0x25893);
6048b908b86SPhilippe Rétornaz 	mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX1, 0x00d35A);
6058b908b86SPhilippe Rétornaz 	mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_TX, 0x420000);
6068b908b86SPhilippe Rétornaz 	mc13xxx_reg_write(priv->mc13xxx, MC13783_SSI_NETWORK, 0x013060);
6078b908b86SPhilippe Rétornaz 	mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_CODEC, 0x180027);
6088b908b86SPhilippe Rétornaz 	mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_DAC, 0x0e0004);
6098b908b86SPhilippe Rétornaz 
6108b908b86SPhilippe Rétornaz 	if (priv->adc_ssi_port == MC13783_SSI1_PORT)
6118b908b86SPhilippe Rétornaz 		mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC,
6128b908b86SPhilippe Rétornaz 				AUDIO_SSI_SEL, 0);
6138b908b86SPhilippe Rétornaz 	else
6148b908b86SPhilippe Rétornaz 		mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC,
615545774bdSAxel Lin 				AUDIO_SSI_SEL, AUDIO_SSI_SEL);
6168b908b86SPhilippe Rétornaz 
6178b908b86SPhilippe Rétornaz 	if (priv->dac_ssi_port == MC13783_SSI1_PORT)
6188b908b86SPhilippe Rétornaz 		mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC,
6198b908b86SPhilippe Rétornaz 				AUDIO_SSI_SEL, 0);
6208b908b86SPhilippe Rétornaz 	else
6218b908b86SPhilippe Rétornaz 		mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC,
622545774bdSAxel Lin 				AUDIO_SSI_SEL, AUDIO_SSI_SEL);
6238b908b86SPhilippe Rétornaz 
6248b908b86SPhilippe Rétornaz 	return 0;
6258b908b86SPhilippe Rétornaz }
6268b908b86SPhilippe Rétornaz 
mc13783_remove(struct snd_soc_component * component)62778c97ec0SKuninori Morimoto static void mc13783_remove(struct snd_soc_component *component)
6288b908b86SPhilippe Rétornaz {
62978c97ec0SKuninori Morimoto 	struct mc13783_priv *priv = snd_soc_component_get_drvdata(component);
6308b908b86SPhilippe Rétornaz 
6318b908b86SPhilippe Rétornaz 	/* Make sure VAUDIOON is off */
6328b908b86SPhilippe Rétornaz 	mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_RX0, 0x3, 0);
6338b908b86SPhilippe Rétornaz }
6348b908b86SPhilippe Rétornaz 
6358b908b86SPhilippe Rétornaz #define MC13783_RATES_RECORD (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
6368b908b86SPhilippe Rétornaz 
6378b908b86SPhilippe Rétornaz #define MC13783_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
6388b908b86SPhilippe Rétornaz 	SNDRV_PCM_FMTBIT_S24_LE)
6398b908b86SPhilippe Rétornaz 
64064793047SAxel Lin static const struct snd_soc_dai_ops mc13783_ops_dac = {
6418b908b86SPhilippe Rétornaz 	.hw_params	= mc13783_pcm_hw_params_dac,
6428b908b86SPhilippe Rétornaz 	.set_fmt	= mc13783_set_fmt_async,
6438b908b86SPhilippe Rétornaz 	.set_sysclk	= mc13783_set_sysclk_dac,
6448b908b86SPhilippe Rétornaz 	.set_tdm_slot	= mc13783_set_tdm_slot_dac,
6458b908b86SPhilippe Rétornaz };
6468b908b86SPhilippe Rétornaz 
64764793047SAxel Lin static const struct snd_soc_dai_ops mc13783_ops_codec = {
6488b908b86SPhilippe Rétornaz 	.hw_params	= mc13783_pcm_hw_params_codec,
6498b908b86SPhilippe Rétornaz 	.set_fmt	= mc13783_set_fmt_async,
6508b908b86SPhilippe Rétornaz 	.set_sysclk	= mc13783_set_sysclk_codec,
6518b908b86SPhilippe Rétornaz 	.set_tdm_slot	= mc13783_set_tdm_slot_codec,
6528b908b86SPhilippe Rétornaz };
6538b908b86SPhilippe Rétornaz 
6548b908b86SPhilippe Rétornaz /*
6558b908b86SPhilippe Rétornaz  * The mc13783 has two SSI ports, both of them can be routed either
6568b908b86SPhilippe Rétornaz  * to the voice codec or the stereo DAC. When two different SSI ports
6578b908b86SPhilippe Rétornaz  * are used for the voice codec and the stereo DAC we can do different
6588b908b86SPhilippe Rétornaz  * formats and sysclock settings for playback and capture
6598b908b86SPhilippe Rétornaz  * (mc13783-hifi-playback and mc13783-hifi-capture). Using the same port
6608b908b86SPhilippe Rétornaz  * forces us to use symmetric rates (mc13783-hifi).
6618b908b86SPhilippe Rétornaz  */
6628b908b86SPhilippe Rétornaz static struct snd_soc_dai_driver mc13783_dai_async[] = {
6638b908b86SPhilippe Rétornaz 	{
6648b908b86SPhilippe Rétornaz 		.name = "mc13783-hifi-playback",
6658b908b86SPhilippe Rétornaz 		.id = MC13783_ID_STEREO_DAC,
6668b908b86SPhilippe Rétornaz 		.playback = {
6678b908b86SPhilippe Rétornaz 			.stream_name = "Playback",
66837f45cc5SFabio Estevam 			.channels_min = 2,
6698b908b86SPhilippe Rétornaz 			.channels_max = 2,
6708b908b86SPhilippe Rétornaz 			.rates = SNDRV_PCM_RATE_8000_96000,
6718b908b86SPhilippe Rétornaz 			.formats = MC13783_FORMATS,
6728b908b86SPhilippe Rétornaz 		},
6738b908b86SPhilippe Rétornaz 		.ops = &mc13783_ops_dac,
6748b908b86SPhilippe Rétornaz 	}, {
6758b908b86SPhilippe Rétornaz 		.name = "mc13783-hifi-capture",
6768b908b86SPhilippe Rétornaz 		.id = MC13783_ID_STEREO_CODEC,
6778b908b86SPhilippe Rétornaz 		.capture = {
6788b908b86SPhilippe Rétornaz 			.stream_name = "Capture",
67937f45cc5SFabio Estevam 			.channels_min = 2,
6808b908b86SPhilippe Rétornaz 			.channels_max = 2,
6818b908b86SPhilippe Rétornaz 			.rates = MC13783_RATES_RECORD,
6828b908b86SPhilippe Rétornaz 			.formats = MC13783_FORMATS,
6838b908b86SPhilippe Rétornaz 		},
6848b908b86SPhilippe Rétornaz 		.ops = &mc13783_ops_codec,
6858b908b86SPhilippe Rétornaz 	},
6868b908b86SPhilippe Rétornaz };
6878b908b86SPhilippe Rétornaz 
68864793047SAxel Lin static const struct snd_soc_dai_ops mc13783_ops_sync = {
6898b908b86SPhilippe Rétornaz 	.hw_params	= mc13783_pcm_hw_params_sync,
6908b908b86SPhilippe Rétornaz 	.set_fmt	= mc13783_set_fmt_sync,
6918b908b86SPhilippe Rétornaz 	.set_sysclk	= mc13783_set_sysclk_sync,
6928b908b86SPhilippe Rétornaz 	.set_tdm_slot	= mc13783_set_tdm_slot_sync,
6938b908b86SPhilippe Rétornaz };
6948b908b86SPhilippe Rétornaz 
6958b908b86SPhilippe Rétornaz static struct snd_soc_dai_driver mc13783_dai_sync[] = {
6968b908b86SPhilippe Rétornaz 	{
6978b908b86SPhilippe Rétornaz 		.name = "mc13783-hifi",
6988b908b86SPhilippe Rétornaz 		.id = MC13783_ID_SYNC,
6998b908b86SPhilippe Rétornaz 		.playback = {
7008b908b86SPhilippe Rétornaz 			.stream_name = "Playback",
70137f45cc5SFabio Estevam 			.channels_min = 2,
7028b908b86SPhilippe Rétornaz 			.channels_max = 2,
7038b908b86SPhilippe Rétornaz 			.rates = SNDRV_PCM_RATE_8000_96000,
7048b908b86SPhilippe Rétornaz 			.formats = MC13783_FORMATS,
7058b908b86SPhilippe Rétornaz 		},
7068b908b86SPhilippe Rétornaz 		.capture = {
7078b908b86SPhilippe Rétornaz 			.stream_name = "Capture",
70837f45cc5SFabio Estevam 			.channels_min = 2,
7098b908b86SPhilippe Rétornaz 			.channels_max = 2,
7108b908b86SPhilippe Rétornaz 			.rates = MC13783_RATES_RECORD,
7118b908b86SPhilippe Rétornaz 			.formats = MC13783_FORMATS,
7128b908b86SPhilippe Rétornaz 		},
7138b908b86SPhilippe Rétornaz 		.ops = &mc13783_ops_sync,
71423df7f69SKuninori Morimoto 		.symmetric_rate = 1,
7158b908b86SPhilippe Rétornaz 	}
7168b908b86SPhilippe Rétornaz };
7178b908b86SPhilippe Rétornaz 
71878c97ec0SKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_mc13783 = {
7198b908b86SPhilippe Rétornaz 	.probe			= mc13783_probe,
7208b908b86SPhilippe Rétornaz 	.remove			= mc13783_remove,
721b77458daSPhilippe Rétornaz 	.controls		= mc13783_control_list,
722b77458daSPhilippe Rétornaz 	.num_controls		= ARRAY_SIZE(mc13783_control_list),
723b77458daSPhilippe Rétornaz 	.dapm_widgets		= mc13783_dapm_widgets,
724b77458daSPhilippe Rétornaz 	.num_dapm_widgets	= ARRAY_SIZE(mc13783_dapm_widgets),
725b77458daSPhilippe Rétornaz 	.dapm_routes		= mc13783_routes,
726b77458daSPhilippe Rétornaz 	.num_dapm_routes	= ARRAY_SIZE(mc13783_routes),
72778c97ec0SKuninori Morimoto 	.idle_bias_on		= 1,
72878c97ec0SKuninori Morimoto 	.use_pmdown_time	= 1,
72978c97ec0SKuninori Morimoto 	.endianness		= 1,
7308b908b86SPhilippe Rétornaz };
7318b908b86SPhilippe Rétornaz 
mc13783_codec_probe(struct platform_device * pdev)732a5d3f6abSAlexander Shiyan static int __init mc13783_codec_probe(struct platform_device *pdev)
7338b908b86SPhilippe Rétornaz {
7348b908b86SPhilippe Rétornaz 	struct mc13783_priv *priv;
7358b908b86SPhilippe Rétornaz 	struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data;
736780aaeffSAlexander Shiyan 	struct device_node *np;
7378b908b86SPhilippe Rétornaz 	int ret;
7388b908b86SPhilippe Rétornaz 
7398b908b86SPhilippe Rétornaz 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
7402b32098fSAlexander Shiyan 	if (!priv)
7418b908b86SPhilippe Rétornaz 		return -ENOMEM;
7428b908b86SPhilippe Rétornaz 
7438b908b86SPhilippe Rétornaz 	if (pdata) {
7448b908b86SPhilippe Rétornaz 		priv->adc_ssi_port = pdata->adc_ssi_port;
7458b908b86SPhilippe Rétornaz 		priv->dac_ssi_port = pdata->dac_ssi_port;
7468b908b86SPhilippe Rétornaz 	} else {
747780aaeffSAlexander Shiyan 		np = of_get_child_by_name(pdev->dev.parent->of_node, "codec");
748780aaeffSAlexander Shiyan 		if (!np)
749295b8423SAlexander Shiyan 			return -ENOSYS;
750780aaeffSAlexander Shiyan 
751780aaeffSAlexander Shiyan 		ret = of_property_read_u32(np, "adc-port", &priv->adc_ssi_port);
752a66ae631SMark Brown 		if (ret) {
753a66ae631SMark Brown 			of_node_put(np);
754a66ae631SMark Brown 			return ret;
755a66ae631SMark Brown 		}
756780aaeffSAlexander Shiyan 
757780aaeffSAlexander Shiyan 		ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port);
758a66ae631SMark Brown 		if (ret) {
759a66ae631SMark Brown 			of_node_put(np);
760a66ae631SMark Brown 			return ret;
761a66ae631SMark Brown 		}
762a66ae631SMark Brown 
763a66ae631SMark Brown 		of_node_put(np);
7648b908b86SPhilippe Rétornaz 	}
7658b908b86SPhilippe Rétornaz 
7662b32098fSAlexander Shiyan 	dev_set_drvdata(&pdev->dev, priv);
7672b32098fSAlexander Shiyan 	priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
7682b32098fSAlexander Shiyan 
7698b908b86SPhilippe Rétornaz 	if (priv->adc_ssi_port == priv->dac_ssi_port)
77078c97ec0SKuninori Morimoto 		ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_mc13783,
7718b908b86SPhilippe Rétornaz 			mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync));
7728b908b86SPhilippe Rétornaz 	else
77378c97ec0SKuninori Morimoto 		ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_mc13783,
7748b908b86SPhilippe Rétornaz 			mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
7758b908b86SPhilippe Rétornaz 
7768b908b86SPhilippe Rétornaz 	return ret;
7778b908b86SPhilippe Rétornaz }
7788b908b86SPhilippe Rétornaz 
7798b908b86SPhilippe Rétornaz static struct platform_driver mc13783_codec_driver = {
7808b908b86SPhilippe Rétornaz 	.driver = {
7818b908b86SPhilippe Rétornaz 		.name	= "mc13783-codec",
7828b908b86SPhilippe Rétornaz 	},
7838b908b86SPhilippe Rétornaz };
784a5d3f6abSAlexander Shiyan module_platform_driver_probe(mc13783_codec_driver, mc13783_codec_probe);
7858b908b86SPhilippe Rétornaz 
7868b908b86SPhilippe Rétornaz MODULE_DESCRIPTION("ASoC MC13783 driver");
7878b908b86SPhilippe Rétornaz MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>");
7888b908b86SPhilippe Rétornaz MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>");
7898b908b86SPhilippe Rétornaz MODULE_LICENSE("GPL");
790