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