1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * es8316.c -- es8316 ALSA SoC audio driver |
4 | * Copyright Everest Semiconductor Co.,Ltd |
5 | * |
6 | * Authors: David Yang <yangxiaohua@everest-semi.com>, |
7 | * Daniel Drake <drake@endlessm.com> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/acpi.h> |
12 | #include <linux/clk.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/i2c.h> |
15 | #include <linux/mod_devicetable.h> |
16 | #include <linux/mutex.h> |
17 | #include <linux/regmap.h> |
18 | #include <sound/pcm.h> |
19 | #include <sound/pcm_params.h> |
20 | #include <sound/soc.h> |
21 | #include <sound/soc-dapm.h> |
22 | #include <sound/tlv.h> |
23 | #include <sound/jack.h> |
24 | #include "es8316.h" |
25 | |
26 | /* In slave mode at single speed, the codec is documented as accepting 5 |
27 | * MCLK/LRCK ratios, but we also add ratio 400, which is commonly used on |
28 | * Intel Cherry Trail platforms (19.2MHz MCLK, 48kHz LRCK). |
29 | */ |
30 | static const unsigned int supported_mclk_lrck_ratios[] = { |
31 | 256, 384, 400, 500, 512, 768, 1024 |
32 | }; |
33 | |
34 | struct es8316_priv { |
35 | struct mutex lock; |
36 | struct clk *mclk; |
37 | struct regmap *regmap; |
38 | struct snd_soc_component *component; |
39 | struct snd_soc_jack *jack; |
40 | int irq; |
41 | unsigned int sysclk; |
42 | unsigned int allowed_rates[ARRAY_SIZE(supported_mclk_lrck_ratios)]; |
43 | struct snd_pcm_hw_constraint_list sysclk_constraints; |
44 | bool jd_inverted; |
45 | }; |
46 | |
47 | /* |
48 | * ES8316 controls |
49 | */ |
50 | static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9600, 50, 1); |
51 | static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9600, 50, 1); |
52 | static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_max_gain_tlv, -650, 150, 0); |
53 | static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_min_gain_tlv, -1200, 150, 0); |
54 | |
55 | static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(alc_target_tlv, |
56 | 0, 10, TLV_DB_SCALE_ITEM(-1650, 150, 0), |
57 | 11, 11, TLV_DB_SCALE_ITEM(-150, 0, 0), |
58 | ); |
59 | |
60 | static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpmixer_gain_tlv, |
61 | 0, 4, TLV_DB_SCALE_ITEM(-1200, 150, 0), |
62 | 8, 11, TLV_DB_SCALE_ITEM(-450, 150, 0), |
63 | ); |
64 | |
65 | static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(adc_pga_gain_tlv, |
66 | 0, 0, TLV_DB_SCALE_ITEM(-350, 0, 0), |
67 | 1, 1, TLV_DB_SCALE_ITEM(0, 0, 0), |
68 | 2, 2, TLV_DB_SCALE_ITEM(250, 0, 0), |
69 | 3, 3, TLV_DB_SCALE_ITEM(450, 0, 0), |
70 | 4, 7, TLV_DB_SCALE_ITEM(700, 300, 0), |
71 | 8, 10, TLV_DB_SCALE_ITEM(1800, 300, 0), |
72 | ); |
73 | |
74 | static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpout_vol_tlv, |
75 | 0, 0, TLV_DB_SCALE_ITEM(-4800, 0, 0), |
76 | 1, 3, TLV_DB_SCALE_ITEM(-2400, 1200, 0), |
77 | ); |
78 | |
79 | static const char * const ng_type_txt[] = |
80 | { "Constant PGA Gain" , "Mute ADC Output" }; |
81 | static const struct soc_enum ng_type = |
82 | SOC_ENUM_SINGLE(ES8316_ADC_ALC_NG, 6, 2, ng_type_txt); |
83 | |
84 | static const char * const adcpol_txt[] = { "Normal" , "Invert" }; |
85 | static const struct soc_enum adcpol = |
86 | SOC_ENUM_SINGLE(ES8316_ADC_MUTE, 1, 2, adcpol_txt); |
87 | static const char *const dacpol_txt[] = |
88 | { "Normal" , "R Invert" , "L Invert" , "L + R Invert" }; |
89 | static const struct soc_enum dacpol = |
90 | SOC_ENUM_SINGLE(ES8316_DAC_SET1, 0, 4, dacpol_txt); |
91 | |
92 | static const struct snd_kcontrol_new es8316_snd_controls[] = { |
93 | SOC_DOUBLE_TLV("Headphone Playback Volume" , ES8316_CPHP_ICAL_VOL, |
94 | 4, 0, 3, 1, hpout_vol_tlv), |
95 | SOC_DOUBLE_TLV("Headphone Mixer Volume" , ES8316_HPMIX_VOL, |
96 | 4, 0, 11, 0, hpmixer_gain_tlv), |
97 | |
98 | SOC_ENUM("Playback Polarity" , dacpol), |
99 | SOC_DOUBLE_R_TLV("DAC Playback Volume" , ES8316_DAC_VOLL, |
100 | ES8316_DAC_VOLR, 0, 0xc0, 1, dac_vol_tlv), |
101 | SOC_SINGLE("DAC Soft Ramp Switch" , ES8316_DAC_SET1, 4, 1, 1), |
102 | SOC_SINGLE("DAC Soft Ramp Rate" , ES8316_DAC_SET1, 2, 4, 0), |
103 | SOC_SINGLE("DAC Notch Filter Switch" , ES8316_DAC_SET2, 6, 1, 0), |
104 | SOC_SINGLE("DAC Double Fs Switch" , ES8316_DAC_SET2, 7, 1, 0), |
105 | SOC_SINGLE("DAC Stereo Enhancement" , ES8316_DAC_SET3, 0, 7, 0), |
106 | SOC_SINGLE("DAC Mono Mix Switch" , ES8316_DAC_SET3, 3, 1, 0), |
107 | |
108 | SOC_ENUM("Capture Polarity" , adcpol), |
109 | SOC_SINGLE("Mic Boost Switch" , ES8316_ADC_D2SEPGA, 0, 1, 0), |
110 | SOC_SINGLE_TLV("ADC Capture Volume" , ES8316_ADC_VOLUME, |
111 | 0, 0xc0, 1, adc_vol_tlv), |
112 | SOC_SINGLE_TLV("ADC PGA Gain Volume" , ES8316_ADC_PGAGAIN, |
113 | 4, 10, 0, adc_pga_gain_tlv), |
114 | SOC_SINGLE("ADC Soft Ramp Switch" , ES8316_ADC_MUTE, 4, 1, 0), |
115 | SOC_SINGLE("ADC Double Fs Switch" , ES8316_ADC_DMIC, 4, 1, 0), |
116 | |
117 | SOC_SINGLE("ALC Capture Switch" , ES8316_ADC_ALC1, 6, 1, 0), |
118 | SOC_SINGLE_TLV("ALC Capture Max Volume" , ES8316_ADC_ALC1, 0, 28, 0, |
119 | alc_max_gain_tlv), |
120 | SOC_SINGLE_TLV("ALC Capture Min Volume" , ES8316_ADC_ALC2, 0, 28, 0, |
121 | alc_min_gain_tlv), |
122 | SOC_SINGLE_TLV("ALC Capture Target Volume" , ES8316_ADC_ALC3, 4, 11, 0, |
123 | alc_target_tlv), |
124 | SOC_SINGLE("ALC Capture Hold Time" , ES8316_ADC_ALC3, 0, 10, 0), |
125 | SOC_SINGLE("ALC Capture Decay Time" , ES8316_ADC_ALC4, 4, 10, 0), |
126 | SOC_SINGLE("ALC Capture Attack Time" , ES8316_ADC_ALC4, 0, 10, 0), |
127 | SOC_SINGLE("ALC Capture Noise Gate Switch" , ES8316_ADC_ALC_NG, |
128 | 5, 1, 0), |
129 | SOC_SINGLE("ALC Capture Noise Gate Threshold" , ES8316_ADC_ALC_NG, |
130 | 0, 31, 0), |
131 | SOC_ENUM("ALC Capture Noise Gate Type" , ng_type), |
132 | }; |
133 | |
134 | /* Analog Input Mux */ |
135 | static const char * const es8316_analog_in_txt[] = { |
136 | "lin1-rin1" , |
137 | "lin2-rin2" , |
138 | "lin1-rin1 with 20db Boost" , |
139 | "lin2-rin2 with 20db Boost" |
140 | }; |
141 | static const unsigned int es8316_analog_in_values[] = { 0, 1, 2, 3 }; |
142 | static const struct soc_enum es8316_analog_input_enum = |
143 | SOC_VALUE_ENUM_SINGLE(ES8316_ADC_PDN_LINSEL, 4, 3, |
144 | ARRAY_SIZE(es8316_analog_in_txt), |
145 | es8316_analog_in_txt, |
146 | es8316_analog_in_values); |
147 | static const struct snd_kcontrol_new es8316_analog_in_mux_controls = |
148 | SOC_DAPM_ENUM("Route" , es8316_analog_input_enum); |
149 | |
150 | static const char * const es8316_dmic_txt[] = { |
151 | "dmic disable" , |
152 | "dmic data at high level" , |
153 | "dmic data at low level" , |
154 | }; |
155 | static const unsigned int es8316_dmic_values[] = { 0, 2, 3 }; |
156 | static const struct soc_enum es8316_dmic_src_enum = |
157 | SOC_VALUE_ENUM_SINGLE(ES8316_ADC_DMIC, 0, 3, |
158 | ARRAY_SIZE(es8316_dmic_txt), |
159 | es8316_dmic_txt, |
160 | es8316_dmic_values); |
161 | static const struct snd_kcontrol_new es8316_dmic_src_controls = |
162 | SOC_DAPM_ENUM("Route" , es8316_dmic_src_enum); |
163 | |
164 | /* hp mixer mux */ |
165 | static const char * const es8316_hpmux_texts[] = { |
166 | "lin1-rin1" , |
167 | "lin2-rin2" , |
168 | "lin-rin with Boost" , |
169 | "lin-rin with Boost and PGA" |
170 | }; |
171 | |
172 | static SOC_ENUM_SINGLE_DECL(es8316_left_hpmux_enum, ES8316_HPMIX_SEL, |
173 | 4, es8316_hpmux_texts); |
174 | |
175 | static const struct snd_kcontrol_new es8316_left_hpmux_controls = |
176 | SOC_DAPM_ENUM("Route" , es8316_left_hpmux_enum); |
177 | |
178 | static SOC_ENUM_SINGLE_DECL(es8316_right_hpmux_enum, ES8316_HPMIX_SEL, |
179 | 0, es8316_hpmux_texts); |
180 | |
181 | static const struct snd_kcontrol_new es8316_right_hpmux_controls = |
182 | SOC_DAPM_ENUM("Route" , es8316_right_hpmux_enum); |
183 | |
184 | /* headphone Output Mixer */ |
185 | static const struct snd_kcontrol_new es8316_out_left_mix[] = { |
186 | SOC_DAPM_SINGLE("LLIN Switch" , ES8316_HPMIX_SWITCH, 6, 1, 0), |
187 | SOC_DAPM_SINGLE("Left DAC Switch" , ES8316_HPMIX_SWITCH, 7, 1, 0), |
188 | }; |
189 | static const struct snd_kcontrol_new es8316_out_right_mix[] = { |
190 | SOC_DAPM_SINGLE("RLIN Switch" , ES8316_HPMIX_SWITCH, 2, 1, 0), |
191 | SOC_DAPM_SINGLE("Right DAC Switch" , ES8316_HPMIX_SWITCH, 3, 1, 0), |
192 | }; |
193 | |
194 | /* DAC data source mux */ |
195 | static const char * const es8316_dacsrc_texts[] = { |
196 | "LDATA TO LDAC, RDATA TO RDAC" , |
197 | "LDATA TO LDAC, LDATA TO RDAC" , |
198 | "RDATA TO LDAC, RDATA TO RDAC" , |
199 | "RDATA TO LDAC, LDATA TO RDAC" , |
200 | }; |
201 | |
202 | static SOC_ENUM_SINGLE_DECL(es8316_dacsrc_mux_enum, ES8316_DAC_SET1, |
203 | 6, es8316_dacsrc_texts); |
204 | |
205 | static const struct snd_kcontrol_new es8316_dacsrc_mux_controls = |
206 | SOC_DAPM_ENUM("Route" , es8316_dacsrc_mux_enum); |
207 | |
208 | static const struct snd_soc_dapm_widget es8316_dapm_widgets[] = { |
209 | SND_SOC_DAPM_SUPPLY("Bias" , ES8316_SYS_PDN, 3, 1, NULL, 0), |
210 | SND_SOC_DAPM_SUPPLY("Analog power" , ES8316_SYS_PDN, 4, 1, NULL, 0), |
211 | SND_SOC_DAPM_SUPPLY("Mic Bias" , ES8316_SYS_PDN, 5, 1, NULL, 0), |
212 | |
213 | SND_SOC_DAPM_INPUT("DMIC" ), |
214 | SND_SOC_DAPM_INPUT("MIC1" ), |
215 | SND_SOC_DAPM_INPUT("MIC2" ), |
216 | |
217 | /* Input Mux */ |
218 | SND_SOC_DAPM_MUX("Differential Mux" , SND_SOC_NOPM, 0, 0, |
219 | &es8316_analog_in_mux_controls), |
220 | |
221 | SND_SOC_DAPM_SUPPLY("ADC Vref" , ES8316_SYS_PDN, 1, 1, NULL, 0), |
222 | SND_SOC_DAPM_SUPPLY("ADC bias" , ES8316_SYS_PDN, 2, 1, NULL, 0), |
223 | SND_SOC_DAPM_SUPPLY("ADC Clock" , ES8316_CLKMGR_CLKSW, 3, 0, NULL, 0), |
224 | SND_SOC_DAPM_PGA("Line input PGA" , ES8316_ADC_PDN_LINSEL, |
225 | 7, 1, NULL, 0), |
226 | SND_SOC_DAPM_ADC("Mono ADC" , NULL, ES8316_ADC_PDN_LINSEL, 6, 1), |
227 | SND_SOC_DAPM_MUX("Digital Mic Mux" , SND_SOC_NOPM, 0, 0, |
228 | &es8316_dmic_src_controls), |
229 | |
230 | /* Digital Interface */ |
231 | SND_SOC_DAPM_AIF_OUT("I2S OUT" , "I2S1 Capture" , 1, |
232 | ES8316_SERDATA_ADC, 6, 1), |
233 | SND_SOC_DAPM_AIF_IN("I2S IN" , "I2S1 Playback" , 0, |
234 | SND_SOC_NOPM, 0, 0), |
235 | |
236 | SND_SOC_DAPM_MUX("DAC Source Mux" , SND_SOC_NOPM, 0, 0, |
237 | &es8316_dacsrc_mux_controls), |
238 | |
239 | SND_SOC_DAPM_SUPPLY("DAC Vref" , ES8316_SYS_PDN, 0, 1, NULL, 0), |
240 | SND_SOC_DAPM_SUPPLY("DAC Clock" , ES8316_CLKMGR_CLKSW, 2, 0, NULL, 0), |
241 | SND_SOC_DAPM_DAC("Right DAC" , NULL, ES8316_DAC_PDN, 0, 1), |
242 | SND_SOC_DAPM_DAC("Left DAC" , NULL, ES8316_DAC_PDN, 4, 1), |
243 | |
244 | /* Headphone Output Side */ |
245 | SND_SOC_DAPM_MUX("Left Headphone Mux" , SND_SOC_NOPM, 0, 0, |
246 | &es8316_left_hpmux_controls), |
247 | SND_SOC_DAPM_MUX("Right Headphone Mux" , SND_SOC_NOPM, 0, 0, |
248 | &es8316_right_hpmux_controls), |
249 | SND_SOC_DAPM_MIXER("Left Headphone Mixer" , ES8316_HPMIX_PDN, |
250 | 5, 1, &es8316_out_left_mix[0], |
251 | ARRAY_SIZE(es8316_out_left_mix)), |
252 | SND_SOC_DAPM_MIXER("Right Headphone Mixer" , ES8316_HPMIX_PDN, |
253 | 1, 1, &es8316_out_right_mix[0], |
254 | ARRAY_SIZE(es8316_out_right_mix)), |
255 | SND_SOC_DAPM_PGA("Left Headphone Mixer Out" , ES8316_HPMIX_PDN, |
256 | 4, 1, NULL, 0), |
257 | SND_SOC_DAPM_PGA("Right Headphone Mixer Out" , ES8316_HPMIX_PDN, |
258 | 0, 1, NULL, 0), |
259 | |
260 | SND_SOC_DAPM_OUT_DRV("Left Headphone Charge Pump" , ES8316_CPHP_OUTEN, |
261 | 6, 0, NULL, 0), |
262 | SND_SOC_DAPM_OUT_DRV("Right Headphone Charge Pump" , ES8316_CPHP_OUTEN, |
263 | 2, 0, NULL, 0), |
264 | SND_SOC_DAPM_SUPPLY("Headphone Charge Pump" , ES8316_CPHP_PDN2, |
265 | 5, 1, NULL, 0), |
266 | SND_SOC_DAPM_SUPPLY("Headphone Charge Pump Clock" , ES8316_CLKMGR_CLKSW, |
267 | 4, 0, NULL, 0), |
268 | |
269 | SND_SOC_DAPM_OUT_DRV("Left Headphone Driver" , ES8316_CPHP_OUTEN, |
270 | 5, 0, NULL, 0), |
271 | SND_SOC_DAPM_OUT_DRV("Right Headphone Driver" , ES8316_CPHP_OUTEN, |
272 | 1, 0, NULL, 0), |
273 | SND_SOC_DAPM_SUPPLY("Headphone Out" , ES8316_CPHP_PDN1, 2, 1, NULL, 0), |
274 | |
275 | /* pdn_Lical and pdn_Rical bits are documented as Reserved, but must |
276 | * be explicitly unset in order to enable HP output |
277 | */ |
278 | SND_SOC_DAPM_SUPPLY("Left Headphone ical" , ES8316_CPHP_ICAL_VOL, |
279 | 7, 1, NULL, 0), |
280 | SND_SOC_DAPM_SUPPLY("Right Headphone ical" , ES8316_CPHP_ICAL_VOL, |
281 | 3, 1, NULL, 0), |
282 | |
283 | SND_SOC_DAPM_OUTPUT("HPOL" ), |
284 | SND_SOC_DAPM_OUTPUT("HPOR" ), |
285 | }; |
286 | |
287 | static const struct snd_soc_dapm_route es8316_dapm_routes[] = { |
288 | /* Recording */ |
289 | {"MIC1" , NULL, "Mic Bias" }, |
290 | {"MIC2" , NULL, "Mic Bias" }, |
291 | {"MIC1" , NULL, "Bias" }, |
292 | {"MIC2" , NULL, "Bias" }, |
293 | {"MIC1" , NULL, "Analog power" }, |
294 | {"MIC2" , NULL, "Analog power" }, |
295 | |
296 | {"Differential Mux" , "lin1-rin1" , "MIC1" }, |
297 | {"Differential Mux" , "lin2-rin2" , "MIC2" }, |
298 | {"Line input PGA" , NULL, "Differential Mux" }, |
299 | |
300 | {"Mono ADC" , NULL, "ADC Clock" }, |
301 | {"Mono ADC" , NULL, "ADC Vref" }, |
302 | {"Mono ADC" , NULL, "ADC bias" }, |
303 | {"Mono ADC" , NULL, "Line input PGA" }, |
304 | |
305 | /* It's not clear why, but to avoid recording only silence, |
306 | * the DAC clock must be running for the ADC to work. |
307 | */ |
308 | {"Mono ADC" , NULL, "DAC Clock" }, |
309 | |
310 | {"Digital Mic Mux" , "dmic disable" , "Mono ADC" }, |
311 | |
312 | {"I2S OUT" , NULL, "Digital Mic Mux" }, |
313 | |
314 | /* Playback */ |
315 | {"DAC Source Mux" , "LDATA TO LDAC, RDATA TO RDAC" , "I2S IN" }, |
316 | |
317 | {"Left DAC" , NULL, "DAC Clock" }, |
318 | {"Right DAC" , NULL, "DAC Clock" }, |
319 | |
320 | {"Left DAC" , NULL, "DAC Vref" }, |
321 | {"Right DAC" , NULL, "DAC Vref" }, |
322 | |
323 | {"Left DAC" , NULL, "DAC Source Mux" }, |
324 | {"Right DAC" , NULL, "DAC Source Mux" }, |
325 | |
326 | {"Left Headphone Mux" , "lin-rin with Boost and PGA" , "Line input PGA" }, |
327 | {"Right Headphone Mux" , "lin-rin with Boost and PGA" , "Line input PGA" }, |
328 | |
329 | {"Left Headphone Mixer" , "LLIN Switch" , "Left Headphone Mux" }, |
330 | {"Left Headphone Mixer" , "Left DAC Switch" , "Left DAC" }, |
331 | |
332 | {"Right Headphone Mixer" , "RLIN Switch" , "Right Headphone Mux" }, |
333 | {"Right Headphone Mixer" , "Right DAC Switch" , "Right DAC" }, |
334 | |
335 | {"Left Headphone Mixer Out" , NULL, "Left Headphone Mixer" }, |
336 | {"Right Headphone Mixer Out" , NULL, "Right Headphone Mixer" }, |
337 | |
338 | {"Left Headphone Charge Pump" , NULL, "Left Headphone Mixer Out" }, |
339 | {"Right Headphone Charge Pump" , NULL, "Right Headphone Mixer Out" }, |
340 | |
341 | {"Left Headphone Charge Pump" , NULL, "Headphone Charge Pump" }, |
342 | {"Right Headphone Charge Pump" , NULL, "Headphone Charge Pump" }, |
343 | |
344 | {"Left Headphone Charge Pump" , NULL, "Headphone Charge Pump Clock" }, |
345 | {"Right Headphone Charge Pump" , NULL, "Headphone Charge Pump Clock" }, |
346 | |
347 | {"Left Headphone Driver" , NULL, "Left Headphone Charge Pump" }, |
348 | {"Right Headphone Driver" , NULL, "Right Headphone Charge Pump" }, |
349 | |
350 | {"HPOL" , NULL, "Left Headphone Driver" }, |
351 | {"HPOR" , NULL, "Right Headphone Driver" }, |
352 | |
353 | {"HPOL" , NULL, "Left Headphone ical" }, |
354 | {"HPOR" , NULL, "Right Headphone ical" }, |
355 | |
356 | {"Headphone Out" , NULL, "Bias" }, |
357 | {"Headphone Out" , NULL, "Analog power" }, |
358 | {"HPOL" , NULL, "Headphone Out" }, |
359 | {"HPOR" , NULL, "Headphone Out" }, |
360 | }; |
361 | |
362 | static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
363 | int clk_id, unsigned int freq, int dir) |
364 | { |
365 | struct snd_soc_component *component = codec_dai->component; |
366 | struct es8316_priv *es8316 = snd_soc_component_get_drvdata(c: component); |
367 | int i, ret; |
368 | int count = 0; |
369 | |
370 | es8316->sysclk = freq; |
371 | es8316->sysclk_constraints.list = NULL; |
372 | es8316->sysclk_constraints.count = 0; |
373 | |
374 | if (freq == 0) |
375 | return 0; |
376 | |
377 | ret = clk_set_rate(clk: es8316->mclk, rate: freq); |
378 | if (ret) |
379 | return ret; |
380 | |
381 | /* Limit supported sample rates to ones that can be autodetected |
382 | * by the codec running in slave mode. |
383 | */ |
384 | for (i = 0; i < ARRAY_SIZE(supported_mclk_lrck_ratios); i++) { |
385 | const unsigned int ratio = supported_mclk_lrck_ratios[i]; |
386 | |
387 | if (freq % ratio == 0) |
388 | es8316->allowed_rates[count++] = freq / ratio; |
389 | } |
390 | |
391 | if (count) { |
392 | es8316->sysclk_constraints.list = es8316->allowed_rates; |
393 | es8316->sysclk_constraints.count = count; |
394 | } |
395 | |
396 | return 0; |
397 | } |
398 | |
399 | static int es8316_set_dai_fmt(struct snd_soc_dai *codec_dai, |
400 | unsigned int fmt) |
401 | { |
402 | struct snd_soc_component *component = codec_dai->component; |
403 | u8 serdata1 = 0; |
404 | u8 serdata2 = 0; |
405 | u8 clksw; |
406 | u8 mask; |
407 | |
408 | if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBP_CFP) |
409 | serdata1 |= ES8316_SERDATA1_MASTER; |
410 | |
411 | if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) { |
412 | dev_err(component->dev, "Codec driver only supports I2S format\n" ); |
413 | return -EINVAL; |
414 | } |
415 | |
416 | /* Clock inversion */ |
417 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
418 | case SND_SOC_DAIFMT_NB_NF: |
419 | break; |
420 | case SND_SOC_DAIFMT_IB_IF: |
421 | serdata1 |= ES8316_SERDATA1_BCLK_INV; |
422 | serdata2 |= ES8316_SERDATA2_ADCLRP; |
423 | break; |
424 | case SND_SOC_DAIFMT_IB_NF: |
425 | serdata1 |= ES8316_SERDATA1_BCLK_INV; |
426 | break; |
427 | case SND_SOC_DAIFMT_NB_IF: |
428 | serdata2 |= ES8316_SERDATA2_ADCLRP; |
429 | break; |
430 | default: |
431 | return -EINVAL; |
432 | } |
433 | |
434 | mask = ES8316_SERDATA1_MASTER | ES8316_SERDATA1_BCLK_INV; |
435 | snd_soc_component_update_bits(component, ES8316_SERDATA1, mask, val: serdata1); |
436 | |
437 | mask = ES8316_SERDATA2_FMT_MASK | ES8316_SERDATA2_ADCLRP; |
438 | snd_soc_component_update_bits(component, ES8316_SERDATA_ADC, mask, val: serdata2); |
439 | snd_soc_component_update_bits(component, ES8316_SERDATA_DAC, mask, val: serdata2); |
440 | |
441 | /* Enable BCLK and MCLK inputs in slave mode */ |
442 | clksw = ES8316_CLKMGR_CLKSW_MCLK_ON | ES8316_CLKMGR_CLKSW_BCLK_ON; |
443 | snd_soc_component_update_bits(component, ES8316_CLKMGR_CLKSW, mask: clksw, val: clksw); |
444 | |
445 | return 0; |
446 | } |
447 | |
448 | static int es8316_pcm_startup(struct snd_pcm_substream *substream, |
449 | struct snd_soc_dai *dai) |
450 | { |
451 | struct snd_soc_component *component = dai->component; |
452 | struct es8316_priv *es8316 = snd_soc_component_get_drvdata(c: component); |
453 | |
454 | if (es8316->sysclk_constraints.list) |
455 | snd_pcm_hw_constraint_list(runtime: substream->runtime, cond: 0, |
456 | SNDRV_PCM_HW_PARAM_RATE, |
457 | l: &es8316->sysclk_constraints); |
458 | |
459 | return 0; |
460 | } |
461 | |
462 | static int es8316_pcm_hw_params(struct snd_pcm_substream *substream, |
463 | struct snd_pcm_hw_params *params, |
464 | struct snd_soc_dai *dai) |
465 | { |
466 | struct snd_soc_component *component = dai->component; |
467 | struct es8316_priv *es8316 = snd_soc_component_get_drvdata(c: component); |
468 | u8 wordlen = 0; |
469 | u8 bclk_divider; |
470 | u16 lrck_divider; |
471 | int i; |
472 | unsigned int clk = es8316->sysclk / 2; |
473 | bool clk_valid = false; |
474 | |
475 | /* We will start with halved sysclk and see if we can use it |
476 | * for proper clocking. This is to minimise the risk of running |
477 | * the CODEC with a too high frequency. We have an SKU where |
478 | * the sysclk frequency is 48Mhz and this causes the sound to be |
479 | * sped up. If we can run with a halved sysclk, we will use it, |
480 | * if we can't use it, then full sysclk will be used. |
481 | */ |
482 | do { |
483 | /* Validate supported sample rates that are autodetected from MCLK */ |
484 | for (i = 0; i < ARRAY_SIZE(supported_mclk_lrck_ratios); i++) { |
485 | const unsigned int ratio = supported_mclk_lrck_ratios[i]; |
486 | |
487 | if (clk % ratio != 0) |
488 | continue; |
489 | if (clk / ratio == params_rate(p: params)) |
490 | break; |
491 | } |
492 | if (i == ARRAY_SIZE(supported_mclk_lrck_ratios)) { |
493 | if (clk == es8316->sysclk) |
494 | return -EINVAL; |
495 | clk = es8316->sysclk; |
496 | } else { |
497 | clk_valid = true; |
498 | } |
499 | } while (!clk_valid); |
500 | |
501 | if (clk != es8316->sysclk) { |
502 | snd_soc_component_update_bits(component, ES8316_CLKMGR_CLKSW, |
503 | ES8316_CLKMGR_CLKSW_MCLK_DIV, |
504 | ES8316_CLKMGR_CLKSW_MCLK_DIV); |
505 | } |
506 | |
507 | lrck_divider = clk / params_rate(p: params); |
508 | bclk_divider = lrck_divider / 4; |
509 | switch (params_format(p: params)) { |
510 | case SNDRV_PCM_FORMAT_S16_LE: |
511 | wordlen = ES8316_SERDATA2_LEN_16; |
512 | bclk_divider /= 16; |
513 | break; |
514 | case SNDRV_PCM_FORMAT_S20_3LE: |
515 | wordlen = ES8316_SERDATA2_LEN_20; |
516 | bclk_divider /= 20; |
517 | break; |
518 | case SNDRV_PCM_FORMAT_S24_LE: |
519 | case SNDRV_PCM_FORMAT_S24_3LE: |
520 | wordlen = ES8316_SERDATA2_LEN_24; |
521 | bclk_divider /= 24; |
522 | break; |
523 | case SNDRV_PCM_FORMAT_S32_LE: |
524 | wordlen = ES8316_SERDATA2_LEN_32; |
525 | bclk_divider /= 32; |
526 | break; |
527 | default: |
528 | return -EINVAL; |
529 | } |
530 | |
531 | snd_soc_component_update_bits(component, ES8316_SERDATA_DAC, |
532 | ES8316_SERDATA2_LEN_MASK, val: wordlen); |
533 | snd_soc_component_update_bits(component, ES8316_SERDATA_ADC, |
534 | ES8316_SERDATA2_LEN_MASK, val: wordlen); |
535 | snd_soc_component_update_bits(component, ES8316_SERDATA1, mask: 0x1f, val: bclk_divider); |
536 | snd_soc_component_update_bits(component, ES8316_CLKMGR_ADCDIV1, mask: 0x0f, val: lrck_divider >> 8); |
537 | snd_soc_component_update_bits(component, ES8316_CLKMGR_ADCDIV2, mask: 0xff, val: lrck_divider & 0xff); |
538 | snd_soc_component_update_bits(component, ES8316_CLKMGR_DACDIV1, mask: 0x0f, val: lrck_divider >> 8); |
539 | snd_soc_component_update_bits(component, ES8316_CLKMGR_DACDIV2, mask: 0xff, val: lrck_divider & 0xff); |
540 | return 0; |
541 | } |
542 | |
543 | static int es8316_mute(struct snd_soc_dai *dai, int mute, int direction) |
544 | { |
545 | snd_soc_component_update_bits(component: dai->component, ES8316_DAC_SET1, mask: 0x20, |
546 | val: mute ? 0x20 : 0); |
547 | return 0; |
548 | } |
549 | |
550 | #define ES8316_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ |
551 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) |
552 | |
553 | static const struct snd_soc_dai_ops es8316_ops = { |
554 | .startup = es8316_pcm_startup, |
555 | .hw_params = es8316_pcm_hw_params, |
556 | .set_fmt = es8316_set_dai_fmt, |
557 | .set_sysclk = es8316_set_dai_sysclk, |
558 | .mute_stream = es8316_mute, |
559 | .no_capture_mute = 1, |
560 | }; |
561 | |
562 | static struct snd_soc_dai_driver es8316_dai = { |
563 | .name = "ES8316 HiFi" , |
564 | .playback = { |
565 | .stream_name = "Playback" , |
566 | .channels_min = 1, |
567 | .channels_max = 2, |
568 | .rates = SNDRV_PCM_RATE_8000_48000, |
569 | .formats = ES8316_FORMATS, |
570 | }, |
571 | .capture = { |
572 | .stream_name = "Capture" , |
573 | .channels_min = 1, |
574 | .channels_max = 2, |
575 | .rates = SNDRV_PCM_RATE_8000_48000, |
576 | .formats = ES8316_FORMATS, |
577 | }, |
578 | .ops = &es8316_ops, |
579 | .symmetric_rate = 1, |
580 | }; |
581 | |
582 | static void es8316_enable_micbias_for_mic_gnd_short_detect( |
583 | struct snd_soc_component *component) |
584 | { |
585 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
586 | |
587 | snd_soc_dapm_mutex_lock(dapm); |
588 | snd_soc_dapm_force_enable_pin_unlocked(dapm, pin: "Bias" ); |
589 | snd_soc_dapm_force_enable_pin_unlocked(dapm, pin: "Analog power" ); |
590 | snd_soc_dapm_force_enable_pin_unlocked(dapm, pin: "Mic Bias" ); |
591 | snd_soc_dapm_sync_unlocked(dapm); |
592 | snd_soc_dapm_mutex_unlock(dapm); |
593 | |
594 | msleep(msecs: 20); |
595 | } |
596 | |
597 | static void es8316_disable_micbias_for_mic_gnd_short_detect( |
598 | struct snd_soc_component *component) |
599 | { |
600 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
601 | |
602 | snd_soc_dapm_mutex_lock(dapm); |
603 | snd_soc_dapm_disable_pin_unlocked(dapm, pin: "Mic Bias" ); |
604 | snd_soc_dapm_disable_pin_unlocked(dapm, pin: "Analog power" ); |
605 | snd_soc_dapm_disable_pin_unlocked(dapm, pin: "Bias" ); |
606 | snd_soc_dapm_sync_unlocked(dapm); |
607 | snd_soc_dapm_mutex_unlock(dapm); |
608 | } |
609 | |
610 | static irqreturn_t es8316_irq(int irq, void *data) |
611 | { |
612 | struct es8316_priv *es8316 = data; |
613 | struct snd_soc_component *comp = es8316->component; |
614 | unsigned int flags; |
615 | |
616 | mutex_lock(&es8316->lock); |
617 | |
618 | regmap_read(map: es8316->regmap, ES8316_GPIO_FLAG, val: &flags); |
619 | if (flags == 0x00) |
620 | goto out; /* Powered-down / reset */ |
621 | |
622 | /* Catch spurious IRQ before set_jack is called */ |
623 | if (!es8316->jack) |
624 | goto out; |
625 | |
626 | if (es8316->jd_inverted) |
627 | flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED; |
628 | |
629 | dev_dbg(comp->dev, "gpio flags %#04x\n" , flags); |
630 | if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) { |
631 | /* Jack removed, or spurious IRQ? */ |
632 | if (es8316->jack->status & SND_JACK_MICROPHONE) |
633 | es8316_disable_micbias_for_mic_gnd_short_detect(component: comp); |
634 | |
635 | if (es8316->jack->status & SND_JACK_HEADPHONE) { |
636 | snd_soc_jack_report(jack: es8316->jack, status: 0, |
637 | mask: SND_JACK_HEADSET | SND_JACK_BTN_0); |
638 | dev_dbg(comp->dev, "jack unplugged\n" ); |
639 | } |
640 | } else if (!(es8316->jack->status & SND_JACK_HEADPHONE)) { |
641 | /* Jack inserted, determine type */ |
642 | es8316_enable_micbias_for_mic_gnd_short_detect(component: comp); |
643 | regmap_read(map: es8316->regmap, ES8316_GPIO_FLAG, val: &flags); |
644 | if (es8316->jd_inverted) |
645 | flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED; |
646 | dev_dbg(comp->dev, "gpio flags %#04x\n" , flags); |
647 | if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) { |
648 | /* Jack unplugged underneath us */ |
649 | es8316_disable_micbias_for_mic_gnd_short_detect(component: comp); |
650 | } else if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) { |
651 | /* Open, headset */ |
652 | snd_soc_jack_report(jack: es8316->jack, |
653 | status: SND_JACK_HEADSET, |
654 | mask: SND_JACK_HEADSET); |
655 | /* Keep mic-gnd-short detection on for button press */ |
656 | } else { |
657 | /* Shorted, headphones */ |
658 | snd_soc_jack_report(jack: es8316->jack, |
659 | status: SND_JACK_HEADPHONE, |
660 | mask: SND_JACK_HEADSET); |
661 | /* No longer need mic-gnd-short detection */ |
662 | es8316_disable_micbias_for_mic_gnd_short_detect(component: comp); |
663 | } |
664 | } else if (es8316->jack->status & SND_JACK_MICROPHONE) { |
665 | /* Interrupt while jack inserted, report button state */ |
666 | if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) { |
667 | /* Open, button release */ |
668 | snd_soc_jack_report(jack: es8316->jack, status: 0, mask: SND_JACK_BTN_0); |
669 | } else { |
670 | /* Short, button press */ |
671 | snd_soc_jack_report(jack: es8316->jack, |
672 | status: SND_JACK_BTN_0, |
673 | mask: SND_JACK_BTN_0); |
674 | } |
675 | } |
676 | |
677 | out: |
678 | mutex_unlock(lock: &es8316->lock); |
679 | return IRQ_HANDLED; |
680 | } |
681 | |
682 | static void es8316_enable_jack_detect(struct snd_soc_component *component, |
683 | struct snd_soc_jack *jack) |
684 | { |
685 | struct es8316_priv *es8316 = snd_soc_component_get_drvdata(c: component); |
686 | |
687 | /* |
688 | * Init es8316->jd_inverted here and not in the probe, as we cannot |
689 | * guarantee that the bytchr-es8316 driver, which might set this |
690 | * property, will probe before us. |
691 | */ |
692 | es8316->jd_inverted = device_property_read_bool(dev: component->dev, |
693 | propname: "everest,jack-detect-inverted" ); |
694 | |
695 | mutex_lock(&es8316->lock); |
696 | |
697 | es8316->jack = jack; |
698 | |
699 | if (es8316->jack->status & SND_JACK_MICROPHONE) |
700 | es8316_enable_micbias_for_mic_gnd_short_detect(component); |
701 | |
702 | snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, |
703 | ES8316_GPIO_ENABLE_INTERRUPT, |
704 | ES8316_GPIO_ENABLE_INTERRUPT); |
705 | |
706 | mutex_unlock(lock: &es8316->lock); |
707 | |
708 | /* Enable irq and sync initial jack state */ |
709 | enable_irq(irq: es8316->irq); |
710 | es8316_irq(irq: es8316->irq, data: es8316); |
711 | } |
712 | |
713 | static void es8316_disable_jack_detect(struct snd_soc_component *component) |
714 | { |
715 | struct es8316_priv *es8316 = snd_soc_component_get_drvdata(c: component); |
716 | |
717 | if (!es8316->jack) |
718 | return; /* Already disabled (or never enabled) */ |
719 | |
720 | disable_irq(irq: es8316->irq); |
721 | |
722 | mutex_lock(&es8316->lock); |
723 | |
724 | snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, |
725 | ES8316_GPIO_ENABLE_INTERRUPT, val: 0); |
726 | |
727 | if (es8316->jack->status & SND_JACK_MICROPHONE) { |
728 | es8316_disable_micbias_for_mic_gnd_short_detect(component); |
729 | snd_soc_jack_report(jack: es8316->jack, status: 0, mask: SND_JACK_BTN_0); |
730 | } |
731 | |
732 | es8316->jack = NULL; |
733 | |
734 | mutex_unlock(lock: &es8316->lock); |
735 | } |
736 | |
737 | static int es8316_set_jack(struct snd_soc_component *component, |
738 | struct snd_soc_jack *jack, void *data) |
739 | { |
740 | if (jack) |
741 | es8316_enable_jack_detect(component, jack); |
742 | else |
743 | es8316_disable_jack_detect(component); |
744 | |
745 | return 0; |
746 | } |
747 | |
748 | static int es8316_probe(struct snd_soc_component *component) |
749 | { |
750 | struct es8316_priv *es8316 = snd_soc_component_get_drvdata(c: component); |
751 | int ret; |
752 | |
753 | es8316->component = component; |
754 | |
755 | es8316->mclk = devm_clk_get_optional(dev: component->dev, id: "mclk" ); |
756 | if (IS_ERR(ptr: es8316->mclk)) { |
757 | dev_err(component->dev, "unable to get mclk\n" ); |
758 | return PTR_ERR(ptr: es8316->mclk); |
759 | } |
760 | if (!es8316->mclk) |
761 | dev_warn(component->dev, "assuming static mclk\n" ); |
762 | |
763 | ret = clk_prepare_enable(clk: es8316->mclk); |
764 | if (ret) { |
765 | dev_err(component->dev, "unable to enable mclk\n" ); |
766 | return ret; |
767 | } |
768 | |
769 | /* Reset codec and enable current state machine */ |
770 | snd_soc_component_write(component, ES8316_RESET, val: 0x3f); |
771 | usleep_range(min: 5000, max: 5500); |
772 | snd_soc_component_write(component, ES8316_RESET, ES8316_RESET_CSM_ON); |
773 | msleep(msecs: 30); |
774 | |
775 | /* |
776 | * Documentation is unclear, but this value from the vendor driver is |
777 | * needed otherwise audio output is silent. |
778 | */ |
779 | snd_soc_component_write(component, ES8316_SYS_VMIDSEL, val: 0xff); |
780 | |
781 | /* |
782 | * Documentation for this register is unclear and incomplete, |
783 | * but here is a vendor-provided value that improves volume |
784 | * and quality for Intel CHT platforms. |
785 | */ |
786 | snd_soc_component_write(component, ES8316_CLKMGR_ADCOSR, val: 0x32); |
787 | |
788 | return 0; |
789 | } |
790 | |
791 | static void es8316_remove(struct snd_soc_component *component) |
792 | { |
793 | struct es8316_priv *es8316 = snd_soc_component_get_drvdata(c: component); |
794 | |
795 | clk_disable_unprepare(clk: es8316->mclk); |
796 | } |
797 | |
798 | static int es8316_resume(struct snd_soc_component *component) |
799 | { |
800 | struct es8316_priv *es8316 = snd_soc_component_get_drvdata(c: component); |
801 | |
802 | regcache_cache_only(map: es8316->regmap, enable: false); |
803 | regcache_sync(map: es8316->regmap); |
804 | |
805 | return 0; |
806 | } |
807 | |
808 | static int es8316_suspend(struct snd_soc_component *component) |
809 | { |
810 | struct es8316_priv *es8316 = snd_soc_component_get_drvdata(c: component); |
811 | |
812 | regcache_cache_only(map: es8316->regmap, enable: true); |
813 | regcache_mark_dirty(map: es8316->regmap); |
814 | |
815 | return 0; |
816 | } |
817 | |
818 | static const struct snd_soc_component_driver soc_component_dev_es8316 = { |
819 | .probe = es8316_probe, |
820 | .remove = es8316_remove, |
821 | .resume = es8316_resume, |
822 | .suspend = es8316_suspend, |
823 | .set_jack = es8316_set_jack, |
824 | .controls = es8316_snd_controls, |
825 | .num_controls = ARRAY_SIZE(es8316_snd_controls), |
826 | .dapm_widgets = es8316_dapm_widgets, |
827 | .num_dapm_widgets = ARRAY_SIZE(es8316_dapm_widgets), |
828 | .dapm_routes = es8316_dapm_routes, |
829 | .num_dapm_routes = ARRAY_SIZE(es8316_dapm_routes), |
830 | .use_pmdown_time = 1, |
831 | .endianness = 1, |
832 | }; |
833 | |
834 | static bool es8316_volatile_reg(struct device *dev, unsigned int reg) |
835 | { |
836 | switch (reg) { |
837 | case ES8316_GPIO_FLAG: |
838 | return true; |
839 | default: |
840 | return false; |
841 | } |
842 | } |
843 | |
844 | static const struct regmap_config es8316_regmap = { |
845 | .reg_bits = 8, |
846 | .val_bits = 8, |
847 | .use_single_read = true, |
848 | .use_single_write = true, |
849 | .max_register = 0x53, |
850 | .volatile_reg = es8316_volatile_reg, |
851 | .cache_type = REGCACHE_MAPLE, |
852 | }; |
853 | |
854 | static int es8316_i2c_probe(struct i2c_client *i2c_client) |
855 | { |
856 | struct device *dev = &i2c_client->dev; |
857 | struct es8316_priv *es8316; |
858 | int ret; |
859 | |
860 | es8316 = devm_kzalloc(dev: &i2c_client->dev, size: sizeof(struct es8316_priv), |
861 | GFP_KERNEL); |
862 | if (es8316 == NULL) |
863 | return -ENOMEM; |
864 | |
865 | i2c_set_clientdata(client: i2c_client, data: es8316); |
866 | |
867 | es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap); |
868 | if (IS_ERR(ptr: es8316->regmap)) |
869 | return PTR_ERR(ptr: es8316->regmap); |
870 | |
871 | es8316->irq = i2c_client->irq; |
872 | mutex_init(&es8316->lock); |
873 | |
874 | if (es8316->irq > 0) { |
875 | ret = devm_request_threaded_irq(dev, irq: es8316->irq, NULL, thread_fn: es8316_irq, |
876 | IRQF_TRIGGER_HIGH | IRQF_ONESHOT | IRQF_NO_AUTOEN, |
877 | devname: "es8316" , dev_id: es8316); |
878 | if (ret) { |
879 | dev_warn(dev, "Failed to get IRQ %d: %d\n" , es8316->irq, ret); |
880 | es8316->irq = -ENXIO; |
881 | } |
882 | } |
883 | |
884 | return devm_snd_soc_register_component(dev: &i2c_client->dev, |
885 | component_driver: &soc_component_dev_es8316, |
886 | dai_drv: &es8316_dai, num_dai: 1); |
887 | } |
888 | |
889 | static const struct i2c_device_id es8316_i2c_id[] = { |
890 | {"es8316" , 0 }, |
891 | {} |
892 | }; |
893 | MODULE_DEVICE_TABLE(i2c, es8316_i2c_id); |
894 | |
895 | #ifdef CONFIG_OF |
896 | static const struct of_device_id es8316_of_match[] = { |
897 | { .compatible = "everest,es8316" , }, |
898 | {}, |
899 | }; |
900 | MODULE_DEVICE_TABLE(of, es8316_of_match); |
901 | #endif |
902 | |
903 | #ifdef CONFIG_ACPI |
904 | static const struct acpi_device_id es8316_acpi_match[] = { |
905 | {"ESSX8316" , 0}, |
906 | {"ESSX8336" , 0}, |
907 | {}, |
908 | }; |
909 | MODULE_DEVICE_TABLE(acpi, es8316_acpi_match); |
910 | #endif |
911 | |
912 | static struct i2c_driver es8316_i2c_driver = { |
913 | .driver = { |
914 | .name = "es8316" , |
915 | .acpi_match_table = ACPI_PTR(es8316_acpi_match), |
916 | .of_match_table = of_match_ptr(es8316_of_match), |
917 | }, |
918 | .probe = es8316_i2c_probe, |
919 | .id_table = es8316_i2c_id, |
920 | }; |
921 | module_i2c_driver(es8316_i2c_driver); |
922 | |
923 | MODULE_DESCRIPTION("Everest Semi ES8316 ALSA SoC Codec Driver" ); |
924 | MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>" ); |
925 | MODULE_LICENSE("GPL v2" ); |
926 | |