1decbc00eSZhengShunQian /* 2decbc00eSZhengShunQian * Driver of Inno codec for rk3036 by Rockchip Inc. 3decbc00eSZhengShunQian * 4decbc00eSZhengShunQian * Author: Rockchip Inc. 5decbc00eSZhengShunQian * Author: Zheng ShunQian<zhengsq@rock-chips.com> 6decbc00eSZhengShunQian */ 7decbc00eSZhengShunQian 8decbc00eSZhengShunQian #include <sound/soc.h> 9decbc00eSZhengShunQian #include <sound/tlv.h> 10decbc00eSZhengShunQian #include <sound/soc-dapm.h> 11decbc00eSZhengShunQian #include <sound/soc-dai.h> 12decbc00eSZhengShunQian #include <sound/pcm.h> 13decbc00eSZhengShunQian #include <sound/pcm_params.h> 14decbc00eSZhengShunQian 15decbc00eSZhengShunQian #include <linux/platform_device.h> 16decbc00eSZhengShunQian #include <linux/of.h> 17decbc00eSZhengShunQian #include <linux/clk.h> 18decbc00eSZhengShunQian #include <linux/regmap.h> 19decbc00eSZhengShunQian #include <linux/device.h> 20decbc00eSZhengShunQian #include <linux/mfd/syscon.h> 21decbc00eSZhengShunQian #include <linux/module.h> 22decbc00eSZhengShunQian #include <linux/io.h> 23decbc00eSZhengShunQian 24decbc00eSZhengShunQian #include "inno_rk3036.h" 25decbc00eSZhengShunQian 26decbc00eSZhengShunQian struct rk3036_codec_priv { 27decbc00eSZhengShunQian void __iomem *base; 28decbc00eSZhengShunQian struct clk *pclk; 29decbc00eSZhengShunQian struct regmap *regmap; 30decbc00eSZhengShunQian struct device *dev; 31decbc00eSZhengShunQian }; 32decbc00eSZhengShunQian 33decbc00eSZhengShunQian static const DECLARE_TLV_DB_MINMAX(rk3036_codec_hp_tlv, -39, 0); 34decbc00eSZhengShunQian 35decbc00eSZhengShunQian static int rk3036_codec_antipop_info(struct snd_kcontrol *kcontrol, 36decbc00eSZhengShunQian struct snd_ctl_elem_info *uinfo) 37decbc00eSZhengShunQian { 38decbc00eSZhengShunQian uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 39decbc00eSZhengShunQian uinfo->count = 2; 40decbc00eSZhengShunQian uinfo->value.integer.min = 0; 41decbc00eSZhengShunQian uinfo->value.integer.max = 1; 42decbc00eSZhengShunQian 43decbc00eSZhengShunQian return 0; 44decbc00eSZhengShunQian } 45decbc00eSZhengShunQian 46decbc00eSZhengShunQian static int rk3036_codec_antipop_get(struct snd_kcontrol *kcontrol, 47decbc00eSZhengShunQian struct snd_ctl_elem_value *ucontrol) 48decbc00eSZhengShunQian { 49decbc00eSZhengShunQian struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 50decbc00eSZhengShunQian int val, ret, regval; 51decbc00eSZhengShunQian 52decbc00eSZhengShunQian ret = snd_soc_component_read(component, INNO_R09, ®val); 53decbc00eSZhengShunQian if (ret) 54decbc00eSZhengShunQian return ret; 55decbc00eSZhengShunQian val = ((regval >> INNO_R09_HPL_ANITPOP_SHIFT) & 56decbc00eSZhengShunQian INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON; 57decbc00eSZhengShunQian ucontrol->value.integer.value[0] = val; 58decbc00eSZhengShunQian 59decbc00eSZhengShunQian val = ((regval >> INNO_R09_HPR_ANITPOP_SHIFT) & 60decbc00eSZhengShunQian INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON; 61decbc00eSZhengShunQian ucontrol->value.integer.value[1] = val; 62decbc00eSZhengShunQian 63decbc00eSZhengShunQian return 0; 64decbc00eSZhengShunQian } 65decbc00eSZhengShunQian 66decbc00eSZhengShunQian static int rk3036_codec_antipop_put(struct snd_kcontrol *kcontrol, 67decbc00eSZhengShunQian struct snd_ctl_elem_value *ucontrol) 68decbc00eSZhengShunQian { 69decbc00eSZhengShunQian struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 70decbc00eSZhengShunQian int val, ret, regmsk; 71decbc00eSZhengShunQian 72decbc00eSZhengShunQian val = (ucontrol->value.integer.value[0] ? 73decbc00eSZhengShunQian INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) << 74decbc00eSZhengShunQian INNO_R09_HPL_ANITPOP_SHIFT; 75decbc00eSZhengShunQian val |= (ucontrol->value.integer.value[1] ? 76decbc00eSZhengShunQian INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) << 77decbc00eSZhengShunQian INNO_R09_HPR_ANITPOP_SHIFT; 78decbc00eSZhengShunQian 79decbc00eSZhengShunQian regmsk = INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPL_ANITPOP_SHIFT | 80decbc00eSZhengShunQian INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPR_ANITPOP_SHIFT; 81decbc00eSZhengShunQian 82decbc00eSZhengShunQian ret = snd_soc_component_update_bits(component, INNO_R09, 83decbc00eSZhengShunQian regmsk, val); 84decbc00eSZhengShunQian if (ret < 0) 85decbc00eSZhengShunQian return ret; 86decbc00eSZhengShunQian 87decbc00eSZhengShunQian return 0; 88decbc00eSZhengShunQian } 89decbc00eSZhengShunQian 90decbc00eSZhengShunQian #define SOC_RK3036_CODEC_ANTIPOP_DECL(xname) \ 91decbc00eSZhengShunQian { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 92decbc00eSZhengShunQian .info = rk3036_codec_antipop_info, .get = rk3036_codec_antipop_get, \ 93decbc00eSZhengShunQian .put = rk3036_codec_antipop_put, } 94decbc00eSZhengShunQian 95decbc00eSZhengShunQian static const struct snd_kcontrol_new rk3036_codec_dapm_controls[] = { 96decbc00eSZhengShunQian SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", INNO_R07, INNO_R08, 97decbc00eSZhengShunQian INNO_HP_GAIN_SHIFT, INNO_HP_GAIN_N39DB, 98decbc00eSZhengShunQian INNO_HP_GAIN_0DB, 0, rk3036_codec_hp_tlv), 99decbc00eSZhengShunQian SOC_DOUBLE("Zero Cross Switch", INNO_R06, INNO_R06_VOUTL_CZ_SHIFT, 100decbc00eSZhengShunQian INNO_R06_VOUTR_CZ_SHIFT, 1, 0), 101decbc00eSZhengShunQian SOC_DOUBLE("Headphone Switch", INNO_R09, INNO_R09_HPL_MUTE_SHIFT, 102decbc00eSZhengShunQian INNO_R09_HPR_MUTE_SHIFT, 1, 0), 103decbc00eSZhengShunQian SOC_RK3036_CODEC_ANTIPOP_DECL("Anti-pop Switch"), 104decbc00eSZhengShunQian }; 105decbc00eSZhengShunQian 106decbc00eSZhengShunQian static const struct snd_kcontrol_new rk3036_codec_hpl_mixer_controls[] = { 107decbc00eSZhengShunQian SOC_DAPM_SINGLE("DAC Left Out Switch", INNO_R09, 108decbc00eSZhengShunQian INNO_R09_DACL_SWITCH_SHIFT, 1, 0), 109decbc00eSZhengShunQian }; 110decbc00eSZhengShunQian 111decbc00eSZhengShunQian static const struct snd_kcontrol_new rk3036_codec_hpr_mixer_controls[] = { 112decbc00eSZhengShunQian SOC_DAPM_SINGLE("DAC Right Out Switch", INNO_R09, 113decbc00eSZhengShunQian INNO_R09_DACR_SWITCH_SHIFT, 1, 0), 114decbc00eSZhengShunQian }; 115decbc00eSZhengShunQian 116decbc00eSZhengShunQian static const struct snd_kcontrol_new rk3036_codec_hpl_switch_controls[] = { 117decbc00eSZhengShunQian SOC_DAPM_SINGLE("HP Left Out Switch", INNO_R05, 118decbc00eSZhengShunQian INNO_R05_HPL_WORK_SHIFT, 1, 0), 119decbc00eSZhengShunQian }; 120decbc00eSZhengShunQian 121decbc00eSZhengShunQian static const struct snd_kcontrol_new rk3036_codec_hpr_switch_controls[] = { 122decbc00eSZhengShunQian SOC_DAPM_SINGLE("HP Right Out Switch", INNO_R05, 123decbc00eSZhengShunQian INNO_R05_HPR_WORK_SHIFT, 1, 0), 124decbc00eSZhengShunQian }; 125decbc00eSZhengShunQian 126decbc00eSZhengShunQian static const struct snd_soc_dapm_widget rk3036_codec_dapm_widgets[] = { 127decbc00eSZhengShunQian SND_SOC_DAPM_SUPPLY_S("DAC PWR", 1, INNO_R06, 128decbc00eSZhengShunQian INNO_R06_DAC_EN_SHIFT, 0, NULL, 0), 129decbc00eSZhengShunQian SND_SOC_DAPM_SUPPLY_S("DACL VREF", 2, INNO_R04, 130decbc00eSZhengShunQian INNO_R04_DACL_VREF_SHIFT, 0, NULL, 0), 131decbc00eSZhengShunQian SND_SOC_DAPM_SUPPLY_S("DACR VREF", 2, INNO_R04, 132decbc00eSZhengShunQian INNO_R04_DACR_VREF_SHIFT, 0, NULL, 0), 133decbc00eSZhengShunQian SND_SOC_DAPM_SUPPLY_S("DACL HiLo VREF", 3, INNO_R06, 134decbc00eSZhengShunQian INNO_R06_DACL_HILO_VREF_SHIFT, 0, NULL, 0), 135decbc00eSZhengShunQian SND_SOC_DAPM_SUPPLY_S("DACR HiLo VREF", 3, INNO_R06, 136decbc00eSZhengShunQian INNO_R06_DACR_HILO_VREF_SHIFT, 0, NULL, 0), 137decbc00eSZhengShunQian SND_SOC_DAPM_SUPPLY_S("DACR CLK", 3, INNO_R04, 138decbc00eSZhengShunQian INNO_R04_DACR_CLK_SHIFT, 0, NULL, 0), 139decbc00eSZhengShunQian SND_SOC_DAPM_SUPPLY_S("DACL CLK", 3, INNO_R04, 140decbc00eSZhengShunQian INNO_R04_DACL_CLK_SHIFT, 0, NULL, 0), 141decbc00eSZhengShunQian 142decbc00eSZhengShunQian SND_SOC_DAPM_DAC("DACL", "Left Playback", INNO_R04, 143decbc00eSZhengShunQian INNO_R04_DACL_SW_SHIFT, 0), 144decbc00eSZhengShunQian SND_SOC_DAPM_DAC("DACR", "Right Playback", INNO_R04, 145decbc00eSZhengShunQian INNO_R04_DACR_SW_SHIFT, 0), 146decbc00eSZhengShunQian 147decbc00eSZhengShunQian SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0, 148decbc00eSZhengShunQian rk3036_codec_hpl_mixer_controls, 149decbc00eSZhengShunQian ARRAY_SIZE(rk3036_codec_hpl_mixer_controls)), 150decbc00eSZhengShunQian SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0, 151decbc00eSZhengShunQian rk3036_codec_hpr_mixer_controls, 152decbc00eSZhengShunQian ARRAY_SIZE(rk3036_codec_hpr_mixer_controls)), 153decbc00eSZhengShunQian 154decbc00eSZhengShunQian SND_SOC_DAPM_PGA("HP Left Out", INNO_R05, 155decbc00eSZhengShunQian INNO_R05_HPL_EN_SHIFT, 0, NULL, 0), 156decbc00eSZhengShunQian SND_SOC_DAPM_PGA("HP Right Out", INNO_R05, 157decbc00eSZhengShunQian INNO_R05_HPR_EN_SHIFT, 0, NULL, 0), 158decbc00eSZhengShunQian 159decbc00eSZhengShunQian SND_SOC_DAPM_MIXER("HP Left Switch", SND_SOC_NOPM, 0, 0, 160decbc00eSZhengShunQian rk3036_codec_hpl_switch_controls, 161decbc00eSZhengShunQian ARRAY_SIZE(rk3036_codec_hpl_switch_controls)), 162decbc00eSZhengShunQian SND_SOC_DAPM_MIXER("HP Right Switch", SND_SOC_NOPM, 0, 0, 163decbc00eSZhengShunQian rk3036_codec_hpr_switch_controls, 164decbc00eSZhengShunQian ARRAY_SIZE(rk3036_codec_hpr_switch_controls)), 165decbc00eSZhengShunQian 166decbc00eSZhengShunQian SND_SOC_DAPM_OUTPUT("HPL"), 167decbc00eSZhengShunQian SND_SOC_DAPM_OUTPUT("HPR"), 168decbc00eSZhengShunQian }; 169decbc00eSZhengShunQian 170decbc00eSZhengShunQian static const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = { 171decbc00eSZhengShunQian {"DACL VREF", NULL, "DAC PWR"}, 172decbc00eSZhengShunQian {"DACR VREF", NULL, "DAC PWR"}, 173decbc00eSZhengShunQian {"DACL HiLo VREF", NULL, "DAC PWR"}, 174decbc00eSZhengShunQian {"DACR HiLo VREF", NULL, "DAC PWR"}, 175decbc00eSZhengShunQian {"DACL CLK", NULL, "DAC PWR"}, 176decbc00eSZhengShunQian {"DACR CLK", NULL, "DAC PWR"}, 177decbc00eSZhengShunQian 178decbc00eSZhengShunQian {"DACL", NULL, "DACL VREF"}, 179decbc00eSZhengShunQian {"DACL", NULL, "DACL HiLo VREF"}, 180decbc00eSZhengShunQian {"DACL", NULL, "DACL CLK"}, 181decbc00eSZhengShunQian {"DACR", NULL, "DACR VREF"}, 182decbc00eSZhengShunQian {"DACR", NULL, "DACR HiLo VREF"}, 183decbc00eSZhengShunQian {"DACR", NULL, "DACR CLK"}, 184decbc00eSZhengShunQian 185decbc00eSZhengShunQian {"Left Headphone Mixer", "DAC Left Out Switch", "DACL"}, 186decbc00eSZhengShunQian {"Right Headphone Mixer", "DAC Right Out Switch", "DACR"}, 187decbc00eSZhengShunQian {"HP Left Out", NULL, "Left Headphone Mixer"}, 188decbc00eSZhengShunQian {"HP Right Out", NULL, "Right Headphone Mixer"}, 189decbc00eSZhengShunQian 190decbc00eSZhengShunQian {"HP Left Switch", "HP Left Out Switch", "HP Left Out"}, 191decbc00eSZhengShunQian {"HP Right Switch", "HP Right Out Switch", "HP Right Out"}, 192decbc00eSZhengShunQian 193decbc00eSZhengShunQian {"HPL", NULL, "HP Left Switch"}, 194decbc00eSZhengShunQian {"HPR", NULL, "HP Right Switch"}, 195decbc00eSZhengShunQian }; 196decbc00eSZhengShunQian 197decbc00eSZhengShunQian static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 198decbc00eSZhengShunQian { 199*40aa60a2SKuninori Morimoto struct snd_soc_component *component = dai->component; 200decbc00eSZhengShunQian unsigned int reg01_val = 0, reg02_val = 0, reg03_val = 0; 201decbc00eSZhengShunQian 202*40aa60a2SKuninori Morimoto dev_dbg(component->dev, "rk3036_codec dai set fmt : %08x\n", fmt); 203decbc00eSZhengShunQian 204decbc00eSZhengShunQian switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 205decbc00eSZhengShunQian case SND_SOC_DAIFMT_CBS_CFS: 206decbc00eSZhengShunQian reg01_val |= INNO_R01_PINDIR_IN_SLAVE | 207decbc00eSZhengShunQian INNO_R01_I2SMODE_SLAVE; 208decbc00eSZhengShunQian break; 209decbc00eSZhengShunQian case SND_SOC_DAIFMT_CBM_CFM: 210decbc00eSZhengShunQian reg01_val |= INNO_R01_PINDIR_OUT_MASTER | 211decbc00eSZhengShunQian INNO_R01_I2SMODE_MASTER; 212decbc00eSZhengShunQian break; 213decbc00eSZhengShunQian default: 214*40aa60a2SKuninori Morimoto dev_err(component->dev, "invalid fmt\n"); 215decbc00eSZhengShunQian return -EINVAL; 216decbc00eSZhengShunQian } 217decbc00eSZhengShunQian 218decbc00eSZhengShunQian switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 219decbc00eSZhengShunQian case SND_SOC_DAIFMT_DSP_A: 220decbc00eSZhengShunQian reg02_val |= INNO_R02_DACM_PCM; 221decbc00eSZhengShunQian break; 222decbc00eSZhengShunQian case SND_SOC_DAIFMT_I2S: 223decbc00eSZhengShunQian reg02_val |= INNO_R02_DACM_I2S; 224decbc00eSZhengShunQian break; 225decbc00eSZhengShunQian case SND_SOC_DAIFMT_RIGHT_J: 226decbc00eSZhengShunQian reg02_val |= INNO_R02_DACM_RJM; 227decbc00eSZhengShunQian break; 228decbc00eSZhengShunQian case SND_SOC_DAIFMT_LEFT_J: 229decbc00eSZhengShunQian reg02_val |= INNO_R02_DACM_LJM; 230decbc00eSZhengShunQian break; 231decbc00eSZhengShunQian default: 232*40aa60a2SKuninori Morimoto dev_err(component->dev, "set dai format failed\n"); 233decbc00eSZhengShunQian return -EINVAL; 234decbc00eSZhengShunQian } 235decbc00eSZhengShunQian 236decbc00eSZhengShunQian switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 237decbc00eSZhengShunQian case SND_SOC_DAIFMT_NB_NF: 238decbc00eSZhengShunQian reg02_val |= INNO_R02_LRCP_NORMAL; 239decbc00eSZhengShunQian reg03_val |= INNO_R03_BCP_NORMAL; 240decbc00eSZhengShunQian break; 241decbc00eSZhengShunQian case SND_SOC_DAIFMT_IB_IF: 242decbc00eSZhengShunQian reg02_val |= INNO_R02_LRCP_REVERSAL; 243decbc00eSZhengShunQian reg03_val |= INNO_R03_BCP_REVERSAL; 244decbc00eSZhengShunQian break; 245decbc00eSZhengShunQian case SND_SOC_DAIFMT_IB_NF: 246decbc00eSZhengShunQian reg02_val |= INNO_R02_LRCP_REVERSAL; 247decbc00eSZhengShunQian reg03_val |= INNO_R03_BCP_NORMAL; 248decbc00eSZhengShunQian break; 249decbc00eSZhengShunQian case SND_SOC_DAIFMT_NB_IF: 250decbc00eSZhengShunQian reg02_val |= INNO_R02_LRCP_NORMAL; 251decbc00eSZhengShunQian reg03_val |= INNO_R03_BCP_REVERSAL; 252decbc00eSZhengShunQian break; 253decbc00eSZhengShunQian default: 254*40aa60a2SKuninori Morimoto dev_err(component->dev, "set dai format failed\n"); 255decbc00eSZhengShunQian return -EINVAL; 256decbc00eSZhengShunQian } 257decbc00eSZhengShunQian 258*40aa60a2SKuninori Morimoto snd_soc_component_update_bits(component, INNO_R01, INNO_R01_I2SMODE_MSK | 259decbc00eSZhengShunQian INNO_R01_PINDIR_MSK, reg01_val); 260*40aa60a2SKuninori Morimoto snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK | 261decbc00eSZhengShunQian INNO_R02_DACM_MSK, reg02_val); 262*40aa60a2SKuninori Morimoto snd_soc_component_update_bits(component, INNO_R03, INNO_R03_BCP_MSK, reg03_val); 263decbc00eSZhengShunQian 264decbc00eSZhengShunQian return 0; 265decbc00eSZhengShunQian } 266decbc00eSZhengShunQian 267decbc00eSZhengShunQian static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream, 268decbc00eSZhengShunQian struct snd_pcm_hw_params *hw_params, 269decbc00eSZhengShunQian struct snd_soc_dai *dai) 270decbc00eSZhengShunQian { 271*40aa60a2SKuninori Morimoto struct snd_soc_component *component = dai->component; 272decbc00eSZhengShunQian unsigned int reg02_val = 0, reg03_val = 0; 273decbc00eSZhengShunQian 274decbc00eSZhengShunQian switch (params_format(hw_params)) { 275decbc00eSZhengShunQian case SNDRV_PCM_FORMAT_S16_LE: 276decbc00eSZhengShunQian reg02_val |= INNO_R02_VWL_16BIT; 277decbc00eSZhengShunQian break; 278decbc00eSZhengShunQian case SNDRV_PCM_FORMAT_S20_3LE: 279decbc00eSZhengShunQian reg02_val |= INNO_R02_VWL_20BIT; 280decbc00eSZhengShunQian break; 281decbc00eSZhengShunQian case SNDRV_PCM_FORMAT_S24_LE: 282decbc00eSZhengShunQian reg02_val |= INNO_R02_VWL_24BIT; 283decbc00eSZhengShunQian break; 284decbc00eSZhengShunQian case SNDRV_PCM_FORMAT_S32_LE: 285decbc00eSZhengShunQian reg02_val |= INNO_R02_VWL_32BIT; 286decbc00eSZhengShunQian break; 287decbc00eSZhengShunQian default: 288decbc00eSZhengShunQian return -EINVAL; 289decbc00eSZhengShunQian } 290decbc00eSZhengShunQian 291decbc00eSZhengShunQian reg02_val |= INNO_R02_LRCP_NORMAL; 292decbc00eSZhengShunQian reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK; 293decbc00eSZhengShunQian 294*40aa60a2SKuninori Morimoto snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK | 295decbc00eSZhengShunQian INNO_R02_VWL_MSK, reg02_val); 296*40aa60a2SKuninori Morimoto snd_soc_component_update_bits(component, INNO_R03, INNO_R03_DACR_MSK | 297decbc00eSZhengShunQian INNO_R03_FWL_MSK, reg03_val); 298decbc00eSZhengShunQian return 0; 299decbc00eSZhengShunQian } 300decbc00eSZhengShunQian 301decbc00eSZhengShunQian #define RK3036_CODEC_RATES (SNDRV_PCM_RATE_8000 | \ 302decbc00eSZhengShunQian SNDRV_PCM_RATE_16000 | \ 303decbc00eSZhengShunQian SNDRV_PCM_RATE_32000 | \ 304decbc00eSZhengShunQian SNDRV_PCM_RATE_44100 | \ 305decbc00eSZhengShunQian SNDRV_PCM_RATE_48000 | \ 306decbc00eSZhengShunQian SNDRV_PCM_RATE_96000) 307decbc00eSZhengShunQian 308decbc00eSZhengShunQian #define RK3036_CODEC_FMTS (SNDRV_PCM_FMTBIT_S16_LE | \ 309decbc00eSZhengShunQian SNDRV_PCM_FMTBIT_S20_3LE | \ 310decbc00eSZhengShunQian SNDRV_PCM_FMTBIT_S24_LE | \ 311decbc00eSZhengShunQian SNDRV_PCM_FMTBIT_S32_LE) 312decbc00eSZhengShunQian 313eb59d73cSArvind Yadav static const struct snd_soc_dai_ops rk3036_codec_dai_ops = { 314decbc00eSZhengShunQian .set_fmt = rk3036_codec_dai_set_fmt, 315decbc00eSZhengShunQian .hw_params = rk3036_codec_dai_hw_params, 316decbc00eSZhengShunQian }; 317decbc00eSZhengShunQian 318decbc00eSZhengShunQian static struct snd_soc_dai_driver rk3036_codec_dai_driver[] = { 319decbc00eSZhengShunQian { 320decbc00eSZhengShunQian .name = "rk3036-codec-dai", 321decbc00eSZhengShunQian .playback = { 322decbc00eSZhengShunQian .stream_name = "Playback", 323decbc00eSZhengShunQian .channels_min = 1, 324decbc00eSZhengShunQian .channels_max = 2, 325decbc00eSZhengShunQian .rates = RK3036_CODEC_RATES, 326decbc00eSZhengShunQian .formats = RK3036_CODEC_FMTS, 327decbc00eSZhengShunQian }, 328decbc00eSZhengShunQian .ops = &rk3036_codec_dai_ops, 329decbc00eSZhengShunQian .symmetric_rates = 1, 330decbc00eSZhengShunQian }, 331decbc00eSZhengShunQian }; 332decbc00eSZhengShunQian 333*40aa60a2SKuninori Morimoto static void rk3036_codec_reset(struct snd_soc_component *component) 334decbc00eSZhengShunQian { 335*40aa60a2SKuninori Morimoto snd_soc_component_write(component, INNO_R00, 336decbc00eSZhengShunQian INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET); 337*40aa60a2SKuninori Morimoto snd_soc_component_write(component, INNO_R00, 338decbc00eSZhengShunQian INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK); 339decbc00eSZhengShunQian } 340decbc00eSZhengShunQian 341*40aa60a2SKuninori Morimoto static int rk3036_codec_probe(struct snd_soc_component *component) 342decbc00eSZhengShunQian { 343*40aa60a2SKuninori Morimoto rk3036_codec_reset(component); 344decbc00eSZhengShunQian return 0; 345decbc00eSZhengShunQian } 346decbc00eSZhengShunQian 347*40aa60a2SKuninori Morimoto static void rk3036_codec_remove(struct snd_soc_component *component) 348decbc00eSZhengShunQian { 349*40aa60a2SKuninori Morimoto rk3036_codec_reset(component); 350decbc00eSZhengShunQian } 351decbc00eSZhengShunQian 352*40aa60a2SKuninori Morimoto static int rk3036_codec_set_bias_level(struct snd_soc_component *component, 353decbc00eSZhengShunQian enum snd_soc_bias_level level) 354decbc00eSZhengShunQian { 355decbc00eSZhengShunQian switch (level) { 356decbc00eSZhengShunQian case SND_SOC_BIAS_STANDBY: 357decbc00eSZhengShunQian /* set a big current for capacitor charging. */ 358*40aa60a2SKuninori Morimoto snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR); 359decbc00eSZhengShunQian /* start precharge */ 360*40aa60a2SKuninori Morimoto snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_PRECHARGE); 361decbc00eSZhengShunQian 362decbc00eSZhengShunQian break; 363decbc00eSZhengShunQian 364decbc00eSZhengShunQian case SND_SOC_BIAS_OFF: 365decbc00eSZhengShunQian /* set a big current for capacitor discharging. */ 366*40aa60a2SKuninori Morimoto snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR); 367decbc00eSZhengShunQian /* start discharge. */ 368*40aa60a2SKuninori Morimoto snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_DISCHARGE); 369decbc00eSZhengShunQian 370decbc00eSZhengShunQian break; 371decbc00eSZhengShunQian default: 372decbc00eSZhengShunQian break; 373decbc00eSZhengShunQian } 374decbc00eSZhengShunQian 375decbc00eSZhengShunQian return 0; 376decbc00eSZhengShunQian } 377decbc00eSZhengShunQian 378*40aa60a2SKuninori Morimoto static const struct snd_soc_component_driver rk3036_codec_driver = { 379decbc00eSZhengShunQian .probe = rk3036_codec_probe, 380decbc00eSZhengShunQian .remove = rk3036_codec_remove, 381decbc00eSZhengShunQian .set_bias_level = rk3036_codec_set_bias_level, 382decbc00eSZhengShunQian .controls = rk3036_codec_dapm_controls, 383decbc00eSZhengShunQian .num_controls = ARRAY_SIZE(rk3036_codec_dapm_controls), 384decbc00eSZhengShunQian .dapm_routes = rk3036_codec_dapm_routes, 385decbc00eSZhengShunQian .num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes), 386decbc00eSZhengShunQian .dapm_widgets = rk3036_codec_dapm_widgets, 387decbc00eSZhengShunQian .num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets), 388*40aa60a2SKuninori Morimoto .idle_bias_on = 1, 389*40aa60a2SKuninori Morimoto .use_pmdown_time = 1, 390*40aa60a2SKuninori Morimoto .endianness = 1, 391*40aa60a2SKuninori Morimoto .non_legacy_dai_naming = 1, 392decbc00eSZhengShunQian }; 393decbc00eSZhengShunQian 394decbc00eSZhengShunQian static const struct regmap_config rk3036_codec_regmap_config = { 395decbc00eSZhengShunQian .reg_bits = 32, 396decbc00eSZhengShunQian .reg_stride = 4, 397decbc00eSZhengShunQian .val_bits = 32, 398decbc00eSZhengShunQian }; 399decbc00eSZhengShunQian 400decbc00eSZhengShunQian #define GRF_SOC_CON0 0x00140 401decbc00eSZhengShunQian #define GRF_ACODEC_SEL (BIT(10) | BIT(16 + 10)) 402decbc00eSZhengShunQian 403decbc00eSZhengShunQian static int rk3036_codec_platform_probe(struct platform_device *pdev) 404decbc00eSZhengShunQian { 405decbc00eSZhengShunQian struct rk3036_codec_priv *priv; 406decbc00eSZhengShunQian struct device_node *of_node = pdev->dev.of_node; 407decbc00eSZhengShunQian struct resource *res; 408decbc00eSZhengShunQian void __iomem *base; 409decbc00eSZhengShunQian struct regmap *grf; 410decbc00eSZhengShunQian int ret; 411decbc00eSZhengShunQian 412decbc00eSZhengShunQian priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 413decbc00eSZhengShunQian if (!priv) 414decbc00eSZhengShunQian return -ENOMEM; 415decbc00eSZhengShunQian 416decbc00eSZhengShunQian res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 417decbc00eSZhengShunQian base = devm_ioremap_resource(&pdev->dev, res); 418decbc00eSZhengShunQian if (IS_ERR(base)) 419decbc00eSZhengShunQian return PTR_ERR(base); 420decbc00eSZhengShunQian 421decbc00eSZhengShunQian priv->base = base; 422decbc00eSZhengShunQian priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base, 423decbc00eSZhengShunQian &rk3036_codec_regmap_config); 424decbc00eSZhengShunQian if (IS_ERR(priv->regmap)) { 425decbc00eSZhengShunQian dev_err(&pdev->dev, "init regmap failed\n"); 426decbc00eSZhengShunQian return PTR_ERR(priv->regmap); 427decbc00eSZhengShunQian } 428decbc00eSZhengShunQian 429decbc00eSZhengShunQian grf = syscon_regmap_lookup_by_phandle(of_node, "rockchip,grf"); 430decbc00eSZhengShunQian if (IS_ERR(grf)) { 431decbc00eSZhengShunQian dev_err(&pdev->dev, "needs 'rockchip,grf' property\n"); 432decbc00eSZhengShunQian return PTR_ERR(grf); 433decbc00eSZhengShunQian } 434decbc00eSZhengShunQian ret = regmap_write(grf, GRF_SOC_CON0, GRF_ACODEC_SEL); 435decbc00eSZhengShunQian if (ret) { 436decbc00eSZhengShunQian dev_err(&pdev->dev, "Could not write to GRF: %d\n", ret); 437decbc00eSZhengShunQian return ret; 438decbc00eSZhengShunQian } 439decbc00eSZhengShunQian 440decbc00eSZhengShunQian priv->pclk = devm_clk_get(&pdev->dev, "acodec_pclk"); 441decbc00eSZhengShunQian if (IS_ERR(priv->pclk)) 442decbc00eSZhengShunQian return PTR_ERR(priv->pclk); 443decbc00eSZhengShunQian 444decbc00eSZhengShunQian ret = clk_prepare_enable(priv->pclk); 445decbc00eSZhengShunQian if (ret < 0) { 446decbc00eSZhengShunQian dev_err(&pdev->dev, "failed to enable clk\n"); 447decbc00eSZhengShunQian return ret; 448decbc00eSZhengShunQian } 449decbc00eSZhengShunQian 450decbc00eSZhengShunQian priv->dev = &pdev->dev; 451decbc00eSZhengShunQian dev_set_drvdata(&pdev->dev, priv); 452decbc00eSZhengShunQian 453*40aa60a2SKuninori Morimoto ret = devm_snd_soc_register_component(&pdev->dev, &rk3036_codec_driver, 454decbc00eSZhengShunQian rk3036_codec_dai_driver, 455decbc00eSZhengShunQian ARRAY_SIZE(rk3036_codec_dai_driver)); 456decbc00eSZhengShunQian if (ret) { 457decbc00eSZhengShunQian clk_disable_unprepare(priv->pclk); 458decbc00eSZhengShunQian dev_set_drvdata(&pdev->dev, NULL); 459decbc00eSZhengShunQian } 460decbc00eSZhengShunQian 461decbc00eSZhengShunQian return ret; 462decbc00eSZhengShunQian } 463decbc00eSZhengShunQian 464decbc00eSZhengShunQian static int rk3036_codec_platform_remove(struct platform_device *pdev) 465decbc00eSZhengShunQian { 466decbc00eSZhengShunQian struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev); 467decbc00eSZhengShunQian 468decbc00eSZhengShunQian clk_disable_unprepare(priv->pclk); 469decbc00eSZhengShunQian 470decbc00eSZhengShunQian return 0; 471decbc00eSZhengShunQian } 472decbc00eSZhengShunQian 473decbc00eSZhengShunQian static const struct of_device_id rk3036_codec_of_match[] = { 474decbc00eSZhengShunQian { .compatible = "rockchip,rk3036-codec", }, 475decbc00eSZhengShunQian {} 476decbc00eSZhengShunQian }; 477decbc00eSZhengShunQian MODULE_DEVICE_TABLE(of, rk3036_codec_of_match); 478decbc00eSZhengShunQian 479decbc00eSZhengShunQian static struct platform_driver rk3036_codec_platform_driver = { 480decbc00eSZhengShunQian .driver = { 481decbc00eSZhengShunQian .name = "rk3036-codec-platform", 482decbc00eSZhengShunQian .of_match_table = of_match_ptr(rk3036_codec_of_match), 483decbc00eSZhengShunQian }, 484decbc00eSZhengShunQian .probe = rk3036_codec_platform_probe, 485decbc00eSZhengShunQian .remove = rk3036_codec_platform_remove, 486decbc00eSZhengShunQian }; 487decbc00eSZhengShunQian 488decbc00eSZhengShunQian module_platform_driver(rk3036_codec_platform_driver); 489decbc00eSZhengShunQian 490decbc00eSZhengShunQian MODULE_AUTHOR("Rockchip Inc."); 491decbc00eSZhengShunQian MODULE_DESCRIPTION("Rockchip rk3036 codec driver"); 492decbc00eSZhengShunQian MODULE_LICENSE("GPL"); 493