1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * nau8810.c -- NAU8810 ALSA Soc Audio driver |
4 | * |
5 | * Copyright 2016 Nuvoton Technology Corp. |
6 | * |
7 | * Author: David Lin <ctlin0@nuvoton.com> |
8 | * |
9 | * Based on WM8974.c |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/moduleparam.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/init.h> |
16 | #include <linux/delay.h> |
17 | #include <linux/pm.h> |
18 | #include <linux/i2c.h> |
19 | #include <linux/regmap.h> |
20 | #include <linux/slab.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/initval.h> |
26 | #include <sound/tlv.h> |
27 | |
28 | #include "nau8810.h" |
29 | |
30 | #define NAU_PLL_FREQ_MAX 100000000 |
31 | #define NAU_PLL_FREQ_MIN 90000000 |
32 | #define NAU_PLL_REF_MAX 33000000 |
33 | #define NAU_PLL_REF_MIN 8000000 |
34 | #define NAU_PLL_OPTOP_MIN 6 |
35 | |
36 | |
37 | static const int nau8810_mclk_scaler[] = { 10, 15, 20, 30, 40, 60, 80, 120 }; |
38 | |
39 | static const struct reg_default nau8810_reg_defaults[] = { |
40 | { NAU8810_REG_POWER1, 0x0000 }, |
41 | { NAU8810_REG_POWER2, 0x0000 }, |
42 | { NAU8810_REG_POWER3, 0x0000 }, |
43 | { NAU8810_REG_IFACE, 0x0050 }, |
44 | { NAU8810_REG_COMP, 0x0000 }, |
45 | { NAU8810_REG_CLOCK, 0x0140 }, |
46 | { NAU8810_REG_SMPLR, 0x0000 }, |
47 | { NAU8810_REG_DAC, 0x0000 }, |
48 | { NAU8810_REG_DACGAIN, 0x00FF }, |
49 | { NAU8810_REG_ADC, 0x0100 }, |
50 | { NAU8810_REG_ADCGAIN, 0x00FF }, |
51 | { NAU8810_REG_EQ1, 0x012C }, |
52 | { NAU8810_REG_EQ2, 0x002C }, |
53 | { NAU8810_REG_EQ3, 0x002C }, |
54 | { NAU8810_REG_EQ4, 0x002C }, |
55 | { NAU8810_REG_EQ5, 0x002C }, |
56 | { NAU8810_REG_DACLIM1, 0x0032 }, |
57 | { NAU8810_REG_DACLIM2, 0x0000 }, |
58 | { NAU8810_REG_NOTCH1, 0x0000 }, |
59 | { NAU8810_REG_NOTCH2, 0x0000 }, |
60 | { NAU8810_REG_NOTCH3, 0x0000 }, |
61 | { NAU8810_REG_NOTCH4, 0x0000 }, |
62 | { NAU8810_REG_ALC1, 0x0038 }, |
63 | { NAU8810_REG_ALC2, 0x000B }, |
64 | { NAU8810_REG_ALC3, 0x0032 }, |
65 | { NAU8810_REG_NOISEGATE, 0x0000 }, |
66 | { NAU8810_REG_PLLN, 0x0008 }, |
67 | { NAU8810_REG_PLLK1, 0x000C }, |
68 | { NAU8810_REG_PLLK2, 0x0093 }, |
69 | { NAU8810_REG_PLLK3, 0x00E9 }, |
70 | { NAU8810_REG_ATTEN, 0x0000 }, |
71 | { NAU8810_REG_INPUT_SIGNAL, 0x0003 }, |
72 | { NAU8810_REG_PGAGAIN, 0x0010 }, |
73 | { NAU8810_REG_ADCBOOST, 0x0100 }, |
74 | { NAU8810_REG_OUTPUT, 0x0002 }, |
75 | { NAU8810_REG_SPKMIX, 0x0001 }, |
76 | { NAU8810_REG_SPKGAIN, 0x0039 }, |
77 | { NAU8810_REG_MONOMIX, 0x0001 }, |
78 | { NAU8810_REG_POWER4, 0x0000 }, |
79 | { NAU8810_REG_TSLOTCTL1, 0x0000 }, |
80 | { NAU8810_REG_TSLOTCTL2, 0x0020 }, |
81 | { NAU8810_REG_DEVICE_REVID, 0x0000 }, |
82 | { NAU8810_REG_I2C_DEVICEID, 0x001A }, |
83 | { NAU8810_REG_ADDITIONID, 0x00CA }, |
84 | { NAU8810_REG_RESERVE, 0x0124 }, |
85 | { NAU8810_REG_OUTCTL, 0x0001 }, |
86 | { NAU8810_REG_ALC1ENHAN1, 0x0010 }, |
87 | { NAU8810_REG_ALC1ENHAN2, 0x0000 }, |
88 | { NAU8810_REG_MISCCTL, 0x0000 }, |
89 | { NAU8810_REG_OUTTIEOFF, 0x0000 }, |
90 | { NAU8810_REG_AGCP2POUT, 0x0000 }, |
91 | { NAU8810_REG_AGCPOUT, 0x0000 }, |
92 | { NAU8810_REG_AMTCTL, 0x0000 }, |
93 | { NAU8810_REG_OUTTIEOFFMAN, 0x0000 }, |
94 | }; |
95 | |
96 | static bool nau8810_readable_reg(struct device *dev, unsigned int reg) |
97 | { |
98 | switch (reg) { |
99 | case NAU8810_REG_RESET ... NAU8810_REG_SMPLR: |
100 | case NAU8810_REG_DAC ... NAU8810_REG_DACGAIN: |
101 | case NAU8810_REG_ADC ... NAU8810_REG_ADCGAIN: |
102 | case NAU8810_REG_EQ1 ... NAU8810_REG_EQ5: |
103 | case NAU8810_REG_DACLIM1 ... NAU8810_REG_DACLIM2: |
104 | case NAU8810_REG_NOTCH1 ... NAU8810_REG_NOTCH4: |
105 | case NAU8810_REG_ALC1 ... NAU8810_REG_ATTEN: |
106 | case NAU8810_REG_INPUT_SIGNAL ... NAU8810_REG_PGAGAIN: |
107 | case NAU8810_REG_ADCBOOST: |
108 | case NAU8810_REG_OUTPUT ... NAU8810_REG_SPKMIX: |
109 | case NAU8810_REG_SPKGAIN: |
110 | case NAU8810_REG_MONOMIX: |
111 | case NAU8810_REG_POWER4 ... NAU8810_REG_TSLOTCTL2: |
112 | case NAU8810_REG_DEVICE_REVID ... NAU8810_REG_RESERVE: |
113 | case NAU8810_REG_OUTCTL ... NAU8810_REG_ALC1ENHAN2: |
114 | case NAU8810_REG_MISCCTL: |
115 | case NAU8810_REG_OUTTIEOFF ... NAU8810_REG_OUTTIEOFFMAN: |
116 | return true; |
117 | default: |
118 | return false; |
119 | } |
120 | } |
121 | |
122 | static bool nau8810_writeable_reg(struct device *dev, unsigned int reg) |
123 | { |
124 | switch (reg) { |
125 | case NAU8810_REG_RESET ... NAU8810_REG_SMPLR: |
126 | case NAU8810_REG_DAC ... NAU8810_REG_DACGAIN: |
127 | case NAU8810_REG_ADC ... NAU8810_REG_ADCGAIN: |
128 | case NAU8810_REG_EQ1 ... NAU8810_REG_EQ5: |
129 | case NAU8810_REG_DACLIM1 ... NAU8810_REG_DACLIM2: |
130 | case NAU8810_REG_NOTCH1 ... NAU8810_REG_NOTCH4: |
131 | case NAU8810_REG_ALC1 ... NAU8810_REG_ATTEN: |
132 | case NAU8810_REG_INPUT_SIGNAL ... NAU8810_REG_PGAGAIN: |
133 | case NAU8810_REG_ADCBOOST: |
134 | case NAU8810_REG_OUTPUT ... NAU8810_REG_SPKMIX: |
135 | case NAU8810_REG_SPKGAIN: |
136 | case NAU8810_REG_MONOMIX: |
137 | case NAU8810_REG_POWER4 ... NAU8810_REG_TSLOTCTL2: |
138 | case NAU8810_REG_OUTCTL ... NAU8810_REG_ALC1ENHAN2: |
139 | case NAU8810_REG_MISCCTL: |
140 | case NAU8810_REG_OUTTIEOFF ... NAU8810_REG_OUTTIEOFFMAN: |
141 | return true; |
142 | default: |
143 | return false; |
144 | } |
145 | } |
146 | |
147 | static bool nau8810_volatile_reg(struct device *dev, unsigned int reg) |
148 | { |
149 | switch (reg) { |
150 | case NAU8810_REG_RESET: |
151 | case NAU8810_REG_DEVICE_REVID ... NAU8810_REG_RESERVE: |
152 | return true; |
153 | default: |
154 | return false; |
155 | } |
156 | } |
157 | |
158 | /* The EQ parameters get function is to get the 5 band equalizer control. |
159 | * The regmap raw read can't work here because regmap doesn't provide |
160 | * value format for value width of 9 bits. Therefore, the driver reads data |
161 | * from cache and makes value format according to the endianness of |
162 | * bytes type control element. |
163 | */ |
164 | static int nau8810_eq_get(struct snd_kcontrol *kcontrol, |
165 | struct snd_ctl_elem_value *ucontrol) |
166 | { |
167 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
168 | struct nau8810 *nau8810 = snd_soc_component_get_drvdata(c: component); |
169 | struct soc_bytes_ext *params = (void *)kcontrol->private_value; |
170 | int i, reg, reg_val; |
171 | u16 *val; |
172 | __be16 tmp; |
173 | |
174 | val = (u16 *)ucontrol->value.bytes.data; |
175 | reg = NAU8810_REG_EQ1; |
176 | for (i = 0; i < params->max / sizeof(u16); i++) { |
177 | regmap_read(map: nau8810->regmap, reg: reg + i, val: ®_val); |
178 | /* conversion of 16-bit integers between native CPU format |
179 | * and big endian format |
180 | */ |
181 | tmp = cpu_to_be16(reg_val); |
182 | memcpy(val + i, &tmp, sizeof(tmp)); |
183 | } |
184 | |
185 | return 0; |
186 | } |
187 | |
188 | /* The EQ parameters put function is to make configuration of 5 band equalizer |
189 | * control. These configuration includes central frequency, equalizer gain, |
190 | * cut-off frequency, bandwidth control, and equalizer path. |
191 | * The regmap raw write can't work here because regmap doesn't provide |
192 | * register and value format for register with address 7 bits and value 9 bits. |
193 | * Therefore, the driver makes value format according to the endianness of |
194 | * bytes type control element and writes data to codec. |
195 | */ |
196 | static int nau8810_eq_put(struct snd_kcontrol *kcontrol, |
197 | struct snd_ctl_elem_value *ucontrol) |
198 | { |
199 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
200 | struct nau8810 *nau8810 = snd_soc_component_get_drvdata(c: component); |
201 | struct soc_bytes_ext *params = (void *)kcontrol->private_value; |
202 | void *data; |
203 | u16 *val, value; |
204 | int i, reg, ret; |
205 | __be16 *tmp; |
206 | |
207 | data = kmemdup(p: ucontrol->value.bytes.data, |
208 | size: params->max, GFP_KERNEL | GFP_DMA); |
209 | if (!data) |
210 | return -ENOMEM; |
211 | |
212 | val = (u16 *)data; |
213 | reg = NAU8810_REG_EQ1; |
214 | for (i = 0; i < params->max / sizeof(u16); i++) { |
215 | /* conversion of 16-bit integers between native CPU format |
216 | * and big endian format |
217 | */ |
218 | tmp = (__be16 *)(val + i); |
219 | value = be16_to_cpup(p: tmp); |
220 | ret = regmap_write(map: nau8810->regmap, reg: reg + i, val: value); |
221 | if (ret) { |
222 | dev_err(component->dev, "EQ configuration fail, register: %x ret: %d\n" , |
223 | reg + i, ret); |
224 | kfree(objp: data); |
225 | return ret; |
226 | } |
227 | } |
228 | kfree(objp: data); |
229 | |
230 | return 0; |
231 | } |
232 | |
233 | static const char * const nau8810_companding[] = { |
234 | "Off" , "NC" , "u-law" , "A-law" }; |
235 | |
236 | static const struct soc_enum nau8810_companding_adc_enum = |
237 | SOC_ENUM_SINGLE(NAU8810_REG_COMP, NAU8810_ADCCM_SFT, |
238 | ARRAY_SIZE(nau8810_companding), nau8810_companding); |
239 | |
240 | static const struct soc_enum nau8810_companding_dac_enum = |
241 | SOC_ENUM_SINGLE(NAU8810_REG_COMP, NAU8810_DACCM_SFT, |
242 | ARRAY_SIZE(nau8810_companding), nau8810_companding); |
243 | |
244 | static const char * const nau8810_deemp[] = { |
245 | "None" , "32kHz" , "44.1kHz" , "48kHz" }; |
246 | |
247 | static const struct soc_enum nau8810_deemp_enum = |
248 | SOC_ENUM_SINGLE(NAU8810_REG_DAC, NAU8810_DEEMP_SFT, |
249 | ARRAY_SIZE(nau8810_deemp), nau8810_deemp); |
250 | |
251 | static const char * const nau8810_eqmode[] = {"Capture" , "Playback" }; |
252 | |
253 | static const struct soc_enum nau8810_eqmode_enum = |
254 | SOC_ENUM_SINGLE(NAU8810_REG_EQ1, NAU8810_EQM_SFT, |
255 | ARRAY_SIZE(nau8810_eqmode), nau8810_eqmode); |
256 | |
257 | static const char * const nau8810_alc[] = {"Normal" , "Limiter" }; |
258 | |
259 | static const struct soc_enum nau8810_alc_enum = |
260 | SOC_ENUM_SINGLE(NAU8810_REG_ALC3, NAU8810_ALCM_SFT, |
261 | ARRAY_SIZE(nau8810_alc), nau8810_alc); |
262 | |
263 | static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); |
264 | static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); |
265 | static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); |
266 | static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); |
267 | |
268 | static const struct snd_kcontrol_new nau8810_snd_controls[] = { |
269 | SOC_ENUM("ADC Companding" , nau8810_companding_adc_enum), |
270 | SOC_ENUM("DAC Companding" , nau8810_companding_dac_enum), |
271 | SOC_ENUM("DAC De-emphasis" , nau8810_deemp_enum), |
272 | |
273 | SOC_ENUM("EQ Function" , nau8810_eqmode_enum), |
274 | SND_SOC_BYTES_EXT("EQ Parameters" , 10, |
275 | nau8810_eq_get, nau8810_eq_put), |
276 | |
277 | SOC_SINGLE("DAC Inversion Switch" , NAU8810_REG_DAC, |
278 | NAU8810_DACPL_SFT, 1, 0), |
279 | SOC_SINGLE_TLV("Playback Volume" , NAU8810_REG_DACGAIN, |
280 | NAU8810_DACGAIN_SFT, 0xff, 0, digital_tlv), |
281 | |
282 | SOC_SINGLE("High Pass Filter Switch" , NAU8810_REG_ADC, |
283 | NAU8810_HPFEN_SFT, 1, 0), |
284 | SOC_SINGLE("High Pass Cut Off" , NAU8810_REG_ADC, |
285 | NAU8810_HPF_SFT, 0x7, 0), |
286 | |
287 | SOC_SINGLE("ADC Inversion Switch" , NAU8810_REG_ADC, |
288 | NAU8810_ADCPL_SFT, 1, 0), |
289 | SOC_SINGLE_TLV("Capture Volume" , NAU8810_REG_ADCGAIN, |
290 | NAU8810_ADCGAIN_SFT, 0xff, 0, digital_tlv), |
291 | |
292 | SOC_SINGLE_TLV("EQ1 Volume" , NAU8810_REG_EQ1, |
293 | NAU8810_EQ1GC_SFT, 0x18, 1, eq_tlv), |
294 | SOC_SINGLE_TLV("EQ2 Volume" , NAU8810_REG_EQ2, |
295 | NAU8810_EQ2GC_SFT, 0x18, 1, eq_tlv), |
296 | SOC_SINGLE_TLV("EQ3 Volume" , NAU8810_REG_EQ3, |
297 | NAU8810_EQ3GC_SFT, 0x18, 1, eq_tlv), |
298 | SOC_SINGLE_TLV("EQ4 Volume" , NAU8810_REG_EQ4, |
299 | NAU8810_EQ4GC_SFT, 0x18, 1, eq_tlv), |
300 | SOC_SINGLE_TLV("EQ5 Volume" , NAU8810_REG_EQ5, |
301 | NAU8810_EQ5GC_SFT, 0x18, 1, eq_tlv), |
302 | |
303 | SOC_SINGLE("DAC Limiter Switch" , NAU8810_REG_DACLIM1, |
304 | NAU8810_DACLIMEN_SFT, 1, 0), |
305 | SOC_SINGLE("DAC Limiter Decay" , NAU8810_REG_DACLIM1, |
306 | NAU8810_DACLIMDCY_SFT, 0xf, 0), |
307 | SOC_SINGLE("DAC Limiter Attack" , NAU8810_REG_DACLIM1, |
308 | NAU8810_DACLIMATK_SFT, 0xf, 0), |
309 | SOC_SINGLE("DAC Limiter Threshold" , NAU8810_REG_DACLIM2, |
310 | NAU8810_DACLIMTHL_SFT, 0x7, 0), |
311 | SOC_SINGLE("DAC Limiter Boost" , NAU8810_REG_DACLIM2, |
312 | NAU8810_DACLIMBST_SFT, 0xf, 0), |
313 | |
314 | SOC_ENUM("ALC Mode" , nau8810_alc_enum), |
315 | SOC_SINGLE("ALC Enable Switch" , NAU8810_REG_ALC1, |
316 | NAU8810_ALCEN_SFT, 1, 0), |
317 | SOC_SINGLE("ALC Max Volume" , NAU8810_REG_ALC1, |
318 | NAU8810_ALCMXGAIN_SFT, 0x7, 0), |
319 | SOC_SINGLE("ALC Min Volume" , NAU8810_REG_ALC1, |
320 | NAU8810_ALCMINGAIN_SFT, 0x7, 0), |
321 | SOC_SINGLE("ALC ZC Switch" , NAU8810_REG_ALC2, |
322 | NAU8810_ALCZC_SFT, 1, 0), |
323 | SOC_SINGLE("ALC Hold" , NAU8810_REG_ALC2, |
324 | NAU8810_ALCHT_SFT, 0xf, 0), |
325 | SOC_SINGLE("ALC Target" , NAU8810_REG_ALC2, |
326 | NAU8810_ALCSL_SFT, 0xf, 0), |
327 | SOC_SINGLE("ALC Decay" , NAU8810_REG_ALC3, |
328 | NAU8810_ALCDCY_SFT, 0xf, 0), |
329 | SOC_SINGLE("ALC Attack" , NAU8810_REG_ALC3, |
330 | NAU8810_ALCATK_SFT, 0xf, 0), |
331 | SOC_SINGLE("ALC Noise Gate Switch" , NAU8810_REG_NOISEGATE, |
332 | NAU8810_ALCNEN_SFT, 1, 0), |
333 | SOC_SINGLE("ALC Noise Gate Threshold" , NAU8810_REG_NOISEGATE, |
334 | NAU8810_ALCNTH_SFT, 0x7, 0), |
335 | |
336 | SOC_SINGLE("PGA ZC Switch" , NAU8810_REG_PGAGAIN, |
337 | NAU8810_PGAZC_SFT, 1, 0), |
338 | SOC_SINGLE_TLV("PGA Volume" , NAU8810_REG_PGAGAIN, |
339 | NAU8810_PGAGAIN_SFT, 0x3f, 0, inpga_tlv), |
340 | |
341 | SOC_SINGLE("Speaker ZC Switch" , NAU8810_REG_SPKGAIN, |
342 | NAU8810_SPKZC_SFT, 1, 0), |
343 | SOC_SINGLE("Speaker Mute Switch" , NAU8810_REG_SPKGAIN, |
344 | NAU8810_SPKMT_SFT, 1, 0), |
345 | SOC_SINGLE_TLV("Speaker Volume" , NAU8810_REG_SPKGAIN, |
346 | NAU8810_SPKGAIN_SFT, 0x3f, 0, spk_tlv), |
347 | |
348 | SOC_SINGLE("Capture Boost(+20dB)" , NAU8810_REG_ADCBOOST, |
349 | NAU8810_PGABST_SFT, 1, 0), |
350 | SOC_SINGLE("Mono Mute Switch" , NAU8810_REG_MONOMIX, |
351 | NAU8810_MOUTMXMT_SFT, 1, 0), |
352 | |
353 | SOC_SINGLE("DAC Oversampling Rate(128x) Switch" , NAU8810_REG_DAC, |
354 | NAU8810_DACOS_SFT, 1, 0), |
355 | SOC_SINGLE("ADC Oversampling Rate(128x) Switch" , NAU8810_REG_ADC, |
356 | NAU8810_ADCOS_SFT, 1, 0), |
357 | }; |
358 | |
359 | /* Speaker Output Mixer */ |
360 | static const struct snd_kcontrol_new nau8810_speaker_mixer_controls[] = { |
361 | SOC_DAPM_SINGLE("AUX Bypass Switch" , NAU8810_REG_SPKMIX, |
362 | NAU8810_AUXSPK_SFT, 1, 0), |
363 | SOC_DAPM_SINGLE("Line Bypass Switch" , NAU8810_REG_SPKMIX, |
364 | NAU8810_BYPSPK_SFT, 1, 0), |
365 | SOC_DAPM_SINGLE("PCM Playback Switch" , NAU8810_REG_SPKMIX, |
366 | NAU8810_DACSPK_SFT, 1, 0), |
367 | }; |
368 | |
369 | /* Mono Output Mixer */ |
370 | static const struct snd_kcontrol_new nau8810_mono_mixer_controls[] = { |
371 | SOC_DAPM_SINGLE("AUX Bypass Switch" , NAU8810_REG_MONOMIX, |
372 | NAU8810_AUXMOUT_SFT, 1, 0), |
373 | SOC_DAPM_SINGLE("Line Bypass Switch" , NAU8810_REG_MONOMIX, |
374 | NAU8810_BYPMOUT_SFT, 1, 0), |
375 | SOC_DAPM_SINGLE("PCM Playback Switch" , NAU8810_REG_MONOMIX, |
376 | NAU8810_DACMOUT_SFT, 1, 0), |
377 | }; |
378 | |
379 | /* PGA Mute */ |
380 | static const struct snd_kcontrol_new nau8810_pgaboost_mixer_controls[] = { |
381 | SOC_DAPM_SINGLE("AUX PGA Switch" , NAU8810_REG_ADCBOOST, |
382 | NAU8810_AUXBSTGAIN_SFT, 0x7, 0), |
383 | SOC_DAPM_SINGLE("PGA Mute Switch" , NAU8810_REG_PGAGAIN, |
384 | NAU8810_PGAMT_SFT, 1, 1), |
385 | SOC_DAPM_SINGLE("PMIC PGA Switch" , NAU8810_REG_ADCBOOST, |
386 | NAU8810_PMICBSTGAIN_SFT, 0x7, 0), |
387 | }; |
388 | |
389 | /* Input PGA */ |
390 | static const struct snd_kcontrol_new nau8810_inpga[] = { |
391 | SOC_DAPM_SINGLE("AUX Switch" , NAU8810_REG_INPUT_SIGNAL, |
392 | NAU8810_AUXPGA_SFT, 1, 0), |
393 | SOC_DAPM_SINGLE("MicN Switch" , NAU8810_REG_INPUT_SIGNAL, |
394 | NAU8810_NMICPGA_SFT, 1, 0), |
395 | SOC_DAPM_SINGLE("MicP Switch" , NAU8810_REG_INPUT_SIGNAL, |
396 | NAU8810_PMICPGA_SFT, 1, 0), |
397 | }; |
398 | |
399 | /* Loopback Switch */ |
400 | static const struct snd_kcontrol_new nau8810_loopback = |
401 | SOC_DAPM_SINGLE("Switch" , NAU8810_REG_COMP, |
402 | NAU8810_ADDAP_SFT, 1, 0); |
403 | |
404 | static int check_mclk_select_pll(struct snd_soc_dapm_widget *source, |
405 | struct snd_soc_dapm_widget *sink) |
406 | { |
407 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: source->dapm); |
408 | struct nau8810 *nau8810 = snd_soc_component_get_drvdata(c: component); |
409 | unsigned int value; |
410 | |
411 | regmap_read(map: nau8810->regmap, NAU8810_REG_CLOCK, val: &value); |
412 | return (value & NAU8810_CLKM_MASK); |
413 | } |
414 | |
415 | static int check_mic_enabled(struct snd_soc_dapm_widget *source, |
416 | struct snd_soc_dapm_widget *sink) |
417 | { |
418 | struct snd_soc_component *component = |
419 | snd_soc_dapm_to_component(dapm: source->dapm); |
420 | struct nau8810 *nau8810 = snd_soc_component_get_drvdata(c: component); |
421 | unsigned int value; |
422 | |
423 | regmap_read(map: nau8810->regmap, NAU8810_REG_INPUT_SIGNAL, val: &value); |
424 | if (value & NAU8810_PMICPGA_EN || value & NAU8810_NMICPGA_EN) |
425 | return 1; |
426 | regmap_read(map: nau8810->regmap, NAU8810_REG_ADCBOOST, val: &value); |
427 | if (value & NAU8810_PMICBSTGAIN_MASK) |
428 | return 1; |
429 | return 0; |
430 | } |
431 | |
432 | static const struct snd_soc_dapm_widget nau8810_dapm_widgets[] = { |
433 | SND_SOC_DAPM_MIXER("Speaker Mixer" , NAU8810_REG_POWER3, |
434 | NAU8810_SPKMX_EN_SFT, 0, &nau8810_speaker_mixer_controls[0], |
435 | ARRAY_SIZE(nau8810_speaker_mixer_controls)), |
436 | SND_SOC_DAPM_MIXER("Mono Mixer" , NAU8810_REG_POWER3, |
437 | NAU8810_MOUTMX_EN_SFT, 0, &nau8810_mono_mixer_controls[0], |
438 | ARRAY_SIZE(nau8810_mono_mixer_controls)), |
439 | SND_SOC_DAPM_DAC("DAC" , "Playback" , NAU8810_REG_POWER3, |
440 | NAU8810_DAC_EN_SFT, 0), |
441 | SND_SOC_DAPM_ADC("ADC" , "Capture" , NAU8810_REG_POWER2, |
442 | NAU8810_ADC_EN_SFT, 0), |
443 | SND_SOC_DAPM_PGA("SpkN Out" , NAU8810_REG_POWER3, |
444 | NAU8810_NSPK_EN_SFT, 0, NULL, 0), |
445 | SND_SOC_DAPM_PGA("SpkP Out" , NAU8810_REG_POWER3, |
446 | NAU8810_PSPK_EN_SFT, 0, NULL, 0), |
447 | SND_SOC_DAPM_PGA("Mono Out" , NAU8810_REG_POWER3, |
448 | NAU8810_MOUT_EN_SFT, 0, NULL, 0), |
449 | |
450 | SND_SOC_DAPM_MIXER("Input PGA" , NAU8810_REG_POWER2, |
451 | NAU8810_PGA_EN_SFT, 0, nau8810_inpga, |
452 | ARRAY_SIZE(nau8810_inpga)), |
453 | SND_SOC_DAPM_MIXER("Input Boost Stage" , NAU8810_REG_POWER2, |
454 | NAU8810_BST_EN_SFT, 0, nau8810_pgaboost_mixer_controls, |
455 | ARRAY_SIZE(nau8810_pgaboost_mixer_controls)), |
456 | SND_SOC_DAPM_PGA("AUX Input" , NAU8810_REG_POWER1, |
457 | NAU8810_AUX_EN_SFT, 0, NULL, 0), |
458 | |
459 | SND_SOC_DAPM_SUPPLY("Mic Bias" , NAU8810_REG_POWER1, |
460 | NAU8810_MICBIAS_EN_SFT, 0, NULL, 0), |
461 | SND_SOC_DAPM_SUPPLY("PLL" , NAU8810_REG_POWER1, |
462 | NAU8810_PLL_EN_SFT, 0, NULL, 0), |
463 | |
464 | SND_SOC_DAPM_SWITCH("Digital Loopback" , SND_SOC_NOPM, 0, 0, |
465 | &nau8810_loopback), |
466 | |
467 | SND_SOC_DAPM_INPUT("AUX" ), |
468 | SND_SOC_DAPM_INPUT("MICN" ), |
469 | SND_SOC_DAPM_INPUT("MICP" ), |
470 | SND_SOC_DAPM_OUTPUT("MONOOUT" ), |
471 | SND_SOC_DAPM_OUTPUT("SPKOUTP" ), |
472 | SND_SOC_DAPM_OUTPUT("SPKOUTN" ), |
473 | }; |
474 | |
475 | static const struct snd_soc_dapm_route nau8810_dapm_routes[] = { |
476 | {"DAC" , NULL, "PLL" , check_mclk_select_pll}, |
477 | |
478 | /* Mono output mixer */ |
479 | {"Mono Mixer" , "AUX Bypass Switch" , "AUX Input" }, |
480 | {"Mono Mixer" , "PCM Playback Switch" , "DAC" }, |
481 | {"Mono Mixer" , "Line Bypass Switch" , "Input Boost Stage" }, |
482 | |
483 | /* Speaker output mixer */ |
484 | {"Speaker Mixer" , "AUX Bypass Switch" , "AUX Input" }, |
485 | {"Speaker Mixer" , "PCM Playback Switch" , "DAC" }, |
486 | {"Speaker Mixer" , "Line Bypass Switch" , "Input Boost Stage" }, |
487 | |
488 | /* Outputs */ |
489 | {"Mono Out" , NULL, "Mono Mixer" }, |
490 | {"MONOOUT" , NULL, "Mono Out" }, |
491 | {"SpkN Out" , NULL, "Speaker Mixer" }, |
492 | {"SpkP Out" , NULL, "Speaker Mixer" }, |
493 | {"SPKOUTN" , NULL, "SpkN Out" }, |
494 | {"SPKOUTP" , NULL, "SpkP Out" }, |
495 | |
496 | /* Input Boost Stage */ |
497 | {"ADC" , NULL, "Input Boost Stage" }, |
498 | {"ADC" , NULL, "PLL" , check_mclk_select_pll}, |
499 | {"Input Boost Stage" , "AUX PGA Switch" , "AUX Input" }, |
500 | {"Input Boost Stage" , "PGA Mute Switch" , "Input PGA" }, |
501 | {"Input Boost Stage" , "PMIC PGA Switch" , "MICP" }, |
502 | |
503 | /* Input PGA */ |
504 | {"Input PGA" , NULL, "Mic Bias" , check_mic_enabled}, |
505 | {"Input PGA" , "AUX Switch" , "AUX Input" }, |
506 | {"Input PGA" , "MicN Switch" , "MICN" }, |
507 | {"Input PGA" , "MicP Switch" , "MICP" }, |
508 | {"AUX Input" , NULL, "AUX" }, |
509 | |
510 | /* Digital Looptack */ |
511 | {"Digital Loopback" , "Switch" , "ADC" }, |
512 | {"DAC" , NULL, "Digital Loopback" }, |
513 | }; |
514 | |
515 | static int nau8810_set_sysclk(struct snd_soc_dai *dai, |
516 | int clk_id, unsigned int freq, int dir) |
517 | { |
518 | struct snd_soc_component *component = dai->component; |
519 | struct nau8810 *nau8810 = snd_soc_component_get_drvdata(c: component); |
520 | |
521 | nau8810->clk_id = clk_id; |
522 | nau8810->sysclk = freq; |
523 | dev_dbg(nau8810->dev, "master sysclk %dHz, source %s\n" , |
524 | freq, clk_id == NAU8810_SCLK_PLL ? "PLL" : "MCLK" ); |
525 | |
526 | return 0; |
527 | } |
528 | |
529 | static int nau8810_calc_pll(unsigned int pll_in, |
530 | unsigned int fs, struct nau8810_pll *pll_param) |
531 | { |
532 | u64 f2, f2_max, pll_ratio; |
533 | int i, scal_sel; |
534 | |
535 | if (pll_in > NAU_PLL_REF_MAX || pll_in < NAU_PLL_REF_MIN) |
536 | return -EINVAL; |
537 | |
538 | f2_max = 0; |
539 | scal_sel = ARRAY_SIZE(nau8810_mclk_scaler); |
540 | for (i = 0; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) { |
541 | f2 = 256ULL * fs * 4 * nau8810_mclk_scaler[i]; |
542 | f2 = div_u64(dividend: f2, divisor: 10); |
543 | if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX && |
544 | f2_max < f2) { |
545 | f2_max = f2; |
546 | scal_sel = i; |
547 | } |
548 | } |
549 | if (ARRAY_SIZE(nau8810_mclk_scaler) == scal_sel) |
550 | return -EINVAL; |
551 | pll_param->mclk_scaler = scal_sel; |
552 | f2 = f2_max; |
553 | |
554 | /* Calculate the PLL 4-bit integer input and the PLL 24-bit fractional |
555 | * input; round up the 24+4bit. |
556 | */ |
557 | pll_ratio = div_u64(dividend: f2 << 28, divisor: pll_in); |
558 | pll_param->pre_factor = 0; |
559 | if (((pll_ratio >> 28) & 0xF) < NAU_PLL_OPTOP_MIN) { |
560 | pll_ratio <<= 1; |
561 | pll_param->pre_factor = 1; |
562 | } |
563 | pll_param->pll_int = (pll_ratio >> 28) & 0xF; |
564 | pll_param->pll_frac = ((pll_ratio & 0xFFFFFFF) >> 4); |
565 | |
566 | return 0; |
567 | } |
568 | |
569 | static int nau8810_set_pll(struct snd_soc_dai *codec_dai, int pll_id, |
570 | int source, unsigned int freq_in, unsigned int freq_out) |
571 | { |
572 | struct snd_soc_component *component = codec_dai->component; |
573 | struct nau8810 *nau8810 = snd_soc_component_get_drvdata(c: component); |
574 | struct regmap *map = nau8810->regmap; |
575 | struct nau8810_pll *pll_param = &nau8810->pll; |
576 | int ret, fs; |
577 | |
578 | fs = freq_out / 256; |
579 | ret = nau8810_calc_pll(pll_in: freq_in, fs, pll_param); |
580 | if (ret < 0) { |
581 | dev_err(nau8810->dev, "Unsupported input clock %d\n" , freq_in); |
582 | return ret; |
583 | } |
584 | dev_info(nau8810->dev, "pll_int=%x pll_frac=%x mclk_scaler=%x pre_factor=%x\n" , |
585 | pll_param->pll_int, pll_param->pll_frac, pll_param->mclk_scaler, |
586 | pll_param->pre_factor); |
587 | |
588 | regmap_update_bits(map, NAU8810_REG_PLLN, |
589 | NAU8810_PLLMCLK_DIV2 | NAU8810_PLLN_MASK, |
590 | val: (pll_param->pre_factor ? NAU8810_PLLMCLK_DIV2 : 0) | |
591 | pll_param->pll_int); |
592 | regmap_write(map, NAU8810_REG_PLLK1, |
593 | val: (pll_param->pll_frac >> NAU8810_PLLK1_SFT) & |
594 | NAU8810_PLLK1_MASK); |
595 | regmap_write(map, NAU8810_REG_PLLK2, |
596 | val: (pll_param->pll_frac >> NAU8810_PLLK2_SFT) & |
597 | NAU8810_PLLK2_MASK); |
598 | regmap_write(map, NAU8810_REG_PLLK3, |
599 | val: pll_param->pll_frac & NAU8810_PLLK3_MASK); |
600 | regmap_update_bits(map, NAU8810_REG_CLOCK, NAU8810_MCLKSEL_MASK, |
601 | val: pll_param->mclk_scaler << NAU8810_MCLKSEL_SFT); |
602 | regmap_update_bits(map, NAU8810_REG_CLOCK, |
603 | NAU8810_CLKM_MASK, NAU8810_CLKM_PLL); |
604 | |
605 | return 0; |
606 | } |
607 | |
608 | static int nau8810_set_dai_fmt(struct snd_soc_dai *codec_dai, |
609 | unsigned int fmt) |
610 | { |
611 | struct snd_soc_component *component = codec_dai->component; |
612 | struct nau8810 *nau8810 = snd_soc_component_get_drvdata(c: component); |
613 | u16 ctrl1_val = 0, ctrl2_val = 0; |
614 | |
615 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
616 | case SND_SOC_DAIFMT_CBM_CFM: |
617 | ctrl2_val |= NAU8810_CLKIO_MASTER; |
618 | break; |
619 | case SND_SOC_DAIFMT_CBS_CFS: |
620 | break; |
621 | default: |
622 | return -EINVAL; |
623 | } |
624 | |
625 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
626 | case SND_SOC_DAIFMT_I2S: |
627 | ctrl1_val |= NAU8810_AIFMT_I2S; |
628 | break; |
629 | case SND_SOC_DAIFMT_RIGHT_J: |
630 | break; |
631 | case SND_SOC_DAIFMT_LEFT_J: |
632 | ctrl1_val |= NAU8810_AIFMT_LEFT; |
633 | break; |
634 | case SND_SOC_DAIFMT_DSP_A: |
635 | ctrl1_val |= NAU8810_AIFMT_PCM_A; |
636 | break; |
637 | default: |
638 | return -EINVAL; |
639 | } |
640 | |
641 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
642 | case SND_SOC_DAIFMT_NB_NF: |
643 | break; |
644 | case SND_SOC_DAIFMT_IB_IF: |
645 | ctrl1_val |= NAU8810_BCLKP_IB | NAU8810_FSP_IF; |
646 | break; |
647 | case SND_SOC_DAIFMT_IB_NF: |
648 | ctrl1_val |= NAU8810_BCLKP_IB; |
649 | break; |
650 | case SND_SOC_DAIFMT_NB_IF: |
651 | ctrl1_val |= NAU8810_FSP_IF; |
652 | break; |
653 | default: |
654 | return -EINVAL; |
655 | } |
656 | |
657 | regmap_update_bits(map: nau8810->regmap, NAU8810_REG_IFACE, |
658 | NAU8810_AIFMT_MASK | NAU8810_FSP_IF | |
659 | NAU8810_BCLKP_IB, val: ctrl1_val); |
660 | regmap_update_bits(map: nau8810->regmap, NAU8810_REG_CLOCK, |
661 | NAU8810_CLKIO_MASK, val: ctrl2_val); |
662 | |
663 | return 0; |
664 | } |
665 | |
666 | static int nau8810_mclk_clkdiv(struct nau8810 *nau8810, int rate) |
667 | { |
668 | int i, sclk, imclk = rate * 256, div = 0; |
669 | |
670 | if (!nau8810->sysclk) { |
671 | dev_err(nau8810->dev, "Make mclk div configuration fail because of invalid system clock\n" ); |
672 | return -EINVAL; |
673 | } |
674 | |
675 | /* Configure the master clock prescaler div to make system |
676 | * clock to approximate the internal master clock (IMCLK); |
677 | * and large or equal to IMCLK. |
678 | */ |
679 | for (i = 1; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) { |
680 | sclk = (nau8810->sysclk * 10) / |
681 | nau8810_mclk_scaler[i]; |
682 | if (sclk < imclk) |
683 | break; |
684 | div = i; |
685 | } |
686 | dev_dbg(nau8810->dev, |
687 | "master clock prescaler %x for fs %d\n" , div, rate); |
688 | |
689 | /* master clock from MCLK and disable PLL */ |
690 | regmap_update_bits(map: nau8810->regmap, NAU8810_REG_CLOCK, |
691 | NAU8810_MCLKSEL_MASK, val: (div << NAU8810_MCLKSEL_SFT)); |
692 | regmap_update_bits(map: nau8810->regmap, NAU8810_REG_CLOCK, |
693 | NAU8810_CLKM_MASK, NAU8810_CLKM_MCLK); |
694 | |
695 | return 0; |
696 | } |
697 | |
698 | static int nau8810_pcm_hw_params(struct snd_pcm_substream *substream, |
699 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) |
700 | { |
701 | struct snd_soc_component *component = dai->component; |
702 | struct nau8810 *nau8810 = snd_soc_component_get_drvdata(c: component); |
703 | int val_len = 0, val_rate = 0, ret = 0; |
704 | unsigned int ctrl_val, bclk_fs, bclk_div; |
705 | |
706 | /* Select BCLK configuration if the codec as master. */ |
707 | regmap_read(map: nau8810->regmap, NAU8810_REG_CLOCK, val: &ctrl_val); |
708 | if (ctrl_val & NAU8810_CLKIO_MASTER) { |
709 | /* get the bclk and fs ratio */ |
710 | bclk_fs = snd_soc_params_to_bclk(parms: params) / params_rate(p: params); |
711 | if (bclk_fs <= 32) |
712 | bclk_div = NAU8810_BCLKDIV_8; |
713 | else if (bclk_fs <= 64) |
714 | bclk_div = NAU8810_BCLKDIV_4; |
715 | else if (bclk_fs <= 128) |
716 | bclk_div = NAU8810_BCLKDIV_2; |
717 | else |
718 | return -EINVAL; |
719 | regmap_update_bits(map: nau8810->regmap, NAU8810_REG_CLOCK, |
720 | NAU8810_BCLKSEL_MASK, val: bclk_div); |
721 | } |
722 | |
723 | switch (params_width(p: params)) { |
724 | case 16: |
725 | break; |
726 | case 20: |
727 | val_len |= NAU8810_WLEN_20; |
728 | break; |
729 | case 24: |
730 | val_len |= NAU8810_WLEN_24; |
731 | break; |
732 | case 32: |
733 | val_len |= NAU8810_WLEN_32; |
734 | break; |
735 | } |
736 | |
737 | switch (params_rate(p: params)) { |
738 | case 8000: |
739 | val_rate |= NAU8810_SMPLR_8K; |
740 | break; |
741 | case 11025: |
742 | val_rate |= NAU8810_SMPLR_12K; |
743 | break; |
744 | case 16000: |
745 | val_rate |= NAU8810_SMPLR_16K; |
746 | break; |
747 | case 22050: |
748 | val_rate |= NAU8810_SMPLR_24K; |
749 | break; |
750 | case 32000: |
751 | val_rate |= NAU8810_SMPLR_32K; |
752 | break; |
753 | case 44100: |
754 | case 48000: |
755 | break; |
756 | } |
757 | |
758 | regmap_update_bits(map: nau8810->regmap, NAU8810_REG_IFACE, |
759 | NAU8810_WLEN_MASK, val: val_len); |
760 | regmap_update_bits(map: nau8810->regmap, NAU8810_REG_SMPLR, |
761 | NAU8810_SMPLR_MASK, val: val_rate); |
762 | |
763 | /* If the master clock is from MCLK, provide the runtime FS for driver |
764 | * to get the master clock prescaler configuration. |
765 | */ |
766 | if (nau8810->clk_id == NAU8810_SCLK_MCLK) { |
767 | ret = nau8810_mclk_clkdiv(nau8810, rate: params_rate(p: params)); |
768 | if (ret < 0) |
769 | dev_err(nau8810->dev, "MCLK div configuration fail\n" ); |
770 | } |
771 | |
772 | return ret; |
773 | } |
774 | |
775 | static int nau8810_set_bias_level(struct snd_soc_component *component, |
776 | enum snd_soc_bias_level level) |
777 | { |
778 | struct nau8810 *nau8810 = snd_soc_component_get_drvdata(c: component); |
779 | struct regmap *map = nau8810->regmap; |
780 | |
781 | switch (level) { |
782 | case SND_SOC_BIAS_ON: |
783 | case SND_SOC_BIAS_PREPARE: |
784 | regmap_update_bits(map, NAU8810_REG_POWER1, |
785 | NAU8810_REFIMP_MASK, NAU8810_REFIMP_80K); |
786 | break; |
787 | |
788 | case SND_SOC_BIAS_STANDBY: |
789 | regmap_update_bits(map, NAU8810_REG_POWER1, |
790 | NAU8810_IOBUF_EN | NAU8810_ABIAS_EN, |
791 | NAU8810_IOBUF_EN | NAU8810_ABIAS_EN); |
792 | |
793 | if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { |
794 | regcache_sync(map); |
795 | regmap_update_bits(map, NAU8810_REG_POWER1, |
796 | NAU8810_REFIMP_MASK, NAU8810_REFIMP_3K); |
797 | mdelay(100); |
798 | } |
799 | regmap_update_bits(map, NAU8810_REG_POWER1, |
800 | NAU8810_REFIMP_MASK, NAU8810_REFIMP_300K); |
801 | break; |
802 | |
803 | case SND_SOC_BIAS_OFF: |
804 | regmap_write(map, NAU8810_REG_POWER1, val: 0); |
805 | regmap_write(map, NAU8810_REG_POWER2, val: 0); |
806 | regmap_write(map, NAU8810_REG_POWER3, val: 0); |
807 | break; |
808 | } |
809 | |
810 | return 0; |
811 | } |
812 | |
813 | |
814 | #define NAU8810_RATES (SNDRV_PCM_RATE_8000_48000) |
815 | |
816 | #define NAU8810_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ |
817 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) |
818 | |
819 | static const struct snd_soc_dai_ops nau8810_ops = { |
820 | .hw_params = nau8810_pcm_hw_params, |
821 | .set_fmt = nau8810_set_dai_fmt, |
822 | .set_sysclk = nau8810_set_sysclk, |
823 | .set_pll = nau8810_set_pll, |
824 | }; |
825 | |
826 | static struct snd_soc_dai_driver nau8810_dai = { |
827 | .name = "nau8810-hifi" , |
828 | .playback = { |
829 | .stream_name = "Playback" , |
830 | .channels_min = 1, |
831 | .channels_max = 2, /* Only 1 channel of data */ |
832 | .rates = NAU8810_RATES, |
833 | .formats = NAU8810_FORMATS, |
834 | }, |
835 | .capture = { |
836 | .stream_name = "Capture" , |
837 | .channels_min = 1, |
838 | .channels_max = 2, /* Only 1 channel of data */ |
839 | .rates = NAU8810_RATES, |
840 | .formats = NAU8810_FORMATS, |
841 | }, |
842 | .ops = &nau8810_ops, |
843 | .symmetric_rate = 1, |
844 | }; |
845 | |
846 | static const struct regmap_config nau8810_regmap_config = { |
847 | .reg_bits = 7, |
848 | .val_bits = 9, |
849 | |
850 | .max_register = NAU8810_REG_MAX, |
851 | .readable_reg = nau8810_readable_reg, |
852 | .writeable_reg = nau8810_writeable_reg, |
853 | .volatile_reg = nau8810_volatile_reg, |
854 | |
855 | .cache_type = REGCACHE_RBTREE, |
856 | .reg_defaults = nau8810_reg_defaults, |
857 | .num_reg_defaults = ARRAY_SIZE(nau8810_reg_defaults), |
858 | }; |
859 | |
860 | static const struct snd_soc_component_driver nau8810_component_driver = { |
861 | .set_bias_level = nau8810_set_bias_level, |
862 | .controls = nau8810_snd_controls, |
863 | .num_controls = ARRAY_SIZE(nau8810_snd_controls), |
864 | .dapm_widgets = nau8810_dapm_widgets, |
865 | .num_dapm_widgets = ARRAY_SIZE(nau8810_dapm_widgets), |
866 | .dapm_routes = nau8810_dapm_routes, |
867 | .num_dapm_routes = ARRAY_SIZE(nau8810_dapm_routes), |
868 | .suspend_bias_off = 1, |
869 | .idle_bias_on = 1, |
870 | .use_pmdown_time = 1, |
871 | .endianness = 1, |
872 | }; |
873 | |
874 | static int nau8810_i2c_probe(struct i2c_client *i2c) |
875 | { |
876 | struct device *dev = &i2c->dev; |
877 | struct nau8810 *nau8810 = dev_get_platdata(dev); |
878 | |
879 | if (!nau8810) { |
880 | nau8810 = devm_kzalloc(dev, size: sizeof(*nau8810), GFP_KERNEL); |
881 | if (!nau8810) |
882 | return -ENOMEM; |
883 | } |
884 | i2c_set_clientdata(client: i2c, data: nau8810); |
885 | |
886 | nau8810->regmap = devm_regmap_init_i2c(i2c, &nau8810_regmap_config); |
887 | if (IS_ERR(ptr: nau8810->regmap)) |
888 | return PTR_ERR(ptr: nau8810->regmap); |
889 | nau8810->dev = dev; |
890 | |
891 | regmap_write(map: nau8810->regmap, NAU8810_REG_RESET, val: 0x00); |
892 | |
893 | return devm_snd_soc_register_component(dev, |
894 | component_driver: &nau8810_component_driver, dai_drv: &nau8810_dai, num_dai: 1); |
895 | } |
896 | |
897 | static const struct i2c_device_id nau8810_i2c_id[] = { |
898 | { "nau8810" , 0 }, |
899 | { "nau8812" , 0 }, |
900 | { "nau8814" , 0 }, |
901 | { } |
902 | }; |
903 | MODULE_DEVICE_TABLE(i2c, nau8810_i2c_id); |
904 | |
905 | #ifdef CONFIG_OF |
906 | static const struct of_device_id nau8810_of_match[] = { |
907 | { .compatible = "nuvoton,nau8810" , }, |
908 | { .compatible = "nuvoton,nau8812" , }, |
909 | { .compatible = "nuvoton,nau8814" , }, |
910 | { } |
911 | }; |
912 | MODULE_DEVICE_TABLE(of, nau8810_of_match); |
913 | #endif |
914 | |
915 | static struct i2c_driver nau8810_i2c_driver = { |
916 | .driver = { |
917 | .name = "nau8810" , |
918 | .of_match_table = of_match_ptr(nau8810_of_match), |
919 | }, |
920 | .probe = nau8810_i2c_probe, |
921 | .id_table = nau8810_i2c_id, |
922 | }; |
923 | |
924 | module_i2c_driver(nau8810_i2c_driver); |
925 | |
926 | MODULE_DESCRIPTION("ASoC NAU8810 driver" ); |
927 | MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>" ); |
928 | MODULE_LICENSE("GPL v2" ); |
929 | |