~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/codecs/pcm512x.c

Version: ~ [ linux-5.3 ] ~ [ linux-5.2.15 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.73 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.144 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.193 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.193 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.73 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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>
 21 #include <linux/pm_runtime.h>
 22 #include <linux/regmap.h>
 23 #include <linux/regulator/consumer.h>
 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
 31 static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = {
 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[] = {
 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 },
 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:
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:
157                 return true;
158         default:
159                 /* There are 256 raw register addresses */
160                 return reg < 0xff;
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:
176         case PCM512x_CRAM_CTRL:
177                 return true;
178         default:
179                 /* There are 256 raw register addresses */
180                 return reg < 0xff;
181         }
182 }
183 
184 static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1);
185 static const DECLARE_TLV_DB_SCALE(analog_tlv, -600, 600, 0);
186 static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0);
187 
188 static const char * const pcm512x_dsp_program_texts[] = {
189         "FIR interpolation with de-emphasis",
190         "Low latency IIR with de-emphasis",
191         "High attenuation with de-emphasis",
192         "Fixed process flow",
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 
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);
208 
209 static const char * const pcm512x_clk_missing_text[] = {
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 
216 static const char * const pcm512x_autom_text[] = {
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 
228 static const char * const pcm512x_ramp_rate_text[] = {
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 
245 static const char * const pcm512x_ramp_step_text[] = {
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("Digital Playback Volume", PCM512x_DIGITAL_VOLUME_2,
263                  PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv),
264 SOC_DOUBLE_TLV("Analogue Playback Volume", PCM512x_ANALOG_GAIN_CTRL,
265                PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv),
266 SOC_DOUBLE_TLV("Analogue Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST,
267                PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv),
268 SOC_DOUBLE("Digital Playback 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_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 
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 
376 const struct regmap_config pcm512x_regmap = {
377         .reg_bits = 8,
378         .val_bits = 8,
379 
380         .readable_reg = pcm512x_readable,
381         .volatile_reg = pcm512x_volatile,
382 
383         .ranges = &pcm512x_range,
384         .num_ranges = 1,
385 
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 };
391 EXPORT_SYMBOL_GPL(pcm512x_regmap);
392 
393 int pcm512x_probe(struct device *dev, struct regmap *regmap)
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 }
505 EXPORT_SYMBOL_GPL(pcm512x_probe);
506 
507 void pcm512x_remove(struct device *dev)
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 }
518 EXPORT_SYMBOL_GPL(pcm512x_remove);
519 
520 #ifdef CONFIG_PM
521 static int pcm512x_suspend(struct device *dev)
522 {
523         struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
524         int ret;
525 
526         ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
527                                  PCM512x_RQPD, PCM512x_RQPD);
528         if (ret != 0) {
529                 dev_err(dev, "Failed to request power down: %d\n", ret);
530                 return ret;
531         }
532 
533         ret = regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
534                                      pcm512x->supplies);
535         if (ret != 0) {
536                 dev_err(dev, "Failed to disable supplies: %d\n", ret);
537                 return ret;
538         }
539 
540         if (!IS_ERR(pcm512x->sclk))
541                 clk_disable_unprepare(pcm512x->sclk);
542 
543         return 0;
544 }
545 
546 static int pcm512x_resume(struct device *dev)
547 {
548         struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
549         int ret;
550 
551         if (!IS_ERR(pcm512x->sclk)) {
552                 ret = clk_prepare_enable(pcm512x->sclk);
553                 if (ret != 0) {
554                         dev_err(dev, "Failed to enable SCLK: %d\n", ret);
555                         return ret;
556                 }
557         }
558 
559         ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
560                                     pcm512x->supplies);
561         if (ret != 0) {
562                 dev_err(dev, "Failed to enable supplies: %d\n", ret);
563                 return ret;
564         }
565 
566         regcache_cache_only(pcm512x->regmap, false);
567         ret = regcache_sync(pcm512x->regmap);
568         if (ret != 0) {
569                 dev_err(dev, "Failed to sync cache: %d\n", ret);
570                 return ret;
571         }
572 
573         ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
574                                  PCM512x_RQPD, 0);
575         if (ret != 0) {
576                 dev_err(dev, "Failed to remove power down: %d\n", ret);
577                 return ret;
578         }
579 
580         return 0;
581 }
582 #endif
583 
584 const struct dev_pm_ops pcm512x_pm_ops = {
585         SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
586 };
587 EXPORT_SYMBOL_GPL(pcm512x_pm_ops);
588 
589 MODULE_DESCRIPTION("ASoC PCM512x codec driver");
590 MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
591 MODULE_LICENSE("GPL v2");
592 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp