]>
Commit | Line | Data |
---|---|---|
8084945d LM |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // MAX9867 ALSA SoC codec driver | |
4 | // | |
5 | // Copyright 2013-2015 Maxim Integrated Products | |
6 | // Copyright 2018 Ladislav Michl <ladis@linux-mips.org> | |
7 | // | |
805d132d | 8 | |
9 | #include <linux/delay.h> | |
10 | #include <linux/i2c.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/regmap.h> | |
13 | #include <sound/pcm_params.h> | |
14 | #include <sound/soc.h> | |
15 | #include <sound/tlv.h> | |
16 | #include "max9867.h" | |
17 | ||
18 | static const char *const max9867_spmode[] = { | |
19 | "Stereo Diff", "Mono Diff", | |
20 | "Stereo Cap", "Mono Cap", | |
21 | "Stereo Single", "Mono Single", | |
22 | "Stereo Single Fast", "Mono Single Fast" | |
23 | }; | |
805d132d | 24 | static const char *const max9867_filter_text[] = {"IIR", "FIR"}; |
25 | ||
af53d573 PD |
26 | static const char *const max9867_adc_dac_filter_text[] = { |
27 | "Disabled", | |
28 | "Elliptical/16/256", | |
29 | "Butterworth/16/500", | |
30 | "Elliptical/8/256", | |
31 | "Butterworth/8/500", | |
32 | "Butterworth/8-24" | |
33 | }; | |
34 | ||
805d132d | 35 | static SOC_ENUM_SINGLE_DECL(max9867_filter, MAX9867_CODECFLTR, 7, |
36 | max9867_filter_text); | |
af53d573 PD |
37 | static SOC_ENUM_SINGLE_DECL(max9867_dac_filter, MAX9867_CODECFLTR, 0, |
38 | max9867_adc_dac_filter_text); | |
39 | static SOC_ENUM_SINGLE_DECL(max9867_adc_filter, MAX9867_CODECFLTR, 4, | |
40 | max9867_adc_dac_filter_text); | |
805d132d | 41 | static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0, |
42 | max9867_spmode); | |
bc2610a6 LM |
43 | static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_master_tlv, |
44 | 0, 2, TLV_DB_SCALE_ITEM(-8600, 200, 1), | |
45 | 3, 17, TLV_DB_SCALE_ITEM(-7800, 400, 0), | |
46 | 18, 25, TLV_DB_SCALE_ITEM(-2000, 200, 0), | |
47 | 26, 34, TLV_DB_SCALE_ITEM( -500, 100, 0), | |
48 | 35, 40, TLV_DB_SCALE_ITEM( 350, 50, 0), | |
49 | ); | |
50 | static DECLARE_TLV_DB_SCALE(max9867_mic_tlv, 0, 100, 0); | |
51 | static DECLARE_TLV_DB_SCALE(max9867_line_tlv, -600, 200, 0); | |
52 | static DECLARE_TLV_DB_SCALE(max9867_adc_tlv, -1200, 100, 0); | |
53 | static DECLARE_TLV_DB_SCALE(max9867_dac_tlv, -1500, 100, 0); | |
54 | static DECLARE_TLV_DB_SCALE(max9867_dacboost_tlv, 0, 600, 0); | |
55 | static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_micboost_tlv, | |
56 | 0, 2, TLV_DB_SCALE_ITEM(-2000, 2000, 1), | |
57 | 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0), | |
349fa18c | 58 | ); |
805d132d | 59 | |
60 | static const struct snd_kcontrol_new max9867_snd_controls[] = { | |
bc2610a6 | 61 | SOC_DOUBLE_R_TLV("Master Playback Volume", MAX9867_LEFTVOL, |
8ba4dc3c | 62 | MAX9867_RIGHTVOL, 0, 40, 1, max9867_master_tlv), |
bc2610a6 LM |
63 | SOC_DOUBLE_R_TLV("Line Capture Volume", MAX9867_LEFTLINELVL, |
64 | MAX9867_RIGHTLINELVL, 0, 15, 1, max9867_line_tlv), | |
65 | SOC_DOUBLE_R_TLV("Mic Capture Volume", MAX9867_LEFTMICGAIN, | |
66 | MAX9867_RIGHTMICGAIN, 0, 20, 1, max9867_mic_tlv), | |
67 | SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", MAX9867_LEFTMICGAIN, | |
8ba4dc3c | 68 | MAX9867_RIGHTMICGAIN, 5, 3, 0, max9867_micboost_tlv), |
bc2610a6 LM |
69 | SOC_SINGLE("Digital Sidetone Volume", MAX9867_SIDETONE, 0, 31, 1), |
70 | SOC_SINGLE_TLV("Digital Playback Volume", MAX9867_DACLEVEL, 0, 15, 1, | |
71 | max9867_dac_tlv), | |
72 | SOC_SINGLE_TLV("Digital Boost Playback Volume", MAX9867_DACLEVEL, 4, 3, 0, | |
73 | max9867_dacboost_tlv), | |
53a58bf9 | 74 | SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 4, 0, 15, 1, |
bc2610a6 | 75 | max9867_adc_tlv), |
805d132d | 76 | SOC_ENUM("Speaker Mode", max9867_spkmode), |
77 | SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0), | |
bc2610a6 | 78 | SOC_SINGLE("Line ZC Switch", MAX9867_MODECONFIG, 5, 1, 0), |
805d132d | 79 | SOC_ENUM("DSP Filter", max9867_filter), |
af53d573 PD |
80 | SOC_ENUM("ADC Filter", max9867_adc_filter), |
81 | SOC_ENUM("DAC Filter", max9867_dac_filter), | |
80b9fa4d | 82 | SOC_SINGLE("Mono Playback Switch", MAX9867_IFC1B, 3, 1, 0), |
805d132d | 83 | }; |
84 | ||
bc2610a6 LM |
85 | /* Input mixer */ |
86 | static const struct snd_kcontrol_new max9867_input_mixer_controls[] = { | |
87 | SOC_DAPM_DOUBLE("Line Capture Switch", MAX9867_INPUTCONFIG, 7, 5, 1, 0), | |
88 | SOC_DAPM_DOUBLE("Mic Capture Switch", MAX9867_INPUTCONFIG, 6, 4, 1, 0), | |
89 | }; | |
90 | ||
91 | /* Output mixer */ | |
92 | static const struct snd_kcontrol_new max9867_output_mixer_controls[] = { | |
93 | SOC_DAPM_DOUBLE_R("Line Bypass Switch", | |
94 | MAX9867_LEFTLINELVL, MAX9867_RIGHTLINELVL, 6, 1, 1), | |
95 | }; | |
805d132d | 96 | |
bc2610a6 LM |
97 | /* Sidetone mixer */ |
98 | static const struct snd_kcontrol_new max9867_sidetone_mixer_controls[] = { | |
99 | SOC_DAPM_DOUBLE("Sidetone Switch", MAX9867_SIDETONE, 6, 7, 1, 0), | |
100 | }; | |
805d132d | 101 | |
bc2610a6 LM |
102 | /* Line out switch */ |
103 | static const struct snd_kcontrol_new max9867_line_out_control = | |
104 | SOC_DAPM_DOUBLE_R("Switch", | |
105 | MAX9867_LEFTVOL, MAX9867_RIGHTVOL, 6, 1, 1); | |
805d132d | 106 | |
980b63f8 PD |
107 | /* DMIC mux */ |
108 | static const char *const dmic_mux_text[] = { | |
109 | "ADC", "DMIC" | |
110 | }; | |
111 | static SOC_ENUM_SINGLE_DECL(left_dmic_mux_enum, | |
112 | MAX9867_MICCONFIG, 5, dmic_mux_text); | |
113 | static SOC_ENUM_SINGLE_DECL(right_dmic_mux_enum, | |
114 | MAX9867_MICCONFIG, 4, dmic_mux_text); | |
115 | static const struct snd_kcontrol_new max9867_left_dmic_mux = | |
116 | SOC_DAPM_ENUM("DMICL Mux", left_dmic_mux_enum); | |
117 | static const struct snd_kcontrol_new max9867_right_dmic_mux = | |
118 | SOC_DAPM_ENUM("DMICR Mux", right_dmic_mux_enum); | |
805d132d | 119 | |
120 | static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = { | |
bc2610a6 LM |
121 | SND_SOC_DAPM_INPUT("MICL"), |
122 | SND_SOC_DAPM_INPUT("MICR"), | |
980b63f8 PD |
123 | SND_SOC_DAPM_INPUT("DMICL"), |
124 | SND_SOC_DAPM_INPUT("DMICR"), | |
bc2610a6 LM |
125 | SND_SOC_DAPM_INPUT("LINL"), |
126 | SND_SOC_DAPM_INPUT("LINR"), | |
127 | ||
29c859df PD |
128 | SND_SOC_DAPM_PGA("Left Line Input", SND_SOC_NOPM, 0, 0, NULL, 0), |
129 | SND_SOC_DAPM_PGA("Right Line Input", SND_SOC_NOPM, 0, 0, NULL, 0), | |
bc2610a6 LM |
130 | SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0, |
131 | max9867_input_mixer_controls, | |
132 | ARRAY_SIZE(max9867_input_mixer_controls)), | |
980b63f8 PD |
133 | SND_SOC_DAPM_MUX("DMICL Mux", SND_SOC_NOPM, 0, 0, |
134 | &max9867_left_dmic_mux), | |
135 | SND_SOC_DAPM_MUX("DMICR Mux", SND_SOC_NOPM, 0, 0, | |
136 | &max9867_right_dmic_mux), | |
29c859df PD |
137 | SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", SND_SOC_NOPM, 0, 0), |
138 | SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", SND_SOC_NOPM, 0, 0), | |
bc2610a6 LM |
139 | |
140 | SND_SOC_DAPM_MIXER("Digital", SND_SOC_NOPM, 0, 0, | |
141 | max9867_sidetone_mixer_controls, | |
142 | ARRAY_SIZE(max9867_sidetone_mixer_controls)), | |
143 | SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", SND_SOC_NOPM, 0, 0, | |
144 | max9867_output_mixer_controls, | |
145 | ARRAY_SIZE(max9867_output_mixer_controls)), | |
29c859df PD |
146 | SND_SOC_DAPM_DAC("DACL", "HiFi Playback", SND_SOC_NOPM, 0, 0), |
147 | SND_SOC_DAPM_DAC("DACR", "HiFi Playback", SND_SOC_NOPM, 0, 0), | |
bc2610a6 LM |
148 | SND_SOC_DAPM_SWITCH("Master Playback", SND_SOC_NOPM, 0, 0, |
149 | &max9867_line_out_control), | |
150 | SND_SOC_DAPM_OUTPUT("LOUT"), | |
151 | SND_SOC_DAPM_OUTPUT("ROUT"), | |
805d132d | 152 | }; |
153 | ||
154 | static const struct snd_soc_dapm_route max9867_audio_map[] = { | |
bc2610a6 LM |
155 | {"Left Line Input", NULL, "LINL"}, |
156 | {"Right Line Input", NULL, "LINR"}, | |
157 | {"Input Mixer", "Mic Capture Switch", "MICL"}, | |
158 | {"Input Mixer", "Mic Capture Switch", "MICR"}, | |
159 | {"Input Mixer", "Line Capture Switch", "Left Line Input"}, | |
160 | {"Input Mixer", "Line Capture Switch", "Right Line Input"}, | |
980b63f8 PD |
161 | {"DMICL Mux", "DMIC", "DMICL"}, |
162 | {"DMICR Mux", "DMIC", "DMICR"}, | |
163 | {"DMICL Mux", "ADC", "Input Mixer"}, | |
164 | {"DMICR Mux", "ADC", "Input Mixer"}, | |
165 | {"ADCL", NULL, "DMICL Mux"}, | |
166 | {"ADCR", NULL, "DMICR Mux"}, | |
bc2610a6 LM |
167 | |
168 | {"Digital", "Sidetone Switch", "ADCL"}, | |
169 | {"Digital", "Sidetone Switch", "ADCR"}, | |
170 | {"DACL", NULL, "Digital"}, | |
171 | {"DACR", NULL, "Digital"}, | |
172 | ||
173 | {"Output Mixer", "Line Bypass Switch", "Left Line Input"}, | |
174 | {"Output Mixer", "Line Bypass Switch", "Right Line Input"}, | |
175 | {"Output Mixer", NULL, "DACL"}, | |
176 | {"Output Mixer", NULL, "DACR"}, | |
177 | {"Master Playback", "Switch", "Output Mixer"}, | |
178 | {"LOUT", NULL, "Master Playback"}, | |
179 | {"ROUT", NULL, "Master Playback"}, | |
805d132d | 180 | }; |
181 | ||
715ee191 LM |
182 | static const unsigned int max9867_rates_44k1[] = { |
183 | 11025, 22050, 44100, | |
805d132d | 184 | }; |
185 | ||
715ee191 LM |
186 | static const struct snd_pcm_hw_constraint_list max9867_constraints_44k1 = { |
187 | .list = max9867_rates_44k1, | |
188 | .count = ARRAY_SIZE(max9867_rates_44k1), | |
805d132d | 189 | }; |
190 | ||
715ee191 LM |
191 | static const unsigned int max9867_rates_48k[] = { |
192 | 8000, 16000, 32000, 48000, | |
193 | }; | |
194 | ||
195 | static const struct snd_pcm_hw_constraint_list max9867_constraints_48k = { | |
196 | .list = max9867_rates_48k, | |
197 | .count = ARRAY_SIZE(max9867_rates_48k), | |
198 | }; | |
199 | ||
200 | struct max9867_priv { | |
201 | struct regmap *regmap; | |
202 | const struct snd_pcm_hw_constraint_list *constraints; | |
203 | unsigned int sysclk, pclk; | |
204 | bool master, dsp_a; | |
205 | }; | |
206 | ||
207 | static int max9867_startup(struct snd_pcm_substream *substream, | |
208 | struct snd_soc_dai *dai) | |
805d132d | 209 | { |
715ee191 LM |
210 | struct max9867_priv *max9867 = |
211 | snd_soc_component_get_drvdata(dai->component); | |
805d132d | 212 | |
715ee191 LM |
213 | if (max9867->constraints) |
214 | snd_pcm_hw_constraint_list(substream->runtime, 0, | |
215 | SNDRV_PCM_HW_PARAM_RATE, max9867->constraints); | |
805d132d | 216 | |
715ee191 | 217 | return 0; |
805d132d | 218 | } |
219 | ||
220 | static int max9867_dai_hw_params(struct snd_pcm_substream *substream, | |
221 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) | |
222 | { | |
715ee191 LM |
223 | int value; |
224 | unsigned long int rate, ratio; | |
91880a6b KM |
225 | struct snd_soc_component *component = dai->component; |
226 | struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); | |
715ee191 LM |
227 | unsigned int ni = DIV_ROUND_CLOSEST_ULL(96ULL * 0x10000 * params_rate(params), |
228 | max9867->pclk); | |
805d132d | 229 | |
805d132d | 230 | /* set up the ni value */ |
231 | regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, | |
715ee191 | 232 | MAX9867_NI_HIGH_MASK, (0xFF00 & ni) >> 8); |
805d132d | 233 | regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, |
715ee191 LM |
234 | MAX9867_NI_LOW_MASK, 0x00FF & ni); |
235 | if (max9867->master) { | |
236 | if (max9867->dsp_a) { | |
237 | value = MAX9867_IFC1B_48X; | |
238 | } else { | |
239 | rate = params_rate(params) * 2 * params_width(params); | |
240 | ratio = max9867->pclk / rate; | |
241 | switch (params_width(params)) { | |
b1461450 | 242 | case 8: |
b1461450 | 243 | case 16: |
715ee191 LM |
244 | switch (ratio) { |
245 | case 2: | |
246 | value = MAX9867_IFC1B_PCLK_2; | |
247 | break; | |
248 | case 4: | |
249 | value = MAX9867_IFC1B_PCLK_4; | |
250 | break; | |
251 | case 8: | |
252 | value = MAX9867_IFC1B_PCLK_8; | |
253 | break; | |
254 | case 16: | |
255 | value = MAX9867_IFC1B_PCLK_16; | |
256 | break; | |
257 | default: | |
258 | return -EINVAL; | |
259 | } | |
260 | break; | |
261 | case 24: | |
262 | value = MAX9867_IFC1B_48X; | |
263 | break; | |
264 | case 32: | |
265 | value = MAX9867_IFC1B_64X; | |
b1461450 | 266 | break; |
267 | default: | |
805d132d | 268 | return -EINVAL; |
269 | } | |
805d132d | 270 | } |
271 | regmap_update_bits(max9867->regmap, MAX9867_IFC1B, | |
715ee191 LM |
272 | MAX9867_IFC1B_BCLK_MASK, value); |
273 | } else { | |
274 | /* | |
275 | * digital pll locks on to any externally supplied LRCLK signal | |
276 | * and also enable rapid lock mode. | |
277 | */ | |
278 | regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, | |
279 | MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK); | |
280 | regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, | |
281 | MAX9867_PLL, MAX9867_PLL); | |
805d132d | 282 | } |
283 | return 0; | |
284 | } | |
285 | ||
18e028e2 | 286 | static int max9867_mute(struct snd_soc_dai *dai, int mute, int direction) |
805d132d | 287 | { |
91880a6b KM |
288 | struct snd_soc_component *component = dai->component; |
289 | struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); | |
805d132d | 290 | |
bc2610a6 LM |
291 | return regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, |
292 | 1 << 6, !!mute << 6); | |
805d132d | 293 | } |
294 | ||
295 | static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai, | |
296 | int clk_id, unsigned int freq, int dir) | |
297 | { | |
91880a6b KM |
298 | struct snd_soc_component *component = codec_dai->component; |
299 | struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); | |
805d132d | 300 | int value = 0; |
301 | ||
302 | /* Set the prescaler based on the master clock frequency*/ | |
303 | if (freq >= 10000000 && freq <= 20000000) { | |
304 | value |= MAX9867_PSCLK_10_20; | |
933662f2 | 305 | max9867->pclk = freq; |
805d132d | 306 | } else if (freq >= 20000000 && freq <= 40000000) { |
307 | value |= MAX9867_PSCLK_20_40; | |
933662f2 | 308 | max9867->pclk = freq / 2; |
805d132d | 309 | } else if (freq >= 40000000 && freq <= 60000000) { |
310 | value |= MAX9867_PSCLK_40_60; | |
933662f2 | 311 | max9867->pclk = freq / 4; |
805d132d | 312 | } else { |
8b9c716a LM |
313 | dev_err(component->dev, |
314 | "Invalid clock frequency %uHz (required 10-60MHz)\n", | |
315 | freq); | |
805d132d | 316 | return -EINVAL; |
317 | } | |
715ee191 LM |
318 | if (freq % 48000 == 0) |
319 | max9867->constraints = &max9867_constraints_48k; | |
320 | else if (freq % 44100 == 0) | |
321 | max9867->constraints = &max9867_constraints_44k1; | |
322 | else | |
323 | dev_warn(component->dev, | |
324 | "Unable to set exact rate with %uHz clock frequency\n", | |
325 | freq); | |
805d132d | 326 | max9867->sysclk = freq; |
715ee191 | 327 | value = value << MAX9867_PSCLK_SHIFT; |
805d132d | 328 | /* exact integer mode is not supported */ |
329 | value &= ~MAX9867_FREQ_MASK; | |
330 | regmap_update_bits(max9867->regmap, MAX9867_SYSCLK, | |
331 | MAX9867_PSCLK_MASK, value); | |
332 | return 0; | |
333 | } | |
334 | ||
335 | static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, | |
336 | unsigned int fmt) | |
337 | { | |
91880a6b KM |
338 | struct snd_soc_component *component = codec_dai->component; |
339 | struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); | |
715ee191 | 340 | u8 iface1A, iface1B; |
805d132d | 341 | |
342 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | |
343 | case SND_SOC_DAIFMT_CBM_CFM: | |
715ee191 LM |
344 | max9867->master = true; |
345 | iface1A = MAX9867_MASTER; | |
346 | iface1B = MAX9867_IFC1B_48X; | |
805d132d | 347 | break; |
348 | case SND_SOC_DAIFMT_CBS_CFS: | |
715ee191 LM |
349 | max9867->master = false; |
350 | iface1A = iface1B = 0; | |
805d132d | 351 | break; |
352 | default: | |
353 | return -EINVAL; | |
354 | } | |
355 | ||
79e13974 LM |
356 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
357 | case SND_SOC_DAIFMT_I2S: | |
715ee191 | 358 | max9867->dsp_a = false; |
79e13974 LM |
359 | iface1A |= MAX9867_I2S_DLY; |
360 | break; | |
361 | case SND_SOC_DAIFMT_DSP_A: | |
715ee191 | 362 | max9867->dsp_a = true; |
79e13974 LM |
363 | iface1A |= MAX9867_TDM_MODE | MAX9867_SDOUT_HIZ; |
364 | break; | |
365 | default: | |
366 | return -EINVAL; | |
367 | } | |
805d132d | 368 | |
369 | /* Clock inversion bits, BCI and WCI */ | |
370 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | |
371 | case SND_SOC_DAIFMT_NB_NF: | |
372 | break; | |
373 | case SND_SOC_DAIFMT_IB_IF: | |
374 | iface1A |= MAX9867_WCI_MODE | MAX9867_BCI_MODE; | |
375 | break; | |
376 | case SND_SOC_DAIFMT_IB_NF: | |
377 | iface1A |= MAX9867_BCI_MODE; | |
378 | break; | |
379 | case SND_SOC_DAIFMT_NB_IF: | |
380 | iface1A |= MAX9867_WCI_MODE; | |
381 | break; | |
382 | default: | |
383 | return -EINVAL; | |
384 | } | |
385 | ||
9fe78b28 | 386 | regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A); |
80b9fa4d PD |
387 | regmap_update_bits(max9867->regmap, MAX9867_IFC1B, |
388 | MAX9867_IFC1B_BCLK_MASK, iface1B); | |
715ee191 | 389 | |
805d132d | 390 | return 0; |
391 | } | |
392 | ||
eb59d73c | 393 | static const struct snd_soc_dai_ops max9867_dai_ops = { |
805d132d | 394 | .set_sysclk = max9867_set_dai_sysclk, |
715ee191 | 395 | .set_fmt = max9867_dai_set_fmt, |
18e028e2 | 396 | .mute_stream = max9867_mute, |
715ee191 LM |
397 | .startup = max9867_startup, |
398 | .hw_params = max9867_dai_hw_params, | |
18e028e2 | 399 | .no_capture_mute = 1, |
805d132d | 400 | }; |
401 | ||
805d132d | 402 | static struct snd_soc_dai_driver max9867_dai[] = { |
403 | { | |
404 | .name = "max9867-aif1", | |
405 | .playback = { | |
406 | .stream_name = "HiFi Playback", | |
e6ceb922 | 407 | .channels_min = 2, |
805d132d | 408 | .channels_max = 2, |
715ee191 LM |
409 | .rates = SNDRV_PCM_RATE_8000_48000, |
410 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | |
805d132d | 411 | }, |
412 | .capture = { | |
413 | .stream_name = "HiFi Capture", | |
e6ceb922 | 414 | .channels_min = 2, |
805d132d | 415 | .channels_max = 2, |
715ee191 LM |
416 | .rates = SNDRV_PCM_RATE_8000_48000, |
417 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | |
805d132d | 418 | }, |
419 | .ops = &max9867_dai_ops, | |
e6ceb922 | 420 | .symmetric_rates = 1, |
805d132d | 421 | } |
422 | }; | |
423 | ||
29f58ff0 LM |
424 | #ifdef CONFIG_PM |
425 | static int max9867_suspend(struct snd_soc_component *component) | |
805d132d | 426 | { |
29f58ff0 | 427 | snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); |
805d132d | 428 | |
805d132d | 429 | return 0; |
430 | } | |
431 | ||
29f58ff0 | 432 | static int max9867_resume(struct snd_soc_component *component) |
805d132d | 433 | { |
29f58ff0 | 434 | snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); |
805d132d | 435 | |
805d132d | 436 | return 0; |
437 | } | |
29f58ff0 LM |
438 | #else |
439 | #define max9867_suspend NULL | |
440 | #define max9867_resume NULL | |
805d132d | 441 | #endif |
442 | ||
29f58ff0 LM |
443 | static int max9867_set_bias_level(struct snd_soc_component *component, |
444 | enum snd_soc_bias_level level) | |
445 | { | |
446 | int err; | |
447 | struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); | |
448 | ||
449 | switch (level) { | |
450 | case SND_SOC_BIAS_STANDBY: | |
451 | if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { | |
452 | err = regcache_sync(max9867->regmap); | |
453 | if (err) | |
454 | return err; | |
455 | ||
29c859df PD |
456 | err = regmap_write(max9867->regmap, |
457 | MAX9867_PWRMAN, 0xff); | |
29f58ff0 LM |
458 | if (err) |
459 | return err; | |
460 | } | |
461 | break; | |
462 | case SND_SOC_BIAS_OFF: | |
29c859df | 463 | err = regmap_write(max9867->regmap, MAX9867_PWRMAN, 0); |
29f58ff0 LM |
464 | if (err) |
465 | return err; | |
466 | ||
467 | regcache_mark_dirty(max9867->regmap); | |
468 | break; | |
469 | default: | |
470 | break; | |
471 | } | |
472 | ||
473 | return 0; | |
474 | } | |
475 | ||
91880a6b | 476 | static const struct snd_soc_component_driver max9867_component = { |
91880a6b KM |
477 | .controls = max9867_snd_controls, |
478 | .num_controls = ARRAY_SIZE(max9867_snd_controls), | |
479 | .dapm_routes = max9867_audio_map, | |
480 | .num_dapm_routes = ARRAY_SIZE(max9867_audio_map), | |
481 | .dapm_widgets = max9867_dapm_widgets, | |
482 | .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets), | |
29f58ff0 LM |
483 | .suspend = max9867_suspend, |
484 | .resume = max9867_resume, | |
485 | .set_bias_level = max9867_set_bias_level, | |
91880a6b KM |
486 | .idle_bias_on = 1, |
487 | .use_pmdown_time = 1, | |
488 | .endianness = 1, | |
489 | .non_legacy_dai_naming = 1, | |
805d132d | 490 | }; |
491 | ||
492 | static bool max9867_volatile_register(struct device *dev, unsigned int reg) | |
493 | { | |
494 | switch (reg) { | |
495 | case MAX9867_STATUS: | |
496 | case MAX9867_JACKSTATUS: | |
497 | case MAX9867_AUXHIGH: | |
498 | case MAX9867_AUXLOW: | |
499 | return true; | |
500 | default: | |
501 | return false; | |
502 | } | |
503 | } | |
504 | ||
250a99e7 | 505 | static const struct regmap_config max9867_regmap = { |
805d132d | 506 | .reg_bits = 8, |
507 | .val_bits = 8, | |
508 | .max_register = MAX9867_REVISION, | |
805d132d | 509 | .volatile_reg = max9867_volatile_register, |
510 | .cache_type = REGCACHE_RBTREE, | |
511 | }; | |
512 | ||
513 | static int max9867_i2c_probe(struct i2c_client *i2c, | |
514 | const struct i2c_device_id *id) | |
515 | { | |
516 | struct max9867_priv *max9867; | |
8efc1afd | 517 | int ret, reg; |
805d132d | 518 | |
933662f2 | 519 | max9867 = devm_kzalloc(&i2c->dev, sizeof(*max9867), GFP_KERNEL); |
805d132d | 520 | if (!max9867) |
521 | return -ENOMEM; | |
522 | ||
523 | i2c_set_clientdata(i2c, max9867); | |
524 | max9867->regmap = devm_regmap_init_i2c(i2c, &max9867_regmap); | |
525 | if (IS_ERR(max9867->regmap)) { | |
526 | ret = PTR_ERR(max9867->regmap); | |
8b9c716a | 527 | dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); |
805d132d | 528 | return ret; |
529 | } | |
933662f2 | 530 | ret = regmap_read(max9867->regmap, MAX9867_REVISION, ®); |
805d132d | 531 | if (ret < 0) { |
532 | dev_err(&i2c->dev, "Failed to read: %d\n", ret); | |
533 | return ret; | |
534 | } | |
535 | dev_info(&i2c->dev, "device revision: %x\n", reg); | |
91880a6b | 536 | ret = devm_snd_soc_register_component(&i2c->dev, &max9867_component, |
805d132d | 537 | max9867_dai, ARRAY_SIZE(max9867_dai)); |
8efc1afd | 538 | if (ret < 0) |
91880a6b | 539 | dev_err(&i2c->dev, "Failed to register component: %d\n", ret); |
805d132d | 540 | return ret; |
541 | } | |
542 | ||
805d132d | 543 | static const struct i2c_device_id max9867_i2c_id[] = { |
544 | { "max9867", 0 }, | |
1b36e4a2 | 545 | { } |
805d132d | 546 | }; |
56af0e4c | 547 | MODULE_DEVICE_TABLE(i2c, max9867_i2c_id); |
805d132d | 548 | |
549 | static const struct of_device_id max9867_of_match[] = { | |
550 | { .compatible = "maxim,max9867", }, | |
551 | { } | |
552 | }; | |
56af0e4c | 553 | MODULE_DEVICE_TABLE(of, max9867_of_match); |
805d132d | 554 | |
805d132d | 555 | static struct i2c_driver max9867_i2c_driver = { |
556 | .driver = { | |
557 | .name = "max9867", | |
558 | .of_match_table = of_match_ptr(max9867_of_match), | |
805d132d | 559 | }, |
560 | .probe = max9867_i2c_probe, | |
805d132d | 561 | .id_table = max9867_i2c_id, |
562 | }; | |
563 | ||
564 | module_i2c_driver(max9867_i2c_driver); | |
565 | ||
8084945d LM |
566 | MODULE_AUTHOR("Ladislav Michl <ladis@linux-mips.org>"); |
567 | MODULE_DESCRIPTION("ASoC MAX9867 driver"); | |
805d132d | 568 | MODULE_LICENSE("GPL"); |