1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 236c68493SMylène Josserand /* 336c68493SMylène Josserand * This driver supports the digital controls for the internal codec 436c68493SMylène Josserand * found in Allwinner's A33 SoCs. 536c68493SMylène Josserand * 636c68493SMylène Josserand * (C) Copyright 2010-2016 736c68493SMylène Josserand * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com> 836c68493SMylène Josserand * huangxin <huangxin@Reuuimllatech.com> 936c68493SMylène Josserand * Mylène Josserand <mylene.josserand@free-electrons.com> 1036c68493SMylène Josserand */ 1136c68493SMylène Josserand 1236c68493SMylène Josserand #include <linux/module.h> 1336c68493SMylène Josserand #include <linux/delay.h> 1436c68493SMylène Josserand #include <linux/clk.h> 1536c68493SMylène Josserand #include <linux/io.h> 1690cac932SSamuel Holland #include <linux/of_device.h> 1736c68493SMylène Josserand #include <linux/pm_runtime.h> 1836c68493SMylène Josserand #include <linux/regmap.h> 1913c3bf17SVasily Khoruzhick #include <linux/log2.h> 2036c68493SMylène Josserand 2136c68493SMylène Josserand #include <sound/pcm_params.h> 2236c68493SMylène Josserand #include <sound/soc.h> 2336c68493SMylène Josserand #include <sound/soc-dapm.h> 2436c68493SMylène Josserand 2536c68493SMylène Josserand #define SUN8I_SYSCLK_CTL 0x00c 2636c68493SMylène Josserand #define SUN8I_SYSCLK_CTL_AIF1CLK_ENA 11 27d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL (0x2 << 8) 28d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_AIF2CLK_ENA 7 29d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_PLL (0x2 << 4) 3036c68493SMylène Josserand #define SUN8I_SYSCLK_CTL_SYSCLK_ENA 3 3136c68493SMylène Josserand #define SUN8I_SYSCLK_CTL_SYSCLK_SRC 0 32d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK (0x0 << 0) 33d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF2CLK (0x1 << 0) 3436c68493SMylène Josserand #define SUN8I_MOD_CLK_ENA 0x010 3536c68493SMylène Josserand #define SUN8I_MOD_CLK_ENA_AIF1 15 36eda85d1fSMylene JOSSERAND #define SUN8I_MOD_CLK_ENA_ADC 3 3736c68493SMylène Josserand #define SUN8I_MOD_CLK_ENA_DAC 2 3836c68493SMylène Josserand #define SUN8I_MOD_RST_CTL 0x014 3936c68493SMylène Josserand #define SUN8I_MOD_RST_CTL_AIF1 15 40eda85d1fSMylene JOSSERAND #define SUN8I_MOD_RST_CTL_ADC 3 4136c68493SMylène Josserand #define SUN8I_MOD_RST_CTL_DAC 2 4236c68493SMylène Josserand #define SUN8I_SYS_SR_CTRL 0x018 4336c68493SMylène Josserand #define SUN8I_SYS_SR_CTRL_AIF1_FS 12 4436c68493SMylène Josserand #define SUN8I_SYS_SR_CTRL_AIF2_FS 8 4536c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL 0x040 4636c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD 15 4736c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV 14 4836c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV 13 4936c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV 9 5036c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV 6 5136c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ 4 5236c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16 (1 << 4) 5336c68493SMylène Josserand #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT 2 54eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_ADCDAT_CTRL 0x044 55fa5c0ca1SSamuel Holland #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA 15 56fa5c0ca1SSamuel Holland #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA 14 5736c68493SMylène Josserand #define SUN8I_AIF1_DACDAT_CTRL 0x048 5836c68493SMylène Josserand #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA 15 5936c68493SMylène Josserand #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA 14 60eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC 0x04c 610ba95493SSamuel Holland #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L 15 620ba95493SSamuel Holland #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL 14 630ba95493SSamuel Holland #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL 13 640ba95493SSamuel Holland #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR 12 65eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R 11 66eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR 10 67eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR 9 68eda85d1fSMylene JOSSERAND #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL 8 69eda85d1fSMylene JOSSERAND #define SUN8I_ADC_DIG_CTRL 0x100 7030aff91eSSamuel Holland #define SUN8I_ADC_DIG_CTRL_ENAD 15 71eda85d1fSMylene JOSSERAND #define SUN8I_ADC_DIG_CTRL_ADOUT_DTS 2 72eda85d1fSMylene JOSSERAND #define SUN8I_ADC_DIG_CTRL_ADOUT_DLY 1 7336c68493SMylène Josserand #define SUN8I_DAC_DIG_CTRL 0x120 7436c68493SMylène Josserand #define SUN8I_DAC_DIG_CTRL_ENDA 15 7536c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC 0x130 7636c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L 15 7736c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L 14 7836c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL 13 7936c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL 12 8036c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R 11 8136c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R 10 8236c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR 9 8336c68493SMylène Josserand #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR 8 8436c68493SMylène Josserand 85d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK GENMASK(9, 8) 86d8f00682SSamuel Holland #define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK GENMASK(5, 4) 8736c68493SMylène Josserand #define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK GENMASK(15, 12) 8836c68493SMylène Josserand #define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK GENMASK(11, 8) 89316b7758SMaxime Ripard #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK GENMASK(12, 9) 90f30ef55cSSamuel Holland #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6) 91f30ef55cSSamuel Holland #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK GENMASK(5, 4) 92f30ef55cSSamuel Holland #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK GENMASK(3, 2) 9336c68493SMylène Josserand 9490cac932SSamuel Holland struct sun8i_codec_quirks { 9590cac932SSamuel Holland bool legacy_widgets : 1; 967518805fSSamuel Holland bool lrck_inversion : 1; 9790cac932SSamuel Holland }; 9890cac932SSamuel Holland 9936c68493SMylène Josserand struct sun8i_codec { 10036c68493SMylène Josserand struct regmap *regmap; 10136c68493SMylène Josserand struct clk *clk_module; 10290cac932SSamuel Holland const struct sun8i_codec_quirks *quirks; 10336c68493SMylène Josserand }; 10436c68493SMylène Josserand 10536c68493SMylène Josserand static int sun8i_codec_runtime_resume(struct device *dev) 10636c68493SMylène Josserand { 10736c68493SMylène Josserand struct sun8i_codec *scodec = dev_get_drvdata(dev); 10836c68493SMylène Josserand int ret; 10936c68493SMylène Josserand 11036c68493SMylène Josserand regcache_cache_only(scodec->regmap, false); 11136c68493SMylène Josserand 11236c68493SMylène Josserand ret = regcache_sync(scodec->regmap); 11336c68493SMylène Josserand if (ret) { 11436c68493SMylène Josserand dev_err(dev, "Failed to sync regmap cache\n"); 1156b3bb3c8SSamuel Holland return ret; 11636c68493SMylène Josserand } 11736c68493SMylène Josserand 11836c68493SMylène Josserand return 0; 11936c68493SMylène Josserand } 12036c68493SMylène Josserand 12136c68493SMylène Josserand static int sun8i_codec_runtime_suspend(struct device *dev) 12236c68493SMylène Josserand { 12336c68493SMylène Josserand struct sun8i_codec *scodec = dev_get_drvdata(dev); 12436c68493SMylène Josserand 12536c68493SMylène Josserand regcache_cache_only(scodec->regmap, true); 12636c68493SMylène Josserand regcache_mark_dirty(scodec->regmap); 12736c68493SMylène Josserand 12836c68493SMylène Josserand return 0; 12936c68493SMylène Josserand } 13036c68493SMylène Josserand 13136c68493SMylène Josserand static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params) 13236c68493SMylène Josserand { 13336c68493SMylène Josserand unsigned int rate = params_rate(params); 13436c68493SMylène Josserand 13536c68493SMylène Josserand switch (rate) { 13636c68493SMylène Josserand case 8000: 13736c68493SMylène Josserand case 7350: 13836c68493SMylène Josserand return 0x0; 13936c68493SMylène Josserand case 11025: 14036c68493SMylène Josserand return 0x1; 14136c68493SMylène Josserand case 12000: 14236c68493SMylène Josserand return 0x2; 14336c68493SMylène Josserand case 16000: 14436c68493SMylène Josserand return 0x3; 14536c68493SMylène Josserand case 22050: 14636c68493SMylène Josserand return 0x4; 14736c68493SMylène Josserand case 24000: 14836c68493SMylène Josserand return 0x5; 14936c68493SMylène Josserand case 32000: 15036c68493SMylène Josserand return 0x6; 15136c68493SMylène Josserand case 44100: 15236c68493SMylène Josserand return 0x7; 15336c68493SMylène Josserand case 48000: 15436c68493SMylène Josserand return 0x8; 15536c68493SMylène Josserand case 96000: 15636c68493SMylène Josserand return 0x9; 15736c68493SMylène Josserand case 192000: 15836c68493SMylène Josserand return 0xa; 15936c68493SMylène Josserand default: 16036c68493SMylène Josserand return -EINVAL; 16136c68493SMylène Josserand } 16236c68493SMylène Josserand } 16336c68493SMylène Josserand 16436c68493SMylène Josserand static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 16536c68493SMylène Josserand { 1667ec9b872SKuninori Morimoto struct sun8i_codec *scodec = snd_soc_component_get_drvdata(dai->component); 16736c68493SMylène Josserand u32 value; 16836c68493SMylène Josserand 16936c68493SMylène Josserand /* clock masters */ 17036c68493SMylène Josserand switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 171560bfe77SMaxime Ripard case SND_SOC_DAIFMT_CBS_CFS: /* Codec slave, DAI master */ 172560bfe77SMaxime Ripard value = 0x1; 17336c68493SMylène Josserand break; 174560bfe77SMaxime Ripard case SND_SOC_DAIFMT_CBM_CFM: /* Codec Master, DAI slave */ 175560bfe77SMaxime Ripard value = 0x0; 17636c68493SMylène Josserand break; 17736c68493SMylène Josserand default: 17836c68493SMylène Josserand return -EINVAL; 17936c68493SMylène Josserand } 18036c68493SMylène Josserand regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, 18136c68493SMylène Josserand BIT(SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD), 18236c68493SMylène Josserand value << SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD); 18336c68493SMylène Josserand 18436c68493SMylène Josserand /* clock inversion */ 18536c68493SMylène Josserand switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 18636c68493SMylène Josserand case SND_SOC_DAIFMT_NB_NF: /* Normal */ 18736c68493SMylène Josserand value = 0x0; 18836c68493SMylène Josserand break; 18936c68493SMylène Josserand case SND_SOC_DAIFMT_IB_IF: /* Inversion */ 19036c68493SMylène Josserand value = 0x1; 19136c68493SMylène Josserand break; 19236c68493SMylène Josserand default: 19336c68493SMylène Josserand return -EINVAL; 19436c68493SMylène Josserand } 19536c68493SMylène Josserand regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, 19636c68493SMylène Josserand BIT(SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV), 19736c68493SMylène Josserand value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV); 198e7b8a6d3SMaxime Ripard 199e7b8a6d3SMaxime Ripard /* 2007518805fSSamuel Holland * It appears that the DAI and the codec in the A33 SoC don't 2017518805fSSamuel Holland * share the same polarity for the LRCK signal when they mean 2027518805fSSamuel Holland * 'normal' and 'inverted' in the datasheet. 203e7b8a6d3SMaxime Ripard * 204e7b8a6d3SMaxime Ripard * Since the DAI here is our regular i2s driver that have been 205e7b8a6d3SMaxime Ripard * tested with way more codecs than just this one, it means 206e7b8a6d3SMaxime Ripard * that the codec probably gets it backward, and we have to 207e7b8a6d3SMaxime Ripard * invert the value here. 208e7b8a6d3SMaxime Ripard */ 2097518805fSSamuel Holland value ^= scodec->quirks->lrck_inversion; 21036c68493SMylène Josserand regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, 21136c68493SMylène Josserand BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV), 2127518805fSSamuel Holland value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV); 21336c68493SMylène Josserand 21436c68493SMylène Josserand /* DAI format */ 21536c68493SMylène Josserand switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 21636c68493SMylène Josserand case SND_SOC_DAIFMT_I2S: 21736c68493SMylène Josserand value = 0x0; 21836c68493SMylène Josserand break; 21936c68493SMylène Josserand case SND_SOC_DAIFMT_LEFT_J: 22036c68493SMylène Josserand value = 0x1; 22136c68493SMylène Josserand break; 22236c68493SMylène Josserand case SND_SOC_DAIFMT_RIGHT_J: 22336c68493SMylène Josserand value = 0x2; 22436c68493SMylène Josserand break; 22536c68493SMylène Josserand case SND_SOC_DAIFMT_DSP_A: 22636c68493SMylène Josserand case SND_SOC_DAIFMT_DSP_B: 22736c68493SMylène Josserand value = 0x3; 22836c68493SMylène Josserand break; 22936c68493SMylène Josserand default: 23036c68493SMylène Josserand return -EINVAL; 23136c68493SMylène Josserand } 23236c68493SMylène Josserand regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, 23396781fd9SSamuel Holland SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK, 23436c68493SMylène Josserand value << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT); 23536c68493SMylène Josserand 23636c68493SMylène Josserand return 0; 23736c68493SMylène Josserand } 23836c68493SMylène Josserand 239316b7758SMaxime Ripard struct sun8i_codec_clk_div { 240316b7758SMaxime Ripard u8 div; 241316b7758SMaxime Ripard u8 val; 242316b7758SMaxime Ripard }; 243316b7758SMaxime Ripard 244316b7758SMaxime Ripard static const struct sun8i_codec_clk_div sun8i_codec_bclk_div[] = { 245316b7758SMaxime Ripard { .div = 1, .val = 0 }, 246316b7758SMaxime Ripard { .div = 2, .val = 1 }, 247316b7758SMaxime Ripard { .div = 4, .val = 2 }, 248316b7758SMaxime Ripard { .div = 6, .val = 3 }, 249316b7758SMaxime Ripard { .div = 8, .val = 4 }, 250316b7758SMaxime Ripard { .div = 12, .val = 5 }, 251316b7758SMaxime Ripard { .div = 16, .val = 6 }, 252316b7758SMaxime Ripard { .div = 24, .val = 7 }, 253316b7758SMaxime Ripard { .div = 32, .val = 8 }, 254316b7758SMaxime Ripard { .div = 48, .val = 9 }, 255316b7758SMaxime Ripard { .div = 64, .val = 10 }, 256316b7758SMaxime Ripard { .div = 96, .val = 11 }, 257316b7758SMaxime Ripard { .div = 128, .val = 12 }, 258316b7758SMaxime Ripard { .div = 192, .val = 13 }, 259316b7758SMaxime Ripard }; 260316b7758SMaxime Ripard 261316b7758SMaxime Ripard static u8 sun8i_codec_get_bclk_div(struct sun8i_codec *scodec, 262316b7758SMaxime Ripard unsigned int rate, 263316b7758SMaxime Ripard unsigned int word_size) 264316b7758SMaxime Ripard { 265316b7758SMaxime Ripard unsigned long clk_rate = clk_get_rate(scodec->clk_module); 266316b7758SMaxime Ripard unsigned int div = clk_rate / rate / word_size / 2; 267316b7758SMaxime Ripard unsigned int best_val = 0, best_diff = ~0; 268316b7758SMaxime Ripard int i; 269316b7758SMaxime Ripard 270316b7758SMaxime Ripard for (i = 0; i < ARRAY_SIZE(sun8i_codec_bclk_div); i++) { 271316b7758SMaxime Ripard const struct sun8i_codec_clk_div *bdiv = &sun8i_codec_bclk_div[i]; 272316b7758SMaxime Ripard unsigned int diff = abs(bdiv->div - div); 273316b7758SMaxime Ripard 274316b7758SMaxime Ripard if (diff < best_diff) { 275316b7758SMaxime Ripard best_diff = diff; 276316b7758SMaxime Ripard best_val = bdiv->val; 277316b7758SMaxime Ripard } 278316b7758SMaxime Ripard } 279316b7758SMaxime Ripard 280316b7758SMaxime Ripard return best_val; 281316b7758SMaxime Ripard } 282316b7758SMaxime Ripard 28313c3bf17SVasily Khoruzhick static int sun8i_codec_get_lrck_div(unsigned int channels, 28413c3bf17SVasily Khoruzhick unsigned int word_size) 28513c3bf17SVasily Khoruzhick { 28613c3bf17SVasily Khoruzhick unsigned int div = word_size * channels; 28713c3bf17SVasily Khoruzhick 28813c3bf17SVasily Khoruzhick if (div < 16 || div > 256) 28913c3bf17SVasily Khoruzhick return -EINVAL; 29013c3bf17SVasily Khoruzhick 29113c3bf17SVasily Khoruzhick return ilog2(div) - 4; 29213c3bf17SVasily Khoruzhick } 29313c3bf17SVasily Khoruzhick 29436c68493SMylène Josserand static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, 29536c68493SMylène Josserand struct snd_pcm_hw_params *params, 29636c68493SMylène Josserand struct snd_soc_dai *dai) 29736c68493SMylène Josserand { 2987ec9b872SKuninori Morimoto struct sun8i_codec *scodec = snd_soc_component_get_drvdata(dai->component); 29913c3bf17SVasily Khoruzhick int sample_rate, lrck_div; 300316b7758SMaxime Ripard u8 bclk_div; 30136c68493SMylène Josserand 30236c68493SMylène Josserand /* 30336c68493SMylène Josserand * The CPU DAI handles only a sample of 16 bits. Configure the 30436c68493SMylène Josserand * codec to handle this type of sample resolution. 30536c68493SMylène Josserand */ 30636c68493SMylène Josserand regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, 30736c68493SMylène Josserand SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK, 30836c68493SMylène Josserand SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16); 30936c68493SMylène Josserand 310316b7758SMaxime Ripard bclk_div = sun8i_codec_get_bclk_div(scodec, params_rate(params), 16); 311316b7758SMaxime Ripard regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, 312316b7758SMaxime Ripard SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK, 313316b7758SMaxime Ripard bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV); 314316b7758SMaxime Ripard 31513c3bf17SVasily Khoruzhick lrck_div = sun8i_codec_get_lrck_div(params_channels(params), 31613c3bf17SVasily Khoruzhick params_physical_width(params)); 31713c3bf17SVasily Khoruzhick if (lrck_div < 0) 31813c3bf17SVasily Khoruzhick return lrck_div; 31913c3bf17SVasily Khoruzhick 32036c68493SMylène Josserand regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, 32136c68493SMylène Josserand SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK, 32213c3bf17SVasily Khoruzhick lrck_div << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV); 32336c68493SMylène Josserand 32436c68493SMylène Josserand sample_rate = sun8i_codec_get_hw_rate(params); 32536c68493SMylène Josserand if (sample_rate < 0) 32636c68493SMylène Josserand return sample_rate; 32736c68493SMylène Josserand 32836c68493SMylène Josserand regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL, 32936c68493SMylène Josserand SUN8I_SYS_SR_CTRL_AIF1_FS_MASK, 33036c68493SMylène Josserand sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS); 33136c68493SMylène Josserand 33236c68493SMylène Josserand return 0; 33336c68493SMylène Josserand } 33436c68493SMylène Josserand 335ca14da6eSMylène Josserand static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = { 336ca14da6eSMylène Josserand SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital DAC Playback Switch", 337ca14da6eSMylène Josserand SUN8I_DAC_MXR_SRC, 338ca14da6eSMylène Josserand SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L, 33936c68493SMylène Josserand SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R, 1, 0), 340ca14da6eSMylène Josserand SOC_DAPM_DOUBLE("AIF1 Slot 1 Digital DAC Playback Switch", 341ca14da6eSMylène Josserand SUN8I_DAC_MXR_SRC, 342ca14da6eSMylène Josserand SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L, 34336c68493SMylène Josserand SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R, 1, 0), 344ca14da6eSMylène Josserand SOC_DAPM_DOUBLE("AIF2 Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC, 345ca14da6eSMylène Josserand SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL, 34636c68493SMylène Josserand SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR, 1, 0), 347ca14da6eSMylène Josserand SOC_DAPM_DOUBLE("ADC Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC, 348ca14da6eSMylène Josserand SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL, 34936c68493SMylène Josserand SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0), 35036c68493SMylène Josserand }; 35136c68493SMylène Josserand 352eda85d1fSMylene JOSSERAND static const struct snd_kcontrol_new sun8i_input_mixer_controls[] = { 353eda85d1fSMylene JOSSERAND SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch", 354eda85d1fSMylene JOSSERAND SUN8I_AIF1_MXR_SRC, 3550ba95493SSamuel Holland SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L, 356eda85d1fSMylene JOSSERAND SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0), 357eda85d1fSMylene JOSSERAND SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC, 3580ba95493SSamuel Holland SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL, 359eda85d1fSMylene JOSSERAND SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0), 360eda85d1fSMylene JOSSERAND SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch", 361eda85d1fSMylene JOSSERAND SUN8I_AIF1_MXR_SRC, 3620ba95493SSamuel Holland SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL, 363eda85d1fSMylene JOSSERAND SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0), 364eda85d1fSMylene JOSSERAND SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch", 365eda85d1fSMylene JOSSERAND SUN8I_AIF1_MXR_SRC, 3660ba95493SSamuel Holland SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR, 367eda85d1fSMylene JOSSERAND SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0), 368eda85d1fSMylene JOSSERAND }; 369eda85d1fSMylene JOSSERAND 37036c68493SMylène Josserand static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { 371d8f00682SSamuel Holland /* System Clocks */ 3726b3bb3c8SSamuel Holland SND_SOC_DAPM_CLOCK_SUPPLY("mod"), 3736b3bb3c8SSamuel Holland 374d8f00682SSamuel Holland SND_SOC_DAPM_SUPPLY("AIF1CLK", 375d8f00682SSamuel Holland SUN8I_SYSCLK_CTL, 376d8f00682SSamuel Holland SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0), 377d8f00682SSamuel Holland SND_SOC_DAPM_SUPPLY("SYSCLK", 378d8f00682SSamuel Holland SUN8I_SYSCLK_CTL, 379d8f00682SSamuel Holland SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0), 380d8f00682SSamuel Holland 381eda85d1fSMylene JOSSERAND /* Digital parts of the DACs and ADC */ 38236c68493SMylène Josserand SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA, 38336c68493SMylène Josserand 0, NULL, 0), 38430aff91eSSamuel Holland SND_SOC_DAPM_SUPPLY("ADC", SUN8I_ADC_DIG_CTRL, SUN8I_ADC_DIG_CTRL_ENAD, 385eda85d1fSMylene JOSSERAND 0, NULL, 0), 38636c68493SMylène Josserand 38790cac932SSamuel Holland /* AIF "DAC" Inputs */ 38890cac932SSamuel Holland SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0, 3899123aa86SMylène Josserand SUN8I_AIF1_DACDAT_CTRL, 39036c68493SMylène Josserand SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0), 39190cac932SSamuel Holland SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 0, 3929123aa86SMylène Josserand SUN8I_AIF1_DACDAT_CTRL, 39336c68493SMylène Josserand SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0), 39436c68493SMylène Josserand 39590cac932SSamuel Holland /* AIF "ADC" Outputs */ 39690cac932SSamuel Holland SND_SOC_DAPM_AIF_IN("AIF1 AD0L", "Capture", 0, 397eda85d1fSMylene JOSSERAND SUN8I_AIF1_ADCDAT_CTRL, 398fa5c0ca1SSamuel Holland SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0), 39990cac932SSamuel Holland SND_SOC_DAPM_AIF_IN("AIF1 AD0R", "Capture", 0, 400eda85d1fSMylene JOSSERAND SUN8I_AIF1_ADCDAT_CTRL, 401fa5c0ca1SSamuel Holland SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0), 402eda85d1fSMylene JOSSERAND 40390cac932SSamuel Holland /* ADC Inputs (connected to analog codec DAPM context) */ 40490cac932SSamuel Holland SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0), 40590cac932SSamuel Holland SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0), 40690cac932SSamuel Holland 40790cac932SSamuel Holland /* DAC Outputs (connected to analog codec DAPM context) */ 40890cac932SSamuel Holland SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), 40990cac932SSamuel Holland SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), 41090cac932SSamuel Holland 411eda85d1fSMylene JOSSERAND /* DAC and ADC Mixers */ 412fa22ca4fSMylène Josserand SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0, 413fa22ca4fSMylène Josserand sun8i_dac_mixer_controls), 414fa22ca4fSMylène Josserand SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0, 415fa22ca4fSMylène Josserand sun8i_dac_mixer_controls), 416eda85d1fSMylene JOSSERAND SOC_MIXER_ARRAY("Left Digital ADC Mixer", SND_SOC_NOPM, 0, 0, 417eda85d1fSMylene JOSSERAND sun8i_input_mixer_controls), 418eda85d1fSMylene JOSSERAND SOC_MIXER_ARRAY("Right Digital ADC Mixer", SND_SOC_NOPM, 0, 0, 419eda85d1fSMylene JOSSERAND sun8i_input_mixer_controls), 42036c68493SMylène Josserand 42136c68493SMylène Josserand /* Clocks */ 4222455e37aSSamuel Holland SND_SOC_DAPM_SUPPLY("MODCLK AIF1", SUN8I_MOD_CLK_ENA, 42336c68493SMylène Josserand SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0), 42436c68493SMylène Josserand SND_SOC_DAPM_SUPPLY("MODCLK DAC", SUN8I_MOD_CLK_ENA, 42536c68493SMylène Josserand SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0), 426eda85d1fSMylene JOSSERAND SND_SOC_DAPM_SUPPLY("MODCLK ADC", SUN8I_MOD_CLK_ENA, 427eda85d1fSMylene JOSSERAND SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0), 42836c68493SMylène Josserand 42936c68493SMylène Josserand /* Module reset */ 43036c68493SMylène Josserand SND_SOC_DAPM_SUPPLY("RST AIF1", SUN8I_MOD_RST_CTL, 43136c68493SMylène Josserand SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0), 43236c68493SMylène Josserand SND_SOC_DAPM_SUPPLY("RST DAC", SUN8I_MOD_RST_CTL, 43336c68493SMylène Josserand SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0), 434eda85d1fSMylene JOSSERAND SND_SOC_DAPM_SUPPLY("RST ADC", SUN8I_MOD_RST_CTL, 435eda85d1fSMylene JOSSERAND SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0), 43636c68493SMylène Josserand }; 43736c68493SMylène Josserand 43836c68493SMylène Josserand static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { 43936c68493SMylène Josserand /* Clock Routes */ 440d8f00682SSamuel Holland { "AIF1CLK", NULL, "mod" }, 4416b3bb3c8SSamuel Holland 442d8f00682SSamuel Holland { "SYSCLK", NULL, "AIF1CLK" }, 44390cac932SSamuel Holland 444d8f00682SSamuel Holland { "RST AIF1", NULL, "AIF1CLK" }, 44590cac932SSamuel Holland { "RST AIF1", NULL, "SYSCLK" }, 4462455e37aSSamuel Holland { "MODCLK AIF1", NULL, "RST AIF1" }, 4472455e37aSSamuel Holland { "AIF1 AD0L", NULL, "MODCLK AIF1" }, 4482455e37aSSamuel Holland { "AIF1 AD0R", NULL, "MODCLK AIF1" }, 4492455e37aSSamuel Holland { "AIF1 DA0L", NULL, "MODCLK AIF1" }, 4502455e37aSSamuel Holland { "AIF1 DA0R", NULL, "MODCLK AIF1" }, 45136c68493SMylène Josserand 45236c68493SMylène Josserand { "RST DAC", NULL, "SYSCLK" }, 45336c68493SMylène Josserand { "MODCLK DAC", NULL, "RST DAC" }, 45436c68493SMylène Josserand { "DAC", NULL, "MODCLK DAC" }, 45590cac932SSamuel Holland { "DACL", NULL, "DAC" }, 45690cac932SSamuel Holland { "DACR", NULL, "DAC" }, 45736c68493SMylène Josserand 458eda85d1fSMylene JOSSERAND { "RST ADC", NULL, "SYSCLK" }, 459eda85d1fSMylene JOSSERAND { "MODCLK ADC", NULL, "RST ADC" }, 460eda85d1fSMylene JOSSERAND { "ADC", NULL, "MODCLK ADC" }, 46190cac932SSamuel Holland { "ADCL", NULL, "ADC" }, 46290cac932SSamuel Holland { "ADCR", NULL, "ADC" }, 463eda85d1fSMylene JOSSERAND 46436c68493SMylène Josserand /* DAC Routes */ 46590cac932SSamuel Holland { "DACL", NULL, "Left Digital DAC Mixer" }, 46690cac932SSamuel Holland { "DACR", NULL, "Right Digital DAC Mixer" }, 46736c68493SMylène Josserand 46836c68493SMylène Josserand /* DAC Mixer Routes */ 46990cac932SSamuel Holland { "Left Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L" }, 470e47d2dcdSSamuel Holland { "Left Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCL" }, 471e47d2dcdSSamuel Holland 47290cac932SSamuel Holland { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R" }, 473e47d2dcdSSamuel Holland { "Right Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCR" }, 474eda85d1fSMylene JOSSERAND 4759ee325d0SVasily Khoruzhick /* ADC Routes */ 47690cac932SSamuel Holland { "AIF1 AD0L", NULL, "Left Digital ADC Mixer" }, 47790cac932SSamuel Holland { "AIF1 AD0R", NULL, "Right Digital ADC Mixer" }, 4789ee325d0SVasily Khoruzhick 4799ee325d0SVasily Khoruzhick /* ADC Mixer Routes */ 480e47d2dcdSSamuel Holland { "Left Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" }, 48190cac932SSamuel Holland { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" }, 482e47d2dcdSSamuel Holland 483e47d2dcdSSamuel Holland { "Right Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" }, 48490cac932SSamuel Holland { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" }, 48536c68493SMylène Josserand }; 48636c68493SMylène Josserand 48790cac932SSamuel Holland static const struct snd_soc_dapm_widget sun8i_codec_legacy_widgets[] = { 48890cac932SSamuel Holland /* Legacy ADC Inputs (connected to analog codec DAPM context) */ 48990cac932SSamuel Holland SND_SOC_DAPM_ADC("AIF1 Slot 0 Left ADC", NULL, SND_SOC_NOPM, 0, 0), 49090cac932SSamuel Holland SND_SOC_DAPM_ADC("AIF1 Slot 0 Right ADC", NULL, SND_SOC_NOPM, 0, 0), 49190cac932SSamuel Holland 49290cac932SSamuel Holland /* Legacy DAC Outputs (connected to analog codec DAPM context) */ 49390cac932SSamuel Holland SND_SOC_DAPM_DAC("AIF1 Slot 0 Left", NULL, SND_SOC_NOPM, 0, 0), 49490cac932SSamuel Holland SND_SOC_DAPM_DAC("AIF1 Slot 0 Right", NULL, SND_SOC_NOPM, 0, 0), 49590cac932SSamuel Holland }; 49690cac932SSamuel Holland 49790cac932SSamuel Holland static const struct snd_soc_dapm_route sun8i_codec_legacy_routes[] = { 49890cac932SSamuel Holland /* Legacy ADC Routes */ 49990cac932SSamuel Holland { "ADCL", NULL, "AIF1 Slot 0 Left ADC" }, 50090cac932SSamuel Holland { "ADCR", NULL, "AIF1 Slot 0 Right ADC" }, 50190cac932SSamuel Holland 50290cac932SSamuel Holland /* Legacy DAC Routes */ 50390cac932SSamuel Holland { "AIF1 Slot 0 Left", NULL, "DACL" }, 50490cac932SSamuel Holland { "AIF1 Slot 0 Right", NULL, "DACR" }, 50590cac932SSamuel Holland }; 50690cac932SSamuel Holland 50790cac932SSamuel Holland static int sun8i_codec_component_probe(struct snd_soc_component *component) 50890cac932SSamuel Holland { 50990cac932SSamuel Holland struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 51090cac932SSamuel Holland struct sun8i_codec *scodec = snd_soc_component_get_drvdata(component); 51190cac932SSamuel Holland int ret; 51290cac932SSamuel Holland 51390cac932SSamuel Holland /* Add widgets for backward compatibility with old device trees. */ 51490cac932SSamuel Holland if (scodec->quirks->legacy_widgets) { 51590cac932SSamuel Holland ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_legacy_widgets, 51690cac932SSamuel Holland ARRAY_SIZE(sun8i_codec_legacy_widgets)); 51790cac932SSamuel Holland if (ret) 51890cac932SSamuel Holland return ret; 51990cac932SSamuel Holland 52090cac932SSamuel Holland ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_legacy_routes, 52190cac932SSamuel Holland ARRAY_SIZE(sun8i_codec_legacy_routes)); 52290cac932SSamuel Holland if (ret) 52390cac932SSamuel Holland return ret; 52490cac932SSamuel Holland } 52590cac932SSamuel Holland 526d8f00682SSamuel Holland /* 527d8f00682SSamuel Holland * AIF1CLK and AIF2CLK share a pair of clock parents: PLL_AUDIO ("mod") 528d8f00682SSamuel Holland * and MCLK (from the CPU DAI connected to AIF1). MCLK's parent is also 529d8f00682SSamuel Holland * PLL_AUDIO, so using it adds no additional flexibility. Use PLL_AUDIO 530d8f00682SSamuel Holland * directly to simplify the clock tree. 531d8f00682SSamuel Holland */ 532d8f00682SSamuel Holland regmap_update_bits(scodec->regmap, SUN8I_SYSCLK_CTL, 533d8f00682SSamuel Holland SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK | 534d8f00682SSamuel Holland SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK, 535d8f00682SSamuel Holland SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL | 536d8f00682SSamuel Holland SUN8I_SYSCLK_CTL_AIF2CLK_SRC_PLL); 537d8f00682SSamuel Holland 538d8f00682SSamuel Holland /* Use AIF1CLK as the SYSCLK parent since AIF1 is used most often. */ 539d8f00682SSamuel Holland regmap_update_bits(scodec->regmap, SUN8I_SYSCLK_CTL, 540d8f00682SSamuel Holland BIT(SUN8I_SYSCLK_CTL_SYSCLK_SRC), 541d8f00682SSamuel Holland SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK); 542d8f00682SSamuel Holland 54390cac932SSamuel Holland return 0; 54490cac932SSamuel Holland } 54590cac932SSamuel Holland 546fe49cd98SGustavo A. R. Silva static const struct snd_soc_dai_ops sun8i_codec_dai_ops = { 54736c68493SMylène Josserand .hw_params = sun8i_codec_hw_params, 54836c68493SMylène Josserand .set_fmt = sun8i_set_fmt, 54936c68493SMylène Josserand }; 55036c68493SMylène Josserand 55136c68493SMylène Josserand static struct snd_soc_dai_driver sun8i_codec_dai = { 55236c68493SMylène Josserand .name = "sun8i", 55336c68493SMylène Josserand /* playback capabilities */ 55436c68493SMylène Josserand .playback = { 55536c68493SMylène Josserand .stream_name = "Playback", 55636c68493SMylène Josserand .channels_min = 1, 55736c68493SMylène Josserand .channels_max = 2, 55836c68493SMylène Josserand .rates = SNDRV_PCM_RATE_8000_192000, 55936c68493SMylène Josserand .formats = SNDRV_PCM_FMTBIT_S16_LE, 56036c68493SMylène Josserand }, 561eda85d1fSMylene JOSSERAND /* capture capabilities */ 562eda85d1fSMylene JOSSERAND .capture = { 563eda85d1fSMylene JOSSERAND .stream_name = "Capture", 564eda85d1fSMylene JOSSERAND .channels_min = 1, 565eda85d1fSMylene JOSSERAND .channels_max = 2, 566eda85d1fSMylene JOSSERAND .rates = SNDRV_PCM_RATE_8000_192000, 567eda85d1fSMylene JOSSERAND .formats = SNDRV_PCM_FMTBIT_S16_LE, 568eda85d1fSMylene JOSSERAND .sig_bits = 24, 569eda85d1fSMylene JOSSERAND }, 57036c68493SMylène Josserand /* pcm operations */ 57136c68493SMylène Josserand .ops = &sun8i_codec_dai_ops, 57236c68493SMylène Josserand }; 57336c68493SMylène Josserand 5747ec9b872SKuninori Morimoto static const struct snd_soc_component_driver sun8i_soc_component = { 57536c68493SMylène Josserand .dapm_widgets = sun8i_codec_dapm_widgets, 57636c68493SMylène Josserand .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_dapm_widgets), 57736c68493SMylène Josserand .dapm_routes = sun8i_codec_dapm_routes, 57836c68493SMylène Josserand .num_dapm_routes = ARRAY_SIZE(sun8i_codec_dapm_routes), 579a2f6d303SSamuel Holland .probe = sun8i_codec_component_probe, 5807ec9b872SKuninori Morimoto .idle_bias_on = 1, 5817ec9b872SKuninori Morimoto .use_pmdown_time = 1, 5827ec9b872SKuninori Morimoto .endianness = 1, 5837ec9b872SKuninori Morimoto .non_legacy_dai_naming = 1, 58436c68493SMylène Josserand }; 58536c68493SMylène Josserand 58636c68493SMylène Josserand static const struct regmap_config sun8i_codec_regmap_config = { 58736c68493SMylène Josserand .reg_bits = 32, 58836c68493SMylène Josserand .reg_stride = 4, 58936c68493SMylène Josserand .val_bits = 32, 59036c68493SMylène Josserand .max_register = SUN8I_DAC_MXR_SRC, 59136c68493SMylène Josserand 59236c68493SMylène Josserand .cache_type = REGCACHE_FLAT, 59336c68493SMylène Josserand }; 59436c68493SMylène Josserand 59536c68493SMylène Josserand static int sun8i_codec_probe(struct platform_device *pdev) 59636c68493SMylène Josserand { 59736c68493SMylène Josserand struct sun8i_codec *scodec; 59836c68493SMylène Josserand void __iomem *base; 59936c68493SMylène Josserand int ret; 60036c68493SMylène Josserand 60136c68493SMylène Josserand scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL); 60236c68493SMylène Josserand if (!scodec) 60336c68493SMylène Josserand return -ENOMEM; 60436c68493SMylène Josserand 60536c68493SMylène Josserand scodec->clk_module = devm_clk_get(&pdev->dev, "mod"); 60636c68493SMylène Josserand if (IS_ERR(scodec->clk_module)) { 60736c68493SMylène Josserand dev_err(&pdev->dev, "Failed to get the module clock\n"); 60836c68493SMylène Josserand return PTR_ERR(scodec->clk_module); 60936c68493SMylène Josserand } 61036c68493SMylène Josserand 611790b3657SYueHaibing base = devm_platform_ioremap_resource(pdev, 0); 61236c68493SMylène Josserand if (IS_ERR(base)) { 61336c68493SMylène Josserand dev_err(&pdev->dev, "Failed to map the registers\n"); 61436c68493SMylène Josserand return PTR_ERR(base); 61536c68493SMylène Josserand } 61636c68493SMylène Josserand 617efb736fbSSamuel Holland scodec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "bus", base, 61836c68493SMylène Josserand &sun8i_codec_regmap_config); 61936c68493SMylène Josserand if (IS_ERR(scodec->regmap)) { 62036c68493SMylène Josserand dev_err(&pdev->dev, "Failed to create our regmap\n"); 62136c68493SMylène Josserand return PTR_ERR(scodec->regmap); 62236c68493SMylène Josserand } 62336c68493SMylène Josserand 62490cac932SSamuel Holland scodec->quirks = of_device_get_match_data(&pdev->dev); 62590cac932SSamuel Holland 62636c68493SMylène Josserand platform_set_drvdata(pdev, scodec); 62736c68493SMylène Josserand 62836c68493SMylène Josserand pm_runtime_enable(&pdev->dev); 62936c68493SMylène Josserand if (!pm_runtime_enabled(&pdev->dev)) { 63036c68493SMylène Josserand ret = sun8i_codec_runtime_resume(&pdev->dev); 63136c68493SMylène Josserand if (ret) 63236c68493SMylène Josserand goto err_pm_disable; 63336c68493SMylène Josserand } 63436c68493SMylène Josserand 6357ec9b872SKuninori Morimoto ret = devm_snd_soc_register_component(&pdev->dev, &sun8i_soc_component, 63636c68493SMylène Josserand &sun8i_codec_dai, 1); 63736c68493SMylène Josserand if (ret) { 63836c68493SMylène Josserand dev_err(&pdev->dev, "Failed to register codec\n"); 63936c68493SMylène Josserand goto err_suspend; 64036c68493SMylène Josserand } 64136c68493SMylène Josserand 64236c68493SMylène Josserand return ret; 64336c68493SMylène Josserand 64436c68493SMylène Josserand err_suspend: 64536c68493SMylène Josserand if (!pm_runtime_status_suspended(&pdev->dev)) 64636c68493SMylène Josserand sun8i_codec_runtime_suspend(&pdev->dev); 64736c68493SMylène Josserand 64836c68493SMylène Josserand err_pm_disable: 64936c68493SMylène Josserand pm_runtime_disable(&pdev->dev); 65036c68493SMylène Josserand 65136c68493SMylène Josserand return ret; 65236c68493SMylène Josserand } 65336c68493SMylène Josserand 65436c68493SMylène Josserand static int sun8i_codec_remove(struct platform_device *pdev) 65536c68493SMylène Josserand { 65636c68493SMylène Josserand pm_runtime_disable(&pdev->dev); 65736c68493SMylène Josserand if (!pm_runtime_status_suspended(&pdev->dev)) 65836c68493SMylène Josserand sun8i_codec_runtime_suspend(&pdev->dev); 65936c68493SMylène Josserand 66036c68493SMylène Josserand return 0; 66136c68493SMylène Josserand } 66236c68493SMylène Josserand 66390cac932SSamuel Holland static const struct sun8i_codec_quirks sun8i_a33_quirks = { 66490cac932SSamuel Holland .legacy_widgets = true, 6657518805fSSamuel Holland .lrck_inversion = true, 66690cac932SSamuel Holland }; 66790cac932SSamuel Holland 66890cac932SSamuel Holland static const struct sun8i_codec_quirks sun50i_a64_quirks = { 66990cac932SSamuel Holland }; 67090cac932SSamuel Holland 67136c68493SMylène Josserand static const struct of_device_id sun8i_codec_of_match[] = { 67290cac932SSamuel Holland { .compatible = "allwinner,sun8i-a33-codec", .data = &sun8i_a33_quirks }, 67390cac932SSamuel Holland { .compatible = "allwinner,sun50i-a64-codec", .data = &sun50i_a64_quirks }, 67436c68493SMylène Josserand {} 67536c68493SMylène Josserand }; 67636c68493SMylène Josserand MODULE_DEVICE_TABLE(of, sun8i_codec_of_match); 67736c68493SMylène Josserand 67836c68493SMylène Josserand static const struct dev_pm_ops sun8i_codec_pm_ops = { 67936c68493SMylène Josserand SET_RUNTIME_PM_OPS(sun8i_codec_runtime_suspend, 68036c68493SMylène Josserand sun8i_codec_runtime_resume, NULL) 68136c68493SMylène Josserand }; 68236c68493SMylène Josserand 68336c68493SMylène Josserand static struct platform_driver sun8i_codec_driver = { 68436c68493SMylène Josserand .driver = { 68536c68493SMylène Josserand .name = "sun8i-codec", 68636c68493SMylène Josserand .of_match_table = sun8i_codec_of_match, 68736c68493SMylène Josserand .pm = &sun8i_codec_pm_ops, 68836c68493SMylène Josserand }, 68936c68493SMylène Josserand .probe = sun8i_codec_probe, 69036c68493SMylène Josserand .remove = sun8i_codec_remove, 69136c68493SMylène Josserand }; 69236c68493SMylène Josserand module_platform_driver(sun8i_codec_driver); 69336c68493SMylène Josserand 69436c68493SMylène Josserand MODULE_DESCRIPTION("Allwinner A33 (sun8i) codec driver"); 69536c68493SMylène Josserand MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>"); 69636c68493SMylène Josserand MODULE_LICENSE("GPL"); 69736c68493SMylène Josserand MODULE_ALIAS("platform:sun8i-codec"); 698