1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * cs4265.c -- CS4265 ALSA SoC audio driver |
4 | * |
5 | * Copyright 2014 Cirrus Logic, Inc. |
6 | * |
7 | * Author: Paul Handrigan <paul.handrigan@cirrus.com> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/moduleparam.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/gpio/consumer.h> |
14 | #include <linux/init.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/i2c.h> |
17 | #include <linux/input.h> |
18 | #include <linux/regmap.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/platform_device.h> |
21 | #include <sound/core.h> |
22 | #include <sound/pcm.h> |
23 | #include <sound/pcm_params.h> |
24 | #include <sound/soc.h> |
25 | #include <sound/soc-dapm.h> |
26 | #include <sound/initval.h> |
27 | #include <sound/tlv.h> |
28 | #include "cs4265.h" |
29 | |
30 | struct cs4265_private { |
31 | struct regmap *regmap; |
32 | struct gpio_desc *reset_gpio; |
33 | u8 format; |
34 | u32 sysclk; |
35 | }; |
36 | |
37 | static const struct reg_default cs4265_reg_defaults[] = { |
38 | { CS4265_PWRCTL, 0x0F }, |
39 | { CS4265_DAC_CTL, 0x08 }, |
40 | { CS4265_ADC_CTL, 0x00 }, |
41 | { CS4265_MCLK_FREQ, 0x00 }, |
42 | { CS4265_SIG_SEL, 0x40 }, |
43 | { CS4265_CHB_PGA_CTL, 0x00 }, |
44 | { CS4265_CHA_PGA_CTL, 0x00 }, |
45 | { CS4265_ADC_CTL2, 0x19 }, |
46 | { CS4265_DAC_CHA_VOL, 0x00 }, |
47 | { CS4265_DAC_CHB_VOL, 0x00 }, |
48 | { CS4265_DAC_CTL2, 0xC0 }, |
49 | { CS4265_SPDIF_CTL1, 0x00 }, |
50 | { CS4265_SPDIF_CTL2, 0x00 }, |
51 | { CS4265_INT_MASK, 0x00 }, |
52 | { CS4265_STATUS_MODE_MSB, 0x00 }, |
53 | { CS4265_STATUS_MODE_LSB, 0x00 }, |
54 | }; |
55 | |
56 | static bool cs4265_readable_register(struct device *dev, unsigned int reg) |
57 | { |
58 | switch (reg) { |
59 | case CS4265_CHIP_ID ... CS4265_MAX_REGISTER: |
60 | return true; |
61 | default: |
62 | return false; |
63 | } |
64 | } |
65 | |
66 | static bool cs4265_volatile_register(struct device *dev, unsigned int reg) |
67 | { |
68 | switch (reg) { |
69 | case CS4265_INT_STATUS: |
70 | return true; |
71 | default: |
72 | return false; |
73 | } |
74 | } |
75 | |
76 | static DECLARE_TLV_DB_SCALE(pga_tlv, -1200, 50, 0); |
77 | |
78 | static DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 0); |
79 | |
80 | static const char * const digital_input_mux_text[] = { |
81 | "SDIN1" , "SDIN2" |
82 | }; |
83 | |
84 | static SOC_ENUM_SINGLE_DECL(digital_input_mux_enum, CS4265_SIG_SEL, 7, |
85 | digital_input_mux_text); |
86 | |
87 | static const struct snd_kcontrol_new digital_input_mux = |
88 | SOC_DAPM_ENUM("Digital Input Mux" , digital_input_mux_enum); |
89 | |
90 | static const char * const mic_linein_text[] = { |
91 | "MIC" , "LINEIN" |
92 | }; |
93 | |
94 | static SOC_ENUM_SINGLE_DECL(mic_linein_enum, CS4265_ADC_CTL2, 0, |
95 | mic_linein_text); |
96 | |
97 | static const char * const cam_mode_text[] = { |
98 | "One Byte" , "Two Byte" |
99 | }; |
100 | |
101 | static SOC_ENUM_SINGLE_DECL(cam_mode_enum, CS4265_SPDIF_CTL1, 5, |
102 | cam_mode_text); |
103 | |
104 | static const char * const cam_mono_stereo_text[] = { |
105 | "Stereo" , "Mono" |
106 | }; |
107 | |
108 | static SOC_ENUM_SINGLE_DECL(spdif_mono_stereo_enum, CS4265_SPDIF_CTL2, 2, |
109 | cam_mono_stereo_text); |
110 | |
111 | static const char * const mono_select_text[] = { |
112 | "Channel A" , "Channel B" |
113 | }; |
114 | |
115 | static SOC_ENUM_SINGLE_DECL(spdif_mono_select_enum, CS4265_SPDIF_CTL2, 0, |
116 | mono_select_text); |
117 | |
118 | static const struct snd_kcontrol_new mic_linein_mux = |
119 | SOC_DAPM_ENUM("ADC Input Capture Mux" , mic_linein_enum); |
120 | |
121 | static const struct snd_kcontrol_new loopback_ctl = |
122 | SOC_DAPM_SINGLE("Switch" , CS4265_SIG_SEL, 1, 1, 0); |
123 | |
124 | static const struct snd_kcontrol_new spdif_switch = |
125 | SOC_DAPM_SINGLE("Switch" , SND_SOC_NOPM, 0, 0, 0); |
126 | |
127 | static const struct snd_kcontrol_new dac_switch = |
128 | SOC_DAPM_SINGLE("Switch" , CS4265_PWRCTL, 1, 1, 0); |
129 | |
130 | static const struct snd_kcontrol_new cs4265_snd_controls[] = { |
131 | |
132 | SOC_DOUBLE_R_SX_TLV("PGA Volume" , CS4265_CHA_PGA_CTL, |
133 | CS4265_CHB_PGA_CTL, 0, 0x28, 0x30, pga_tlv), |
134 | SOC_DOUBLE_R_TLV("DAC Volume" , CS4265_DAC_CHA_VOL, |
135 | CS4265_DAC_CHB_VOL, 0, 0xFF, 1, dac_tlv), |
136 | SOC_SINGLE("De-emp 44.1kHz Switch" , CS4265_DAC_CTL, 1, |
137 | 1, 0), |
138 | SOC_SINGLE("DAC INV Switch" , CS4265_DAC_CTL2, 5, |
139 | 1, 0), |
140 | SOC_SINGLE("DAC Zero Cross Switch" , CS4265_DAC_CTL2, 6, |
141 | 1, 0), |
142 | SOC_SINGLE("DAC Soft Ramp Switch" , CS4265_DAC_CTL2, 7, |
143 | 1, 0), |
144 | SOC_SINGLE("ADC HPF Switch" , CS4265_ADC_CTL, 1, |
145 | 1, 0), |
146 | SOC_SINGLE("ADC Zero Cross Switch" , CS4265_ADC_CTL2, 3, |
147 | 1, 1), |
148 | SOC_SINGLE("ADC Soft Ramp Switch" , CS4265_ADC_CTL2, 7, |
149 | 1, 0), |
150 | SOC_SINGLE("E to F Buffer Disable Switch" , CS4265_SPDIF_CTL1, |
151 | 6, 1, 0), |
152 | SOC_ENUM("C Data Access" , cam_mode_enum), |
153 | SOC_SINGLE("Validity Bit Control Switch" , CS4265_SPDIF_CTL2, |
154 | 3, 1, 0), |
155 | SOC_ENUM("SPDIF Mono/Stereo" , spdif_mono_stereo_enum), |
156 | SOC_SINGLE("MMTLR Data Switch" , CS4265_SPDIF_CTL2, 0, 1, 0), |
157 | SOC_ENUM("Mono Channel Select" , spdif_mono_select_enum), |
158 | SND_SOC_BYTES("C Data Buffer" , CS4265_C_DATA_BUFF, 24), |
159 | }; |
160 | |
161 | static const struct snd_soc_dapm_widget cs4265_dapm_widgets[] = { |
162 | |
163 | SND_SOC_DAPM_INPUT("LINEINL" ), |
164 | SND_SOC_DAPM_INPUT("LINEINR" ), |
165 | SND_SOC_DAPM_INPUT("MICL" ), |
166 | SND_SOC_DAPM_INPUT("MICR" ), |
167 | |
168 | SND_SOC_DAPM_AIF_OUT("DOUT" , NULL, 0, |
169 | SND_SOC_NOPM, 0, 0), |
170 | SND_SOC_DAPM_AIF_OUT("SPDIFOUT" , NULL, 0, |
171 | SND_SOC_NOPM, 0, 0), |
172 | |
173 | SND_SOC_DAPM_MUX("ADC Mux" , SND_SOC_NOPM, 0, 0, &mic_linein_mux), |
174 | |
175 | SND_SOC_DAPM_ADC("ADC" , NULL, CS4265_PWRCTL, 2, 1), |
176 | SND_SOC_DAPM_PGA("Pre-amp MIC" , CS4265_PWRCTL, 3, |
177 | 1, NULL, 0), |
178 | |
179 | SND_SOC_DAPM_MUX("Input Mux" , SND_SOC_NOPM, |
180 | 0, 0, &digital_input_mux), |
181 | |
182 | SND_SOC_DAPM_MIXER("SDIN1 Input Mixer" , SND_SOC_NOPM, 0, 0, NULL, 0), |
183 | SND_SOC_DAPM_MIXER("SDIN2 Input Mixer" , SND_SOC_NOPM, 0, 0, NULL, 0), |
184 | SND_SOC_DAPM_MIXER("SPDIF Transmitter" , SND_SOC_NOPM, 0, 0, NULL, 0), |
185 | |
186 | SND_SOC_DAPM_SWITCH("Loopback" , SND_SOC_NOPM, 0, 0, |
187 | &loopback_ctl), |
188 | SND_SOC_DAPM_SWITCH("SPDIF" , CS4265_SPDIF_CTL2, 5, 1, |
189 | &spdif_switch), |
190 | SND_SOC_DAPM_SWITCH("DAC" , CS4265_PWRCTL, 1, 1, |
191 | &dac_switch), |
192 | |
193 | SND_SOC_DAPM_AIF_IN("DIN1" , NULL, 0, |
194 | SND_SOC_NOPM, 0, 0), |
195 | SND_SOC_DAPM_AIF_IN("DIN2" , NULL, 0, |
196 | SND_SOC_NOPM, 0, 0), |
197 | SND_SOC_DAPM_AIF_IN("TXIN" , NULL, 0, |
198 | CS4265_SPDIF_CTL2, 5, 1), |
199 | |
200 | SND_SOC_DAPM_OUTPUT("LINEOUTL" ), |
201 | SND_SOC_DAPM_OUTPUT("LINEOUTR" ), |
202 | |
203 | }; |
204 | |
205 | static const struct snd_soc_dapm_route cs4265_audio_map[] = { |
206 | |
207 | {"DIN1" , NULL, "DAI1 Playback" }, |
208 | {"DIN2" , NULL, "DAI2 Playback" }, |
209 | {"SDIN1 Input Mixer" , NULL, "DIN1" }, |
210 | {"SDIN2 Input Mixer" , NULL, "DIN2" }, |
211 | {"Input Mux" , "SDIN1" , "SDIN1 Input Mixer" }, |
212 | {"Input Mux" , "SDIN2" , "SDIN2 Input Mixer" }, |
213 | {"DAC" , "Switch" , "Input Mux" }, |
214 | {"SPDIF" , "Switch" , "Input Mux" }, |
215 | {"LINEOUTL" , NULL, "DAC" }, |
216 | {"LINEOUTR" , NULL, "DAC" }, |
217 | {"SPDIFOUT" , NULL, "SPDIF" }, |
218 | |
219 | {"Pre-amp MIC" , NULL, "MICL" }, |
220 | {"Pre-amp MIC" , NULL, "MICR" }, |
221 | {"ADC Mux" , "MIC" , "Pre-amp MIC" }, |
222 | {"ADC Mux" , "LINEIN" , "LINEINL" }, |
223 | {"ADC Mux" , "LINEIN" , "LINEINR" }, |
224 | {"ADC" , NULL, "ADC Mux" }, |
225 | {"DOUT" , NULL, "ADC" }, |
226 | {"DAI1 Capture" , NULL, "DOUT" }, |
227 | {"DAI2 Capture" , NULL, "DOUT" }, |
228 | |
229 | /* Loopback */ |
230 | {"Loopback" , "Switch" , "ADC" }, |
231 | {"DAC" , NULL, "Loopback" }, |
232 | }; |
233 | |
234 | struct cs4265_clk_para { |
235 | u32 mclk; |
236 | u32 rate; |
237 | u8 fm_mode; /* values 1, 2, or 4 */ |
238 | u8 mclkdiv; |
239 | }; |
240 | |
241 | static const struct cs4265_clk_para clk_map_table[] = { |
242 | /*32k*/ |
243 | {8192000, 32000, 0, 0}, |
244 | {12288000, 32000, 0, 1}, |
245 | {16384000, 32000, 0, 2}, |
246 | {24576000, 32000, 0, 3}, |
247 | {32768000, 32000, 0, 4}, |
248 | |
249 | /*44.1k*/ |
250 | {11289600, 44100, 0, 0}, |
251 | {16934400, 44100, 0, 1}, |
252 | {22579200, 44100, 0, 2}, |
253 | {33868000, 44100, 0, 3}, |
254 | {45158400, 44100, 0, 4}, |
255 | |
256 | /*48k*/ |
257 | {12288000, 48000, 0, 0}, |
258 | {18432000, 48000, 0, 1}, |
259 | {24576000, 48000, 0, 2}, |
260 | {36864000, 48000, 0, 3}, |
261 | {49152000, 48000, 0, 4}, |
262 | |
263 | /*64k*/ |
264 | {8192000, 64000, 1, 0}, |
265 | {12288000, 64000, 1, 1}, |
266 | {16934400, 64000, 1, 2}, |
267 | {24576000, 64000, 1, 3}, |
268 | {32768000, 64000, 1, 4}, |
269 | |
270 | /* 88.2k */ |
271 | {11289600, 88200, 1, 0}, |
272 | {16934400, 88200, 1, 1}, |
273 | {22579200, 88200, 1, 2}, |
274 | {33868000, 88200, 1, 3}, |
275 | {45158400, 88200, 1, 4}, |
276 | |
277 | /* 96k */ |
278 | {12288000, 96000, 1, 0}, |
279 | {18432000, 96000, 1, 1}, |
280 | {24576000, 96000, 1, 2}, |
281 | {36864000, 96000, 1, 3}, |
282 | {49152000, 96000, 1, 4}, |
283 | |
284 | /* 128k */ |
285 | {8192000, 128000, 2, 0}, |
286 | {12288000, 128000, 2, 1}, |
287 | {16934400, 128000, 2, 2}, |
288 | {24576000, 128000, 2, 3}, |
289 | {32768000, 128000, 2, 4}, |
290 | |
291 | /* 176.4k */ |
292 | {11289600, 176400, 2, 0}, |
293 | {16934400, 176400, 2, 1}, |
294 | {22579200, 176400, 2, 2}, |
295 | {33868000, 176400, 2, 3}, |
296 | {49152000, 176400, 2, 4}, |
297 | |
298 | /* 192k */ |
299 | {12288000, 192000, 2, 0}, |
300 | {18432000, 192000, 2, 1}, |
301 | {24576000, 192000, 2, 2}, |
302 | {36864000, 192000, 2, 3}, |
303 | {49152000, 192000, 2, 4}, |
304 | }; |
305 | |
306 | static int cs4265_get_clk_index(int mclk, int rate) |
307 | { |
308 | int i; |
309 | |
310 | for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) { |
311 | if (clk_map_table[i].rate == rate && |
312 | clk_map_table[i].mclk == mclk) |
313 | return i; |
314 | } |
315 | return -EINVAL; |
316 | } |
317 | |
318 | static int cs4265_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id, |
319 | unsigned int freq, int dir) |
320 | { |
321 | struct snd_soc_component *component = codec_dai->component; |
322 | struct cs4265_private *cs4265 = snd_soc_component_get_drvdata(c: component); |
323 | int i; |
324 | |
325 | if (clk_id != 0) { |
326 | dev_err(component->dev, "Invalid clk_id %d\n" , clk_id); |
327 | return -EINVAL; |
328 | } |
329 | for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) { |
330 | if (clk_map_table[i].mclk == freq) { |
331 | cs4265->sysclk = freq; |
332 | return 0; |
333 | } |
334 | } |
335 | cs4265->sysclk = 0; |
336 | dev_err(component->dev, "Invalid freq parameter %d\n" , freq); |
337 | return -EINVAL; |
338 | } |
339 | |
340 | static int cs4265_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) |
341 | { |
342 | struct snd_soc_component *component = codec_dai->component; |
343 | struct cs4265_private *cs4265 = snd_soc_component_get_drvdata(c: component); |
344 | u8 iface = 0; |
345 | |
346 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
347 | case SND_SOC_DAIFMT_CBM_CFM: |
348 | snd_soc_component_update_bits(component, CS4265_ADC_CTL, |
349 | CS4265_ADC_MASTER, |
350 | CS4265_ADC_MASTER); |
351 | break; |
352 | case SND_SOC_DAIFMT_CBS_CFS: |
353 | snd_soc_component_update_bits(component, CS4265_ADC_CTL, |
354 | CS4265_ADC_MASTER, |
355 | val: 0); |
356 | break; |
357 | default: |
358 | return -EINVAL; |
359 | } |
360 | |
361 | /* interface format */ |
362 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
363 | case SND_SOC_DAIFMT_I2S: |
364 | iface |= SND_SOC_DAIFMT_I2S; |
365 | break; |
366 | case SND_SOC_DAIFMT_RIGHT_J: |
367 | iface |= SND_SOC_DAIFMT_RIGHT_J; |
368 | break; |
369 | case SND_SOC_DAIFMT_LEFT_J: |
370 | iface |= SND_SOC_DAIFMT_LEFT_J; |
371 | break; |
372 | default: |
373 | return -EINVAL; |
374 | } |
375 | |
376 | cs4265->format = iface; |
377 | return 0; |
378 | } |
379 | |
380 | static int cs4265_mute(struct snd_soc_dai *dai, int mute, int direction) |
381 | { |
382 | struct snd_soc_component *component = dai->component; |
383 | |
384 | if (mute) { |
385 | snd_soc_component_update_bits(component, CS4265_DAC_CTL, |
386 | CS4265_DAC_CTL_MUTE, |
387 | CS4265_DAC_CTL_MUTE); |
388 | snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2, |
389 | CS4265_SPDIF_CTL2_MUTE, |
390 | CS4265_SPDIF_CTL2_MUTE); |
391 | } else { |
392 | snd_soc_component_update_bits(component, CS4265_DAC_CTL, |
393 | CS4265_DAC_CTL_MUTE, |
394 | val: 0); |
395 | snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2, |
396 | CS4265_SPDIF_CTL2_MUTE, |
397 | val: 0); |
398 | } |
399 | return 0; |
400 | } |
401 | |
402 | static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream, |
403 | struct snd_pcm_hw_params *params, |
404 | struct snd_soc_dai *dai) |
405 | { |
406 | struct snd_soc_component *component = dai->component; |
407 | struct cs4265_private *cs4265 = snd_soc_component_get_drvdata(c: component); |
408 | int index; |
409 | |
410 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && |
411 | ((cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK) |
412 | == SND_SOC_DAIFMT_RIGHT_J)) |
413 | return -EINVAL; |
414 | |
415 | index = cs4265_get_clk_index(mclk: cs4265->sysclk, rate: params_rate(p: params)); |
416 | if (index >= 0) { |
417 | snd_soc_component_update_bits(component, CS4265_ADC_CTL, |
418 | CS4265_ADC_FM, val: clk_map_table[index].fm_mode << 6); |
419 | snd_soc_component_update_bits(component, CS4265_MCLK_FREQ, |
420 | CS4265_MCLK_FREQ_MASK, |
421 | val: clk_map_table[index].mclkdiv << 4); |
422 | |
423 | } else { |
424 | dev_err(component->dev, "can't get correct mclk\n" ); |
425 | return -EINVAL; |
426 | } |
427 | |
428 | switch (cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK) { |
429 | case SND_SOC_DAIFMT_I2S: |
430 | snd_soc_component_update_bits(component, CS4265_DAC_CTL, |
431 | CS4265_DAC_CTL_DIF, val: (1 << 4)); |
432 | snd_soc_component_update_bits(component, CS4265_ADC_CTL, |
433 | CS4265_ADC_DIF, val: (1 << 4)); |
434 | snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2, |
435 | CS4265_SPDIF_CTL2_DIF, val: (1 << 6)); |
436 | break; |
437 | case SND_SOC_DAIFMT_RIGHT_J: |
438 | if (params_width(p: params) == 16) { |
439 | snd_soc_component_update_bits(component, CS4265_DAC_CTL, |
440 | CS4265_DAC_CTL_DIF, val: (2 << 4)); |
441 | snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2, |
442 | CS4265_SPDIF_CTL2_DIF, val: (2 << 6)); |
443 | } else { |
444 | snd_soc_component_update_bits(component, CS4265_DAC_CTL, |
445 | CS4265_DAC_CTL_DIF, val: (3 << 4)); |
446 | snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2, |
447 | CS4265_SPDIF_CTL2_DIF, val: (3 << 6)); |
448 | } |
449 | break; |
450 | case SND_SOC_DAIFMT_LEFT_J: |
451 | snd_soc_component_update_bits(component, CS4265_DAC_CTL, |
452 | CS4265_DAC_CTL_DIF, val: 0); |
453 | snd_soc_component_update_bits(component, CS4265_ADC_CTL, |
454 | CS4265_ADC_DIF, val: 0); |
455 | snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2, |
456 | CS4265_SPDIF_CTL2_DIF, val: 0); |
457 | |
458 | break; |
459 | default: |
460 | return -EINVAL; |
461 | } |
462 | return 0; |
463 | } |
464 | |
465 | static int cs4265_set_bias_level(struct snd_soc_component *component, |
466 | enum snd_soc_bias_level level) |
467 | { |
468 | switch (level) { |
469 | case SND_SOC_BIAS_ON: |
470 | break; |
471 | case SND_SOC_BIAS_PREPARE: |
472 | snd_soc_component_update_bits(component, CS4265_PWRCTL, |
473 | CS4265_PWRCTL_PDN, val: 0); |
474 | break; |
475 | case SND_SOC_BIAS_STANDBY: |
476 | snd_soc_component_update_bits(component, CS4265_PWRCTL, |
477 | CS4265_PWRCTL_PDN, |
478 | CS4265_PWRCTL_PDN); |
479 | break; |
480 | case SND_SOC_BIAS_OFF: |
481 | snd_soc_component_update_bits(component, CS4265_PWRCTL, |
482 | CS4265_PWRCTL_PDN, |
483 | CS4265_PWRCTL_PDN); |
484 | break; |
485 | } |
486 | return 0; |
487 | } |
488 | |
489 | #define CS4265_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ |
490 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ |
491 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \ |
492 | SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) |
493 | |
494 | #define CS4265_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \ |
495 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE | \ |
496 | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) |
497 | |
498 | static const struct snd_soc_dai_ops cs4265_ops = { |
499 | .hw_params = cs4265_pcm_hw_params, |
500 | .mute_stream = cs4265_mute, |
501 | .set_fmt = cs4265_set_fmt, |
502 | .set_sysclk = cs4265_set_sysclk, |
503 | .no_capture_mute = 1, |
504 | }; |
505 | |
506 | static struct snd_soc_dai_driver cs4265_dai[] = { |
507 | { |
508 | .name = "cs4265-dai1" , |
509 | .playback = { |
510 | .stream_name = "DAI1 Playback" , |
511 | .channels_min = 1, |
512 | .channels_max = 2, |
513 | .rates = CS4265_RATES, |
514 | .formats = CS4265_FORMATS, |
515 | }, |
516 | .capture = { |
517 | .stream_name = "DAI1 Capture" , |
518 | .channels_min = 1, |
519 | .channels_max = 2, |
520 | .rates = CS4265_RATES, |
521 | .formats = CS4265_FORMATS, |
522 | }, |
523 | .ops = &cs4265_ops, |
524 | }, |
525 | { |
526 | .name = "cs4265-dai2" , |
527 | .playback = { |
528 | .stream_name = "DAI2 Playback" , |
529 | .channels_min = 1, |
530 | .channels_max = 2, |
531 | .rates = CS4265_RATES, |
532 | .formats = CS4265_FORMATS, |
533 | }, |
534 | .capture = { |
535 | .stream_name = "DAI2 Capture" , |
536 | .channels_min = 1, |
537 | .channels_max = 2, |
538 | .rates = CS4265_RATES, |
539 | .formats = CS4265_FORMATS, |
540 | }, |
541 | .ops = &cs4265_ops, |
542 | }, |
543 | }; |
544 | |
545 | static const struct snd_soc_component_driver soc_component_cs4265 = { |
546 | .set_bias_level = cs4265_set_bias_level, |
547 | .controls = cs4265_snd_controls, |
548 | .num_controls = ARRAY_SIZE(cs4265_snd_controls), |
549 | .dapm_widgets = cs4265_dapm_widgets, |
550 | .num_dapm_widgets = ARRAY_SIZE(cs4265_dapm_widgets), |
551 | .dapm_routes = cs4265_audio_map, |
552 | .num_dapm_routes = ARRAY_SIZE(cs4265_audio_map), |
553 | .idle_bias_on = 1, |
554 | .use_pmdown_time = 1, |
555 | .endianness = 1, |
556 | }; |
557 | |
558 | static const struct regmap_config cs4265_regmap = { |
559 | .reg_bits = 8, |
560 | .val_bits = 8, |
561 | |
562 | .max_register = CS4265_MAX_REGISTER, |
563 | .reg_defaults = cs4265_reg_defaults, |
564 | .num_reg_defaults = ARRAY_SIZE(cs4265_reg_defaults), |
565 | .readable_reg = cs4265_readable_register, |
566 | .volatile_reg = cs4265_volatile_register, |
567 | .cache_type = REGCACHE_MAPLE, |
568 | }; |
569 | |
570 | static int cs4265_i2c_probe(struct i2c_client *i2c_client) |
571 | { |
572 | struct cs4265_private *cs4265; |
573 | int ret; |
574 | unsigned int devid = 0; |
575 | unsigned int reg; |
576 | |
577 | cs4265 = devm_kzalloc(dev: &i2c_client->dev, size: sizeof(struct cs4265_private), |
578 | GFP_KERNEL); |
579 | if (cs4265 == NULL) |
580 | return -ENOMEM; |
581 | |
582 | cs4265->regmap = devm_regmap_init_i2c(i2c_client, &cs4265_regmap); |
583 | if (IS_ERR(ptr: cs4265->regmap)) { |
584 | ret = PTR_ERR(ptr: cs4265->regmap); |
585 | dev_err(&i2c_client->dev, "regmap_init() failed: %d\n" , ret); |
586 | return ret; |
587 | } |
588 | |
589 | cs4265->reset_gpio = devm_gpiod_get_optional(dev: &i2c_client->dev, |
590 | con_id: "reset" , flags: GPIOD_OUT_LOW); |
591 | if (IS_ERR(ptr: cs4265->reset_gpio)) |
592 | return PTR_ERR(ptr: cs4265->reset_gpio); |
593 | |
594 | if (cs4265->reset_gpio) { |
595 | mdelay(1); |
596 | gpiod_set_value_cansleep(desc: cs4265->reset_gpio, value: 1); |
597 | } |
598 | |
599 | i2c_set_clientdata(client: i2c_client, data: cs4265); |
600 | |
601 | ret = regmap_read(map: cs4265->regmap, CS4265_CHIP_ID, val: ®); |
602 | if (ret) { |
603 | dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n" , ret); |
604 | return ret; |
605 | } |
606 | |
607 | devid = reg & CS4265_CHIP_ID_MASK; |
608 | if (devid != CS4265_CHIP_ID_VAL) { |
609 | ret = -ENODEV; |
610 | dev_err(&i2c_client->dev, |
611 | "CS4265 Part Number ID: 0x%x Expected: 0x%x\n" , |
612 | devid >> 4, CS4265_CHIP_ID_VAL >> 4); |
613 | return ret; |
614 | } |
615 | dev_info(&i2c_client->dev, |
616 | "CS4265 Version %x\n" , |
617 | reg & CS4265_REV_ID_MASK); |
618 | |
619 | regmap_write(map: cs4265->regmap, CS4265_PWRCTL, val: 0x0F); |
620 | |
621 | return devm_snd_soc_register_component(dev: &i2c_client->dev, |
622 | component_driver: &soc_component_cs4265, dai_drv: cs4265_dai, |
623 | ARRAY_SIZE(cs4265_dai)); |
624 | } |
625 | |
626 | static void cs4265_i2c_remove(struct i2c_client *i2c) |
627 | { |
628 | struct cs4265_private *cs4265 = i2c_get_clientdata(client: i2c); |
629 | |
630 | if (cs4265->reset_gpio) |
631 | gpiod_set_value_cansleep(desc: cs4265->reset_gpio, value: 0); |
632 | } |
633 | |
634 | static const struct of_device_id cs4265_of_match[] = { |
635 | { .compatible = "cirrus,cs4265" , }, |
636 | { } |
637 | }; |
638 | MODULE_DEVICE_TABLE(of, cs4265_of_match); |
639 | |
640 | static const struct i2c_device_id cs4265_id[] = { |
641 | { "cs4265" , 0 }, |
642 | { } |
643 | }; |
644 | MODULE_DEVICE_TABLE(i2c, cs4265_id); |
645 | |
646 | static struct i2c_driver cs4265_i2c_driver = { |
647 | .driver = { |
648 | .name = "cs4265" , |
649 | .of_match_table = cs4265_of_match, |
650 | }, |
651 | .id_table = cs4265_id, |
652 | .probe = cs4265_i2c_probe, |
653 | .remove = cs4265_i2c_remove, |
654 | }; |
655 | |
656 | module_i2c_driver(cs4265_i2c_driver); |
657 | |
658 | MODULE_DESCRIPTION("ASoC CS4265 driver" ); |
659 | MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paul.handrigan@cirrus.com>" ); |
660 | MODULE_LICENSE("GPL" ); |
661 | |