Merge remote-tracking branches 'asoc/topic/wm2200', 'asoc/topic/wm5100', 'asoc/topic/wm8731', 'asoc/topic/wm8804' and 'asoc/topic/wm8996' into asoc-next

This commit is contained in:
Mark Brown 2015-04-12 19:49:22 +01:00
11 changed files with 337 additions and 137 deletions

View File

@ -10,6 +10,13 @@ Required properties:
- reg : the I2C address of the device for I2C, the chip select - reg : the I2C address of the device for I2C, the chip select
number for SPI. number for SPI.
- PVDD-supply, DVDD-supply : Power supplies for the device, as covered
in Documentation/devicetree/bindings/regulator/regulator.txt
Optional properties:
- wlf,reset-gpio: A GPIO specifier for the GPIO controlling the reset pin
Example: Example:
codec: wm8804@1a { codec: wm8804@1a {

View File

@ -413,3 +413,88 @@ void devm_regulator_bulk_unregister_supply_alias(struct device *dev,
devm_regulator_unregister_supply_alias(dev, id[i]); devm_regulator_unregister_supply_alias(dev, id[i]);
} }
EXPORT_SYMBOL_GPL(devm_regulator_bulk_unregister_supply_alias); EXPORT_SYMBOL_GPL(devm_regulator_bulk_unregister_supply_alias);
struct regulator_notifier_match {
struct regulator *regulator;
struct notifier_block *nb;
};
static int devm_regulator_match_notifier(struct device *dev, void *res,
void *data)
{
struct regulator_notifier_match *match = res;
struct regulator_notifier_match *target = data;
return match->regulator == target->regulator && match->nb == target->nb;
}
static void devm_regulator_destroy_notifier(struct device *dev, void *res)
{
struct regulator_notifier_match *match = res;
regulator_unregister_notifier(match->regulator, match->nb);
}
/**
* devm_regulator_register_notifier - Resource managed
* regulator_register_notifier
*
* @regulator: regulator source
* @nb: notifier block
*
* The notifier will be registers under the consumer device and be
* automatically be unregistered when the source device is unbound.
*/
int devm_regulator_register_notifier(struct regulator *regulator,
struct notifier_block *nb)
{
struct regulator_notifier_match *match;
int ret;
match = devres_alloc(devm_regulator_destroy_notifier,
sizeof(struct regulator_notifier_match),
GFP_KERNEL);
if (!match)
return -ENOMEM;
match->regulator = regulator;
match->nb = nb;
ret = regulator_register_notifier(regulator, nb);
if (ret < 0) {
devres_free(match);
return ret;
}
devres_add(regulator->dev, match);
return 0;
}
EXPORT_SYMBOL_GPL(devm_regulator_register_notifier);
/**
* devm_regulator_unregister_notifier - Resource managed
* regulator_unregister_notifier()
*
* @regulator: regulator source
* @nb: notifier block
*
* Unregister a notifier registered with devm_regulator_register_notifier().
* Normally this function will not need to be called and the resource
* management code will ensure that the resource is freed.
*/
void devm_regulator_unregister_notifier(struct regulator *regulator,
struct notifier_block *nb)
{
struct regulator_notifier_match match;
int rc;
match.regulator = regulator;
match.nb = nb;
rc = devres_release(regulator->dev, devm_regulator_destroy_notifier,
devm_regulator_match_notifier, &match);
if (rc != 0)
WARN_ON(rc);
}
EXPORT_SYMBOL_GPL(devm_regulator_unregister_notifier);

View File

@ -252,8 +252,12 @@ int regulator_list_hardware_vsel(struct regulator *regulator,
/* regulator notifier block */ /* regulator notifier block */
int regulator_register_notifier(struct regulator *regulator, int regulator_register_notifier(struct regulator *regulator,
struct notifier_block *nb); struct notifier_block *nb);
int devm_regulator_register_notifier(struct regulator *regulator,
struct notifier_block *nb);
int regulator_unregister_notifier(struct regulator *regulator, int regulator_unregister_notifier(struct regulator *regulator,
struct notifier_block *nb); struct notifier_block *nb);
void devm_regulator_unregister_notifier(struct regulator *regulator,
struct notifier_block *nb);
/* driver data - core doesn't touch */ /* driver data - core doesn't touch */
void *regulator_get_drvdata(struct regulator *regulator); void *regulator_get_drvdata(struct regulator *regulator);
@ -515,12 +519,24 @@ static inline int regulator_register_notifier(struct regulator *regulator,
return 0; return 0;
} }
static inline int devm_regulator_register_notifier(struct regulator *regulator,
struct notifier_block *nb)
{
return 0;
}
static inline int regulator_unregister_notifier(struct regulator *regulator, static inline int regulator_unregister_notifier(struct regulator *regulator,
struct notifier_block *nb) struct notifier_block *nb)
{ {
return 0; return 0;
} }
static inline int devm_regulator_unregister_notifier(struct regulator *regulator,
struct notifier_block *nb)
{
return 0;
}
static inline void *regulator_get_drvdata(struct regulator *regulator) static inline void *regulator_get_drvdata(struct regulator *regulator)
{ {
return NULL; return NULL;

View File

@ -1554,7 +1554,6 @@ static int wm2200_probe(struct snd_soc_codec *codec)
int ret; int ret;
wm2200->codec = codec; wm2200->codec = codec;
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
ret = snd_soc_add_codec_controls(codec, wm_adsp1_fw_controls, 2); ret = snd_soc_add_codec_controls(codec, wm_adsp1_fw_controls, 2);
if (ret != 0) if (ret != 0)
@ -1942,6 +1941,7 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec); struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec);
struct _fll_div factors; struct _fll_div factors;
int ret, i, timeout; int ret, i, timeout;
unsigned long time_left;
if (!Fout) { if (!Fout) {
dev_dbg(codec->dev, "FLL disabled"); dev_dbg(codec->dev, "FLL disabled");
@ -2021,9 +2021,10 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
/* Poll for the lock; will use the interrupt to exit quickly */ /* Poll for the lock; will use the interrupt to exit quickly */
for (i = 0; i < timeout; i++) { for (i = 0; i < timeout; i++) {
if (i2c->irq) { if (i2c->irq) {
ret = wait_for_completion_timeout(&wm2200->fll_lock, time_left = wait_for_completion_timeout(
&wm2200->fll_lock,
msecs_to_jiffies(25)); msecs_to_jiffies(25));
if (ret > 0) if (time_left > 0)
break; break;
} else { } else {
msleep(1); msleep(1);

View File

@ -1762,6 +1762,7 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
struct _fll_div factors; struct _fll_div factors;
struct wm5100_fll *fll; struct wm5100_fll *fll;
int ret, base, lock, i, timeout; int ret, base, lock, i, timeout;
unsigned long time_left;
switch (fll_id) { switch (fll_id) {
case WM5100_FLL1: case WM5100_FLL1:
@ -1842,9 +1843,9 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
/* Poll for the lock; will use interrupt when we can test */ /* Poll for the lock; will use interrupt when we can test */
for (i = 0; i < timeout; i++) { for (i = 0; i < timeout; i++) {
if (i2c->irq) { if (i2c->irq) {
ret = wait_for_completion_timeout(&fll->lock, time_left = wait_for_completion_timeout(&fll->lock,
msecs_to_jiffies(25)); msecs_to_jiffies(25));
if (ret > 0) if (time_left > 0)
break; break;
} else { } else {
msleep(1); msleep(1);

View File

@ -25,6 +25,7 @@
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/clk.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
@ -45,6 +46,7 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
/* codec private data */ /* codec private data */
struct wm8731_priv { struct wm8731_priv {
struct regmap *regmap; struct regmap *regmap;
struct clk *mclk;
struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
const struct snd_pcm_hw_constraint_list *constraints; const struct snd_pcm_hw_constraint_list *constraints;
unsigned int sysclk; unsigned int sysclk;
@ -390,6 +392,8 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
switch (clk_id) { switch (clk_id) {
case WM8731_SYSCLK_XTAL: case WM8731_SYSCLK_XTAL:
case WM8731_SYSCLK_MCLK: case WM8731_SYSCLK_MCLK:
if (wm8731->mclk && clk_set_rate(wm8731->mclk, freq))
return -EINVAL;
wm8731->sysclk_type = clk_id; wm8731->sysclk_type = clk_id;
break; break;
default: default:
@ -491,6 +495,8 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
switch (level) { switch (level) {
case SND_SOC_BIAS_ON: case SND_SOC_BIAS_ON:
if (wm8731->mclk)
clk_prepare_enable(wm8731->mclk);
break; break;
case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_PREPARE:
break; break;
@ -509,6 +515,8 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, WM8731_PWR, reg | 0x0040); snd_soc_write(codec, WM8731_PWR, reg | 0x0040);
break; break;
case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_OFF:
if (wm8731->mclk)
clk_disable_unprepare(wm8731->mclk);
snd_soc_write(codec, WM8731_PWR, 0xffff); snd_soc_write(codec, WM8731_PWR, 0xffff);
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
wm8731->supplies); wm8731->supplies);
@ -667,6 +675,19 @@ static int wm8731_spi_probe(struct spi_device *spi)
if (wm8731 == NULL) if (wm8731 == NULL)
return -ENOMEM; return -ENOMEM;
wm8731->mclk = devm_clk_get(&spi->dev, "mclk");
if (IS_ERR(wm8731->mclk)) {
ret = PTR_ERR(wm8731->mclk);
if (ret == -ENOENT) {
wm8731->mclk = NULL;
dev_warn(&spi->dev, "Assuming static MCLK\n");
} else {
dev_err(&spi->dev, "Failed to get MCLK: %d\n",
ret);
return ret;
}
}
mutex_init(&wm8731->lock); mutex_init(&wm8731->lock);
wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
@ -718,6 +739,19 @@ static int wm8731_i2c_probe(struct i2c_client *i2c,
if (wm8731 == NULL) if (wm8731 == NULL)
return -ENOMEM; return -ENOMEM;
wm8731->mclk = devm_clk_get(&i2c->dev, "mclk");
if (IS_ERR(wm8731->mclk)) {
ret = PTR_ERR(wm8731->mclk);
if (ret == -ENOENT) {
wm8731->mclk = NULL;
dev_warn(&i2c->dev, "Assuming static MCLK\n");
} else {
dev_err(&i2c->dev, "Failed to get MCLK: %d\n",
ret);
return ret;
}
}
mutex_init(&wm8731->lock); mutex_init(&wm8731->lock);
wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap);

View File

@ -50,6 +50,7 @@ static struct i2c_driver wm8804_i2c_driver = {
.driver = { .driver = {
.name = "wm8804", .name = "wm8804",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &wm8804_pm,
.of_match_table = wm8804_of_match, .of_match_table = wm8804_of_match,
}, },
.probe = wm8804_i2c_probe, .probe = wm8804_i2c_probe,

View File

@ -43,6 +43,7 @@ static struct spi_driver wm8804_spi_driver = {
.driver = { .driver = {
.name = "wm8804", .name = "wm8804",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &wm8804_pm,
.of_match_table = wm8804_of_match, .of_match_table = wm8804_of_match,
}, },
.probe = wm8804_spi_probe, .probe = wm8804_spi_probe,

View File

@ -13,8 +13,10 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -24,6 +26,7 @@
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/tlv.h> #include <sound/tlv.h>
#include <sound/soc-dapm.h>
#include "wm8804.h" #include "wm8804.h"
@ -57,18 +60,23 @@ static const struct reg_default wm8804_reg_defaults[] = {
}; };
struct wm8804_priv { struct wm8804_priv {
struct device *dev;
struct regmap *regmap; struct regmap *regmap;
struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES]; struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES];
struct notifier_block disable_nb[WM8804_NUM_SUPPLIES]; struct notifier_block disable_nb[WM8804_NUM_SUPPLIES];
int mclk_div; int mclk_div;
};
static int txsrc_get(struct snd_kcontrol *kcontrol, struct gpio_desc *reset;
struct snd_ctl_elem_value *ucontrol);
int aif_pwr;
};
static int txsrc_put(struct snd_kcontrol *kcontrol, static int txsrc_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol); struct snd_ctl_elem_value *ucontrol);
static int wm8804_aif_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
/* /*
* We can't use the same notifier block for more than one supply and * 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 * there's no way I can see to get from a callback to the caller
@ -90,26 +98,62 @@ WM8804_REGULATOR_EVENT(0)
WM8804_REGULATOR_EVENT(1) WM8804_REGULATOR_EVENT(1)
static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; static const char *txsrc_text[] = { "S/PDIF RX", "AIF" };
static SOC_ENUM_SINGLE_EXT_DECL(txsrc, txsrc_text); static const SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text);
static const struct snd_kcontrol_new wm8804_snd_controls[] = { static const struct snd_kcontrol_new wm8804_tx_source_mux[] = {
SOC_ENUM_EXT("Input Source", txsrc, txsrc_get, txsrc_put), SOC_DAPM_ENUM_EXT("Input Source", txsrc,
SOC_SINGLE("TX Playback Switch", WM8804_PWRDN, 2, 1, 1), snd_soc_dapm_get_enum_double, txsrc_put),
SOC_SINGLE("AIF Playback Switch", WM8804_PWRDN, 4, 1, 1)
}; };
static int txsrc_get(struct snd_kcontrol *kcontrol, static const struct snd_soc_dapm_widget wm8804_dapm_widgets[] = {
struct snd_ctl_elem_value *ucontrol) SND_SOC_DAPM_OUTPUT("SPDIF Out"),
{ SND_SOC_DAPM_INPUT("SPDIF In"),
struct snd_soc_codec *codec;
unsigned int src;
codec = snd_soc_kcontrol_codec(kcontrol); SND_SOC_DAPM_PGA("SPDIFTX", WM8804_PWRDN, 2, 1, NULL, 0),
src = snd_soc_read(codec, WM8804_SPDTX4); SND_SOC_DAPM_PGA("SPDIFRX", WM8804_PWRDN, 1, 1, NULL, 0),
if (src & 0x40)
ucontrol->value.integer.value[0] = 1; SND_SOC_DAPM_MUX("Tx Source", SND_SOC_NOPM, 6, 0, wm8804_tx_source_mux),
else
ucontrol->value.integer.value[0] = 0; SND_SOC_DAPM_AIF_OUT_E("AIFTX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_AIF_IN_E("AIFRX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route wm8804_dapm_routes[] = {
{ "AIFRX", NULL, "Playback" },
{ "Tx Source", "AIF", "AIFRX" },
{ "SPDIFRX", NULL, "SPDIF In" },
{ "Tx Source", "S/PDIF RX", "SPDIFRX" },
{ "SPDIFTX", NULL, "Tx Source" },
{ "SPDIF Out", NULL, "SPDIFTX" },
{ "AIFTX", NULL, "SPDIFRX" },
{ "Capture", NULL, "AIFTX" },
};
static int wm8804_aif_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wm8804_priv *wm8804 = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* power up the aif */
if (!wm8804->aif_pwr)
snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x0);
wm8804->aif_pwr++;
break;
case SND_SOC_DAPM_POST_PMD:
/* power down only both paths are disabled */
wm8804->aif_pwr--;
if (!wm8804->aif_pwr)
snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x10);
break;
}
return 0; return 0;
} }
@ -117,48 +161,33 @@ static int txsrc_get(struct snd_kcontrol *kcontrol,
static int txsrc_put(struct snd_kcontrol *kcontrol, static int txsrc_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_codec *codec; struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
unsigned int src, txpwr; struct snd_soc_dapm_context *dapm = &codec->dapm;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val = ucontrol->value.enumerated.item[0] << e->shift_l;
unsigned int mask = 1 << e->shift_l;
unsigned int txpwr;
codec = snd_soc_kcontrol_codec(kcontrol); if (val != 0 && val != mask)
if (ucontrol->value.integer.value[0] != 0
&& ucontrol->value.integer.value[0] != 1)
return -EINVAL; return -EINVAL;
src = snd_soc_read(codec, WM8804_SPDTX4); snd_soc_dapm_mutex_lock(dapm);
switch ((src & 0x40) >> 6) {
case 0:
if (!ucontrol->value.integer.value[0])
return 0;
break;
case 1:
if (ucontrol->value.integer.value[1])
return 0;
break;
}
if (snd_soc_test_bits(codec, e->reg, mask, val)) {
/* save the current power state of the transmitter */ /* save the current power state of the transmitter */
txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4; txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4;
/* power down the transmitter */ /* power down the transmitter */
snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4); snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4);
/* set the tx source */
snd_soc_update_bits(codec, WM8804_SPDTX4, 0x40,
ucontrol->value.integer.value[0] << 6);
if (ucontrol->value.integer.value[0]) { /* set the tx source */
/* power down the receiver */ snd_soc_update_bits(codec, e->reg, mask, val);
snd_soc_update_bits(codec, WM8804_PWRDN, 0x2, 0x2);
/* power up the AIF */
snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0);
} else {
/* don't power down the AIF -- may be used as an output */
/* power up the receiver */
snd_soc_update_bits(codec, WM8804_PWRDN, 0x2, 0);
}
/* restore the transmitter's configuration */ /* restore the transmitter's configuration */
snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr); snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr);
}
snd_soc_dapm_mutex_unlock(dapm);
return 0; return 0;
} }
@ -182,7 +211,7 @@ static bool wm8804_volatile(struct device *dev, unsigned int reg)
} }
} }
static int wm8804_reset(struct wm8804_priv *wm8804) static int wm8804_soft_reset(struct wm8804_priv *wm8804)
{ {
return regmap_write(wm8804->regmap, WM8804_RST_DEVID1, 0x0); return regmap_write(wm8804->regmap, WM8804_RST_DEVID1, 0x0);
} }
@ -376,19 +405,19 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
int source, unsigned int freq_in, int source, unsigned int freq_in,
unsigned int freq_out) unsigned int freq_out)
{ {
struct snd_soc_codec *codec; struct snd_soc_codec *codec = dai->codec;
struct wm8804_priv *wm8804 = snd_soc_codec_get_drvdata(codec);
bool change;
codec = dai->codec;
if (!freq_in || !freq_out) { if (!freq_in || !freq_out) {
/* disable the PLL */ /* disable the PLL */
snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1); regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN,
return 0; 0x1, 0x1, &change);
if (change)
pm_runtime_put(wm8804->dev);
} else { } else {
int ret; int ret;
struct pll_div pll_div; struct pll_div pll_div;
struct wm8804_priv *wm8804;
wm8804 = snd_soc_codec_get_drvdata(codec);
ret = pll_factors(&pll_div, freq_out, freq_in, ret = pll_factors(&pll_div, freq_out, freq_in,
wm8804->mclk_div); wm8804->mclk_div);
@ -396,7 +425,10 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
return ret; return ret;
/* power down the PLL before reprogramming it */ /* power down the PLL before reprogramming it */
snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1); regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN,
0x1, 0x1, &change);
if (!change)
pm_runtime_get_sync(wm8804->dev);
/* set PLLN and PRESCALE */ /* set PLLN and PRESCALE */
snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10, snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10,
@ -474,47 +506,6 @@ static int wm8804_set_clkdiv(struct snd_soc_dai *dai,
return 0; return 0;
} }
static int wm8804_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
int ret;
struct wm8804_priv *wm8804;
wm8804 = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
/* power up the OSC and the PLL */
snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies),
wm8804->supplies);
if (ret) {
dev_err(codec->dev,
"Failed to enable supplies: %d\n",
ret);
return ret;
}
regcache_sync(wm8804->regmap);
}
/* power down the OSC and the PLL */
snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9);
break;
case SND_SOC_BIAS_OFF:
/* power down the OSC and the PLL */
snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9);
regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies),
wm8804->supplies);
break;
}
codec->dapm.bias_level = level;
return 0;
}
static const struct snd_soc_dai_ops wm8804_dai_ops = { static const struct snd_soc_dai_ops wm8804_dai_ops = {
.hw_params = wm8804_hw_params, .hw_params = wm8804_hw_params,
.set_fmt = wm8804_set_fmt, .set_fmt = wm8804_set_fmt,
@ -552,11 +543,12 @@ static struct snd_soc_dai_driver wm8804_dai = {
}; };
static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = {
.set_bias_level = wm8804_set_bias_level,
.idle_bias_off = true, .idle_bias_off = true,
.controls = wm8804_snd_controls, .dapm_widgets = wm8804_dapm_widgets,
.num_controls = ARRAY_SIZE(wm8804_snd_controls), .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets),
.dapm_routes = wm8804_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(wm8804_dapm_routes),
}; };
const struct regmap_config wm8804_regmap_config = { const struct regmap_config wm8804_regmap_config = {
@ -584,8 +576,17 @@ int wm8804_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, wm8804); dev_set_drvdata(dev, wm8804);
wm8804->dev = dev;
wm8804->regmap = regmap; wm8804->regmap = regmap;
wm8804->reset = devm_gpiod_get_optional(dev, "wlf,reset",
GPIOD_OUT_LOW);
if (IS_ERR(wm8804->reset)) {
ret = PTR_ERR(wm8804->reset);
dev_err(dev, "Failed to get reset line: %d\n", ret);
return ret;
}
for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++)
wm8804->supplies[i].supply = wm8804_supply_names[i]; wm8804->supplies[i].supply = wm8804_supply_names[i];
@ -601,12 +602,15 @@ int wm8804_probe(struct device *dev, struct regmap *regmap)
/* This should really be moved into the regulator core */ /* This should really be moved into the regulator core */
for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) { for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) {
ret = regulator_register_notifier(wm8804->supplies[i].consumer, struct regulator *regulator = wm8804->supplies[i].consumer;
ret = devm_regulator_register_notifier(regulator,
&wm8804->disable_nb[i]); &wm8804->disable_nb[i]);
if (ret != 0) { if (ret != 0) {
dev_err(dev, dev_err(dev,
"Failed to register regulator notifier: %d\n", "Failed to register regulator notifier: %d\n",
ret); ret);
return ret;
} }
} }
@ -614,9 +618,12 @@ int wm8804_probe(struct device *dev, struct regmap *regmap)
wm8804->supplies); wm8804->supplies);
if (ret) { if (ret) {
dev_err(dev, "Failed to enable supplies: %d\n", ret); dev_err(dev, "Failed to enable supplies: %d\n", ret);
goto err_reg_enable; return ret;
} }
if (wm8804->reset)
gpiod_set_value_cansleep(wm8804->reset, 1);
ret = regmap_read(regmap, WM8804_RST_DEVID1, &id1); ret = regmap_read(regmap, WM8804_RST_DEVID1, &id1);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "Failed to read device ID: %d\n", ret); dev_err(dev, "Failed to read device ID: %d\n", ret);
@ -645,14 +652,26 @@ int wm8804_probe(struct device *dev, struct regmap *regmap)
} }
dev_info(dev, "revision %c\n", id1 + 'A'); dev_info(dev, "revision %c\n", id1 + 'A');
ret = wm8804_reset(wm8804); if (!wm8804->reset) {
ret = wm8804_soft_reset(wm8804);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "Failed to issue reset: %d\n", ret); dev_err(dev, "Failed to issue reset: %d\n", ret);
goto err_reg_enable; goto err_reg_enable;
} }
}
return snd_soc_register_codec(dev, &soc_codec_dev_wm8804, ret = snd_soc_register_codec(dev, &soc_codec_dev_wm8804,
&wm8804_dai, 1); &wm8804_dai, 1);
if (ret < 0) {
dev_err(dev, "Failed to register CODEC: %d\n", ret);
goto err_reg_enable;
}
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_idle(dev);
return 0;
err_reg_enable: err_reg_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies);
@ -662,19 +681,51 @@ EXPORT_SYMBOL_GPL(wm8804_probe);
void wm8804_remove(struct device *dev) void wm8804_remove(struct device *dev)
{ {
struct wm8804_priv *wm8804; pm_runtime_disable(dev);
int i;
wm8804 = dev_get_drvdata(dev);
for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i)
regulator_unregister_notifier(wm8804->supplies[i].consumer,
&wm8804->disable_nb[i]);
snd_soc_unregister_codec(dev); snd_soc_unregister_codec(dev);
} }
EXPORT_SYMBOL_GPL(wm8804_remove); EXPORT_SYMBOL_GPL(wm8804_remove);
#if IS_ENABLED(CONFIG_PM)
static int wm8804_runtime_resume(struct device *dev)
{
struct wm8804_priv *wm8804 = dev_get_drvdata(dev);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies),
wm8804->supplies);
if (ret) {
dev_err(wm8804->dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
regcache_sync(wm8804->regmap);
/* Power up OSCCLK */
regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x0);
return 0;
}
static int wm8804_runtime_suspend(struct device *dev)
{
struct wm8804_priv *wm8804 = dev_get_drvdata(dev);
/* Power down OSCCLK */
regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x8);
regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies),
wm8804->supplies);
return 0;
}
#endif
const struct dev_pm_ops wm8804_pm = {
SET_RUNTIME_PM_OPS(wm8804_runtime_suspend, wm8804_runtime_resume, NULL)
};
EXPORT_SYMBOL_GPL(wm8804_pm);
MODULE_DESCRIPTION("ASoC WM8804 driver"); MODULE_DESCRIPTION("ASoC WM8804 driver");
MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -65,6 +65,7 @@
#define WM8804_MCLKDIV_128FS 1 #define WM8804_MCLKDIV_128FS 1
extern const struct regmap_config wm8804_regmap_config; extern const struct regmap_config wm8804_regmap_config;
extern const struct dev_pm_ops wm8804_pm;
int wm8804_probe(struct device *dev, struct regmap *regmap); int wm8804_probe(struct device *dev, struct regmap *regmap);
void wm8804_remove(struct device *dev); void wm8804_remove(struct device *dev);

View File

@ -2009,7 +2009,7 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *i2c = to_i2c_client(codec->dev); struct i2c_client *i2c = to_i2c_client(codec->dev);
struct _fll_div fll_div; struct _fll_div fll_div;
unsigned long timeout; unsigned long timeout, time_left;
int ret, reg, retry; int ret, reg, retry;
/* Any change? */ /* Any change? */
@ -2110,13 +2110,15 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
if (i2c->irq) if (i2c->irq)
timeout *= 10; timeout *= 10;
else else
timeout /= 2; /* ensure timeout of atleast 1 jiffies */
timeout = timeout/2 ? : 1;
for (retry = 0; retry < 10; retry++) { for (retry = 0; retry < 10; retry++) {
ret = wait_for_completion_timeout(&wm8996->fll_lock, time_left = wait_for_completion_timeout(&wm8996->fll_lock,
timeout); timeout);
if (ret != 0) { if (time_left != 0) {
WARN_ON(!i2c->irq); WARN_ON(!i2c->irq);
ret = 1;
break; break;
} }