1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | // |
3 | // File: sound/soc/codecs/ssm2602.c |
4 | // Author: Cliff Cai <Cliff.Cai@analog.com> |
5 | // |
6 | // Created: Tue June 06 2008 |
7 | // Description: Driver for ssm2602 sound chip |
8 | // |
9 | // Modified: |
10 | // Copyright 2008 Analog Devices Inc. |
11 | // |
12 | // Bugs: Enter bugs at http://blackfin.uclinux.org/ |
13 | |
14 | #include <linux/delay.h> |
15 | #include <linux/module.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/slab.h> |
18 | |
19 | #include <sound/pcm.h> |
20 | #include <sound/pcm_params.h> |
21 | #include <sound/soc.h> |
22 | #include <sound/tlv.h> |
23 | |
24 | #include "ssm2602.h" |
25 | |
26 | /* codec private data */ |
27 | struct ssm2602_priv { |
28 | unsigned int sysclk; |
29 | const struct snd_pcm_hw_constraint_list *sysclk_constraints; |
30 | |
31 | struct regmap *regmap; |
32 | |
33 | enum ssm2602_type type; |
34 | unsigned int clk_out_pwr; |
35 | }; |
36 | |
37 | /* |
38 | * ssm2602 register cache |
39 | * We can't read the ssm2602 register space when we are |
40 | * using 2 wire for device control, so we cache them instead. |
41 | * There is no point in caching the reset register |
42 | */ |
43 | static const struct reg_default ssm2602_reg[SSM2602_CACHEREGNUM] = { |
44 | { .reg = 0x00, .def = 0x0097 }, |
45 | { .reg = 0x01, .def = 0x0097 }, |
46 | { .reg = 0x02, .def = 0x0079 }, |
47 | { .reg = 0x03, .def = 0x0079 }, |
48 | { .reg = 0x04, .def = 0x000a }, |
49 | { .reg = 0x05, .def = 0x0008 }, |
50 | { .reg = 0x06, .def = 0x009f }, |
51 | { .reg = 0x07, .def = 0x000a }, |
52 | { .reg = 0x08, .def = 0x0000 }, |
53 | { .reg = 0x09, .def = 0x0000 } |
54 | }; |
55 | |
56 | /* |
57 | * ssm2602 register patch |
58 | * Workaround for playback distortions after power up: activates digital |
59 | * core, and then powers on output, DAC, and whole chip at the same time |
60 | */ |
61 | |
62 | static const struct reg_sequence ssm2602_patch[] = { |
63 | { SSM2602_ACTIVE, 0x01 }, |
64 | { SSM2602_PWR, 0x07 }, |
65 | { SSM2602_RESET, 0x00 }, |
66 | }; |
67 | |
68 | |
69 | /*Appending several "None"s just for OSS mixer use*/ |
70 | static const char *ssm2602_input_select[] = { |
71 | "Line" , "Mic" , |
72 | }; |
73 | |
74 | static const char *ssm2602_deemph[] = {"None" , "32Khz" , "44.1Khz" , "48Khz" }; |
75 | |
76 | static const struct soc_enum ssm2602_enum[] = { |
77 | SOC_ENUM_SINGLE(SSM2602_APANA, 2, ARRAY_SIZE(ssm2602_input_select), |
78 | ssm2602_input_select), |
79 | SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, ARRAY_SIZE(ssm2602_deemph), |
80 | ssm2602_deemph), |
81 | }; |
82 | |
83 | static const DECLARE_TLV_DB_RANGE(ssm260x_outmix_tlv, |
84 | 0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), |
85 | 48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0) |
86 | ); |
87 | |
88 | static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0); |
89 | static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0); |
90 | |
91 | static const struct snd_kcontrol_new ssm260x_snd_controls[] = { |
92 | SOC_DOUBLE_R_TLV("Capture Volume" , SSM2602_LINVOL, SSM2602_RINVOL, 0, 45, 0, |
93 | ssm260x_inpga_tlv), |
94 | SOC_DOUBLE_R("Capture Switch" , SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1), |
95 | |
96 | SOC_SINGLE("ADC High Pass Filter Switch" , SSM2602_APDIGI, 0, 1, 1), |
97 | SOC_SINGLE("Store DC Offset Switch" , SSM2602_APDIGI, 4, 1, 0), |
98 | |
99 | SOC_ENUM("Playback De-emphasis" , ssm2602_enum[1]), |
100 | }; |
101 | |
102 | static const struct snd_kcontrol_new ssm2602_snd_controls[] = { |
103 | SOC_DOUBLE_R_TLV("Master Playback Volume" , SSM2602_LOUT1V, SSM2602_ROUT1V, |
104 | 0, 127, 0, ssm260x_outmix_tlv), |
105 | SOC_DOUBLE_R("Master Playback ZC Switch" , SSM2602_LOUT1V, SSM2602_ROUT1V, |
106 | 7, 1, 0), |
107 | SOC_SINGLE_TLV("Sidetone Playback Volume" , SSM2602_APANA, 6, 3, 1, |
108 | ssm260x_sidetone_tlv), |
109 | |
110 | SOC_SINGLE("Mic Boost (+20dB)" , SSM2602_APANA, 0, 1, 0), |
111 | SOC_SINGLE("Mic Boost2 (+20dB)" , SSM2602_APANA, 8, 1, 0), |
112 | }; |
113 | |
114 | /* Output Mixer */ |
115 | static const struct snd_kcontrol_new ssm260x_output_mixer_controls[] = { |
116 | SOC_DAPM_SINGLE("Line Bypass Switch" , SSM2602_APANA, 3, 1, 0), |
117 | SOC_DAPM_SINGLE("HiFi Playback Switch" , SSM2602_APANA, 4, 1, 0), |
118 | SOC_DAPM_SINGLE("Mic Sidetone Switch" , SSM2602_APANA, 5, 1, 0), |
119 | }; |
120 | |
121 | static const struct snd_kcontrol_new mic_ctl = |
122 | SOC_DAPM_SINGLE("Switch" , SSM2602_APANA, 1, 1, 1); |
123 | |
124 | /* Input mux */ |
125 | static const struct snd_kcontrol_new ssm2602_input_mux_controls = |
126 | SOC_DAPM_ENUM("Input Select" , ssm2602_enum[0]); |
127 | |
128 | static int ssm2602_mic_switch_event(struct snd_soc_dapm_widget *w, |
129 | struct snd_kcontrol *kcontrol, int event) |
130 | { |
131 | /* |
132 | * According to the ssm2603 data sheet (control register sequencing), |
133 | * the digital core should be activated only after all necessary bits |
134 | * in the power register are enabled, and a delay determined by the |
135 | * decoupling capacitor on the VMID pin has passed. If the digital core |
136 | * is activated too early, or even before the ADC is powered up, audible |
137 | * artifacts appear at the beginning and end of the recorded signal. |
138 | * |
139 | * In practice, audible artifacts disappear well over 500 ms. |
140 | */ |
141 | msleep(msecs: 500); |
142 | |
143 | return 0; |
144 | } |
145 | |
146 | static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = { |
147 | SND_SOC_DAPM_DAC("DAC" , "HiFi Playback" , SSM2602_PWR, 3, 1), |
148 | SND_SOC_DAPM_ADC("ADC" , "HiFi Capture" , SSM2602_PWR, 2, 1), |
149 | SND_SOC_DAPM_PGA("Line Input" , SSM2602_PWR, 0, 1, NULL, 0), |
150 | |
151 | SND_SOC_DAPM_SUPPLY("Digital Core Power" , SSM2602_ACTIVE, 0, 0, NULL, 0), |
152 | |
153 | SND_SOC_DAPM_OUTPUT("LOUT" ), |
154 | SND_SOC_DAPM_OUTPUT("ROUT" ), |
155 | SND_SOC_DAPM_INPUT("RLINEIN" ), |
156 | SND_SOC_DAPM_INPUT("LLINEIN" ), |
157 | }; |
158 | |
159 | static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = { |
160 | SND_SOC_DAPM_MIXER("Output Mixer" , SSM2602_PWR, 4, 1, |
161 | ssm260x_output_mixer_controls, |
162 | ARRAY_SIZE(ssm260x_output_mixer_controls)), |
163 | |
164 | SND_SOC_DAPM_MUX("Input Mux" , SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls), |
165 | SND_SOC_DAPM_MICBIAS("Mic Bias" , SSM2602_PWR, 1, 1), |
166 | |
167 | SND_SOC_DAPM_SWITCH_E("Mic Switch" , SSM2602_APANA, 1, 1, &mic_ctl, |
168 | ssm2602_mic_switch_event, SND_SOC_DAPM_PRE_PMU), |
169 | |
170 | SND_SOC_DAPM_OUTPUT("LHPOUT" ), |
171 | SND_SOC_DAPM_OUTPUT("RHPOUT" ), |
172 | SND_SOC_DAPM_INPUT("MICIN" ), |
173 | }; |
174 | |
175 | static const struct snd_soc_dapm_widget ssm2604_dapm_widgets[] = { |
176 | SND_SOC_DAPM_MIXER("Output Mixer" , SND_SOC_NOPM, 0, 0, |
177 | ssm260x_output_mixer_controls, |
178 | ARRAY_SIZE(ssm260x_output_mixer_controls) - 1), /* Last element is the mic */ |
179 | }; |
180 | |
181 | static const struct snd_soc_dapm_route ssm260x_routes[] = { |
182 | {"DAC" , NULL, "Digital Core Power" }, |
183 | {"ADC" , NULL, "Digital Core Power" }, |
184 | |
185 | {"Output Mixer" , "Line Bypass Switch" , "Line Input" }, |
186 | {"Output Mixer" , "HiFi Playback Switch" , "DAC" }, |
187 | |
188 | {"ROUT" , NULL, "Output Mixer" }, |
189 | {"LOUT" , NULL, "Output Mixer" }, |
190 | |
191 | {"Line Input" , NULL, "LLINEIN" }, |
192 | {"Line Input" , NULL, "RLINEIN" }, |
193 | }; |
194 | |
195 | static const struct snd_soc_dapm_route ssm2602_routes[] = { |
196 | {"Output Mixer" , "Mic Sidetone Switch" , "Mic Bias" }, |
197 | |
198 | {"RHPOUT" , NULL, "Output Mixer" }, |
199 | {"LHPOUT" , NULL, "Output Mixer" }, |
200 | |
201 | {"Input Mux" , "Line" , "Line Input" }, |
202 | {"Input Mux" , "Mic" , "Mic Switch" }, |
203 | {"ADC" , NULL, "Input Mux" }, |
204 | |
205 | {"Mic Switch" , NULL, "Mic Bias" }, |
206 | |
207 | {"Mic Bias" , NULL, "MICIN" }, |
208 | }; |
209 | |
210 | static const struct snd_soc_dapm_route ssm2604_routes[] = { |
211 | {"ADC" , NULL, "Line Input" }, |
212 | }; |
213 | |
214 | static const unsigned int ssm2602_rates_12288000[] = { |
215 | 8000, 16000, 32000, 48000, 96000, |
216 | }; |
217 | |
218 | static const struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = { |
219 | .list = ssm2602_rates_12288000, |
220 | .count = ARRAY_SIZE(ssm2602_rates_12288000), |
221 | }; |
222 | |
223 | static const unsigned int ssm2602_rates_11289600[] = { |
224 | 8000, 11025, 22050, 44100, 88200, |
225 | }; |
226 | |
227 | static const struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = { |
228 | .list = ssm2602_rates_11289600, |
229 | .count = ARRAY_SIZE(ssm2602_rates_11289600), |
230 | }; |
231 | |
232 | struct ssm2602_coeff { |
233 | u32 mclk; |
234 | u32 rate; |
235 | u8 srate; |
236 | }; |
237 | |
238 | #define SSM2602_COEFF_SRATE(sr, bosr, usb) (((sr) << 2) | ((bosr) << 1) | (usb)) |
239 | |
240 | /* codec mclk clock coefficients */ |
241 | static const struct ssm2602_coeff ssm2602_coeff_table[] = { |
242 | /* 48k */ |
243 | {12288000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x0)}, |
244 | {18432000, 48000, SSM2602_COEFF_SRATE(0x0, 0x1, 0x0)}, |
245 | {12000000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x1)}, |
246 | |
247 | /* 32k */ |
248 | {12288000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x0)}, |
249 | {18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)}, |
250 | {12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)}, |
251 | |
252 | /* 16k */ |
253 | {12288000, 16000, SSM2602_COEFF_SRATE(0x5, 0x0, 0x0)}, |
254 | {18432000, 16000, SSM2602_COEFF_SRATE(0x5, 0x1, 0x0)}, |
255 | {12000000, 16000, SSM2602_COEFF_SRATE(0xa, 0x0, 0x1)}, |
256 | |
257 | /* 8k */ |
258 | {12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)}, |
259 | {18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)}, |
260 | {11289600, 8000, SSM2602_COEFF_SRATE(0xb, 0x0, 0x0)}, |
261 | {16934400, 8000, SSM2602_COEFF_SRATE(0xb, 0x1, 0x0)}, |
262 | {12000000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x1)}, |
263 | |
264 | /* 96k */ |
265 | {12288000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x0)}, |
266 | {18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)}, |
267 | {12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)}, |
268 | |
269 | /* 11.025k */ |
270 | {11289600, 11025, SSM2602_COEFF_SRATE(0xc, 0x0, 0x0)}, |
271 | {16934400, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x0)}, |
272 | {12000000, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x1)}, |
273 | |
274 | /* 22.05k */ |
275 | {11289600, 22050, SSM2602_COEFF_SRATE(0xd, 0x0, 0x0)}, |
276 | {16934400, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x0)}, |
277 | {12000000, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x1)}, |
278 | |
279 | /* 44.1k */ |
280 | {11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)}, |
281 | {16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)}, |
282 | {12000000, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x1)}, |
283 | |
284 | /* 88.2k */ |
285 | {11289600, 88200, SSM2602_COEFF_SRATE(0xf, 0x0, 0x0)}, |
286 | {16934400, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x0)}, |
287 | {12000000, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x1)}, |
288 | }; |
289 | |
290 | static inline int ssm2602_get_coeff(int mclk, int rate) |
291 | { |
292 | int i; |
293 | |
294 | for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) { |
295 | if (ssm2602_coeff_table[i].rate == rate) { |
296 | if (ssm2602_coeff_table[i].mclk == mclk) |
297 | return ssm2602_coeff_table[i].srate; |
298 | if (ssm2602_coeff_table[i].mclk == mclk / 2) |
299 | return ssm2602_coeff_table[i].srate | SRATE_CORECLK_DIV2; |
300 | } |
301 | } |
302 | return -EINVAL; |
303 | } |
304 | |
305 | static int ssm2602_hw_params(struct snd_pcm_substream *substream, |
306 | struct snd_pcm_hw_params *params, |
307 | struct snd_soc_dai *dai) |
308 | { |
309 | struct snd_soc_component *component = dai->component; |
310 | struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(c: component); |
311 | int srate = ssm2602_get_coeff(mclk: ssm2602->sysclk, rate: params_rate(p: params)); |
312 | unsigned int iface; |
313 | |
314 | if (srate < 0) |
315 | return srate; |
316 | |
317 | regmap_write(map: ssm2602->regmap, SSM2602_SRATE, val: srate); |
318 | |
319 | /* bit size */ |
320 | switch (params_width(p: params)) { |
321 | case 16: |
322 | iface = 0x0; |
323 | break; |
324 | case 20: |
325 | iface = 0x4; |
326 | break; |
327 | case 24: |
328 | iface = 0x8; |
329 | break; |
330 | case 32: |
331 | iface = 0xc; |
332 | break; |
333 | default: |
334 | return -EINVAL; |
335 | } |
336 | regmap_update_bits(map: ssm2602->regmap, SSM2602_IFACE, |
337 | IFACE_AUDIO_DATA_LEN, val: iface); |
338 | return 0; |
339 | } |
340 | |
341 | static int ssm2602_startup(struct snd_pcm_substream *substream, |
342 | struct snd_soc_dai *dai) |
343 | { |
344 | struct snd_soc_component *component = dai->component; |
345 | struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(c: component); |
346 | |
347 | if (ssm2602->sysclk_constraints) { |
348 | snd_pcm_hw_constraint_list(runtime: substream->runtime, cond: 0, |
349 | SNDRV_PCM_HW_PARAM_RATE, |
350 | l: ssm2602->sysclk_constraints); |
351 | } |
352 | |
353 | return 0; |
354 | } |
355 | |
356 | static int ssm2602_mute(struct snd_soc_dai *dai, int mute, int direction) |
357 | { |
358 | struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(c: dai->component); |
359 | |
360 | if (mute) |
361 | regmap_update_bits(map: ssm2602->regmap, SSM2602_APDIGI, |
362 | APDIGI_ENABLE_DAC_MUTE, |
363 | APDIGI_ENABLE_DAC_MUTE); |
364 | else |
365 | regmap_update_bits(map: ssm2602->regmap, SSM2602_APDIGI, |
366 | APDIGI_ENABLE_DAC_MUTE, val: 0); |
367 | return 0; |
368 | } |
369 | |
370 | static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
371 | int clk_id, unsigned int freq, int dir) |
372 | { |
373 | struct snd_soc_component *component = codec_dai->component; |
374 | struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(c: component); |
375 | |
376 | if (dir == SND_SOC_CLOCK_IN) { |
377 | if (clk_id != SSM2602_SYSCLK) |
378 | return -EINVAL; |
379 | |
380 | switch (freq) { |
381 | case 12288000: |
382 | case 18432000: |
383 | case 24576000: |
384 | case 36864000: |
385 | ssm2602->sysclk_constraints = &ssm2602_constraints_12288000; |
386 | break; |
387 | case 11289600: |
388 | case 16934400: |
389 | case 22579200: |
390 | case 33868800: |
391 | ssm2602->sysclk_constraints = &ssm2602_constraints_11289600; |
392 | break; |
393 | case 12000000: |
394 | case 24000000: |
395 | ssm2602->sysclk_constraints = NULL; |
396 | break; |
397 | default: |
398 | return -EINVAL; |
399 | } |
400 | |
401 | ssm2602->sysclk = freq; |
402 | } else { |
403 | unsigned int mask; |
404 | |
405 | switch (clk_id) { |
406 | case SSM2602_CLK_CLKOUT: |
407 | mask = PWR_CLK_OUT_PDN; |
408 | break; |
409 | case SSM2602_CLK_XTO: |
410 | mask = PWR_OSC_PDN; |
411 | break; |
412 | default: |
413 | return -EINVAL; |
414 | } |
415 | |
416 | if (freq == 0) |
417 | ssm2602->clk_out_pwr |= mask; |
418 | else |
419 | ssm2602->clk_out_pwr &= ~mask; |
420 | |
421 | regmap_update_bits(map: ssm2602->regmap, SSM2602_PWR, |
422 | PWR_CLK_OUT_PDN | PWR_OSC_PDN, val: ssm2602->clk_out_pwr); |
423 | } |
424 | |
425 | return 0; |
426 | } |
427 | |
428 | static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai, |
429 | unsigned int fmt) |
430 | { |
431 | struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(c: codec_dai->component); |
432 | unsigned int iface = 0; |
433 | |
434 | /* set master/slave audio interface */ |
435 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
436 | case SND_SOC_DAIFMT_CBP_CFP: |
437 | iface |= 0x0040; |
438 | break; |
439 | case SND_SOC_DAIFMT_CBC_CFC: |
440 | break; |
441 | default: |
442 | return -EINVAL; |
443 | } |
444 | |
445 | /* interface format */ |
446 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
447 | case SND_SOC_DAIFMT_I2S: |
448 | iface |= 0x0002; |
449 | break; |
450 | case SND_SOC_DAIFMT_RIGHT_J: |
451 | break; |
452 | case SND_SOC_DAIFMT_LEFT_J: |
453 | iface |= 0x0001; |
454 | break; |
455 | case SND_SOC_DAIFMT_DSP_A: |
456 | iface |= 0x0013; |
457 | break; |
458 | case SND_SOC_DAIFMT_DSP_B: |
459 | iface |= 0x0003; |
460 | break; |
461 | default: |
462 | return -EINVAL; |
463 | } |
464 | |
465 | /* clock inversion */ |
466 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
467 | case SND_SOC_DAIFMT_NB_NF: |
468 | break; |
469 | case SND_SOC_DAIFMT_IB_IF: |
470 | iface |= 0x0090; |
471 | break; |
472 | case SND_SOC_DAIFMT_IB_NF: |
473 | iface |= 0x0080; |
474 | break; |
475 | case SND_SOC_DAIFMT_NB_IF: |
476 | iface |= 0x0010; |
477 | break; |
478 | default: |
479 | return -EINVAL; |
480 | } |
481 | |
482 | /* set iface */ |
483 | regmap_write(map: ssm2602->regmap, SSM2602_IFACE, val: iface); |
484 | return 0; |
485 | } |
486 | |
487 | static int ssm2602_set_bias_level(struct snd_soc_component *component, |
488 | enum snd_soc_bias_level level) |
489 | { |
490 | struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(c: component); |
491 | |
492 | switch (level) { |
493 | case SND_SOC_BIAS_ON: |
494 | /* vref/mid on, osc and clkout on if enabled */ |
495 | regmap_update_bits(map: ssm2602->regmap, SSM2602_PWR, |
496 | PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN, |
497 | val: ssm2602->clk_out_pwr); |
498 | break; |
499 | case SND_SOC_BIAS_PREPARE: |
500 | break; |
501 | case SND_SOC_BIAS_STANDBY: |
502 | /* everything off except vref/vmid, */ |
503 | regmap_update_bits(map: ssm2602->regmap, SSM2602_PWR, |
504 | PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN, |
505 | PWR_CLK_OUT_PDN | PWR_OSC_PDN); |
506 | break; |
507 | case SND_SOC_BIAS_OFF: |
508 | /* everything off */ |
509 | regmap_update_bits(map: ssm2602->regmap, SSM2602_PWR, |
510 | PWR_POWER_OFF, PWR_POWER_OFF); |
511 | break; |
512 | |
513 | } |
514 | return 0; |
515 | } |
516 | |
517 | #define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ |
518 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ |
519 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ |
520 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ |
521 | SNDRV_PCM_RATE_96000) |
522 | |
523 | #define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ |
524 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) |
525 | |
526 | static const struct snd_soc_dai_ops ssm2602_dai_ops = { |
527 | .startup = ssm2602_startup, |
528 | .hw_params = ssm2602_hw_params, |
529 | .mute_stream = ssm2602_mute, |
530 | .set_sysclk = ssm2602_set_dai_sysclk, |
531 | .set_fmt = ssm2602_set_dai_fmt, |
532 | .no_capture_mute = 1, |
533 | }; |
534 | |
535 | static struct snd_soc_dai_driver ssm2602_dai = { |
536 | .name = "ssm2602-hifi" , |
537 | .playback = { |
538 | .stream_name = "Playback" , |
539 | .channels_min = 2, |
540 | .channels_max = 2, |
541 | .rates = SSM2602_RATES, |
542 | .formats = SSM2602_FORMATS,}, |
543 | .capture = { |
544 | .stream_name = "Capture" , |
545 | .channels_min = 2, |
546 | .channels_max = 2, |
547 | .rates = SSM2602_RATES, |
548 | .formats = SSM2602_FORMATS,}, |
549 | .ops = &ssm2602_dai_ops, |
550 | .symmetric_rate = 1, |
551 | .symmetric_sample_bits = 1, |
552 | }; |
553 | |
554 | static int ssm2602_resume(struct snd_soc_component *component) |
555 | { |
556 | struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(c: component); |
557 | |
558 | regcache_sync(map: ssm2602->regmap); |
559 | |
560 | return 0; |
561 | } |
562 | |
563 | static int ssm2602_component_probe(struct snd_soc_component *component) |
564 | { |
565 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
566 | struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(c: component); |
567 | int ret; |
568 | |
569 | regmap_update_bits(map: ssm2602->regmap, SSM2602_LOUT1V, |
570 | LOUT1V_LRHP_BOTH, LOUT1V_LRHP_BOTH); |
571 | regmap_update_bits(map: ssm2602->regmap, SSM2602_ROUT1V, |
572 | ROUT1V_RLHP_BOTH, ROUT1V_RLHP_BOTH); |
573 | |
574 | ret = snd_soc_add_component_controls(component, controls: ssm2602_snd_controls, |
575 | ARRAY_SIZE(ssm2602_snd_controls)); |
576 | if (ret) |
577 | return ret; |
578 | |
579 | ret = snd_soc_dapm_new_controls(dapm, widget: ssm2602_dapm_widgets, |
580 | ARRAY_SIZE(ssm2602_dapm_widgets)); |
581 | if (ret) |
582 | return ret; |
583 | |
584 | return snd_soc_dapm_add_routes(dapm, route: ssm2602_routes, |
585 | ARRAY_SIZE(ssm2602_routes)); |
586 | } |
587 | |
588 | static int ssm2604_component_probe(struct snd_soc_component *component) |
589 | { |
590 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
591 | int ret; |
592 | |
593 | ret = snd_soc_dapm_new_controls(dapm, widget: ssm2604_dapm_widgets, |
594 | ARRAY_SIZE(ssm2604_dapm_widgets)); |
595 | if (ret) |
596 | return ret; |
597 | |
598 | return snd_soc_dapm_add_routes(dapm, route: ssm2604_routes, |
599 | ARRAY_SIZE(ssm2604_routes)); |
600 | } |
601 | |
602 | static int ssm260x_component_probe(struct snd_soc_component *component) |
603 | { |
604 | struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(c: component); |
605 | int ret; |
606 | |
607 | ret = regmap_write(map: ssm2602->regmap, SSM2602_RESET, val: 0); |
608 | if (ret < 0) { |
609 | dev_err(component->dev, "Failed to issue reset: %d\n" , ret); |
610 | return ret; |
611 | } |
612 | |
613 | regmap_register_patch(map: ssm2602->regmap, regs: ssm2602_patch, |
614 | ARRAY_SIZE(ssm2602_patch)); |
615 | |
616 | /* set the update bits */ |
617 | regmap_update_bits(map: ssm2602->regmap, SSM2602_LINVOL, |
618 | LINVOL_LRIN_BOTH, LINVOL_LRIN_BOTH); |
619 | regmap_update_bits(map: ssm2602->regmap, SSM2602_RINVOL, |
620 | RINVOL_RLIN_BOTH, RINVOL_RLIN_BOTH); |
621 | /*select Line in as default input*/ |
622 | regmap_write(map: ssm2602->regmap, SSM2602_APANA, APANA_SELECT_DAC | |
623 | APANA_ENABLE_MIC_BOOST); |
624 | |
625 | switch (ssm2602->type) { |
626 | case SSM2602: |
627 | ret = ssm2602_component_probe(component); |
628 | break; |
629 | case SSM2604: |
630 | ret = ssm2604_component_probe(component); |
631 | break; |
632 | } |
633 | |
634 | return ret; |
635 | } |
636 | |
637 | static const struct snd_soc_component_driver soc_component_dev_ssm2602 = { |
638 | .probe = ssm260x_component_probe, |
639 | .resume = ssm2602_resume, |
640 | .set_bias_level = ssm2602_set_bias_level, |
641 | .controls = ssm260x_snd_controls, |
642 | .num_controls = ARRAY_SIZE(ssm260x_snd_controls), |
643 | .dapm_widgets = ssm260x_dapm_widgets, |
644 | .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets), |
645 | .dapm_routes = ssm260x_routes, |
646 | .num_dapm_routes = ARRAY_SIZE(ssm260x_routes), |
647 | .suspend_bias_off = 1, |
648 | .idle_bias_on = 1, |
649 | .use_pmdown_time = 1, |
650 | .endianness = 1, |
651 | }; |
652 | |
653 | static bool ssm2602_register_volatile(struct device *dev, unsigned int reg) |
654 | { |
655 | return reg == SSM2602_RESET; |
656 | } |
657 | |
658 | const struct regmap_config ssm2602_regmap_config = { |
659 | .val_bits = 9, |
660 | .reg_bits = 7, |
661 | |
662 | .max_register = SSM2602_RESET, |
663 | .volatile_reg = ssm2602_register_volatile, |
664 | |
665 | .cache_type = REGCACHE_RBTREE, |
666 | .reg_defaults = ssm2602_reg, |
667 | .num_reg_defaults = ARRAY_SIZE(ssm2602_reg), |
668 | }; |
669 | EXPORT_SYMBOL_GPL(ssm2602_regmap_config); |
670 | |
671 | int ssm2602_probe(struct device *dev, enum ssm2602_type type, |
672 | struct regmap *regmap) |
673 | { |
674 | struct ssm2602_priv *ssm2602; |
675 | |
676 | if (IS_ERR(ptr: regmap)) |
677 | return PTR_ERR(ptr: regmap); |
678 | |
679 | ssm2602 = devm_kzalloc(dev, size: sizeof(*ssm2602), GFP_KERNEL); |
680 | if (ssm2602 == NULL) |
681 | return -ENOMEM; |
682 | |
683 | dev_set_drvdata(dev, data: ssm2602); |
684 | ssm2602->type = type; |
685 | ssm2602->regmap = regmap; |
686 | |
687 | return devm_snd_soc_register_component(dev, component_driver: &soc_component_dev_ssm2602, |
688 | dai_drv: &ssm2602_dai, num_dai: 1); |
689 | } |
690 | EXPORT_SYMBOL_GPL(ssm2602_probe); |
691 | |
692 | MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver" ); |
693 | MODULE_AUTHOR("Cliff Cai" ); |
694 | MODULE_LICENSE("GPL" ); |
695 | |