1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // PCM3060 codec driver 4 // 5 // Copyright (C) 2018 Kirill Marinushkin <kmarinushkin@birdec.tech> 6 7 #include <linux/module.h> 8 #include <sound/pcm_params.h> 9 #include <sound/soc.h> 10 #include <sound/tlv.h> 11 12 #include "pcm3060.h" 13 14 /* dai */ 15 16 static int pcm3060_set_sysclk(struct snd_soc_dai *dai, int clk_id, 17 unsigned int freq, int dir) 18 { 19 struct snd_soc_component *comp = dai->component; 20 struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp); 21 22 if (dir != SND_SOC_CLOCK_IN) { 23 dev_err(comp->dev, "unsupported sysclock dir: %d\n", dir); 24 return -EINVAL; 25 } 26 27 priv->dai[dai->id].sclk_freq = freq; 28 29 return 0; 30 } 31 32 static int pcm3060_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 33 { 34 struct snd_soc_component *comp = dai->component; 35 struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp); 36 unsigned int reg; 37 unsigned int val; 38 39 if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) { 40 dev_err(comp->dev, "unsupported DAI polarity: 0x%x\n", fmt); 41 return -EINVAL; 42 } 43 44 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 45 case SND_SOC_DAIFMT_CBM_CFM: 46 priv->dai[dai->id].is_master = true; 47 break; 48 case SND_SOC_DAIFMT_CBS_CFS: 49 priv->dai[dai->id].is_master = false; 50 break; 51 default: 52 dev_err(comp->dev, "unsupported DAI master mode: 0x%x\n", fmt); 53 return -EINVAL; 54 } 55 56 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 57 case SND_SOC_DAIFMT_I2S: 58 val = PCM3060_REG_FMT_I2S; 59 break; 60 case SND_SOC_DAIFMT_RIGHT_J: 61 val = PCM3060_REG_FMT_RJ; 62 break; 63 case SND_SOC_DAIFMT_LEFT_J: 64 val = PCM3060_REG_FMT_LJ; 65 break; 66 default: 67 dev_err(comp->dev, "unsupported DAI format: 0x%x\n", fmt); 68 return -EINVAL; 69 } 70 71 if (dai->id == PCM3060_DAI_ID_DAC) 72 reg = PCM3060_REG67; 73 else 74 reg = PCM3060_REG72; 75 76 regmap_update_bits(priv->regmap, reg, PCM3060_REG_MASK_FMT, val); 77 78 return 0; 79 } 80 81 static int pcm3060_hw_params(struct snd_pcm_substream *substream, 82 struct snd_pcm_hw_params *params, 83 struct snd_soc_dai *dai) 84 { 85 struct snd_soc_component *comp = dai->component; 86 struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp); 87 unsigned int rate; 88 unsigned int ratio; 89 unsigned int reg; 90 unsigned int val; 91 92 if (!priv->dai[dai->id].is_master) { 93 val = PCM3060_REG_MS_S; 94 goto val_ready; 95 } 96 97 rate = params_rate(params); 98 if (!rate) { 99 dev_err(comp->dev, "rate is not configured\n"); 100 return -EINVAL; 101 } 102 103 ratio = priv->dai[dai->id].sclk_freq / rate; 104 105 switch (ratio) { 106 case 768: 107 val = PCM3060_REG_MS_M768; 108 break; 109 case 512: 110 val = PCM3060_REG_MS_M512; 111 break; 112 case 384: 113 val = PCM3060_REG_MS_M384; 114 break; 115 case 256: 116 val = PCM3060_REG_MS_M256; 117 break; 118 case 192: 119 val = PCM3060_REG_MS_M192; 120 break; 121 case 128: 122 val = PCM3060_REG_MS_M128; 123 break; 124 default: 125 dev_err(comp->dev, "unsupported ratio: %d\n", ratio); 126 return -EINVAL; 127 } 128 129 val_ready: 130 if (dai->id == PCM3060_DAI_ID_DAC) 131 reg = PCM3060_REG67; 132 else 133 reg = PCM3060_REG72; 134 135 regmap_update_bits(priv->regmap, reg, PCM3060_REG_MASK_MS, val); 136 137 return 0; 138 } 139 140 static const struct snd_soc_dai_ops pcm3060_dai_ops = { 141 .set_sysclk = pcm3060_set_sysclk, 142 .set_fmt = pcm3060_set_fmt, 143 .hw_params = pcm3060_hw_params, 144 }; 145 146 #define PCM3060_DAI_RATES_ADC (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \ 147 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ 148 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) 149 150 #define PCM3060_DAI_RATES_DAC (PCM3060_DAI_RATES_ADC | \ 151 SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) 152 153 static struct snd_soc_dai_driver pcm3060_dai[] = { 154 { 155 .name = "pcm3060-dac", 156 .id = PCM3060_DAI_ID_DAC, 157 .playback = { 158 .stream_name = "Playback", 159 .channels_min = 2, 160 .channels_max = 2, 161 .rates = PCM3060_DAI_RATES_DAC, 162 .formats = SNDRV_PCM_FMTBIT_S24_LE, 163 }, 164 .ops = &pcm3060_dai_ops, 165 }, 166 { 167 .name = "pcm3060-adc", 168 .id = PCM3060_DAI_ID_ADC, 169 .capture = { 170 .stream_name = "Capture", 171 .channels_min = 2, 172 .channels_max = 2, 173 .rates = PCM3060_DAI_RATES_ADC, 174 .formats = SNDRV_PCM_FMTBIT_S24_LE, 175 }, 176 .ops = &pcm3060_dai_ops, 177 }, 178 }; 179 180 /* dapm */ 181 182 static DECLARE_TLV_DB_SCALE(pcm3060_dapm_tlv, -10050, 50, 1); 183 184 static const struct snd_kcontrol_new pcm3060_dapm_controls[] = { 185 SOC_DOUBLE_R_RANGE_TLV("Master Playback Volume", 186 PCM3060_REG65, PCM3060_REG66, 0, 187 PCM3060_REG_AT2_MIN, PCM3060_REG_AT2_MAX, 188 0, pcm3060_dapm_tlv), 189 SOC_DOUBLE("Master Playback Switch", PCM3060_REG68, 190 PCM3060_REG_SHIFT_MUT21, PCM3060_REG_SHIFT_MUT22, 1, 1), 191 192 SOC_DOUBLE_R_RANGE_TLV("Master Capture Volume", 193 PCM3060_REG70, PCM3060_REG71, 0, 194 PCM3060_REG_AT1_MIN, PCM3060_REG_AT1_MAX, 195 0, pcm3060_dapm_tlv), 196 SOC_DOUBLE("Master Capture Switch", PCM3060_REG73, 197 PCM3060_REG_SHIFT_MUT11, PCM3060_REG_SHIFT_MUT12, 1, 1), 198 }; 199 200 static const struct snd_soc_dapm_widget pcm3060_dapm_widgets[] = { 201 SND_SOC_DAPM_DAC("DAC", "Playback", PCM3060_REG64, 202 PCM3060_REG_SHIFT_DAPSV, 1), 203 204 SND_SOC_DAPM_OUTPUT("OUTL"), 205 SND_SOC_DAPM_OUTPUT("OUTR"), 206 207 SND_SOC_DAPM_INPUT("INL"), 208 SND_SOC_DAPM_INPUT("INR"), 209 210 SND_SOC_DAPM_ADC("ADC", "Capture", PCM3060_REG64, 211 PCM3060_REG_SHIFT_ADPSV, 1), 212 }; 213 214 static const struct snd_soc_dapm_route pcm3060_dapm_map[] = { 215 { "OUTL", NULL, "DAC" }, 216 { "OUTR", NULL, "DAC" }, 217 218 { "ADC", NULL, "INL" }, 219 { "ADC", NULL, "INR" }, 220 }; 221 222 /* soc component */ 223 224 static const struct snd_soc_component_driver pcm3060_soc_comp_driver = { 225 .controls = pcm3060_dapm_controls, 226 .num_controls = ARRAY_SIZE(pcm3060_dapm_controls), 227 .dapm_widgets = pcm3060_dapm_widgets, 228 .num_dapm_widgets = ARRAY_SIZE(pcm3060_dapm_widgets), 229 .dapm_routes = pcm3060_dapm_map, 230 .num_dapm_routes = ARRAY_SIZE(pcm3060_dapm_map), 231 }; 232 233 /* regmap */ 234 235 static bool pcm3060_reg_writeable(struct device *dev, unsigned int reg) 236 { 237 return (reg >= PCM3060_REG64); 238 } 239 240 static bool pcm3060_reg_readable(struct device *dev, unsigned int reg) 241 { 242 return (reg >= PCM3060_REG64); 243 } 244 245 static bool pcm3060_reg_volatile(struct device *dev, unsigned int reg) 246 { 247 /* PCM3060_REG64 is volatile */ 248 return (reg == PCM3060_REG64); 249 } 250 251 static const struct reg_default pcm3060_reg_defaults[] = { 252 { PCM3060_REG64, 0xF0 }, 253 { PCM3060_REG65, 0xFF }, 254 { PCM3060_REG66, 0xFF }, 255 { PCM3060_REG67, 0x00 }, 256 { PCM3060_REG68, 0x00 }, 257 { PCM3060_REG69, 0x00 }, 258 { PCM3060_REG70, 0xD7 }, 259 { PCM3060_REG71, 0xD7 }, 260 { PCM3060_REG72, 0x00 }, 261 { PCM3060_REG73, 0x00 }, 262 }; 263 264 const struct regmap_config pcm3060_regmap = { 265 .reg_bits = 8, 266 .val_bits = 8, 267 .writeable_reg = pcm3060_reg_writeable, 268 .readable_reg = pcm3060_reg_readable, 269 .volatile_reg = pcm3060_reg_volatile, 270 .max_register = PCM3060_REG73, 271 .reg_defaults = pcm3060_reg_defaults, 272 .num_reg_defaults = ARRAY_SIZE(pcm3060_reg_defaults), 273 .cache_type = REGCACHE_RBTREE, 274 }; 275 EXPORT_SYMBOL(pcm3060_regmap); 276 277 /* device */ 278 279 static void pcm3060_parse_dt(const struct device_node *np, 280 struct pcm3060_priv *priv) 281 { 282 priv->out_se = of_property_read_bool(np, "ti,out-single-ended"); 283 } 284 285 int pcm3060_probe(struct device *dev) 286 { 287 int rc; 288 struct pcm3060_priv *priv = dev_get_drvdata(dev); 289 290 if (dev->of_node) 291 pcm3060_parse_dt(dev->of_node, priv); 292 293 if (priv->out_se) 294 regmap_update_bits(priv->regmap, PCM3060_REG64, 295 PCM3060_REG_SE, PCM3060_REG_SE); 296 297 rc = devm_snd_soc_register_component(dev, &pcm3060_soc_comp_driver, 298 pcm3060_dai, 299 ARRAY_SIZE(pcm3060_dai)); 300 if (rc) { 301 dev_err(dev, "failed to register component, rc=%d\n", rc); 302 return rc; 303 } 304 305 return 0; 306 } 307 EXPORT_SYMBOL(pcm3060_probe); 308 309 MODULE_DESCRIPTION("PCM3060 codec driver"); 310 MODULE_AUTHOR("Kirill Marinushkin <kmarinushkin@birdec.tech>"); 311 MODULE_LICENSE("GPL v2"); 312