1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // Copyright (c) 2020 BayLibre, SAS. 4 // Author: Jerome Brunet <jbrunet@baylibre.com> 5 6 #include <linux/clk.h> 7 #include <linux/delay.h> 8 #include <linux/module.h> 9 #include <linux/regmap.h> 10 #include <linux/regulator/consumer.h> 11 #include <linux/reset.h> 12 #include <sound/soc.h> 13 #include <sound/tlv.h> 14 15 #define BLOCK_EN 0x00 16 #define LORN_EN 0 17 #define LORP_EN 1 18 #define LOLN_EN 2 19 #define LOLP_EN 3 20 #define DACR_EN 4 21 #define DACL_EN 5 22 #define DACR_INV 20 23 #define DACL_INV 21 24 #define DACR_SRC 22 25 #define DACL_SRC 23 26 #define REFP_BUF_EN BIT(12) 27 #define BIAS_CURRENT_EN BIT(13) 28 #define VMID_GEN_FAST BIT(14) 29 #define VMID_GEN_EN BIT(15) 30 #define I2S_MODE BIT(30) 31 #define VOL_CTRL0 0x04 32 #define GAIN_H 31 33 #define GAIN_L 23 34 #define VOL_CTRL1 0x08 35 #define DAC_MONO 8 36 #define RAMP_RATE 10 37 #define VC_RAMP_MODE 12 38 #define MUTE_MODE 13 39 #define UNMUTE_MODE 14 40 #define DAC_SOFT_MUTE 15 41 #define DACR_VC 16 42 #define DACL_VC 24 43 #define LINEOUT_CFG 0x0c 44 #define LORN_POL 0 45 #define LORP_POL 4 46 #define LOLN_POL 8 47 #define LOLP_POL 12 48 #define POWER_CFG 0x10 49 50 struct t9015 { 51 struct regulator *avdd; 52 }; 53 54 static int t9015_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 55 { 56 struct snd_soc_component *component = dai->component; 57 unsigned int val; 58 59 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 60 case SND_SOC_DAIFMT_CBM_CFM: 61 val = I2S_MODE; 62 break; 63 64 case SND_SOC_DAIFMT_CBS_CFS: 65 val = 0; 66 break; 67 68 default: 69 return -EINVAL; 70 } 71 72 snd_soc_component_update_bits(component, BLOCK_EN, I2S_MODE, val); 73 74 if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) && 75 ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_LEFT_J)) 76 return -EINVAL; 77 78 return 0; 79 } 80 81 static const struct snd_soc_dai_ops t9015_dai_ops = { 82 .set_fmt = t9015_dai_set_fmt, 83 }; 84 85 static struct snd_soc_dai_driver t9015_dai = { 86 .name = "t9015-hifi", 87 .playback = { 88 .stream_name = "Playback", 89 .channels_min = 1, 90 .channels_max = 2, 91 .rates = SNDRV_PCM_RATE_8000_96000, 92 .formats = (SNDRV_PCM_FMTBIT_S8 | 93 SNDRV_PCM_FMTBIT_S16_LE | 94 SNDRV_PCM_FMTBIT_S20_LE | 95 SNDRV_PCM_FMTBIT_S24_LE), 96 }, 97 .ops = &t9015_dai_ops, 98 }; 99 100 static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -9525, 0); 101 102 static const char * const ramp_rate_txt[] = { "Fast", "Slow" }; 103 static SOC_ENUM_SINGLE_DECL(ramp_rate_enum, VOL_CTRL1, RAMP_RATE, 104 ramp_rate_txt); 105 106 static const char * const dacr_in_txt[] = { "Right", "Left" }; 107 static SOC_ENUM_SINGLE_DECL(dacr_in_enum, BLOCK_EN, DACR_SRC, dacr_in_txt); 108 109 static const char * const dacl_in_txt[] = { "Left", "Right" }; 110 static SOC_ENUM_SINGLE_DECL(dacl_in_enum, BLOCK_EN, DACL_SRC, dacl_in_txt); 111 112 static const char * const mono_txt[] = { "Stereo", "Mono"}; 113 static SOC_ENUM_SINGLE_DECL(mono_enum, VOL_CTRL1, DAC_MONO, mono_txt); 114 115 static const struct snd_kcontrol_new t9015_snd_controls[] = { 116 /* Volume Controls */ 117 SOC_ENUM("Playback Channel Mode", mono_enum), 118 SOC_SINGLE("Playback Switch", VOL_CTRL1, DAC_SOFT_MUTE, 1, 1), 119 SOC_DOUBLE_TLV("Playback Volume", VOL_CTRL1, DACL_VC, DACR_VC, 120 0xff, 0, dac_vol_tlv), 121 122 /* Ramp Controls */ 123 SOC_ENUM("Ramp Rate", ramp_rate_enum), 124 SOC_SINGLE("Volume Ramp Switch", VOL_CTRL1, VC_RAMP_MODE, 1, 0), 125 SOC_SINGLE("Mute Ramp Switch", VOL_CTRL1, MUTE_MODE, 1, 0), 126 SOC_SINGLE("Unmute Ramp Switch", VOL_CTRL1, UNMUTE_MODE, 1, 0), 127 }; 128 129 static const struct snd_kcontrol_new t9015_right_dac_mux = 130 SOC_DAPM_ENUM("Right DAC Source", dacr_in_enum); 131 static const struct snd_kcontrol_new t9015_left_dac_mux = 132 SOC_DAPM_ENUM("Left DAC Source", dacl_in_enum); 133 134 static const struct snd_soc_dapm_widget t9015_dapm_widgets[] = { 135 SND_SOC_DAPM_AIF_IN("Right IN", NULL, 0, SND_SOC_NOPM, 0, 0), 136 SND_SOC_DAPM_AIF_IN("Left IN", NULL, 0, SND_SOC_NOPM, 0, 0), 137 SND_SOC_DAPM_MUX("Right DAC Sel", SND_SOC_NOPM, 0, 0, 138 &t9015_right_dac_mux), 139 SND_SOC_DAPM_MUX("Left DAC Sel", SND_SOC_NOPM, 0, 0, 140 &t9015_left_dac_mux), 141 SND_SOC_DAPM_DAC("Right DAC", NULL, BLOCK_EN, DACR_EN, 0), 142 SND_SOC_DAPM_DAC("Left DAC", NULL, BLOCK_EN, DACL_EN, 0), 143 SND_SOC_DAPM_OUT_DRV("Right- Driver", BLOCK_EN, LORN_EN, 0, 144 NULL, 0), 145 SND_SOC_DAPM_OUT_DRV("Right+ Driver", BLOCK_EN, LORP_EN, 0, 146 NULL, 0), 147 SND_SOC_DAPM_OUT_DRV("Left- Driver", BLOCK_EN, LOLN_EN, 0, 148 NULL, 0), 149 SND_SOC_DAPM_OUT_DRV("Left+ Driver", BLOCK_EN, LOLP_EN, 0, 150 NULL, 0), 151 SND_SOC_DAPM_OUTPUT("LORN"), 152 SND_SOC_DAPM_OUTPUT("LORP"), 153 SND_SOC_DAPM_OUTPUT("LOLN"), 154 SND_SOC_DAPM_OUTPUT("LOLP"), 155 }; 156 157 static const struct snd_soc_dapm_route t9015_dapm_routes[] = { 158 { "Right IN", NULL, "Playback" }, 159 { "Left IN", NULL, "Playback" }, 160 { "Right DAC Sel", "Right", "Right IN" }, 161 { "Right DAC Sel", "Left", "Left IN" }, 162 { "Left DAC Sel", "Right", "Right IN" }, 163 { "Left DAC Sel", "Left", "Left IN" }, 164 { "Right DAC", NULL, "Right DAC Sel" }, 165 { "Left DAC", NULL, "Left DAC Sel" }, 166 { "Right- Driver", NULL, "Right DAC" }, 167 { "Right+ Driver", NULL, "Right DAC" }, 168 { "Left- Driver", NULL, "Left DAC" }, 169 { "Left+ Driver", NULL, "Left DAC" }, 170 { "LORN", NULL, "Right- Driver", }, 171 { "LORP", NULL, "Right+ Driver", }, 172 { "LOLN", NULL, "Left- Driver", }, 173 { "LOLP", NULL, "Left+ Driver", }, 174 }; 175 176 static int t9015_set_bias_level(struct snd_soc_component *component, 177 enum snd_soc_bias_level level) 178 { 179 struct t9015 *priv = snd_soc_component_get_drvdata(component); 180 enum snd_soc_bias_level now = 181 snd_soc_component_get_bias_level(component); 182 int ret; 183 184 switch (level) { 185 case SND_SOC_BIAS_ON: 186 snd_soc_component_update_bits(component, BLOCK_EN, 187 BIAS_CURRENT_EN, 188 BIAS_CURRENT_EN); 189 break; 190 case SND_SOC_BIAS_PREPARE: 191 snd_soc_component_update_bits(component, BLOCK_EN, 192 BIAS_CURRENT_EN, 193 0); 194 break; 195 case SND_SOC_BIAS_STANDBY: 196 ret = regulator_enable(priv->avdd); 197 if (ret) { 198 dev_err(component->dev, "AVDD enable failed\n"); 199 return ret; 200 } 201 202 if (now == SND_SOC_BIAS_OFF) { 203 snd_soc_component_update_bits(component, BLOCK_EN, 204 VMID_GEN_EN | VMID_GEN_FAST | REFP_BUF_EN, 205 VMID_GEN_EN | VMID_GEN_FAST | REFP_BUF_EN); 206 207 mdelay(200); 208 snd_soc_component_update_bits(component, BLOCK_EN, 209 VMID_GEN_FAST, 210 0); 211 } 212 213 break; 214 case SND_SOC_BIAS_OFF: 215 snd_soc_component_update_bits(component, BLOCK_EN, 216 VMID_GEN_EN | VMID_GEN_FAST | REFP_BUF_EN, 217 0); 218 219 regulator_disable(priv->avdd); 220 break; 221 } 222 223 return 0; 224 } 225 226 static const struct snd_soc_component_driver t9015_codec_driver = { 227 .set_bias_level = t9015_set_bias_level, 228 .controls = t9015_snd_controls, 229 .num_controls = ARRAY_SIZE(t9015_snd_controls), 230 .dapm_widgets = t9015_dapm_widgets, 231 .num_dapm_widgets = ARRAY_SIZE(t9015_dapm_widgets), 232 .dapm_routes = t9015_dapm_routes, 233 .num_dapm_routes = ARRAY_SIZE(t9015_dapm_routes), 234 .suspend_bias_off = 1, 235 .endianness = 1, 236 }; 237 238 static const struct regmap_config t9015_regmap_config = { 239 .reg_bits = 32, 240 .reg_stride = 4, 241 .val_bits = 32, 242 .max_register = POWER_CFG, 243 }; 244 245 static int t9015_probe(struct platform_device *pdev) 246 { 247 struct device *dev = &pdev->dev; 248 struct t9015 *priv; 249 void __iomem *regs; 250 struct regmap *regmap; 251 struct clk *pclk; 252 int ret; 253 254 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 255 if (!priv) 256 return -ENOMEM; 257 platform_set_drvdata(pdev, priv); 258 259 pclk = devm_clk_get_enabled(dev, "pclk"); 260 if (IS_ERR(pclk)) 261 return dev_err_probe(dev, PTR_ERR(pclk), "failed to get core clock\n"); 262 263 priv->avdd = devm_regulator_get(dev, "AVDD"); 264 if (IS_ERR(priv->avdd)) 265 return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n"); 266 267 ret = device_reset(dev); 268 if (ret) { 269 dev_err(dev, "reset failed\n"); 270 return ret; 271 } 272 273 regs = devm_platform_ioremap_resource(pdev, 0); 274 if (IS_ERR(regs)) { 275 dev_err(dev, "register map failed\n"); 276 return PTR_ERR(regs); 277 } 278 279 regmap = devm_regmap_init_mmio(dev, regs, &t9015_regmap_config); 280 if (IS_ERR(regmap)) { 281 dev_err(dev, "regmap init failed\n"); 282 return PTR_ERR(regmap); 283 } 284 285 /* 286 * Initialize output polarity: 287 * ATM the output polarity is fixed but in the future it might useful 288 * to add DT property to set this depending on the platform needs 289 */ 290 regmap_write(regmap, LINEOUT_CFG, 0x1111); 291 292 return devm_snd_soc_register_component(dev, &t9015_codec_driver, 293 &t9015_dai, 1); 294 } 295 296 static const struct of_device_id t9015_ids[] __maybe_unused = { 297 { .compatible = "amlogic,t9015", }, 298 { } 299 }; 300 MODULE_DEVICE_TABLE(of, t9015_ids); 301 302 static struct platform_driver t9015_driver = { 303 .driver = { 304 .name = "t9015-codec", 305 .of_match_table = of_match_ptr(t9015_ids), 306 }, 307 .probe = t9015_probe, 308 }; 309 310 module_platform_driver(t9015_driver); 311 312 MODULE_DESCRIPTION("ASoC Amlogic T9015 codec driver"); 313 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 314 MODULE_LICENSE("GPL"); 315
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.