1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * CS4271 ASoC codec driver |
4 | * |
5 | * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru> |
6 | * |
7 | * This driver support CS4271 codec being master or slave, working |
8 | * in control port mode, connected either via SPI or I2C. |
9 | * The data format accepted is I2S or left-justified. |
10 | * DAPM support not implemented. |
11 | */ |
12 | |
13 | #include <linux/module.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/gpio/consumer.h> |
17 | #include <linux/of.h> |
18 | #include <linux/regulator/consumer.h> |
19 | #include <sound/pcm.h> |
20 | #include <sound/soc.h> |
21 | #include <sound/tlv.h> |
22 | #include <sound/cs4271.h> |
23 | #include "cs4271.h" |
24 | |
25 | #define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ |
26 | SNDRV_PCM_FMTBIT_S24_LE | \ |
27 | SNDRV_PCM_FMTBIT_S32_LE) |
28 | #define CS4271_PCM_RATES SNDRV_PCM_RATE_8000_192000 |
29 | |
30 | /* |
31 | * CS4271 registers |
32 | */ |
33 | #define CS4271_MODE1 0x01 /* Mode Control 1 */ |
34 | #define CS4271_DACCTL 0x02 /* DAC Control */ |
35 | #define CS4271_DACVOL 0x03 /* DAC Volume & Mixing Control */ |
36 | #define CS4271_VOLA 0x04 /* DAC Channel A Volume Control */ |
37 | #define CS4271_VOLB 0x05 /* DAC Channel B Volume Control */ |
38 | #define CS4271_ADCCTL 0x06 /* ADC Control */ |
39 | #define CS4271_MODE2 0x07 /* Mode Control 2 */ |
40 | #define CS4271_CHIPID 0x08 /* Chip ID */ |
41 | |
42 | #define CS4271_FIRSTREG CS4271_MODE1 |
43 | #define CS4271_LASTREG CS4271_MODE2 |
44 | #define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1) |
45 | |
46 | /* Bit masks for the CS4271 registers */ |
47 | #define CS4271_MODE1_MODE_MASK 0xC0 |
48 | #define CS4271_MODE1_MODE_1X 0x00 |
49 | #define CS4271_MODE1_MODE_2X 0x80 |
50 | #define CS4271_MODE1_MODE_4X 0xC0 |
51 | |
52 | #define CS4271_MODE1_DIV_MASK 0x30 |
53 | #define CS4271_MODE1_DIV_1 0x00 |
54 | #define CS4271_MODE1_DIV_15 0x10 |
55 | #define CS4271_MODE1_DIV_2 0x20 |
56 | #define CS4271_MODE1_DIV_3 0x30 |
57 | |
58 | #define CS4271_MODE1_MASTER 0x08 |
59 | |
60 | #define CS4271_MODE1_DAC_DIF_MASK 0x07 |
61 | #define CS4271_MODE1_DAC_DIF_LJ 0x00 |
62 | #define CS4271_MODE1_DAC_DIF_I2S 0x01 |
63 | #define CS4271_MODE1_DAC_DIF_RJ16 0x02 |
64 | #define CS4271_MODE1_DAC_DIF_RJ24 0x03 |
65 | #define CS4271_MODE1_DAC_DIF_RJ20 0x04 |
66 | #define CS4271_MODE1_DAC_DIF_RJ18 0x05 |
67 | |
68 | #define CS4271_DACCTL_AMUTE 0x80 |
69 | #define CS4271_DACCTL_IF_SLOW 0x40 |
70 | |
71 | #define CS4271_DACCTL_DEM_MASK 0x30 |
72 | #define CS4271_DACCTL_DEM_DIS 0x00 |
73 | #define CS4271_DACCTL_DEM_441 0x10 |
74 | #define CS4271_DACCTL_DEM_48 0x20 |
75 | #define CS4271_DACCTL_DEM_32 0x30 |
76 | |
77 | #define CS4271_DACCTL_SVRU 0x08 |
78 | #define CS4271_DACCTL_SRD 0x04 |
79 | #define CS4271_DACCTL_INVA 0x02 |
80 | #define CS4271_DACCTL_INVB 0x01 |
81 | |
82 | #define CS4271_DACVOL_BEQUA 0x40 |
83 | #define CS4271_DACVOL_SOFT 0x20 |
84 | #define CS4271_DACVOL_ZEROC 0x10 |
85 | |
86 | #define CS4271_DACVOL_ATAPI_MASK 0x0F |
87 | #define CS4271_DACVOL_ATAPI_M_M 0x00 |
88 | #define CS4271_DACVOL_ATAPI_M_BR 0x01 |
89 | #define CS4271_DACVOL_ATAPI_M_BL 0x02 |
90 | #define CS4271_DACVOL_ATAPI_M_BLR2 0x03 |
91 | #define CS4271_DACVOL_ATAPI_AR_M 0x04 |
92 | #define CS4271_DACVOL_ATAPI_AR_BR 0x05 |
93 | #define CS4271_DACVOL_ATAPI_AR_BL 0x06 |
94 | #define CS4271_DACVOL_ATAPI_AR_BLR2 0x07 |
95 | #define CS4271_DACVOL_ATAPI_AL_M 0x08 |
96 | #define CS4271_DACVOL_ATAPI_AL_BR 0x09 |
97 | #define CS4271_DACVOL_ATAPI_AL_BL 0x0A |
98 | #define CS4271_DACVOL_ATAPI_AL_BLR2 0x0B |
99 | #define CS4271_DACVOL_ATAPI_ALR2_M 0x0C |
100 | #define CS4271_DACVOL_ATAPI_ALR2_BR 0x0D |
101 | #define CS4271_DACVOL_ATAPI_ALR2_BL 0x0E |
102 | #define CS4271_DACVOL_ATAPI_ALR2_BLR2 0x0F |
103 | |
104 | #define CS4271_VOLA_MUTE 0x80 |
105 | #define CS4271_VOLA_VOL_MASK 0x7F |
106 | #define CS4271_VOLB_MUTE 0x80 |
107 | #define CS4271_VOLB_VOL_MASK 0x7F |
108 | |
109 | #define CS4271_ADCCTL_DITHER16 0x20 |
110 | |
111 | #define CS4271_ADCCTL_ADC_DIF_MASK 0x10 |
112 | #define CS4271_ADCCTL_ADC_DIF_LJ 0x00 |
113 | #define CS4271_ADCCTL_ADC_DIF_I2S 0x10 |
114 | |
115 | #define CS4271_ADCCTL_MUTEA 0x08 |
116 | #define CS4271_ADCCTL_MUTEB 0x04 |
117 | #define CS4271_ADCCTL_HPFDA 0x02 |
118 | #define CS4271_ADCCTL_HPFDB 0x01 |
119 | |
120 | #define CS4271_MODE2_LOOP 0x10 |
121 | #define CS4271_MODE2_MUTECAEQUB 0x08 |
122 | #define CS4271_MODE2_FREEZE 0x04 |
123 | #define CS4271_MODE2_CPEN 0x02 |
124 | #define CS4271_MODE2_PDN 0x01 |
125 | |
126 | #define CS4271_CHIPID_PART_MASK 0xF0 |
127 | #define CS4271_CHIPID_REV_MASK 0x0F |
128 | |
129 | /* |
130 | * Default CS4271 power-up configuration |
131 | * Array contains non-existing in hw register at address 0 |
132 | * Array do not include Chip ID, as codec driver does not use |
133 | * registers read operations at all |
134 | */ |
135 | static const struct reg_default cs4271_reg_defaults[] = { |
136 | { CS4271_MODE1, 0, }, |
137 | { CS4271_DACCTL, CS4271_DACCTL_AMUTE, }, |
138 | { CS4271_DACVOL, CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR, }, |
139 | { CS4271_VOLA, 0, }, |
140 | { CS4271_VOLB, 0, }, |
141 | { CS4271_ADCCTL, 0, }, |
142 | { CS4271_MODE2, 0, }, |
143 | }; |
144 | |
145 | static bool cs4271_volatile_reg(struct device *dev, unsigned int reg) |
146 | { |
147 | return reg == CS4271_CHIPID; |
148 | } |
149 | |
150 | static const char * const supply_names[] = { |
151 | "vd" , "vl" , "va" |
152 | }; |
153 | |
154 | struct cs4271_private { |
155 | unsigned int mclk; |
156 | bool master; |
157 | bool deemph; |
158 | struct regmap *regmap; |
159 | /* Current sample rate for de-emphasis control */ |
160 | int rate; |
161 | /* GPIO driving Reset pin, if any */ |
162 | struct gpio_desc *reset; |
163 | /* enable soft reset workaround */ |
164 | bool enable_soft_reset; |
165 | struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; |
166 | }; |
167 | |
168 | static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = { |
169 | SND_SOC_DAPM_INPUT("AINA" ), |
170 | SND_SOC_DAPM_INPUT("AINB" ), |
171 | |
172 | SND_SOC_DAPM_OUTPUT("AOUTA+" ), |
173 | SND_SOC_DAPM_OUTPUT("AOUTA-" ), |
174 | SND_SOC_DAPM_OUTPUT("AOUTB+" ), |
175 | SND_SOC_DAPM_OUTPUT("AOUTB-" ), |
176 | }; |
177 | |
178 | static const struct snd_soc_dapm_route cs4271_dapm_routes[] = { |
179 | { "Capture" , NULL, "AINA" }, |
180 | { "Capture" , NULL, "AINB" }, |
181 | |
182 | { "AOUTA+" , NULL, "Playback" }, |
183 | { "AOUTA-" , NULL, "Playback" }, |
184 | { "AOUTB+" , NULL, "Playback" }, |
185 | { "AOUTB-" , NULL, "Playback" }, |
186 | }; |
187 | |
188 | /* |
189 | * @freq is the desired MCLK rate |
190 | * MCLK rate should (c) be the sample rate, multiplied by one of the |
191 | * ratios listed in cs4271_mclk_fs_ratios table |
192 | */ |
193 | static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
194 | int clk_id, unsigned int freq, int dir) |
195 | { |
196 | struct snd_soc_component *component = codec_dai->component; |
197 | struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(c: component); |
198 | |
199 | cs4271->mclk = freq; |
200 | return 0; |
201 | } |
202 | |
203 | static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai, |
204 | unsigned int format) |
205 | { |
206 | struct snd_soc_component *component = codec_dai->component; |
207 | struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(c: component); |
208 | unsigned int val = 0; |
209 | int ret; |
210 | |
211 | switch (format & SND_SOC_DAIFMT_MASTER_MASK) { |
212 | case SND_SOC_DAIFMT_CBS_CFS: |
213 | cs4271->master = false; |
214 | break; |
215 | case SND_SOC_DAIFMT_CBM_CFM: |
216 | cs4271->master = true; |
217 | val |= CS4271_MODE1_MASTER; |
218 | break; |
219 | default: |
220 | dev_err(component->dev, "Invalid DAI format\n" ); |
221 | return -EINVAL; |
222 | } |
223 | |
224 | switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { |
225 | case SND_SOC_DAIFMT_LEFT_J: |
226 | val |= CS4271_MODE1_DAC_DIF_LJ; |
227 | ret = regmap_update_bits(map: cs4271->regmap, CS4271_ADCCTL, |
228 | CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ); |
229 | if (ret < 0) |
230 | return ret; |
231 | break; |
232 | case SND_SOC_DAIFMT_I2S: |
233 | val |= CS4271_MODE1_DAC_DIF_I2S; |
234 | ret = regmap_update_bits(map: cs4271->regmap, CS4271_ADCCTL, |
235 | CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S); |
236 | if (ret < 0) |
237 | return ret; |
238 | break; |
239 | default: |
240 | dev_err(component->dev, "Invalid DAI format\n" ); |
241 | return -EINVAL; |
242 | } |
243 | |
244 | ret = regmap_update_bits(map: cs4271->regmap, CS4271_MODE1, |
245 | CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val); |
246 | if (ret < 0) |
247 | return ret; |
248 | return 0; |
249 | } |
250 | |
251 | static int cs4271_deemph[] = {0, 44100, 48000, 32000}; |
252 | |
253 | static int cs4271_set_deemph(struct snd_soc_component *component) |
254 | { |
255 | struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(c: component); |
256 | int i, ret; |
257 | int val = CS4271_DACCTL_DEM_DIS; |
258 | |
259 | if (cs4271->deemph) { |
260 | /* Find closest de-emphasis freq */ |
261 | val = 1; |
262 | for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++) |
263 | if (abs(cs4271_deemph[i] - cs4271->rate) < |
264 | abs(cs4271_deemph[val] - cs4271->rate)) |
265 | val = i; |
266 | val <<= 4; |
267 | } |
268 | |
269 | ret = regmap_update_bits(map: cs4271->regmap, CS4271_DACCTL, |
270 | CS4271_DACCTL_DEM_MASK, val); |
271 | if (ret < 0) |
272 | return ret; |
273 | return 0; |
274 | } |
275 | |
276 | static int cs4271_get_deemph(struct snd_kcontrol *kcontrol, |
277 | struct snd_ctl_elem_value *ucontrol) |
278 | { |
279 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
280 | struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(c: component); |
281 | |
282 | ucontrol->value.integer.value[0] = cs4271->deemph; |
283 | return 0; |
284 | } |
285 | |
286 | static int cs4271_put_deemph(struct snd_kcontrol *kcontrol, |
287 | struct snd_ctl_elem_value *ucontrol) |
288 | { |
289 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
290 | struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(c: component); |
291 | |
292 | cs4271->deemph = ucontrol->value.integer.value[0]; |
293 | return cs4271_set_deemph(component); |
294 | } |
295 | |
296 | struct cs4271_clk_cfg { |
297 | bool master; /* codec mode */ |
298 | u8 speed_mode; /* codec speed mode: 1x, 2x, 4x */ |
299 | unsigned short ratio; /* MCLK / sample rate */ |
300 | u8 ratio_mask; /* ratio bit mask for Master mode */ |
301 | }; |
302 | |
303 | static struct cs4271_clk_cfg cs4271_clk_tab[] = { |
304 | {1, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1}, |
305 | {1, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_15}, |
306 | {1, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_2}, |
307 | {1, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_3}, |
308 | {1, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1}, |
309 | {1, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_15}, |
310 | {1, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_2}, |
311 | {1, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_3}, |
312 | {1, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1}, |
313 | {1, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_15}, |
314 | {1, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_2}, |
315 | {1, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_3}, |
316 | {0, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1}, |
317 | {0, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_1}, |
318 | {0, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_1}, |
319 | {0, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_2}, |
320 | {0, CS4271_MODE1_MODE_1X, 1024, CS4271_MODE1_DIV_2}, |
321 | {0, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1}, |
322 | {0, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_1}, |
323 | {0, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_1}, |
324 | {0, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_2}, |
325 | {0, CS4271_MODE1_MODE_2X, 512, CS4271_MODE1_DIV_2}, |
326 | {0, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1}, |
327 | {0, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_1}, |
328 | {0, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_1}, |
329 | {0, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_2}, |
330 | {0, CS4271_MODE1_MODE_4X, 256, CS4271_MODE1_DIV_2}, |
331 | }; |
332 | |
333 | #define CS4271_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab) |
334 | |
335 | static int cs4271_hw_params(struct snd_pcm_substream *substream, |
336 | struct snd_pcm_hw_params *params, |
337 | struct snd_soc_dai *dai) |
338 | { |
339 | struct snd_soc_component *component = dai->component; |
340 | struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(c: component); |
341 | int i, ret; |
342 | unsigned int ratio, val; |
343 | |
344 | if (cs4271->enable_soft_reset) { |
345 | /* |
346 | * Put the codec in soft reset and back again in case it's not |
347 | * currently streaming data. This way of bringing the codec in |
348 | * sync to the current clocks is not explicitly documented in |
349 | * the data sheet, but it seems to work fine, and in contrast |
350 | * to a read hardware reset, we don't have to sync back all |
351 | * registers every time. |
352 | */ |
353 | |
354 | if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK && |
355 | !snd_soc_dai_stream_active(dai, stream: SNDRV_PCM_STREAM_CAPTURE)) || |
356 | (substream->stream == SNDRV_PCM_STREAM_CAPTURE && |
357 | !snd_soc_dai_stream_active(dai, stream: SNDRV_PCM_STREAM_PLAYBACK))) { |
358 | ret = regmap_update_bits(map: cs4271->regmap, CS4271_MODE2, |
359 | CS4271_MODE2_PDN, |
360 | CS4271_MODE2_PDN); |
361 | if (ret < 0) |
362 | return ret; |
363 | |
364 | ret = regmap_update_bits(map: cs4271->regmap, CS4271_MODE2, |
365 | CS4271_MODE2_PDN, val: 0); |
366 | if (ret < 0) |
367 | return ret; |
368 | } |
369 | } |
370 | |
371 | cs4271->rate = params_rate(p: params); |
372 | |
373 | /* Configure DAC */ |
374 | if (cs4271->rate < 50000) |
375 | val = CS4271_MODE1_MODE_1X; |
376 | else if (cs4271->rate < 100000) |
377 | val = CS4271_MODE1_MODE_2X; |
378 | else |
379 | val = CS4271_MODE1_MODE_4X; |
380 | |
381 | ratio = cs4271->mclk / cs4271->rate; |
382 | for (i = 0; i < CS4271_NR_RATIOS; i++) |
383 | if ((cs4271_clk_tab[i].master == cs4271->master) && |
384 | (cs4271_clk_tab[i].speed_mode == val) && |
385 | (cs4271_clk_tab[i].ratio == ratio)) |
386 | break; |
387 | |
388 | if (i == CS4271_NR_RATIOS) { |
389 | dev_err(component->dev, "Invalid sample rate\n" ); |
390 | return -EINVAL; |
391 | } |
392 | |
393 | val |= cs4271_clk_tab[i].ratio_mask; |
394 | |
395 | ret = regmap_update_bits(map: cs4271->regmap, CS4271_MODE1, |
396 | CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val); |
397 | if (ret < 0) |
398 | return ret; |
399 | |
400 | return cs4271_set_deemph(component); |
401 | } |
402 | |
403 | static int cs4271_mute_stream(struct snd_soc_dai *dai, int mute, int stream) |
404 | { |
405 | struct snd_soc_component *component = dai->component; |
406 | struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(c: component); |
407 | int ret; |
408 | int val_a = 0; |
409 | int val_b = 0; |
410 | |
411 | if (stream != SNDRV_PCM_STREAM_PLAYBACK) |
412 | return 0; |
413 | |
414 | if (mute) { |
415 | val_a = CS4271_VOLA_MUTE; |
416 | val_b = CS4271_VOLB_MUTE; |
417 | } |
418 | |
419 | ret = regmap_update_bits(map: cs4271->regmap, CS4271_VOLA, |
420 | CS4271_VOLA_MUTE, val: val_a); |
421 | if (ret < 0) |
422 | return ret; |
423 | |
424 | ret = regmap_update_bits(map: cs4271->regmap, CS4271_VOLB, |
425 | CS4271_VOLB_MUTE, val: val_b); |
426 | if (ret < 0) |
427 | return ret; |
428 | |
429 | return 0; |
430 | } |
431 | |
432 | /* CS4271 controls */ |
433 | static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0); |
434 | |
435 | static const struct snd_kcontrol_new cs4271_snd_controls[] = { |
436 | SOC_DOUBLE_R_TLV("Master Playback Volume" , CS4271_VOLA, CS4271_VOLB, |
437 | 0, 0x7F, 1, cs4271_dac_tlv), |
438 | SOC_SINGLE("Digital Loopback Switch" , CS4271_MODE2, 4, 1, 0), |
439 | SOC_SINGLE("Soft Ramp Switch" , CS4271_DACVOL, 5, 1, 0), |
440 | SOC_SINGLE("Zero Cross Switch" , CS4271_DACVOL, 4, 1, 0), |
441 | SOC_SINGLE_BOOL_EXT("De-emphasis Switch" , 0, |
442 | cs4271_get_deemph, cs4271_put_deemph), |
443 | SOC_SINGLE("Auto-Mute Switch" , CS4271_DACCTL, 7, 1, 0), |
444 | SOC_SINGLE("Slow Roll Off Filter Switch" , CS4271_DACCTL, 6, 1, 0), |
445 | SOC_SINGLE("Soft Volume Ramp-Up Switch" , CS4271_DACCTL, 3, 1, 0), |
446 | SOC_SINGLE("Soft Ramp-Down Switch" , CS4271_DACCTL, 2, 1, 0), |
447 | SOC_SINGLE("Left Channel Inversion Switch" , CS4271_DACCTL, 1, 1, 0), |
448 | SOC_SINGLE("Right Channel Inversion Switch" , CS4271_DACCTL, 0, 1, 0), |
449 | SOC_DOUBLE("Master Capture Switch" , CS4271_ADCCTL, 3, 2, 1, 1), |
450 | SOC_SINGLE("Dither 16-Bit Data Switch" , CS4271_ADCCTL, 5, 1, 0), |
451 | SOC_DOUBLE("High Pass Filter Switch" , CS4271_ADCCTL, 1, 0, 1, 1), |
452 | SOC_DOUBLE_R("Master Playback Switch" , CS4271_VOLA, CS4271_VOLB, |
453 | 7, 1, 1), |
454 | }; |
455 | |
456 | static const struct snd_soc_dai_ops cs4271_dai_ops = { |
457 | .hw_params = cs4271_hw_params, |
458 | .set_sysclk = cs4271_set_dai_sysclk, |
459 | .set_fmt = cs4271_set_dai_fmt, |
460 | .mute_stream = cs4271_mute_stream, |
461 | }; |
462 | |
463 | static struct snd_soc_dai_driver cs4271_dai = { |
464 | .name = "cs4271-hifi" , |
465 | .playback = { |
466 | .stream_name = "Playback" , |
467 | .channels_min = 2, |
468 | .channels_max = 2, |
469 | .rates = CS4271_PCM_RATES, |
470 | .formats = CS4271_PCM_FORMATS, |
471 | }, |
472 | .capture = { |
473 | .stream_name = "Capture" , |
474 | .channels_min = 2, |
475 | .channels_max = 2, |
476 | .rates = CS4271_PCM_RATES, |
477 | .formats = CS4271_PCM_FORMATS, |
478 | }, |
479 | .ops = &cs4271_dai_ops, |
480 | .symmetric_rate = 1, |
481 | }; |
482 | |
483 | static int cs4271_reset(struct snd_soc_component *component) |
484 | { |
485 | struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(c: component); |
486 | |
487 | gpiod_direction_output(desc: cs4271->reset, value: 1); |
488 | mdelay(1); |
489 | gpiod_set_value(desc: cs4271->reset, value: 0); |
490 | mdelay(1); |
491 | |
492 | return 0; |
493 | } |
494 | |
495 | #ifdef CONFIG_PM |
496 | static int cs4271_soc_suspend(struct snd_soc_component *component) |
497 | { |
498 | int ret; |
499 | struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(c: component); |
500 | |
501 | /* Set power-down bit */ |
502 | ret = regmap_update_bits(map: cs4271->regmap, CS4271_MODE2, |
503 | CS4271_MODE2_PDN, CS4271_MODE2_PDN); |
504 | if (ret < 0) |
505 | return ret; |
506 | |
507 | regcache_mark_dirty(map: cs4271->regmap); |
508 | regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), consumers: cs4271->supplies); |
509 | |
510 | return 0; |
511 | } |
512 | |
513 | static int cs4271_soc_resume(struct snd_soc_component *component) |
514 | { |
515 | int ret; |
516 | struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(c: component); |
517 | |
518 | ret = regulator_bulk_enable(ARRAY_SIZE(cs4271->supplies), |
519 | consumers: cs4271->supplies); |
520 | if (ret < 0) { |
521 | dev_err(component->dev, "Failed to enable regulators: %d\n" , ret); |
522 | return ret; |
523 | } |
524 | |
525 | /* Do a proper reset after power up */ |
526 | cs4271_reset(component); |
527 | |
528 | /* Restore codec state */ |
529 | ret = regcache_sync(map: cs4271->regmap); |
530 | if (ret < 0) |
531 | return ret; |
532 | |
533 | /* then disable the power-down bit */ |
534 | ret = regmap_update_bits(map: cs4271->regmap, CS4271_MODE2, |
535 | CS4271_MODE2_PDN, val: 0); |
536 | if (ret < 0) |
537 | return ret; |
538 | |
539 | return 0; |
540 | } |
541 | #else |
542 | #define cs4271_soc_suspend NULL |
543 | #define cs4271_soc_resume NULL |
544 | #endif /* CONFIG_PM */ |
545 | |
546 | #ifdef CONFIG_OF |
547 | const struct of_device_id cs4271_dt_ids[] = { |
548 | { .compatible = "cirrus,cs4271" , }, |
549 | { } |
550 | }; |
551 | MODULE_DEVICE_TABLE(of, cs4271_dt_ids); |
552 | EXPORT_SYMBOL_GPL(cs4271_dt_ids); |
553 | #endif |
554 | |
555 | static int cs4271_component_probe(struct snd_soc_component *component) |
556 | { |
557 | struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(c: component); |
558 | struct cs4271_platform_data *cs4271plat = component->dev->platform_data; |
559 | int ret; |
560 | bool amutec_eq_bmutec; |
561 | |
562 | amutec_eq_bmutec = of_property_read_bool(np: component->dev->of_node, |
563 | propname: "cirrus,amutec-eq-bmutec" ); |
564 | cs4271->enable_soft_reset = of_property_read_bool(np: component->dev->of_node, |
565 | propname: "cirrus,enable-soft-reset" ); |
566 | |
567 | ret = regulator_bulk_enable(ARRAY_SIZE(cs4271->supplies), |
568 | consumers: cs4271->supplies); |
569 | if (ret < 0) { |
570 | dev_err(component->dev, "Failed to enable regulators: %d\n" , ret); |
571 | return ret; |
572 | } |
573 | |
574 | if (cs4271plat) { |
575 | amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec; |
576 | cs4271->enable_soft_reset = cs4271plat->enable_soft_reset; |
577 | } |
578 | |
579 | /* Reset codec */ |
580 | cs4271_reset(component); |
581 | |
582 | ret = regcache_sync(map: cs4271->regmap); |
583 | if (ret < 0) |
584 | return ret; |
585 | |
586 | ret = regmap_update_bits(map: cs4271->regmap, CS4271_MODE2, |
587 | CS4271_MODE2_PDN | CS4271_MODE2_CPEN, |
588 | CS4271_MODE2_PDN | CS4271_MODE2_CPEN); |
589 | if (ret < 0) |
590 | return ret; |
591 | ret = regmap_update_bits(map: cs4271->regmap, CS4271_MODE2, |
592 | CS4271_MODE2_PDN, val: 0); |
593 | if (ret < 0) |
594 | return ret; |
595 | /* Power-up sequence requires 85 uS */ |
596 | udelay(85); |
597 | |
598 | if (amutec_eq_bmutec) |
599 | regmap_update_bits(map: cs4271->regmap, CS4271_MODE2, |
600 | CS4271_MODE2_MUTECAEQUB, |
601 | CS4271_MODE2_MUTECAEQUB); |
602 | |
603 | return 0; |
604 | } |
605 | |
606 | static void cs4271_component_remove(struct snd_soc_component *component) |
607 | { |
608 | struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(c: component); |
609 | |
610 | /* Set codec to the reset state */ |
611 | gpiod_set_value(desc: cs4271->reset, value: 1); |
612 | |
613 | regcache_mark_dirty(map: cs4271->regmap); |
614 | regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), consumers: cs4271->supplies); |
615 | }; |
616 | |
617 | static const struct snd_soc_component_driver soc_component_dev_cs4271 = { |
618 | .probe = cs4271_component_probe, |
619 | .remove = cs4271_component_remove, |
620 | .suspend = cs4271_soc_suspend, |
621 | .resume = cs4271_soc_resume, |
622 | .controls = cs4271_snd_controls, |
623 | .num_controls = ARRAY_SIZE(cs4271_snd_controls), |
624 | .dapm_widgets = cs4271_dapm_widgets, |
625 | .num_dapm_widgets = ARRAY_SIZE(cs4271_dapm_widgets), |
626 | .dapm_routes = cs4271_dapm_routes, |
627 | .num_dapm_routes = ARRAY_SIZE(cs4271_dapm_routes), |
628 | .idle_bias_on = 1, |
629 | .use_pmdown_time = 1, |
630 | .endianness = 1, |
631 | }; |
632 | |
633 | static int cs4271_common_probe(struct device *dev, |
634 | struct cs4271_private **c) |
635 | { |
636 | struct cs4271_private *cs4271; |
637 | int i, ret; |
638 | |
639 | cs4271 = devm_kzalloc(dev, size: sizeof(*cs4271), GFP_KERNEL); |
640 | if (!cs4271) |
641 | return -ENOMEM; |
642 | |
643 | cs4271->reset = devm_gpiod_get_optional(dev, con_id: "reset" , flags: GPIOD_ASIS); |
644 | if (IS_ERR(ptr: cs4271->reset)) |
645 | return dev_err_probe(dev, err: PTR_ERR(ptr: cs4271->reset), |
646 | fmt: "error retrieving RESET GPIO\n" ); |
647 | gpiod_set_consumer_name(desc: cs4271->reset, name: "CS4271 Reset" ); |
648 | |
649 | for (i = 0; i < ARRAY_SIZE(supply_names); i++) |
650 | cs4271->supplies[i].supply = supply_names[i]; |
651 | |
652 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs4271->supplies), |
653 | consumers: cs4271->supplies); |
654 | |
655 | if (ret < 0) { |
656 | dev_err(dev, "Failed to get regulators: %d\n" , ret); |
657 | return ret; |
658 | } |
659 | |
660 | *c = cs4271; |
661 | return 0; |
662 | } |
663 | |
664 | const struct regmap_config cs4271_regmap_config = { |
665 | .max_register = CS4271_LASTREG, |
666 | |
667 | .reg_defaults = cs4271_reg_defaults, |
668 | .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults), |
669 | .cache_type = REGCACHE_FLAT, |
670 | .val_bits = 8, |
671 | .volatile_reg = cs4271_volatile_reg, |
672 | }; |
673 | EXPORT_SYMBOL_GPL(cs4271_regmap_config); |
674 | |
675 | int cs4271_probe(struct device *dev, struct regmap *regmap) |
676 | { |
677 | struct cs4271_private *cs4271; |
678 | int ret; |
679 | |
680 | if (IS_ERR(ptr: regmap)) |
681 | return PTR_ERR(ptr: regmap); |
682 | |
683 | ret = cs4271_common_probe(dev, c: &cs4271); |
684 | if (ret < 0) |
685 | return ret; |
686 | |
687 | dev_set_drvdata(dev, data: cs4271); |
688 | cs4271->regmap = regmap; |
689 | |
690 | return devm_snd_soc_register_component(dev, component_driver: &soc_component_dev_cs4271, |
691 | dai_drv: &cs4271_dai, num_dai: 1); |
692 | } |
693 | EXPORT_SYMBOL_GPL(cs4271_probe); |
694 | |
695 | MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>" ); |
696 | MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver" ); |
697 | MODULE_LICENSE("GPL" ); |
698 | |