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