brintos

brintos / linux-shallow public Read only

0
0
Text · 18.0 KiB · cfa78e4 Raw
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