1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * cs42l51.c |
4 | * |
5 | * ASoC Driver for Cirrus Logic CS42L51 codecs |
6 | * |
7 | * Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com> |
8 | * |
9 | * Based on cs4270.c - Copyright (c) Freescale Semiconductor |
10 | * |
11 | * For now: |
12 | * - Only I2C is support. Not SPI |
13 | * - master mode *NOT* supported |
14 | */ |
15 | |
16 | #include <linux/clk.h> |
17 | #include <linux/module.h> |
18 | #include <linux/slab.h> |
19 | #include <sound/core.h> |
20 | #include <sound/soc.h> |
21 | #include <sound/tlv.h> |
22 | #include <sound/initval.h> |
23 | #include <sound/pcm_params.h> |
24 | #include <sound/pcm.h> |
25 | #include <linux/gpio/consumer.h> |
26 | #include <linux/regmap.h> |
27 | #include <linux/regulator/consumer.h> |
28 | |
29 | #include "cs42l51.h" |
30 | |
31 | enum master_slave_mode { |
32 | MODE_SLAVE, |
33 | MODE_SLAVE_AUTO, |
34 | MODE_MASTER, |
35 | }; |
36 | |
37 | static const char * const cs42l51_supply_names[] = { |
38 | "VL" , |
39 | "VD" , |
40 | "VA" , |
41 | "VAHP" , |
42 | }; |
43 | |
44 | struct cs42l51_private { |
45 | unsigned int mclk; |
46 | struct clk *mclk_handle; |
47 | unsigned int audio_mode; /* The mode (I2S or left-justified) */ |
48 | enum master_slave_mode func; |
49 | struct regulator_bulk_data supplies[ARRAY_SIZE(cs42l51_supply_names)]; |
50 | struct gpio_desc *reset_gpio; |
51 | struct regmap *regmap; |
52 | }; |
53 | |
54 | #define CS42L51_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ |
55 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) |
56 | |
57 | static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol, |
58 | struct snd_ctl_elem_value *ucontrol) |
59 | { |
60 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
61 | unsigned long value = snd_soc_component_read(component, CS42L51_PCM_MIXER)&3; |
62 | |
63 | switch (value) { |
64 | default: |
65 | case 0: |
66 | ucontrol->value.enumerated.item[0] = 0; |
67 | break; |
68 | /* same value : (L+R)/2 and (R+L)/2 */ |
69 | case 1: |
70 | case 2: |
71 | ucontrol->value.enumerated.item[0] = 1; |
72 | break; |
73 | case 3: |
74 | ucontrol->value.enumerated.item[0] = 2; |
75 | break; |
76 | } |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | #define CHAN_MIX_NORMAL 0x00 |
82 | #define CHAN_MIX_BOTH 0x55 |
83 | #define CHAN_MIX_SWAP 0xFF |
84 | |
85 | static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol, |
86 | struct snd_ctl_elem_value *ucontrol) |
87 | { |
88 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
89 | unsigned char val; |
90 | |
91 | switch (ucontrol->value.enumerated.item[0]) { |
92 | default: |
93 | case 0: |
94 | val = CHAN_MIX_NORMAL; |
95 | break; |
96 | case 1: |
97 | val = CHAN_MIX_BOTH; |
98 | break; |
99 | case 2: |
100 | val = CHAN_MIX_SWAP; |
101 | break; |
102 | } |
103 | |
104 | snd_soc_component_write(component, CS42L51_PCM_MIXER, val); |
105 | |
106 | return 1; |
107 | } |
108 | |
109 | static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0); |
110 | static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0); |
111 | |
112 | static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0); |
113 | |
114 | static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0); |
115 | static const DECLARE_TLV_DB_SCALE(adc_boost_tlv, 2000, 2000, 0); |
116 | static const char *chan_mix[] = { |
117 | "L R" , |
118 | "L+R" , |
119 | "R L" , |
120 | }; |
121 | |
122 | static const DECLARE_TLV_DB_SCALE(pga_tlv, -300, 50, 0); |
123 | static const DECLARE_TLV_DB_SCALE(adc_att_tlv, -9600, 100, 0); |
124 | |
125 | static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix); |
126 | |
127 | static const struct snd_kcontrol_new cs42l51_snd_controls[] = { |
128 | SOC_DOUBLE_R_SX_TLV("PCM Playback Volume" , |
129 | CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, |
130 | 0, 0x19, 0x7F, adc_pcm_tlv), |
131 | SOC_DOUBLE_R("PCM Playback Switch" , |
132 | CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1), |
133 | SOC_DOUBLE_R_SX_TLV("Analog Playback Volume" , |
134 | CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL, |
135 | 0, 0x34, 0xE4, aout_tlv), |
136 | SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume" , |
137 | CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, |
138 | 0, 0x19, 0x7F, adc_pcm_tlv), |
139 | SOC_DOUBLE_R("ADC Mixer Switch" , |
140 | CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1), |
141 | SOC_DOUBLE_R_SX_TLV("ADC Attenuator Volume" , |
142 | CS42L51_ADCA_ATT, CS42L51_ADCB_ATT, |
143 | 0, 0xA0, 96, adc_att_tlv), |
144 | SOC_DOUBLE_R_SX_TLV("PGA Volume" , |
145 | CS42L51_ALC_PGA_CTL, CS42L51_ALC_PGB_CTL, |
146 | 0, 0x1A, 30, pga_tlv), |
147 | SOC_SINGLE("Playback Deemphasis Switch" , CS42L51_DAC_CTL, 3, 1, 0), |
148 | SOC_SINGLE("Auto-Mute Switch" , CS42L51_DAC_CTL, 2, 1, 0), |
149 | SOC_SINGLE("Soft Ramp Switch" , CS42L51_DAC_CTL, 1, 1, 0), |
150 | SOC_SINGLE("Zero Cross Switch" , CS42L51_DAC_CTL, 0, 0, 0), |
151 | SOC_DOUBLE_TLV("Mic Boost Volume" , |
152 | CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv), |
153 | SOC_DOUBLE_TLV("ADC Boost Volume" , |
154 | CS42L51_MIC_CTL, 5, 6, 1, 0, adc_boost_tlv), |
155 | SOC_SINGLE_TLV("Bass Volume" , CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv), |
156 | SOC_SINGLE_TLV("Treble Volume" , CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv), |
157 | SOC_ENUM_EXT("PCM channel mixer" , |
158 | cs42l51_chan_mix, |
159 | cs42l51_get_chan_mix, cs42l51_set_chan_mix), |
160 | }; |
161 | |
162 | /* |
163 | * to power down, one must: |
164 | * 1.) Enable the PDN bit |
165 | * 2.) enable power-down for the select channels |
166 | * 3.) disable the PDN bit. |
167 | */ |
168 | static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w, |
169 | struct snd_kcontrol *kcontrol, int event) |
170 | { |
171 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
172 | |
173 | switch (event) { |
174 | case SND_SOC_DAPM_PRE_PMD: |
175 | snd_soc_component_update_bits(component, CS42L51_POWER_CTL1, |
176 | CS42L51_POWER_CTL1_PDN, |
177 | CS42L51_POWER_CTL1_PDN); |
178 | break; |
179 | default: |
180 | case SND_SOC_DAPM_POST_PMD: |
181 | snd_soc_component_update_bits(component, CS42L51_POWER_CTL1, |
182 | CS42L51_POWER_CTL1_PDN, val: 0); |
183 | break; |
184 | } |
185 | |
186 | return 0; |
187 | } |
188 | |
189 | static const char *cs42l51_dac_names[] = {"Direct PCM" , |
190 | "DSP PCM" , "ADC" }; |
191 | static SOC_ENUM_SINGLE_DECL(cs42l51_dac_mux_enum, |
192 | CS42L51_DAC_CTL, 6, cs42l51_dac_names); |
193 | static const struct snd_kcontrol_new cs42l51_dac_mux_controls = |
194 | SOC_DAPM_ENUM("Route" , cs42l51_dac_mux_enum); |
195 | |
196 | static const char *cs42l51_adcl_names[] = {"AIN1 Left" , "AIN2 Left" , |
197 | "MIC Left" , "MIC+preamp Left" }; |
198 | static SOC_ENUM_SINGLE_DECL(cs42l51_adcl_mux_enum, |
199 | CS42L51_ADC_INPUT, 4, cs42l51_adcl_names); |
200 | static const struct snd_kcontrol_new cs42l51_adcl_mux_controls = |
201 | SOC_DAPM_ENUM("Route" , cs42l51_adcl_mux_enum); |
202 | |
203 | static const char *cs42l51_adcr_names[] = {"AIN1 Right" , "AIN2 Right" , |
204 | "MIC Right" , "MIC+preamp Right" }; |
205 | static SOC_ENUM_SINGLE_DECL(cs42l51_adcr_mux_enum, |
206 | CS42L51_ADC_INPUT, 6, cs42l51_adcr_names); |
207 | static const struct snd_kcontrol_new cs42l51_adcr_mux_controls = |
208 | SOC_DAPM_ENUM("Route" , cs42l51_adcr_mux_enum); |
209 | |
210 | static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = { |
211 | SND_SOC_DAPM_SUPPLY("Mic Bias" , CS42L51_MIC_POWER_CTL, 1, 1, NULL, |
212 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
213 | SND_SOC_DAPM_PGA_E("Left PGA" , CS42L51_POWER_CTL1, 3, 1, NULL, 0, |
214 | cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), |
215 | SND_SOC_DAPM_PGA_E("Right PGA" , CS42L51_POWER_CTL1, 4, 1, NULL, 0, |
216 | cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), |
217 | SND_SOC_DAPM_ADC_E("Left ADC" , "Left HiFi Capture" , |
218 | CS42L51_POWER_CTL1, 1, 1, |
219 | cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), |
220 | SND_SOC_DAPM_ADC_E("Right ADC" , "Right HiFi Capture" , |
221 | CS42L51_POWER_CTL1, 2, 1, |
222 | cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), |
223 | SND_SOC_DAPM_DAC_E("Left DAC" , NULL, CS42L51_POWER_CTL1, 5, 1, |
224 | cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), |
225 | SND_SOC_DAPM_DAC_E("Right DAC" , NULL, CS42L51_POWER_CTL1, 6, 1, |
226 | cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), |
227 | |
228 | /* analog/mic */ |
229 | SND_SOC_DAPM_INPUT("AIN1L" ), |
230 | SND_SOC_DAPM_INPUT("AIN1R" ), |
231 | SND_SOC_DAPM_INPUT("AIN2L" ), |
232 | SND_SOC_DAPM_INPUT("AIN2R" ), |
233 | SND_SOC_DAPM_INPUT("MICL" ), |
234 | SND_SOC_DAPM_INPUT("MICR" ), |
235 | |
236 | SND_SOC_DAPM_MIXER("Mic Preamp Left" , |
237 | CS42L51_MIC_POWER_CTL, 2, 1, NULL, 0), |
238 | SND_SOC_DAPM_MIXER("Mic Preamp Right" , |
239 | CS42L51_MIC_POWER_CTL, 3, 1, NULL, 0), |
240 | |
241 | /* HP */ |
242 | SND_SOC_DAPM_OUTPUT("HPL" ), |
243 | SND_SOC_DAPM_OUTPUT("HPR" ), |
244 | |
245 | /* mux */ |
246 | SND_SOC_DAPM_MUX("DAC Mux" , SND_SOC_NOPM, 0, 0, |
247 | &cs42l51_dac_mux_controls), |
248 | SND_SOC_DAPM_MUX("PGA-ADC Mux Left" , SND_SOC_NOPM, 0, 0, |
249 | &cs42l51_adcl_mux_controls), |
250 | SND_SOC_DAPM_MUX("PGA-ADC Mux Right" , SND_SOC_NOPM, 0, 0, |
251 | &cs42l51_adcr_mux_controls), |
252 | }; |
253 | |
254 | static int mclk_event(struct snd_soc_dapm_widget *w, |
255 | struct snd_kcontrol *kcontrol, int event) |
256 | { |
257 | struct snd_soc_component *comp = snd_soc_dapm_to_component(dapm: w->dapm); |
258 | struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(c: comp); |
259 | |
260 | switch (event) { |
261 | case SND_SOC_DAPM_PRE_PMU: |
262 | return clk_prepare_enable(clk: cs42l51->mclk_handle); |
263 | case SND_SOC_DAPM_POST_PMD: |
264 | /* Delay mclk shutdown to fulfill power-down sequence requirements */ |
265 | msleep(msecs: 20); |
266 | clk_disable_unprepare(clk: cs42l51->mclk_handle); |
267 | break; |
268 | } |
269 | |
270 | return 0; |
271 | } |
272 | |
273 | static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets[] = { |
274 | SND_SOC_DAPM_SUPPLY("MCLK" , SND_SOC_NOPM, 0, 0, mclk_event, |
275 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
276 | }; |
277 | |
278 | static const struct snd_soc_dapm_route cs42l51_routes[] = { |
279 | {"HPL" , NULL, "Left DAC" }, |
280 | {"HPR" , NULL, "Right DAC" }, |
281 | |
282 | {"Right DAC" , NULL, "DAC Mux" }, |
283 | {"Left DAC" , NULL, "DAC Mux" }, |
284 | |
285 | {"DAC Mux" , "Direct PCM" , "Playback" }, |
286 | {"DAC Mux" , "DSP PCM" , "Playback" }, |
287 | |
288 | {"Left ADC" , NULL, "Left PGA" }, |
289 | {"Right ADC" , NULL, "Right PGA" }, |
290 | |
291 | {"Mic Preamp Left" , NULL, "MICL" }, |
292 | {"Mic Preamp Right" , NULL, "MICR" }, |
293 | |
294 | {"PGA-ADC Mux Left" , "AIN1 Left" , "AIN1L" }, |
295 | {"PGA-ADC Mux Left" , "AIN2 Left" , "AIN2L" }, |
296 | {"PGA-ADC Mux Left" , "MIC Left" , "MICL" }, |
297 | {"PGA-ADC Mux Left" , "MIC+preamp Left" , "Mic Preamp Left" }, |
298 | {"PGA-ADC Mux Right" , "AIN1 Right" , "AIN1R" }, |
299 | {"PGA-ADC Mux Right" , "AIN2 Right" , "AIN2R" }, |
300 | {"PGA-ADC Mux Right" , "MIC Right" , "MICR" }, |
301 | {"PGA-ADC Mux Right" , "MIC+preamp Right" , "Mic Preamp Right" }, |
302 | |
303 | {"Left PGA" , NULL, "PGA-ADC Mux Left" }, |
304 | {"Right PGA" , NULL, "PGA-ADC Mux Right" }, |
305 | }; |
306 | |
307 | static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai, |
308 | unsigned int format) |
309 | { |
310 | struct snd_soc_component *component = codec_dai->component; |
311 | struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(c: component); |
312 | |
313 | switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { |
314 | case SND_SOC_DAIFMT_I2S: |
315 | case SND_SOC_DAIFMT_LEFT_J: |
316 | case SND_SOC_DAIFMT_RIGHT_J: |
317 | cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK; |
318 | break; |
319 | default: |
320 | dev_err(component->dev, "invalid DAI format\n" ); |
321 | return -EINVAL; |
322 | } |
323 | |
324 | switch (format & SND_SOC_DAIFMT_MASTER_MASK) { |
325 | case SND_SOC_DAIFMT_CBM_CFM: |
326 | cs42l51->func = MODE_MASTER; |
327 | break; |
328 | case SND_SOC_DAIFMT_CBS_CFS: |
329 | cs42l51->func = MODE_SLAVE_AUTO; |
330 | break; |
331 | default: |
332 | dev_err(component->dev, "Unknown master/slave configuration\n" ); |
333 | return -EINVAL; |
334 | } |
335 | |
336 | return 0; |
337 | } |
338 | |
339 | struct cs42l51_ratios { |
340 | unsigned int ratio; |
341 | unsigned char speed_mode; |
342 | unsigned char mclk; |
343 | }; |
344 | |
345 | static struct cs42l51_ratios slave_ratios[] = { |
346 | { 512, CS42L51_QSM_MODE, 0 }, { 768, CS42L51_QSM_MODE, 0 }, |
347 | { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 }, |
348 | { 2048, CS42L51_QSM_MODE, 0 }, { 3072, CS42L51_QSM_MODE, 0 }, |
349 | { 256, CS42L51_HSM_MODE, 0 }, { 384, CS42L51_HSM_MODE, 0 }, |
350 | { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 }, |
351 | { 1024, CS42L51_HSM_MODE, 0 }, { 1536, CS42L51_HSM_MODE, 0 }, |
352 | { 128, CS42L51_SSM_MODE, 0 }, { 192, CS42L51_SSM_MODE, 0 }, |
353 | { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 }, |
354 | { 512, CS42L51_SSM_MODE, 0 }, { 768, CS42L51_SSM_MODE, 0 }, |
355 | { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 }, |
356 | { 256, CS42L51_DSM_MODE, 0 }, { 384, CS42L51_DSM_MODE, 0 }, |
357 | }; |
358 | |
359 | static struct cs42l51_ratios slave_auto_ratios[] = { |
360 | { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 }, |
361 | { 2048, CS42L51_QSM_MODE, 1 }, { 3072, CS42L51_QSM_MODE, 1 }, |
362 | { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 }, |
363 | { 1024, CS42L51_HSM_MODE, 1 }, { 1536, CS42L51_HSM_MODE, 1 }, |
364 | { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 }, |
365 | { 512, CS42L51_SSM_MODE, 1 }, { 768, CS42L51_SSM_MODE, 1 }, |
366 | { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 }, |
367 | { 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 }, |
368 | }; |
369 | |
370 | /* |
371 | * Master mode mclk/fs ratios. |
372 | * Recommended configurations are SSM for 4-50khz and DSM for 50-100kHz ranges |
373 | * The table below provides support of following ratios: |
374 | * 128: SSM (%128) with div2 disabled |
375 | * 256: SSM (%128) with div2 enabled |
376 | * In both cases, if sampling rate is above 50kHz, SSM is overridden |
377 | * with DSM (%128) configuration |
378 | */ |
379 | static struct cs42l51_ratios master_ratios[] = { |
380 | { 128, CS42L51_SSM_MODE, 0 }, { 256, CS42L51_SSM_MODE, 1 }, |
381 | }; |
382 | |
383 | static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
384 | int clk_id, unsigned int freq, int dir) |
385 | { |
386 | struct snd_soc_component *component = codec_dai->component; |
387 | struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(c: component); |
388 | |
389 | cs42l51->mclk = freq; |
390 | return 0; |
391 | } |
392 | |
393 | static int cs42l51_hw_params(struct snd_pcm_substream *substream, |
394 | struct snd_pcm_hw_params *params, |
395 | struct snd_soc_dai *dai) |
396 | { |
397 | struct snd_soc_component *component = dai->component; |
398 | struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(c: component); |
399 | int ret; |
400 | unsigned int i; |
401 | unsigned int rate; |
402 | unsigned int ratio; |
403 | struct cs42l51_ratios *ratios = NULL; |
404 | int nr_ratios = 0; |
405 | int intf_ctl, power_ctl, fmt, mode; |
406 | |
407 | switch (cs42l51->func) { |
408 | case MODE_MASTER: |
409 | ratios = master_ratios; |
410 | nr_ratios = ARRAY_SIZE(master_ratios); |
411 | break; |
412 | case MODE_SLAVE: |
413 | ratios = slave_ratios; |
414 | nr_ratios = ARRAY_SIZE(slave_ratios); |
415 | break; |
416 | case MODE_SLAVE_AUTO: |
417 | ratios = slave_auto_ratios; |
418 | nr_ratios = ARRAY_SIZE(slave_auto_ratios); |
419 | break; |
420 | } |
421 | |
422 | /* Figure out which MCLK/LRCK ratio to use */ |
423 | rate = params_rate(p: params); /* Sampling rate, in Hz */ |
424 | ratio = cs42l51->mclk / rate; /* MCLK/LRCK ratio */ |
425 | for (i = 0; i < nr_ratios; i++) { |
426 | if (ratios[i].ratio == ratio) |
427 | break; |
428 | } |
429 | |
430 | if (i == nr_ratios) { |
431 | /* We did not find a matching ratio */ |
432 | dev_err(component->dev, "could not find matching ratio\n" ); |
433 | return -EINVAL; |
434 | } |
435 | |
436 | intf_ctl = snd_soc_component_read(component, CS42L51_INTF_CTL); |
437 | power_ctl = snd_soc_component_read(component, CS42L51_MIC_POWER_CTL); |
438 | |
439 | intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S |
440 | | CS42L51_INTF_CTL_DAC_FORMAT(7)); |
441 | power_ctl &= ~(CS42L51_MIC_POWER_CTL_SPEED(3) |
442 | | CS42L51_MIC_POWER_CTL_MCLK_DIV2); |
443 | |
444 | switch (cs42l51->func) { |
445 | case MODE_MASTER: |
446 | intf_ctl |= CS42L51_INTF_CTL_MASTER; |
447 | mode = ratios[i].speed_mode; |
448 | /* Force DSM mode if sampling rate is above 50kHz */ |
449 | if (rate > 50000) |
450 | mode = CS42L51_DSM_MODE; |
451 | power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(mode); |
452 | /* |
453 | * Auto detect mode is not applicable for master mode and has to |
454 | * be disabled. Otherwise SPEED[1:0] bits will be ignored. |
455 | */ |
456 | power_ctl &= ~CS42L51_MIC_POWER_CTL_AUTO; |
457 | break; |
458 | case MODE_SLAVE: |
459 | power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode); |
460 | break; |
461 | case MODE_SLAVE_AUTO: |
462 | power_ctl |= CS42L51_MIC_POWER_CTL_AUTO; |
463 | break; |
464 | } |
465 | |
466 | switch (cs42l51->audio_mode) { |
467 | case SND_SOC_DAIFMT_I2S: |
468 | intf_ctl |= CS42L51_INTF_CTL_ADC_I2S; |
469 | intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_I2S); |
470 | break; |
471 | case SND_SOC_DAIFMT_LEFT_J: |
472 | intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24); |
473 | break; |
474 | case SND_SOC_DAIFMT_RIGHT_J: |
475 | switch (params_width(p: params)) { |
476 | case 16: |
477 | fmt = CS42L51_DAC_DIF_RJ16; |
478 | break; |
479 | case 18: |
480 | fmt = CS42L51_DAC_DIF_RJ18; |
481 | break; |
482 | case 20: |
483 | fmt = CS42L51_DAC_DIF_RJ20; |
484 | break; |
485 | case 24: |
486 | fmt = CS42L51_DAC_DIF_RJ24; |
487 | break; |
488 | default: |
489 | dev_err(component->dev, "unknown format\n" ); |
490 | return -EINVAL; |
491 | } |
492 | intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt); |
493 | break; |
494 | default: |
495 | dev_err(component->dev, "unknown format\n" ); |
496 | return -EINVAL; |
497 | } |
498 | |
499 | if (ratios[i].mclk) |
500 | power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2; |
501 | |
502 | ret = snd_soc_component_write(component, CS42L51_INTF_CTL, val: intf_ctl); |
503 | if (ret < 0) |
504 | return ret; |
505 | |
506 | ret = snd_soc_component_write(component, CS42L51_MIC_POWER_CTL, val: power_ctl); |
507 | if (ret < 0) |
508 | return ret; |
509 | |
510 | return 0; |
511 | } |
512 | |
513 | static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute, int direction) |
514 | { |
515 | struct snd_soc_component *component = dai->component; |
516 | int reg; |
517 | int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE; |
518 | |
519 | reg = snd_soc_component_read(component, CS42L51_DAC_OUT_CTL); |
520 | |
521 | if (mute) |
522 | reg |= mask; |
523 | else |
524 | reg &= ~mask; |
525 | |
526 | return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, val: reg); |
527 | } |
528 | |
529 | static int cs42l51_of_xlate_dai_id(struct snd_soc_component *component, |
530 | struct device_node *endpoint) |
531 | { |
532 | /* return dai id 0, whatever the endpoint index */ |
533 | return 0; |
534 | } |
535 | |
536 | static const struct snd_soc_dai_ops cs42l51_dai_ops = { |
537 | .hw_params = cs42l51_hw_params, |
538 | .set_sysclk = cs42l51_set_dai_sysclk, |
539 | .set_fmt = cs42l51_set_dai_fmt, |
540 | .mute_stream = cs42l51_dai_mute, |
541 | .no_capture_mute = 1, |
542 | }; |
543 | |
544 | static struct snd_soc_dai_driver cs42l51_dai = { |
545 | .name = "cs42l51-hifi" , |
546 | .playback = { |
547 | .stream_name = "Playback" , |
548 | .channels_min = 1, |
549 | .channels_max = 2, |
550 | .rates = SNDRV_PCM_RATE_8000_96000, |
551 | .formats = CS42L51_FORMATS, |
552 | }, |
553 | .capture = { |
554 | .stream_name = "Capture" , |
555 | .channels_min = 1, |
556 | .channels_max = 2, |
557 | .rates = SNDRV_PCM_RATE_8000_96000, |
558 | .formats = CS42L51_FORMATS, |
559 | }, |
560 | .ops = &cs42l51_dai_ops, |
561 | }; |
562 | |
563 | static int cs42l51_component_probe(struct snd_soc_component *component) |
564 | { |
565 | int ret, reg; |
566 | struct snd_soc_dapm_context *dapm; |
567 | struct cs42l51_private *cs42l51; |
568 | |
569 | cs42l51 = snd_soc_component_get_drvdata(c: component); |
570 | dapm = snd_soc_component_get_dapm(component); |
571 | |
572 | if (cs42l51->mclk_handle) |
573 | snd_soc_dapm_new_controls(dapm, widget: cs42l51_dapm_mclk_widgets, num: 1); |
574 | |
575 | /* |
576 | * DAC configuration |
577 | * - Use signal processor |
578 | * - auto mute |
579 | * - vol changes immediate |
580 | * - no de-emphasize |
581 | */ |
582 | reg = CS42L51_DAC_CTL_DATA_SEL(1) |
583 | | CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0); |
584 | ret = snd_soc_component_write(component, CS42L51_DAC_CTL, val: reg); |
585 | if (ret < 0) |
586 | return ret; |
587 | |
588 | return 0; |
589 | } |
590 | |
591 | static const struct snd_soc_component_driver soc_component_device_cs42l51 = { |
592 | .probe = cs42l51_component_probe, |
593 | .controls = cs42l51_snd_controls, |
594 | .num_controls = ARRAY_SIZE(cs42l51_snd_controls), |
595 | .dapm_widgets = cs42l51_dapm_widgets, |
596 | .num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets), |
597 | .dapm_routes = cs42l51_routes, |
598 | .num_dapm_routes = ARRAY_SIZE(cs42l51_routes), |
599 | .of_xlate_dai_id = cs42l51_of_xlate_dai_id, |
600 | .idle_bias_on = 1, |
601 | .use_pmdown_time = 1, |
602 | .endianness = 1, |
603 | }; |
604 | |
605 | static bool cs42l51_writeable_reg(struct device *dev, unsigned int reg) |
606 | { |
607 | switch (reg) { |
608 | case CS42L51_POWER_CTL1: |
609 | case CS42L51_MIC_POWER_CTL: |
610 | case CS42L51_INTF_CTL: |
611 | case CS42L51_MIC_CTL: |
612 | case CS42L51_ADC_CTL: |
613 | case CS42L51_ADC_INPUT: |
614 | case CS42L51_DAC_OUT_CTL: |
615 | case CS42L51_DAC_CTL: |
616 | case CS42L51_ALC_PGA_CTL: |
617 | case CS42L51_ALC_PGB_CTL: |
618 | case CS42L51_ADCA_ATT: |
619 | case CS42L51_ADCB_ATT: |
620 | case CS42L51_ADCA_VOL: |
621 | case CS42L51_ADCB_VOL: |
622 | case CS42L51_PCMA_VOL: |
623 | case CS42L51_PCMB_VOL: |
624 | case CS42L51_BEEP_FREQ: |
625 | case CS42L51_BEEP_VOL: |
626 | case CS42L51_BEEP_CONF: |
627 | case CS42L51_TONE_CTL: |
628 | case CS42L51_AOUTA_VOL: |
629 | case CS42L51_AOUTB_VOL: |
630 | case CS42L51_PCM_MIXER: |
631 | case CS42L51_LIMIT_THRES_DIS: |
632 | case CS42L51_LIMIT_REL: |
633 | case CS42L51_LIMIT_ATT: |
634 | case CS42L51_ALC_EN: |
635 | case CS42L51_ALC_REL: |
636 | case CS42L51_ALC_THRES: |
637 | case CS42L51_NOISE_CONF: |
638 | case CS42L51_CHARGE_FREQ: |
639 | return true; |
640 | default: |
641 | return false; |
642 | } |
643 | } |
644 | |
645 | static bool cs42l51_volatile_reg(struct device *dev, unsigned int reg) |
646 | { |
647 | switch (reg) { |
648 | case CS42L51_STATUS: |
649 | return true; |
650 | default: |
651 | return false; |
652 | } |
653 | } |
654 | |
655 | static bool cs42l51_readable_reg(struct device *dev, unsigned int reg) |
656 | { |
657 | switch (reg) { |
658 | case CS42L51_CHIP_REV_ID: |
659 | case CS42L51_POWER_CTL1: |
660 | case CS42L51_MIC_POWER_CTL: |
661 | case CS42L51_INTF_CTL: |
662 | case CS42L51_MIC_CTL: |
663 | case CS42L51_ADC_CTL: |
664 | case CS42L51_ADC_INPUT: |
665 | case CS42L51_DAC_OUT_CTL: |
666 | case CS42L51_DAC_CTL: |
667 | case CS42L51_ALC_PGA_CTL: |
668 | case CS42L51_ALC_PGB_CTL: |
669 | case CS42L51_ADCA_ATT: |
670 | case CS42L51_ADCB_ATT: |
671 | case CS42L51_ADCA_VOL: |
672 | case CS42L51_ADCB_VOL: |
673 | case CS42L51_PCMA_VOL: |
674 | case CS42L51_PCMB_VOL: |
675 | case CS42L51_BEEP_FREQ: |
676 | case CS42L51_BEEP_VOL: |
677 | case CS42L51_BEEP_CONF: |
678 | case CS42L51_TONE_CTL: |
679 | case CS42L51_AOUTA_VOL: |
680 | case CS42L51_AOUTB_VOL: |
681 | case CS42L51_PCM_MIXER: |
682 | case CS42L51_LIMIT_THRES_DIS: |
683 | case CS42L51_LIMIT_REL: |
684 | case CS42L51_LIMIT_ATT: |
685 | case CS42L51_ALC_EN: |
686 | case CS42L51_ALC_REL: |
687 | case CS42L51_ALC_THRES: |
688 | case CS42L51_NOISE_CONF: |
689 | case CS42L51_STATUS: |
690 | case CS42L51_CHARGE_FREQ: |
691 | return true; |
692 | default: |
693 | return false; |
694 | } |
695 | } |
696 | |
697 | const struct regmap_config cs42l51_regmap = { |
698 | .reg_bits = 8, |
699 | .reg_stride = 1, |
700 | .val_bits = 8, |
701 | .use_single_write = true, |
702 | .readable_reg = cs42l51_readable_reg, |
703 | .volatile_reg = cs42l51_volatile_reg, |
704 | .writeable_reg = cs42l51_writeable_reg, |
705 | .max_register = CS42L51_CHARGE_FREQ, |
706 | .cache_type = REGCACHE_MAPLE, |
707 | }; |
708 | EXPORT_SYMBOL_GPL(cs42l51_regmap); |
709 | |
710 | int cs42l51_probe(struct device *dev, struct regmap *regmap) |
711 | { |
712 | struct cs42l51_private *cs42l51; |
713 | unsigned int val; |
714 | int ret, i; |
715 | |
716 | if (IS_ERR(ptr: regmap)) |
717 | return PTR_ERR(ptr: regmap); |
718 | |
719 | cs42l51 = devm_kzalloc(dev, size: sizeof(struct cs42l51_private), |
720 | GFP_KERNEL); |
721 | if (!cs42l51) |
722 | return -ENOMEM; |
723 | |
724 | dev_set_drvdata(dev, data: cs42l51); |
725 | cs42l51->regmap = regmap; |
726 | |
727 | cs42l51->mclk_handle = devm_clk_get_optional(dev, id: "MCLK" ); |
728 | if (IS_ERR(ptr: cs42l51->mclk_handle)) |
729 | return PTR_ERR(ptr: cs42l51->mclk_handle); |
730 | |
731 | for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++) |
732 | cs42l51->supplies[i].supply = cs42l51_supply_names[i]; |
733 | |
734 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs42l51->supplies), |
735 | consumers: cs42l51->supplies); |
736 | if (ret != 0) { |
737 | dev_err(dev, "Failed to request supplies: %d\n" , ret); |
738 | return ret; |
739 | } |
740 | |
741 | ret = regulator_bulk_enable(ARRAY_SIZE(cs42l51->supplies), |
742 | consumers: cs42l51->supplies); |
743 | if (ret != 0) { |
744 | dev_err(dev, "Failed to enable supplies: %d\n" , ret); |
745 | return ret; |
746 | } |
747 | |
748 | cs42l51->reset_gpio = devm_gpiod_get_optional(dev, con_id: "reset" , |
749 | flags: GPIOD_OUT_LOW); |
750 | if (IS_ERR(ptr: cs42l51->reset_gpio)) |
751 | return PTR_ERR(ptr: cs42l51->reset_gpio); |
752 | |
753 | if (cs42l51->reset_gpio) { |
754 | dev_dbg(dev, "Release reset gpio\n" ); |
755 | gpiod_set_value_cansleep(desc: cs42l51->reset_gpio, value: 0); |
756 | mdelay(2); |
757 | } |
758 | |
759 | /* Verify that we have a CS42L51 */ |
760 | ret = regmap_read(map: regmap, CS42L51_CHIP_REV_ID, val: &val); |
761 | if (ret < 0) { |
762 | dev_err(dev, "failed to read I2C\n" ); |
763 | goto error; |
764 | } |
765 | |
766 | if ((val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) && |
767 | (val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) { |
768 | dev_err(dev, "Invalid chip id: %x\n" , val); |
769 | ret = -ENODEV; |
770 | goto error; |
771 | } |
772 | dev_info(dev, "Cirrus Logic CS42L51, Revision: %02X\n" , |
773 | val & CS42L51_CHIP_REV_MASK); |
774 | |
775 | ret = devm_snd_soc_register_component(dev, |
776 | component_driver: &soc_component_device_cs42l51, dai_drv: &cs42l51_dai, num_dai: 1); |
777 | if (ret < 0) |
778 | goto error; |
779 | |
780 | return 0; |
781 | |
782 | error: |
783 | regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), |
784 | consumers: cs42l51->supplies); |
785 | return ret; |
786 | } |
787 | EXPORT_SYMBOL_GPL(cs42l51_probe); |
788 | |
789 | void cs42l51_remove(struct device *dev) |
790 | { |
791 | struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); |
792 | int ret; |
793 | |
794 | gpiod_set_value_cansleep(desc: cs42l51->reset_gpio, value: 1); |
795 | |
796 | ret = regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), |
797 | consumers: cs42l51->supplies); |
798 | if (ret) |
799 | dev_warn(dev, "Failed to disable all regulators (%pe)\n" , |
800 | ERR_PTR(ret)); |
801 | |
802 | } |
803 | EXPORT_SYMBOL_GPL(cs42l51_remove); |
804 | |
805 | int __maybe_unused cs42l51_suspend(struct device *dev) |
806 | { |
807 | struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); |
808 | |
809 | regcache_cache_only(map: cs42l51->regmap, enable: true); |
810 | regcache_mark_dirty(map: cs42l51->regmap); |
811 | |
812 | return 0; |
813 | } |
814 | EXPORT_SYMBOL_GPL(cs42l51_suspend); |
815 | |
816 | int __maybe_unused cs42l51_resume(struct device *dev) |
817 | { |
818 | struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); |
819 | |
820 | regcache_cache_only(map: cs42l51->regmap, enable: false); |
821 | |
822 | return regcache_sync(map: cs42l51->regmap); |
823 | } |
824 | EXPORT_SYMBOL_GPL(cs42l51_resume); |
825 | |
826 | MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>" ); |
827 | MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver" ); |
828 | MODULE_LICENSE("GPL" ); |
829 | |