Commit | Line | Data |
---|---|---|
5a3af129 MB |
1 | /* |
2 | * Driver for the PCM512x CODECs | |
3 | * | |
4 | * Author: Mark Brown <broonie@linaro.org> | |
5 | * Copyright 2014 Linaro Ltd | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * version 2 as published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | */ | |
16 | ||
17 | ||
18 | #include <linux/init.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/clk.h> | |
5a3af129 MB |
21 | #include <linux/pm_runtime.h> |
22 | #include <linux/regmap.h> | |
23 | #include <linux/regulator/consumer.h> | |
5a3af129 MB |
24 | #include <sound/soc.h> |
25 | #include <sound/soc-dapm.h> | |
26 | #include <sound/tlv.h> | |
27 | ||
28 | #include "pcm512x.h" | |
29 | ||
30 | #define PCM512x_NUM_SUPPLIES 3 | |
06d0ffcc | 31 | static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = { |
5a3af129 MB |
32 | "AVDD", |
33 | "DVDD", | |
34 | "CPVDD", | |
35 | }; | |
36 | ||
37 | struct pcm512x_priv { | |
38 | struct regmap *regmap; | |
39 | struct clk *sclk; | |
40 | struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES]; | |
41 | struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES]; | |
42 | }; | |
43 | ||
44 | /* | |
45 | * We can't use the same notifier block for more than one supply and | |
46 | * there's no way I can see to get from a callback to the caller | |
47 | * except container_of(). | |
48 | */ | |
49 | #define PCM512x_REGULATOR_EVENT(n) \ | |
50 | static int pcm512x_regulator_event_##n(struct notifier_block *nb, \ | |
51 | unsigned long event, void *data) \ | |
52 | { \ | |
53 | struct pcm512x_priv *pcm512x = container_of(nb, struct pcm512x_priv, \ | |
54 | supply_nb[n]); \ | |
55 | if (event & REGULATOR_EVENT_DISABLE) { \ | |
56 | regcache_mark_dirty(pcm512x->regmap); \ | |
57 | regcache_cache_only(pcm512x->regmap, true); \ | |
58 | } \ | |
59 | return 0; \ | |
60 | } | |
61 | ||
62 | PCM512x_REGULATOR_EVENT(0) | |
63 | PCM512x_REGULATOR_EVENT(1) | |
64 | PCM512x_REGULATOR_EVENT(2) | |
65 | ||
66 | static const struct reg_default pcm512x_reg_defaults[] = { | |
806d6466 MB |
67 | { PCM512x_RESET, 0x00 }, |
68 | { PCM512x_POWER, 0x00 }, | |
69 | { PCM512x_MUTE, 0x00 }, | |
70 | { PCM512x_DSP, 0x00 }, | |
71 | { PCM512x_PLL_REF, 0x00 }, | |
72 | { PCM512x_DAC_ROUTING, 0x11 }, | |
73 | { PCM512x_DSP_PROGRAM, 0x01 }, | |
74 | { PCM512x_CLKDET, 0x00 }, | |
75 | { PCM512x_AUTO_MUTE, 0x00 }, | |
76 | { PCM512x_ERROR_DETECT, 0x00 }, | |
77 | { PCM512x_DIGITAL_VOLUME_1, 0x00 }, | |
78 | { PCM512x_DIGITAL_VOLUME_2, 0x30 }, | |
79 | { PCM512x_DIGITAL_VOLUME_3, 0x30 }, | |
80 | { PCM512x_DIGITAL_MUTE_1, 0x22 }, | |
81 | { PCM512x_DIGITAL_MUTE_2, 0x00 }, | |
82 | { PCM512x_DIGITAL_MUTE_3, 0x07 }, | |
83 | { PCM512x_OUTPUT_AMPLITUDE, 0x00 }, | |
84 | { PCM512x_ANALOG_GAIN_CTRL, 0x00 }, | |
85 | { PCM512x_UNDERVOLTAGE_PROT, 0x00 }, | |
86 | { PCM512x_ANALOG_MUTE_CTRL, 0x00 }, | |
87 | { PCM512x_ANALOG_GAIN_BOOST, 0x00 }, | |
88 | { PCM512x_VCOM_CTRL_1, 0x00 }, | |
89 | { PCM512x_VCOM_CTRL_2, 0x01 }, | |
5a3af129 MB |
90 | }; |
91 | ||
92 | static bool pcm512x_readable(struct device *dev, unsigned int reg) | |
93 | { | |
94 | switch (reg) { | |
95 | case PCM512x_RESET: | |
96 | case PCM512x_POWER: | |
97 | case PCM512x_MUTE: | |
98 | case PCM512x_PLL_EN: | |
99 | case PCM512x_SPI_MISO_FUNCTION: | |
100 | case PCM512x_DSP: | |
101 | case PCM512x_GPIO_EN: | |
102 | case PCM512x_BCLK_LRCLK_CFG: | |
103 | case PCM512x_DSP_GPIO_INPUT: | |
104 | case PCM512x_MASTER_MODE: | |
105 | case PCM512x_PLL_REF: | |
106 | case PCM512x_PLL_COEFF_0: | |
107 | case PCM512x_PLL_COEFF_1: | |
108 | case PCM512x_PLL_COEFF_2: | |
109 | case PCM512x_PLL_COEFF_3: | |
110 | case PCM512x_PLL_COEFF_4: | |
111 | case PCM512x_DSP_CLKDIV: | |
112 | case PCM512x_DAC_CLKDIV: | |
113 | case PCM512x_NCP_CLKDIV: | |
114 | case PCM512x_OSR_CLKDIV: | |
115 | case PCM512x_MASTER_CLKDIV_1: | |
116 | case PCM512x_MASTER_CLKDIV_2: | |
117 | case PCM512x_FS_SPEED_MODE: | |
118 | case PCM512x_IDAC_1: | |
119 | case PCM512x_IDAC_2: | |
120 | case PCM512x_ERROR_DETECT: | |
121 | case PCM512x_I2S_1: | |
122 | case PCM512x_I2S_2: | |
123 | case PCM512x_DAC_ROUTING: | |
124 | case PCM512x_DSP_PROGRAM: | |
125 | case PCM512x_CLKDET: | |
126 | case PCM512x_AUTO_MUTE: | |
127 | case PCM512x_DIGITAL_VOLUME_1: | |
128 | case PCM512x_DIGITAL_VOLUME_2: | |
129 | case PCM512x_DIGITAL_VOLUME_3: | |
130 | case PCM512x_DIGITAL_MUTE_1: | |
131 | case PCM512x_DIGITAL_MUTE_2: | |
132 | case PCM512x_DIGITAL_MUTE_3: | |
133 | case PCM512x_GPIO_OUTPUT_1: | |
134 | case PCM512x_GPIO_OUTPUT_2: | |
135 | case PCM512x_GPIO_OUTPUT_3: | |
136 | case PCM512x_GPIO_OUTPUT_4: | |
137 | case PCM512x_GPIO_OUTPUT_5: | |
138 | case PCM512x_GPIO_OUTPUT_6: | |
139 | case PCM512x_GPIO_CONTROL_1: | |
140 | case PCM512x_GPIO_CONTROL_2: | |
141 | case PCM512x_OVERFLOW: | |
142 | case PCM512x_RATE_DET_1: | |
143 | case PCM512x_RATE_DET_2: | |
144 | case PCM512x_RATE_DET_3: | |
145 | case PCM512x_RATE_DET_4: | |
146 | case PCM512x_ANALOG_MUTE_DET: | |
147 | case PCM512x_GPIN: | |
148 | case PCM512x_DIGITAL_MUTE_DET: | |
806d6466 MB |
149 | case PCM512x_OUTPUT_AMPLITUDE: |
150 | case PCM512x_ANALOG_GAIN_CTRL: | |
151 | case PCM512x_UNDERVOLTAGE_PROT: | |
152 | case PCM512x_ANALOG_MUTE_CTRL: | |
153 | case PCM512x_ANALOG_GAIN_BOOST: | |
154 | case PCM512x_VCOM_CTRL_1: | |
155 | case PCM512x_VCOM_CTRL_2: | |
156 | case PCM512x_CRAM_CTRL: | |
5a3af129 MB |
157 | return true; |
158 | default: | |
806d6466 MB |
159 | /* There are 256 raw register addresses */ |
160 | return reg < 0xff; | |
5a3af129 MB |
161 | } |
162 | } | |
163 | ||
164 | static bool pcm512x_volatile(struct device *dev, unsigned int reg) | |
165 | { | |
166 | switch (reg) { | |
167 | case PCM512x_PLL_EN: | |
168 | case PCM512x_OVERFLOW: | |
169 | case PCM512x_RATE_DET_1: | |
170 | case PCM512x_RATE_DET_2: | |
171 | case PCM512x_RATE_DET_3: | |
172 | case PCM512x_RATE_DET_4: | |
173 | case PCM512x_ANALOG_MUTE_DET: | |
174 | case PCM512x_GPIN: | |
175 | case PCM512x_DIGITAL_MUTE_DET: | |
806d6466 | 176 | case PCM512x_CRAM_CTRL: |
5a3af129 MB |
177 | return true; |
178 | default: | |
806d6466 MB |
179 | /* There are 256 raw register addresses */ |
180 | return reg < 0xff; | |
5a3af129 MB |
181 | } |
182 | } | |
183 | ||
184 | static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1); | |
5be2fc20 MB |
185 | static const DECLARE_TLV_DB_SCALE(analog_tlv, -600, 600, 0); |
186 | static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0); | |
5a3af129 | 187 | |
06d0ffcc | 188 | static const char * const pcm512x_dsp_program_texts[] = { |
5a3af129 MB |
189 | "FIR interpolation with de-emphasis", |
190 | "Low latency IIR with de-emphasis", | |
191 | "Fixed process flow", | |
192 | "High attenuation with de-emphasis", | |
193 | "Ringing-less low latency FIR", | |
194 | }; | |
195 | ||
196 | static const unsigned int pcm512x_dsp_program_values[] = { | |
197 | 1, | |
198 | 2, | |
199 | 3, | |
200 | 5, | |
201 | 7, | |
202 | }; | |
203 | ||
e97db9ab MB |
204 | static SOC_VALUE_ENUM_SINGLE_DECL(pcm512x_dsp_program, |
205 | PCM512x_DSP_PROGRAM, 0, 0x1f, | |
206 | pcm512x_dsp_program_texts, | |
207 | pcm512x_dsp_program_values); | |
5a3af129 | 208 | |
06d0ffcc | 209 | static const char * const pcm512x_clk_missing_text[] = { |
5a3af129 MB |
210 | "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s" |
211 | }; | |
212 | ||
213 | static const struct soc_enum pcm512x_clk_missing = | |
214 | SOC_ENUM_SINGLE(PCM512x_CLKDET, 0, 8, pcm512x_clk_missing_text); | |
215 | ||
06d0ffcc | 216 | static const char * const pcm512x_autom_text[] = { |
5a3af129 MB |
217 | "21ms", "106ms", "213ms", "533ms", "1.07s", "2.13s", "5.33s", "10.66s" |
218 | }; | |
219 | ||
220 | static const struct soc_enum pcm512x_autom_l = | |
221 | SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATML_SHIFT, 8, | |
222 | pcm512x_autom_text); | |
223 | ||
224 | static const struct soc_enum pcm512x_autom_r = | |
225 | SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATMR_SHIFT, 8, | |
226 | pcm512x_autom_text); | |
227 | ||
06d0ffcc | 228 | static const char * const pcm512x_ramp_rate_text[] = { |
5a3af129 MB |
229 | "1 sample/update", "2 samples/update", "4 samples/update", |
230 | "Immediate" | |
231 | }; | |
232 | ||
233 | static const struct soc_enum pcm512x_vndf = | |
234 | SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDF_SHIFT, 4, | |
235 | pcm512x_ramp_rate_text); | |
236 | ||
237 | static const struct soc_enum pcm512x_vnuf = | |
238 | SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUF_SHIFT, 4, | |
239 | pcm512x_ramp_rate_text); | |
240 | ||
241 | static const struct soc_enum pcm512x_vedf = | |
242 | SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDF_SHIFT, 4, | |
243 | pcm512x_ramp_rate_text); | |
244 | ||
06d0ffcc | 245 | static const char * const pcm512x_ramp_step_text[] = { |
5a3af129 MB |
246 | "4dB/step", "2dB/step", "1dB/step", "0.5dB/step" |
247 | }; | |
248 | ||
249 | static const struct soc_enum pcm512x_vnds = | |
250 | SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDS_SHIFT, 4, | |
251 | pcm512x_ramp_step_text); | |
252 | ||
253 | static const struct soc_enum pcm512x_vnus = | |
254 | SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUS_SHIFT, 4, | |
255 | pcm512x_ramp_step_text); | |
256 | ||
257 | static const struct soc_enum pcm512x_veds = | |
258 | SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4, | |
259 | pcm512x_ramp_step_text); | |
260 | ||
261 | static const struct snd_kcontrol_new pcm512x_controls[] = { | |
262 | SOC_DOUBLE_R_TLV("Playback Digital Volume", PCM512x_DIGITAL_VOLUME_2, | |
263 | PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv), | |
5be2fc20 MB |
264 | SOC_DOUBLE_TLV("Playback Volume", PCM512x_ANALOG_GAIN_CTRL, |
265 | PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv), | |
266 | SOC_DOUBLE_TLV("Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST, | |
267 | PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv), | |
5a3af129 MB |
268 | SOC_DOUBLE("Playback Digital Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT, |
269 | PCM512x_RQMR_SHIFT, 1, 1), | |
270 | ||
271 | SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1), | |
272 | SOC_VALUE_ENUM("DSP Program", pcm512x_dsp_program), | |
273 | ||
274 | SOC_ENUM("Clock Missing Period", pcm512x_clk_missing), | |
275 | SOC_ENUM("Auto Mute Time Left", pcm512x_autom_l), | |
276 | SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r), | |
277 | SOC_SINGLE("Auto Mute Mono Switch", PCM512x_DIGITAL_MUTE_3, | |
278 | PCM512x_ACTL_SHIFT, 1, 0), | |
279 | SOC_DOUBLE("Auto Mute Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT, | |
280 | PCM512x_AMLR_SHIFT, 1, 0), | |
281 | ||
282 | SOC_ENUM("Volume Ramp Down Rate", pcm512x_vndf), | |
283 | SOC_ENUM("Volume Ramp Down Step", pcm512x_vnds), | |
284 | SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf), | |
285 | SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus), | |
286 | SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf), | |
287 | SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds), | |
288 | }; | |
289 | ||
290 | static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = { | |
291 | SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), | |
292 | SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), | |
293 | ||
294 | SND_SOC_DAPM_OUTPUT("OUTL"), | |
295 | SND_SOC_DAPM_OUTPUT("OUTR"), | |
296 | }; | |
297 | ||
298 | static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = { | |
299 | { "DACL", NULL, "Playback" }, | |
300 | { "DACR", NULL, "Playback" }, | |
301 | ||
302 | { "OUTL", NULL, "DACL" }, | |
303 | { "OUTR", NULL, "DACR" }, | |
304 | }; | |
305 | ||
306 | static int pcm512x_set_bias_level(struct snd_soc_codec *codec, | |
307 | enum snd_soc_bias_level level) | |
308 | { | |
309 | struct pcm512x_priv *pcm512x = dev_get_drvdata(codec->dev); | |
310 | int ret; | |
311 | ||
312 | switch (level) { | |
313 | case SND_SOC_BIAS_ON: | |
314 | case SND_SOC_BIAS_PREPARE: | |
315 | break; | |
316 | ||
317 | case SND_SOC_BIAS_STANDBY: | |
318 | ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, | |
319 | PCM512x_RQST, 0); | |
320 | if (ret != 0) { | |
321 | dev_err(codec->dev, "Failed to remove standby: %d\n", | |
322 | ret); | |
323 | return ret; | |
324 | } | |
325 | break; | |
326 | ||
327 | case SND_SOC_BIAS_OFF: | |
328 | ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, | |
329 | PCM512x_RQST, PCM512x_RQST); | |
330 | if (ret != 0) { | |
331 | dev_err(codec->dev, "Failed to request standby: %d\n", | |
332 | ret); | |
333 | return ret; | |
334 | } | |
335 | break; | |
336 | } | |
337 | ||
338 | codec->dapm.bias_level = level; | |
339 | ||
340 | return 0; | |
341 | } | |
342 | ||
343 | static struct snd_soc_dai_driver pcm512x_dai = { | |
344 | .name = "pcm512x-hifi", | |
345 | .playback = { | |
346 | .stream_name = "Playback", | |
347 | .channels_min = 2, | |
348 | .channels_max = 2, | |
349 | .rates = SNDRV_PCM_RATE_8000_192000, | |
350 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | |
351 | SNDRV_PCM_FMTBIT_S24_LE | | |
352 | SNDRV_PCM_FMTBIT_S32_LE | |
353 | }, | |
354 | }; | |
355 | ||
356 | static struct snd_soc_codec_driver pcm512x_codec_driver = { | |
357 | .set_bias_level = pcm512x_set_bias_level, | |
358 | .idle_bias_off = true, | |
359 | ||
360 | .controls = pcm512x_controls, | |
361 | .num_controls = ARRAY_SIZE(pcm512x_controls), | |
362 | .dapm_widgets = pcm512x_dapm_widgets, | |
363 | .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets), | |
364 | .dapm_routes = pcm512x_dapm_routes, | |
365 | .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes), | |
366 | }; | |
367 | ||
806d6466 MB |
368 | static const struct regmap_range_cfg pcm512x_range = { |
369 | .name = "Pages", .range_min = PCM512x_VIRT_BASE, | |
370 | .range_max = PCM512x_MAX_REGISTER, | |
371 | .selector_reg = PCM512x_PAGE, | |
372 | .selector_mask = 0xff, | |
373 | .window_start = 0, .window_len = 0x100, | |
374 | }; | |
375 | ||
22066226 | 376 | const struct regmap_config pcm512x_regmap = { |
5a3af129 MB |
377 | .reg_bits = 8, |
378 | .val_bits = 8, | |
379 | ||
380 | .readable_reg = pcm512x_readable, | |
381 | .volatile_reg = pcm512x_volatile, | |
382 | ||
806d6466 MB |
383 | .ranges = &pcm512x_range, |
384 | .num_ranges = 1, | |
385 | ||
5a3af129 MB |
386 | .max_register = PCM512x_MAX_REGISTER, |
387 | .reg_defaults = pcm512x_reg_defaults, | |
388 | .num_reg_defaults = ARRAY_SIZE(pcm512x_reg_defaults), | |
389 | .cache_type = REGCACHE_RBTREE, | |
390 | }; | |
22066226 | 391 | EXPORT_SYMBOL_GPL(pcm512x_regmap); |
5a3af129 | 392 | |
22066226 | 393 | int pcm512x_probe(struct device *dev, struct regmap *regmap) |
5a3af129 MB |
394 | { |
395 | struct pcm512x_priv *pcm512x; | |
396 | int i, ret; | |
397 | ||
398 | pcm512x = devm_kzalloc(dev, sizeof(struct pcm512x_priv), GFP_KERNEL); | |
399 | if (!pcm512x) | |
400 | return -ENOMEM; | |
401 | ||
402 | dev_set_drvdata(dev, pcm512x); | |
403 | pcm512x->regmap = regmap; | |
404 | ||
405 | for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) | |
406 | pcm512x->supplies[i].supply = pcm512x_supply_names[i]; | |
407 | ||
408 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm512x->supplies), | |
409 | pcm512x->supplies); | |
410 | if (ret != 0) { | |
411 | dev_err(dev, "Failed to get supplies: %d\n", ret); | |
412 | return ret; | |
413 | } | |
414 | ||
415 | pcm512x->supply_nb[0].notifier_call = pcm512x_regulator_event_0; | |
416 | pcm512x->supply_nb[1].notifier_call = pcm512x_regulator_event_1; | |
417 | pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2; | |
418 | ||
419 | for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) { | |
420 | ret = regulator_register_notifier(pcm512x->supplies[i].consumer, | |
421 | &pcm512x->supply_nb[i]); | |
422 | if (ret != 0) { | |
423 | dev_err(dev, | |
424 | "Failed to register regulator notifier: %d\n", | |
425 | ret); | |
426 | } | |
427 | } | |
428 | ||
429 | ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies), | |
430 | pcm512x->supplies); | |
431 | if (ret != 0) { | |
432 | dev_err(dev, "Failed to enable supplies: %d\n", ret); | |
433 | return ret; | |
434 | } | |
435 | ||
436 | /* Reset the device, verifying I/O in the process for I2C */ | |
437 | ret = regmap_write(regmap, PCM512x_RESET, | |
438 | PCM512x_RSTM | PCM512x_RSTR); | |
439 | if (ret != 0) { | |
440 | dev_err(dev, "Failed to reset device: %d\n", ret); | |
441 | goto err; | |
442 | } | |
443 | ||
444 | ret = regmap_write(regmap, PCM512x_RESET, 0); | |
445 | if (ret != 0) { | |
446 | dev_err(dev, "Failed to reset device: %d\n", ret); | |
447 | goto err; | |
448 | } | |
449 | ||
450 | pcm512x->sclk = devm_clk_get(dev, NULL); | |
451 | if (IS_ERR(pcm512x->sclk)) { | |
452 | if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER) | |
453 | return -EPROBE_DEFER; | |
454 | ||
455 | dev_info(dev, "No SCLK, using BCLK: %ld\n", | |
456 | PTR_ERR(pcm512x->sclk)); | |
457 | ||
458 | /* Disable reporting of missing SCLK as an error */ | |
459 | regmap_update_bits(regmap, PCM512x_ERROR_DETECT, | |
460 | PCM512x_IDCH, PCM512x_IDCH); | |
461 | ||
462 | /* Switch PLL input to BCLK */ | |
463 | regmap_update_bits(regmap, PCM512x_PLL_REF, | |
464 | PCM512x_SREF, PCM512x_SREF); | |
465 | } else { | |
466 | ret = clk_prepare_enable(pcm512x->sclk); | |
467 | if (ret != 0) { | |
468 | dev_err(dev, "Failed to enable SCLK: %d\n", ret); | |
469 | return ret; | |
470 | } | |
471 | } | |
472 | ||
473 | /* Default to standby mode */ | |
474 | ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, | |
475 | PCM512x_RQST, PCM512x_RQST); | |
476 | if (ret != 0) { | |
477 | dev_err(dev, "Failed to request standby: %d\n", | |
478 | ret); | |
479 | goto err_clk; | |
480 | } | |
481 | ||
482 | pm_runtime_set_active(dev); | |
483 | pm_runtime_enable(dev); | |
484 | pm_runtime_idle(dev); | |
485 | ||
486 | ret = snd_soc_register_codec(dev, &pcm512x_codec_driver, | |
487 | &pcm512x_dai, 1); | |
488 | if (ret != 0) { | |
489 | dev_err(dev, "Failed to register CODEC: %d\n", ret); | |
490 | goto err_pm; | |
491 | } | |
492 | ||
493 | return 0; | |
494 | ||
495 | err_pm: | |
496 | pm_runtime_disable(dev); | |
497 | err_clk: | |
498 | if (!IS_ERR(pcm512x->sclk)) | |
499 | clk_disable_unprepare(pcm512x->sclk); | |
500 | err: | |
501 | regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies), | |
502 | pcm512x->supplies); | |
503 | return ret; | |
504 | } | |
22066226 | 505 | EXPORT_SYMBOL_GPL(pcm512x_probe); |
5a3af129 | 506 | |
22066226 | 507 | void pcm512x_remove(struct device *dev) |
5a3af129 MB |
508 | { |
509 | struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); | |
510 | ||
511 | snd_soc_unregister_codec(dev); | |
512 | pm_runtime_disable(dev); | |
513 | if (!IS_ERR(pcm512x->sclk)) | |
514 | clk_disable_unprepare(pcm512x->sclk); | |
515 | regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies), | |
516 | pcm512x->supplies); | |
517 | } | |
22066226 | 518 | EXPORT_SYMBOL_GPL(pcm512x_remove); |
5a3af129 MB |
519 | |
520 | static int pcm512x_suspend(struct device *dev) | |
521 | { | |
522 | struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); | |
523 | int ret; | |
524 | ||
525 | ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, | |
526 | PCM512x_RQPD, PCM512x_RQPD); | |
527 | if (ret != 0) { | |
528 | dev_err(dev, "Failed to request power down: %d\n", ret); | |
529 | return ret; | |
530 | } | |
531 | ||
532 | ret = regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies), | |
533 | pcm512x->supplies); | |
534 | if (ret != 0) { | |
535 | dev_err(dev, "Failed to disable supplies: %d\n", ret); | |
536 | return ret; | |
537 | } | |
538 | ||
539 | if (!IS_ERR(pcm512x->sclk)) | |
540 | clk_disable_unprepare(pcm512x->sclk); | |
541 | ||
542 | return 0; | |
543 | } | |
544 | ||
545 | static int pcm512x_resume(struct device *dev) | |
546 | { | |
547 | struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); | |
548 | int ret; | |
549 | ||
550 | if (!IS_ERR(pcm512x->sclk)) { | |
551 | ret = clk_prepare_enable(pcm512x->sclk); | |
552 | if (ret != 0) { | |
553 | dev_err(dev, "Failed to enable SCLK: %d\n", ret); | |
554 | return ret; | |
555 | } | |
556 | } | |
557 | ||
558 | ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies), | |
559 | pcm512x->supplies); | |
560 | if (ret != 0) { | |
561 | dev_err(dev, "Failed to enable supplies: %d\n", ret); | |
562 | return ret; | |
563 | } | |
564 | ||
565 | regcache_cache_only(pcm512x->regmap, false); | |
566 | ret = regcache_sync(pcm512x->regmap); | |
567 | if (ret != 0) { | |
568 | dev_err(dev, "Failed to sync cache: %d\n", ret); | |
569 | return ret; | |
570 | } | |
571 | ||
572 | ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, | |
573 | PCM512x_RQPD, 0); | |
574 | if (ret != 0) { | |
575 | dev_err(dev, "Failed to remove power down: %d\n", ret); | |
576 | return ret; | |
577 | } | |
578 | ||
579 | return 0; | |
580 | } | |
581 | ||
22066226 | 582 | const struct dev_pm_ops pcm512x_pm_ops = { |
5a3af129 MB |
583 | SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL) |
584 | }; | |
22066226 | 585 | EXPORT_SYMBOL_GPL(pcm512x_pm_ops); |
5a3af129 MB |
586 | |
587 | MODULE_DESCRIPTION("ASoC PCM512x codec driver"); | |
588 | MODULE_AUTHOR("Mark Brown <broonie@linaro.org>"); | |
589 | MODULE_LICENSE("GPL v2"); |