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