726 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * wm8804.c -- WM8804 S/PDIF transceiver driver4 *5 * Copyright 2010-11 Wolfson Microelectronics plc6 *7 * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>8 */9 10#include <linux/module.h>11#include <linux/moduleparam.h>12#include <linux/init.h>13#include <linux/gpio/consumer.h>14#include <linux/delay.h>15#include <linux/pm.h>16#include <linux/pm_runtime.h>17#include <linux/regulator/consumer.h>18#include <linux/slab.h>19#include <sound/core.h>20#include <sound/pcm.h>21#include <sound/pcm_params.h>22#include <sound/soc.h>23#include <sound/initval.h>24#include <sound/tlv.h>25#include <sound/soc-dapm.h>26 27#include "wm8804.h"28 29#define WM8804_NUM_SUPPLIES 230static const char *wm8804_supply_names[WM8804_NUM_SUPPLIES] = {31 "PVDD",32 "DVDD"33};34 35static const struct reg_default wm8804_reg_defaults[] = {36 { 3, 0x21 }, /* R3 - PLL1 */37 { 4, 0xFD }, /* R4 - PLL2 */38 { 5, 0x36 }, /* R5 - PLL3 */39 { 6, 0x07 }, /* R6 - PLL4 */40 { 7, 0x16 }, /* R7 - PLL5 */41 { 8, 0x18 }, /* R8 - PLL6 */42 { 9, 0xFF }, /* R9 - SPDMODE */43 { 10, 0x00 }, /* R10 - INTMASK */44 { 18, 0x00 }, /* R18 - SPDTX1 */45 { 19, 0x00 }, /* R19 - SPDTX2 */46 { 20, 0x00 }, /* R20 - SPDTX3 */47 { 21, 0x71 }, /* R21 - SPDTX4 */48 { 22, 0x0B }, /* R22 - SPDTX5 */49 { 23, 0x70 }, /* R23 - GPO0 */50 { 24, 0x57 }, /* R24 - GPO1 */51 { 26, 0x42 }, /* R26 - GPO2 */52 { 27, 0x06 }, /* R27 - AIFTX */53 { 28, 0x06 }, /* R28 - AIFRX */54 { 29, 0x80 }, /* R29 - SPDRX1 */55 { 30, 0x07 }, /* R30 - PWRDN */56};57 58struct wm8804_priv {59 struct device *dev;60 struct regmap *regmap;61 struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES];62 struct notifier_block disable_nb[WM8804_NUM_SUPPLIES];63 int mclk_div;64 65 struct gpio_desc *reset;66 67 int aif_pwr;68};69 70static int txsrc_put(struct snd_kcontrol *kcontrol,71 struct snd_ctl_elem_value *ucontrol);72 73static int wm8804_aif_event(struct snd_soc_dapm_widget *w,74 struct snd_kcontrol *kcontrol, int event);75 76/*77 * We can't use the same notifier block for more than one supply and78 * there's no way I can see to get from a callback to the caller79 * except container_of().80 */81#define WM8804_REGULATOR_EVENT(n) \82static int wm8804_regulator_event_##n(struct notifier_block *nb, \83 unsigned long event, void *data) \84{ \85 struct wm8804_priv *wm8804 = container_of(nb, struct wm8804_priv, \86 disable_nb[n]); \87 if (event & REGULATOR_EVENT_DISABLE) { \88 regcache_mark_dirty(wm8804->regmap); \89 } \90 return 0; \91}92 93WM8804_REGULATOR_EVENT(0)94WM8804_REGULATOR_EVENT(1)95 96static const char *txsrc_text[] = { "S/PDIF RX", "AIF" };97static SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text);98 99static const struct snd_kcontrol_new wm8804_tx_source_mux[] = {100 SOC_DAPM_ENUM_EXT("Input Source", txsrc,101 snd_soc_dapm_get_enum_double, txsrc_put),102};103 104static const struct snd_soc_dapm_widget wm8804_dapm_widgets[] = {105SND_SOC_DAPM_OUTPUT("SPDIF Out"),106SND_SOC_DAPM_INPUT("SPDIF In"),107 108SND_SOC_DAPM_PGA("SPDIFTX", WM8804_PWRDN, 2, 1, NULL, 0),109SND_SOC_DAPM_PGA("SPDIFRX", WM8804_PWRDN, 1, 1, NULL, 0),110 111SND_SOC_DAPM_MUX("Tx Source", SND_SOC_NOPM, 6, 0, wm8804_tx_source_mux),112 113SND_SOC_DAPM_AIF_OUT_E("AIFTX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event,114 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),115SND_SOC_DAPM_AIF_IN_E("AIFRX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event,116 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),117};118 119static const struct snd_soc_dapm_route wm8804_dapm_routes[] = {120 { "AIFRX", NULL, "Playback" },121 { "Tx Source", "AIF", "AIFRX" },122 123 { "SPDIFRX", NULL, "SPDIF In" },124 { "Tx Source", "S/PDIF RX", "SPDIFRX" },125 126 { "SPDIFTX", NULL, "Tx Source" },127 { "SPDIF Out", NULL, "SPDIFTX" },128 129 { "AIFTX", NULL, "SPDIFRX" },130 { "Capture", NULL, "AIFTX" },131};132 133static int wm8804_aif_event(struct snd_soc_dapm_widget *w,134 struct snd_kcontrol *kcontrol, int event)135{136 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);137 struct wm8804_priv *wm8804 = snd_soc_component_get_drvdata(component);138 139 switch (event) {140 case SND_SOC_DAPM_POST_PMU:141 /* power up the aif */142 if (!wm8804->aif_pwr)143 snd_soc_component_update_bits(component, WM8804_PWRDN, 0x10, 0x0);144 wm8804->aif_pwr++;145 break;146 case SND_SOC_DAPM_POST_PMD:147 /* power down only both paths are disabled */148 wm8804->aif_pwr--;149 if (!wm8804->aif_pwr)150 snd_soc_component_update_bits(component, WM8804_PWRDN, 0x10, 0x10);151 break;152 }153 154 return 0;155}156 157static int txsrc_put(struct snd_kcontrol *kcontrol,158 struct snd_ctl_elem_value *ucontrol)159{160 struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);161 struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);162 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;163 unsigned int val = ucontrol->value.enumerated.item[0] << e->shift_l;164 unsigned int mask = 1 << e->shift_l;165 unsigned int txpwr;166 167 if (val != 0 && val != mask)168 return -EINVAL;169 170 snd_soc_dapm_mutex_lock(dapm);171 172 if (snd_soc_component_test_bits(component, e->reg, mask, val)) {173 /* save the current power state of the transmitter */174 txpwr = snd_soc_component_read(component, WM8804_PWRDN) & 0x4;175 176 /* power down the transmitter */177 snd_soc_component_update_bits(component, WM8804_PWRDN, 0x4, 0x4);178 179 /* set the tx source */180 snd_soc_component_update_bits(component, e->reg, mask, val);181 182 /* restore the transmitter's configuration */183 snd_soc_component_update_bits(component, WM8804_PWRDN, 0x4, txpwr);184 }185 186 snd_soc_dapm_mutex_unlock(dapm);187 188 return 0;189}190 191static bool wm8804_volatile(struct device *dev, unsigned int reg)192{193 switch (reg) {194 case WM8804_RST_DEVID1:195 case WM8804_DEVID2:196 case WM8804_DEVREV:197 case WM8804_INTSTAT:198 case WM8804_SPDSTAT:199 case WM8804_RXCHAN1:200 case WM8804_RXCHAN2:201 case WM8804_RXCHAN3:202 case WM8804_RXCHAN4:203 case WM8804_RXCHAN5:204 return true;205 default:206 return false;207 }208}209 210static int wm8804_soft_reset(struct wm8804_priv *wm8804)211{212 return regmap_write(wm8804->regmap, WM8804_RST_DEVID1, 0x0);213}214 215static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)216{217 struct snd_soc_component *component;218 u16 format, master, bcp, lrp;219 220 component = dai->component;221 222 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {223 case SND_SOC_DAIFMT_I2S:224 format = 0x2;225 break;226 case SND_SOC_DAIFMT_RIGHT_J:227 format = 0x0;228 break;229 case SND_SOC_DAIFMT_LEFT_J:230 format = 0x1;231 break;232 case SND_SOC_DAIFMT_DSP_A:233 case SND_SOC_DAIFMT_DSP_B:234 format = 0x3;235 break;236 default:237 dev_err(dai->dev, "Unknown dai format\n");238 return -EINVAL;239 }240 241 /* set data format */242 snd_soc_component_update_bits(component, WM8804_AIFTX, 0x3, format);243 snd_soc_component_update_bits(component, WM8804_AIFRX, 0x3, format);244 245 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {246 case SND_SOC_DAIFMT_CBM_CFM:247 master = 1;248 break;249 case SND_SOC_DAIFMT_CBS_CFS:250 master = 0;251 break;252 default:253 dev_err(dai->dev, "Unknown master/slave configuration\n");254 return -EINVAL;255 }256 257 /* set master/slave mode */258 snd_soc_component_update_bits(component, WM8804_AIFRX, 0x40, master << 6);259 260 bcp = lrp = 0;261 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {262 case SND_SOC_DAIFMT_NB_NF:263 break;264 case SND_SOC_DAIFMT_IB_IF:265 bcp = lrp = 1;266 break;267 case SND_SOC_DAIFMT_IB_NF:268 bcp = 1;269 break;270 case SND_SOC_DAIFMT_NB_IF:271 lrp = 1;272 break;273 default:274 dev_err(dai->dev, "Unknown polarity configuration\n");275 return -EINVAL;276 }277 278 /* set frame inversion */279 snd_soc_component_update_bits(component, WM8804_AIFTX, 0x10 | 0x20,280 (bcp << 4) | (lrp << 5));281 snd_soc_component_update_bits(component, WM8804_AIFRX, 0x10 | 0x20,282 (bcp << 4) | (lrp << 5));283 return 0;284}285 286static int wm8804_hw_params(struct snd_pcm_substream *substream,287 struct snd_pcm_hw_params *params,288 struct snd_soc_dai *dai)289{290 struct snd_soc_component *component;291 u16 blen;292 293 component = dai->component;294 295 switch (params_width(params)) {296 case 16:297 blen = 0x0;298 break;299 case 20:300 blen = 0x1;301 break;302 case 24:303 blen = 0x2;304 break;305 default:306 dev_err(dai->dev, "Unsupported word length: %u\n",307 params_width(params));308 return -EINVAL;309 }310 311 /* set word length */312 snd_soc_component_update_bits(component, WM8804_AIFTX, 0xc, blen << 2);313 snd_soc_component_update_bits(component, WM8804_AIFRX, 0xc, blen << 2);314 315 return 0;316}317 318struct pll_div {319 u32 prescale:1;320 u32 mclkdiv:1;321 u32 freqmode:2;322 u32 n:4;323 u32 k:22;324};325 326/* PLL rate to output rate divisions */327static struct {328 unsigned int div;329 unsigned int freqmode;330 unsigned int mclkdiv;331} post_table[] = {332 { 2, 0, 0 },333 { 4, 0, 1 },334 { 4, 1, 0 },335 { 8, 1, 1 },336 { 8, 2, 0 },337 { 16, 2, 1 },338 { 12, 3, 0 },339 { 24, 3, 1 }340};341 342#define FIXED_PLL_SIZE ((1ULL << 22) * 10)343static int pll_factors(struct pll_div *pll_div, unsigned int target,344 unsigned int source, unsigned int mclk_div)345{346 u64 Kpart;347 unsigned long int K, Ndiv, Nmod, tmp;348 int i;349 350 /*351 * Scale the output frequency up; the PLL should run in the352 * region of 90-100MHz.353 */354 for (i = 0; i < ARRAY_SIZE(post_table); i++) {355 tmp = target * post_table[i].div;356 if ((tmp >= 90000000 && tmp <= 100000000) &&357 (mclk_div == post_table[i].mclkdiv)) {358 pll_div->freqmode = post_table[i].freqmode;359 pll_div->mclkdiv = post_table[i].mclkdiv;360 target *= post_table[i].div;361 break;362 }363 }364 365 if (i == ARRAY_SIZE(post_table)) {366 pr_err("%s: Unable to scale output frequency: %uHz\n",367 __func__, target);368 return -EINVAL;369 }370 371 pll_div->prescale = 0;372 Ndiv = target / source;373 if (Ndiv < 5) {374 source >>= 1;375 pll_div->prescale = 1;376 Ndiv = target / source;377 }378 379 if (Ndiv < 5 || Ndiv > 13) {380 pr_err("%s: WM8804 N value is not within the recommended range: %lu\n",381 __func__, Ndiv);382 return -EINVAL;383 }384 pll_div->n = Ndiv;385 386 Nmod = target % source;387 Kpart = FIXED_PLL_SIZE * (u64)Nmod;388 389 do_div(Kpart, source);390 391 K = Kpart & 0xffffffff;392 if ((K % 10) >= 5)393 K += 5;394 K /= 10;395 pll_div->k = K;396 397 return 0;398}399 400static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,401 int source, unsigned int freq_in,402 unsigned int freq_out)403{404 struct snd_soc_component *component = dai->component;405 struct wm8804_priv *wm8804 = snd_soc_component_get_drvdata(component);406 bool change;407 408 if (!freq_in || !freq_out) {409 /* disable the PLL */410 regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN,411 0x1, 0x1, &change);412 if (change)413 pm_runtime_put(wm8804->dev);414 } else {415 int ret;416 struct pll_div pll_div;417 418 ret = pll_factors(&pll_div, freq_out, freq_in,419 wm8804->mclk_div);420 if (ret)421 return ret;422 423 /* power down the PLL before reprogramming it */424 regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN,425 0x1, 0x1, &change);426 if (!change)427 pm_runtime_get_sync(wm8804->dev);428 429 /* set PLLN and PRESCALE */430 snd_soc_component_update_bits(component, WM8804_PLL4, 0xf | 0x10,431 pll_div.n | (pll_div.prescale << 4));432 /* set mclkdiv and freqmode */433 snd_soc_component_update_bits(component, WM8804_PLL5, 0x3 | 0x8,434 pll_div.freqmode | (pll_div.mclkdiv << 3));435 /* set PLLK */436 snd_soc_component_write(component, WM8804_PLL1, pll_div.k & 0xff);437 snd_soc_component_write(component, WM8804_PLL2, (pll_div.k >> 8) & 0xff);438 snd_soc_component_write(component, WM8804_PLL3, pll_div.k >> 16);439 440 /* power up the PLL */441 snd_soc_component_update_bits(component, WM8804_PWRDN, 0x1, 0);442 }443 444 return 0;445}446 447static int wm8804_set_sysclk(struct snd_soc_dai *dai,448 int clk_id, unsigned int freq, int dir)449{450 struct snd_soc_component *component;451 452 component = dai->component;453 454 switch (clk_id) {455 case WM8804_TX_CLKSRC_MCLK:456 if ((freq >= 10000000 && freq <= 14400000)457 || (freq >= 16280000 && freq <= 27000000))458 snd_soc_component_update_bits(component, WM8804_PLL6, 0x80, 0x80);459 else {460 dev_err(dai->dev, "OSCCLOCK is not within the "461 "recommended range: %uHz\n", freq);462 return -EINVAL;463 }464 break;465 case WM8804_TX_CLKSRC_PLL:466 snd_soc_component_update_bits(component, WM8804_PLL6, 0x80, 0);467 break;468 case WM8804_CLKOUT_SRC_CLK1:469 snd_soc_component_update_bits(component, WM8804_PLL6, 0x8, 0);470 break;471 case WM8804_CLKOUT_SRC_OSCCLK:472 snd_soc_component_update_bits(component, WM8804_PLL6, 0x8, 0x8);473 break;474 default:475 dev_err(dai->dev, "Unknown clock source: %d\n", clk_id);476 return -EINVAL;477 }478 479 return 0;480}481 482static int wm8804_set_clkdiv(struct snd_soc_dai *dai,483 int div_id, int div)484{485 struct snd_soc_component *component;486 struct wm8804_priv *wm8804;487 488 component = dai->component;489 switch (div_id) {490 case WM8804_CLKOUT_DIV:491 snd_soc_component_update_bits(component, WM8804_PLL5, 0x30,492 (div & 0x3) << 4);493 break;494 case WM8804_MCLK_DIV:495 wm8804 = snd_soc_component_get_drvdata(component);496 wm8804->mclk_div = div;497 break;498 default:499 dev_err(dai->dev, "Unknown clock divider: %d\n", div_id);500 return -EINVAL;501 }502 return 0;503}504 505static const struct snd_soc_dai_ops wm8804_dai_ops = {506 .hw_params = wm8804_hw_params,507 .set_fmt = wm8804_set_fmt,508 .set_sysclk = wm8804_set_sysclk,509 .set_clkdiv = wm8804_set_clkdiv,510 .set_pll = wm8804_set_pll511};512 513#define WM8804_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \514 SNDRV_PCM_FMTBIT_S24_LE)515 516#define WM8804_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \517 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \518 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \519 SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)520 521static struct snd_soc_dai_driver wm8804_dai = {522 .name = "wm8804-spdif",523 .playback = {524 .stream_name = "Playback",525 .channels_min = 2,526 .channels_max = 2,527 .rates = WM8804_RATES,528 .formats = WM8804_FORMATS,529 },530 .capture = {531 .stream_name = "Capture",532 .channels_min = 2,533 .channels_max = 2,534 .rates = WM8804_RATES,535 .formats = WM8804_FORMATS,536 },537 .ops = &wm8804_dai_ops,538 .symmetric_rate = 1539};540 541static const struct snd_soc_component_driver soc_component_dev_wm8804 = {542 .dapm_widgets = wm8804_dapm_widgets,543 .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets),544 .dapm_routes = wm8804_dapm_routes,545 .num_dapm_routes = ARRAY_SIZE(wm8804_dapm_routes),546 .use_pmdown_time = 1,547 .endianness = 1,548};549 550const struct regmap_config wm8804_regmap_config = {551 .reg_bits = 8,552 .val_bits = 8,553 554 .max_register = WM8804_MAX_REGISTER,555 .volatile_reg = wm8804_volatile,556 557 .cache_type = REGCACHE_MAPLE,558 .reg_defaults = wm8804_reg_defaults,559 .num_reg_defaults = ARRAY_SIZE(wm8804_reg_defaults),560};561EXPORT_SYMBOL_GPL(wm8804_regmap_config);562 563int wm8804_probe(struct device *dev, struct regmap *regmap)564{565 struct wm8804_priv *wm8804;566 unsigned int id1, id2;567 int i, ret;568 569 wm8804 = devm_kzalloc(dev, sizeof(*wm8804), GFP_KERNEL);570 if (!wm8804)571 return -ENOMEM;572 573 dev_set_drvdata(dev, wm8804);574 575 wm8804->dev = dev;576 wm8804->regmap = regmap;577 578 wm8804->reset = devm_gpiod_get_optional(dev, "wlf,reset",579 GPIOD_OUT_LOW);580 if (IS_ERR(wm8804->reset)) {581 ret = PTR_ERR(wm8804->reset);582 dev_err(dev, "Failed to get reset line: %d\n", ret);583 return ret;584 }585 586 for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++)587 wm8804->supplies[i].supply = wm8804_supply_names[i];588 589 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8804->supplies),590 wm8804->supplies);591 if (ret) {592 dev_err(dev, "Failed to request supplies: %d\n", ret);593 return ret;594 }595 596 wm8804->disable_nb[0].notifier_call = wm8804_regulator_event_0;597 wm8804->disable_nb[1].notifier_call = wm8804_regulator_event_1;598 599 /* This should really be moved into the regulator core */600 for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) {601 struct regulator *regulator = wm8804->supplies[i].consumer;602 603 ret = devm_regulator_register_notifier(regulator,604 &wm8804->disable_nb[i]);605 if (ret != 0) {606 dev_err(dev,607 "Failed to register regulator notifier: %d\n",608 ret);609 return ret;610 }611 }612 613 ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies),614 wm8804->supplies);615 if (ret) {616 dev_err(dev, "Failed to enable supplies: %d\n", ret);617 return ret;618 }619 620 gpiod_set_value_cansleep(wm8804->reset, 1);621 622 ret = regmap_read(regmap, WM8804_RST_DEVID1, &id1);623 if (ret < 0) {624 dev_err(dev, "Failed to read device ID: %d\n", ret);625 goto err_reg_enable;626 }627 628 ret = regmap_read(regmap, WM8804_DEVID2, &id2);629 if (ret < 0) {630 dev_err(dev, "Failed to read device ID: %d\n", ret);631 goto err_reg_enable;632 }633 634 id2 = (id2 << 8) | id1;635 636 if (id2 != 0x8805) {637 dev_err(dev, "Invalid device ID: %#x\n", id2);638 ret = -EINVAL;639 goto err_reg_enable;640 }641 642 ret = regmap_read(regmap, WM8804_DEVREV, &id1);643 if (ret < 0) {644 dev_err(dev, "Failed to read device revision: %d\n",645 ret);646 goto err_reg_enable;647 }648 dev_info(dev, "revision %c\n", id1 + 'A');649 650 if (!wm8804->reset) {651 ret = wm8804_soft_reset(wm8804);652 if (ret < 0) {653 dev_err(dev, "Failed to issue reset: %d\n", ret);654 goto err_reg_enable;655 }656 }657 658 ret = devm_snd_soc_register_component(dev, &soc_component_dev_wm8804,659 &wm8804_dai, 1);660 if (ret < 0) {661 dev_err(dev, "Failed to register CODEC: %d\n", ret);662 goto err_reg_enable;663 }664 665 pm_runtime_set_active(dev);666 pm_runtime_enable(dev);667 pm_runtime_idle(dev);668 669 return 0;670 671err_reg_enable:672 regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies);673 return ret;674}675EXPORT_SYMBOL_GPL(wm8804_probe);676 677void wm8804_remove(struct device *dev)678{679 pm_runtime_disable(dev);680}681EXPORT_SYMBOL_GPL(wm8804_remove);682 683#if IS_ENABLED(CONFIG_PM)684static int wm8804_runtime_resume(struct device *dev)685{686 struct wm8804_priv *wm8804 = dev_get_drvdata(dev);687 int ret;688 689 ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies),690 wm8804->supplies);691 if (ret) {692 dev_err(wm8804->dev, "Failed to enable supplies: %d\n", ret);693 return ret;694 }695 696 regcache_sync(wm8804->regmap);697 698 /* Power up OSCCLK */699 regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x0);700 701 return 0;702}703 704static int wm8804_runtime_suspend(struct device *dev)705{706 struct wm8804_priv *wm8804 = dev_get_drvdata(dev);707 708 /* Power down OSCCLK */709 regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x8);710 711 regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies),712 wm8804->supplies);713 714 return 0;715}716#endif717 718const struct dev_pm_ops wm8804_pm = {719 SET_RUNTIME_PM_OPS(wm8804_runtime_suspend, wm8804_runtime_resume, NULL)720};721EXPORT_SYMBOL_GPL(wm8804_pm);722 723MODULE_DESCRIPTION("ASoC WM8804 driver");724MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");725MODULE_LICENSE("GPL");726