| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591 |
- /*
- * Driver for the PCM512x CODECs
- *
- * Author: Mark Brown <broonie@linaro.org>
- * Copyright 2014 Linaro Ltd
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/clk.h>
- #include <linux/pm_runtime.h>
- #include <linux/regmap.h>
- #include <linux/regulator/consumer.h>
- #include <sound/soc.h>
- #include <sound/soc-dapm.h>
- #include <sound/tlv.h>
- #include "pcm512x.h"
- #define PCM512x_NUM_SUPPLIES 3
- static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = {
- "AVDD",
- "DVDD",
- "CPVDD",
- };
- struct pcm512x_priv {
- struct regmap *regmap;
- struct clk *sclk;
- struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES];
- struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES];
- };
- /*
- * We can't use the same notifier block for more than one supply and
- * there's no way I can see to get from a callback to the caller
- * except container_of().
- */
- #define PCM512x_REGULATOR_EVENT(n) \
- static int pcm512x_regulator_event_##n(struct notifier_block *nb, \
- unsigned long event, void *data) \
- { \
- struct pcm512x_priv *pcm512x = container_of(nb, struct pcm512x_priv, \
- supply_nb[n]); \
- if (event & REGULATOR_EVENT_DISABLE) { \
- regcache_mark_dirty(pcm512x->regmap); \
- regcache_cache_only(pcm512x->regmap, true); \
- } \
- return 0; \
- }
- PCM512x_REGULATOR_EVENT(0)
- PCM512x_REGULATOR_EVENT(1)
- PCM512x_REGULATOR_EVENT(2)
- static const struct reg_default pcm512x_reg_defaults[] = {
- { PCM512x_RESET, 0x00 },
- { PCM512x_POWER, 0x00 },
- { PCM512x_MUTE, 0x00 },
- { PCM512x_DSP, 0x00 },
- { PCM512x_PLL_REF, 0x00 },
- { PCM512x_DAC_ROUTING, 0x11 },
- { PCM512x_DSP_PROGRAM, 0x01 },
- { PCM512x_CLKDET, 0x00 },
- { PCM512x_AUTO_MUTE, 0x00 },
- { PCM512x_ERROR_DETECT, 0x00 },
- { PCM512x_DIGITAL_VOLUME_1, 0x00 },
- { PCM512x_DIGITAL_VOLUME_2, 0x30 },
- { PCM512x_DIGITAL_VOLUME_3, 0x30 },
- { PCM512x_DIGITAL_MUTE_1, 0x22 },
- { PCM512x_DIGITAL_MUTE_2, 0x00 },
- { PCM512x_DIGITAL_MUTE_3, 0x07 },
- { PCM512x_OUTPUT_AMPLITUDE, 0x00 },
- { PCM512x_ANALOG_GAIN_CTRL, 0x00 },
- { PCM512x_UNDERVOLTAGE_PROT, 0x00 },
- { PCM512x_ANALOG_MUTE_CTRL, 0x00 },
- { PCM512x_ANALOG_GAIN_BOOST, 0x00 },
- { PCM512x_VCOM_CTRL_1, 0x00 },
- { PCM512x_VCOM_CTRL_2, 0x01 },
- };
- static bool pcm512x_readable(struct device *dev, unsigned int reg)
- {
- switch (reg) {
- case PCM512x_RESET:
- case PCM512x_POWER:
- case PCM512x_MUTE:
- case PCM512x_PLL_EN:
- case PCM512x_SPI_MISO_FUNCTION:
- case PCM512x_DSP:
- case PCM512x_GPIO_EN:
- case PCM512x_BCLK_LRCLK_CFG:
- case PCM512x_DSP_GPIO_INPUT:
- case PCM512x_MASTER_MODE:
- case PCM512x_PLL_REF:
- case PCM512x_PLL_COEFF_0:
- case PCM512x_PLL_COEFF_1:
- case PCM512x_PLL_COEFF_2:
- case PCM512x_PLL_COEFF_3:
- case PCM512x_PLL_COEFF_4:
- case PCM512x_DSP_CLKDIV:
- case PCM512x_DAC_CLKDIV:
- case PCM512x_NCP_CLKDIV:
- case PCM512x_OSR_CLKDIV:
- case PCM512x_MASTER_CLKDIV_1:
- case PCM512x_MASTER_CLKDIV_2:
- case PCM512x_FS_SPEED_MODE:
- case PCM512x_IDAC_1:
- case PCM512x_IDAC_2:
- case PCM512x_ERROR_DETECT:
- case PCM512x_I2S_1:
- case PCM512x_I2S_2:
- case PCM512x_DAC_ROUTING:
- case PCM512x_DSP_PROGRAM:
- case PCM512x_CLKDET:
- case PCM512x_AUTO_MUTE:
- case PCM512x_DIGITAL_VOLUME_1:
- case PCM512x_DIGITAL_VOLUME_2:
- case PCM512x_DIGITAL_VOLUME_3:
- case PCM512x_DIGITAL_MUTE_1:
- case PCM512x_DIGITAL_MUTE_2:
- case PCM512x_DIGITAL_MUTE_3:
- case PCM512x_GPIO_OUTPUT_1:
- case PCM512x_GPIO_OUTPUT_2:
- case PCM512x_GPIO_OUTPUT_3:
- case PCM512x_GPIO_OUTPUT_4:
- case PCM512x_GPIO_OUTPUT_5:
- case PCM512x_GPIO_OUTPUT_6:
- case PCM512x_GPIO_CONTROL_1:
- case PCM512x_GPIO_CONTROL_2:
- case PCM512x_OVERFLOW:
- case PCM512x_RATE_DET_1:
- case PCM512x_RATE_DET_2:
- case PCM512x_RATE_DET_3:
- case PCM512x_RATE_DET_4:
- case PCM512x_ANALOG_MUTE_DET:
- case PCM512x_GPIN:
- case PCM512x_DIGITAL_MUTE_DET:
- case PCM512x_OUTPUT_AMPLITUDE:
- case PCM512x_ANALOG_GAIN_CTRL:
- case PCM512x_UNDERVOLTAGE_PROT:
- case PCM512x_ANALOG_MUTE_CTRL:
- case PCM512x_ANALOG_GAIN_BOOST:
- case PCM512x_VCOM_CTRL_1:
- case PCM512x_VCOM_CTRL_2:
- case PCM512x_CRAM_CTRL:
- return true;
- default:
- /* There are 256 raw register addresses */
- return reg < 0xff;
- }
- }
- static bool pcm512x_volatile(struct device *dev, unsigned int reg)
- {
- switch (reg) {
- case PCM512x_PLL_EN:
- case PCM512x_OVERFLOW:
- case PCM512x_RATE_DET_1:
- case PCM512x_RATE_DET_2:
- case PCM512x_RATE_DET_3:
- case PCM512x_RATE_DET_4:
- case PCM512x_ANALOG_MUTE_DET:
- case PCM512x_GPIN:
- case PCM512x_DIGITAL_MUTE_DET:
- case PCM512x_CRAM_CTRL:
- return true;
- default:
- /* There are 256 raw register addresses */
- return reg < 0xff;
- }
- }
- static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1);
- static const DECLARE_TLV_DB_SCALE(analog_tlv, -600, 600, 0);
- static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0);
- static const char * const pcm512x_dsp_program_texts[] = {
- "FIR interpolation with de-emphasis",
- "Low latency IIR with de-emphasis",
- "High attenuation with de-emphasis",
- "Fixed process flow",
- "Ringing-less low latency FIR",
- };
- static const unsigned int pcm512x_dsp_program_values[] = {
- 1,
- 2,
- 3,
- 5,
- 7,
- };
- static SOC_VALUE_ENUM_SINGLE_DECL(pcm512x_dsp_program,
- PCM512x_DSP_PROGRAM, 0, 0x1f,
- pcm512x_dsp_program_texts,
- pcm512x_dsp_program_values);
- static const char * const pcm512x_clk_missing_text[] = {
- "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s"
- };
- static const struct soc_enum pcm512x_clk_missing =
- SOC_ENUM_SINGLE(PCM512x_CLKDET, 0, 8, pcm512x_clk_missing_text);
- static const char * const pcm512x_autom_text[] = {
- "21ms", "106ms", "213ms", "533ms", "1.07s", "2.13s", "5.33s", "10.66s"
- };
- static const struct soc_enum pcm512x_autom_l =
- SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATML_SHIFT, 8,
- pcm512x_autom_text);
- static const struct soc_enum pcm512x_autom_r =
- SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATMR_SHIFT, 8,
- pcm512x_autom_text);
- static const char * const pcm512x_ramp_rate_text[] = {
- "1 sample/update", "2 samples/update", "4 samples/update",
- "Immediate"
- };
- static const struct soc_enum pcm512x_vndf =
- SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDF_SHIFT, 4,
- pcm512x_ramp_rate_text);
- static const struct soc_enum pcm512x_vnuf =
- SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUF_SHIFT, 4,
- pcm512x_ramp_rate_text);
- static const struct soc_enum pcm512x_vedf =
- SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDF_SHIFT, 4,
- pcm512x_ramp_rate_text);
- static const char * const pcm512x_ramp_step_text[] = {
- "4dB/step", "2dB/step", "1dB/step", "0.5dB/step"
- };
- static const struct soc_enum pcm512x_vnds =
- SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDS_SHIFT, 4,
- pcm512x_ramp_step_text);
- static const struct soc_enum pcm512x_vnus =
- SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUS_SHIFT, 4,
- pcm512x_ramp_step_text);
- static const struct soc_enum pcm512x_veds =
- SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4,
- pcm512x_ramp_step_text);
- static const struct snd_kcontrol_new pcm512x_controls[] = {
- SOC_DOUBLE_R_TLV("Digital Playback Volume", PCM512x_DIGITAL_VOLUME_2,
- PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv),
- SOC_DOUBLE_TLV("Analogue Playback Volume", PCM512x_ANALOG_GAIN_CTRL,
- PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv),
- SOC_DOUBLE_TLV("Analogue Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST,
- PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv),
- SOC_DOUBLE("Digital Playback Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
- PCM512x_RQMR_SHIFT, 1, 1),
- SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1),
- SOC_ENUM("DSP Program", pcm512x_dsp_program),
- SOC_ENUM("Clock Missing Period", pcm512x_clk_missing),
- SOC_ENUM("Auto Mute Time Left", pcm512x_autom_l),
- SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r),
- SOC_SINGLE("Auto Mute Mono Switch", PCM512x_DIGITAL_MUTE_3,
- PCM512x_ACTL_SHIFT, 1, 0),
- SOC_DOUBLE("Auto Mute Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT,
- PCM512x_AMLR_SHIFT, 1, 0),
- SOC_ENUM("Volume Ramp Down Rate", pcm512x_vndf),
- SOC_ENUM("Volume Ramp Down Step", pcm512x_vnds),
- SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf),
- SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus),
- SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf),
- SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds),
- };
- static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = {
- SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_OUTPUT("OUTL"),
- SND_SOC_DAPM_OUTPUT("OUTR"),
- };
- static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = {
- { "DACL", NULL, "Playback" },
- { "DACR", NULL, "Playback" },
- { "OUTL", NULL, "DACL" },
- { "OUTR", NULL, "DACR" },
- };
- static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
- enum snd_soc_bias_level level)
- {
- struct pcm512x_priv *pcm512x = dev_get_drvdata(codec->dev);
- int ret;
- switch (level) {
- case SND_SOC_BIAS_ON:
- case SND_SOC_BIAS_PREPARE:
- break;
- case SND_SOC_BIAS_STANDBY:
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
- PCM512x_RQST, 0);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to remove standby: %d\n",
- ret);
- return ret;
- }
- break;
- case SND_SOC_BIAS_OFF:
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
- PCM512x_RQST, PCM512x_RQST);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request standby: %d\n",
- ret);
- return ret;
- }
- break;
- }
- codec->dapm.bias_level = level;
- return 0;
- }
- static struct snd_soc_dai_driver pcm512x_dai = {
- .name = "pcm512x-hifi",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE
- },
- };
- static struct snd_soc_codec_driver pcm512x_codec_driver = {
- .set_bias_level = pcm512x_set_bias_level,
- .idle_bias_off = true,
- .controls = pcm512x_controls,
- .num_controls = ARRAY_SIZE(pcm512x_controls),
- .dapm_widgets = pcm512x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets),
- .dapm_routes = pcm512x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes),
- };
- static const struct regmap_range_cfg pcm512x_range = {
- .name = "Pages", .range_min = PCM512x_VIRT_BASE,
- .range_max = PCM512x_MAX_REGISTER,
- .selector_reg = PCM512x_PAGE,
- .selector_mask = 0xff,
- .window_start = 0, .window_len = 0x100,
- };
- const struct regmap_config pcm512x_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
- .readable_reg = pcm512x_readable,
- .volatile_reg = pcm512x_volatile,
- .ranges = &pcm512x_range,
- .num_ranges = 1,
- .max_register = PCM512x_MAX_REGISTER,
- .reg_defaults = pcm512x_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(pcm512x_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
- };
- EXPORT_SYMBOL_GPL(pcm512x_regmap);
- int pcm512x_probe(struct device *dev, struct regmap *regmap)
- {
- struct pcm512x_priv *pcm512x;
- int i, ret;
- pcm512x = devm_kzalloc(dev, sizeof(struct pcm512x_priv), GFP_KERNEL);
- if (!pcm512x)
- return -ENOMEM;
- dev_set_drvdata(dev, pcm512x);
- pcm512x->regmap = regmap;
- for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++)
- pcm512x->supplies[i].supply = pcm512x_supply_names[i];
- ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm512x->supplies),
- pcm512x->supplies);
- if (ret != 0) {
- dev_err(dev, "Failed to get supplies: %d\n", ret);
- return ret;
- }
- pcm512x->supply_nb[0].notifier_call = pcm512x_regulator_event_0;
- pcm512x->supply_nb[1].notifier_call = pcm512x_regulator_event_1;
- pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2;
- for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) {
- ret = regulator_register_notifier(pcm512x->supplies[i].consumer,
- &pcm512x->supply_nb[i]);
- if (ret != 0) {
- dev_err(dev,
- "Failed to register regulator notifier: %d\n",
- ret);
- }
- }
- ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
- pcm512x->supplies);
- if (ret != 0) {
- dev_err(dev, "Failed to enable supplies: %d\n", ret);
- return ret;
- }
- /* Reset the device, verifying I/O in the process for I2C */
- ret = regmap_write(regmap, PCM512x_RESET,
- PCM512x_RSTM | PCM512x_RSTR);
- if (ret != 0) {
- dev_err(dev, "Failed to reset device: %d\n", ret);
- goto err;
- }
- ret = regmap_write(regmap, PCM512x_RESET, 0);
- if (ret != 0) {
- dev_err(dev, "Failed to reset device: %d\n", ret);
- goto err;
- }
- pcm512x->sclk = devm_clk_get(dev, NULL);
- if (IS_ERR(pcm512x->sclk)) {
- if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- dev_info(dev, "No SCLK, using BCLK: %ld\n",
- PTR_ERR(pcm512x->sclk));
- /* Disable reporting of missing SCLK as an error */
- regmap_update_bits(regmap, PCM512x_ERROR_DETECT,
- PCM512x_IDCH, PCM512x_IDCH);
- /* Switch PLL input to BCLK */
- regmap_update_bits(regmap, PCM512x_PLL_REF,
- PCM512x_SREF, PCM512x_SREF);
- } else {
- ret = clk_prepare_enable(pcm512x->sclk);
- if (ret != 0) {
- dev_err(dev, "Failed to enable SCLK: %d\n", ret);
- return ret;
- }
- }
- /* Default to standby mode */
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
- PCM512x_RQST, PCM512x_RQST);
- if (ret != 0) {
- dev_err(dev, "Failed to request standby: %d\n",
- ret);
- goto err_clk;
- }
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- pm_runtime_idle(dev);
- ret = snd_soc_register_codec(dev, &pcm512x_codec_driver,
- &pcm512x_dai, 1);
- if (ret != 0) {
- dev_err(dev, "Failed to register CODEC: %d\n", ret);
- goto err_pm;
- }
- return 0;
- err_pm:
- pm_runtime_disable(dev);
- err_clk:
- if (!IS_ERR(pcm512x->sclk))
- clk_disable_unprepare(pcm512x->sclk);
- err:
- regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
- pcm512x->supplies);
- return ret;
- }
- EXPORT_SYMBOL_GPL(pcm512x_probe);
- void pcm512x_remove(struct device *dev)
- {
- struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
- snd_soc_unregister_codec(dev);
- pm_runtime_disable(dev);
- if (!IS_ERR(pcm512x->sclk))
- clk_disable_unprepare(pcm512x->sclk);
- regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
- pcm512x->supplies);
- }
- EXPORT_SYMBOL_GPL(pcm512x_remove);
- #ifdef CONFIG_PM_RUNTIME
- static int pcm512x_suspend(struct device *dev)
- {
- struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
- int ret;
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
- PCM512x_RQPD, PCM512x_RQPD);
- if (ret != 0) {
- dev_err(dev, "Failed to request power down: %d\n", ret);
- return ret;
- }
- ret = regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
- pcm512x->supplies);
- if (ret != 0) {
- dev_err(dev, "Failed to disable supplies: %d\n", ret);
- return ret;
- }
- if (!IS_ERR(pcm512x->sclk))
- clk_disable_unprepare(pcm512x->sclk);
- return 0;
- }
- static int pcm512x_resume(struct device *dev)
- {
- struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
- int ret;
- if (!IS_ERR(pcm512x->sclk)) {
- ret = clk_prepare_enable(pcm512x->sclk);
- if (ret != 0) {
- dev_err(dev, "Failed to enable SCLK: %d\n", ret);
- return ret;
- }
- }
- ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
- pcm512x->supplies);
- if (ret != 0) {
- dev_err(dev, "Failed to enable supplies: %d\n", ret);
- return ret;
- }
- regcache_cache_only(pcm512x->regmap, false);
- ret = regcache_sync(pcm512x->regmap);
- if (ret != 0) {
- dev_err(dev, "Failed to sync cache: %d\n", ret);
- return ret;
- }
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
- PCM512x_RQPD, 0);
- if (ret != 0) {
- dev_err(dev, "Failed to remove power down: %d\n", ret);
- return ret;
- }
- return 0;
- }
- #endif
- const struct dev_pm_ops pcm512x_pm_ops = {
- SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
- };
- EXPORT_SYMBOL_GPL(pcm512x_pm_ops);
- MODULE_DESCRIPTION("ASoC PCM512x codec driver");
- MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
- MODULE_LICENSE("GPL v2");
|