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:
commit
7667428f80
|
@ -10,6 +10,13 @@ Required properties:
|
|||
- reg : the I2C address of the device for I2C, the chip select
|
||||
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:
|
||||
|
||||
codec: wm8804@1a {
|
||||
|
|
|
@ -413,3 +413,88 @@ void devm_regulator_bulk_unregister_supply_alias(struct device *dev,
|
|||
devm_regulator_unregister_supply_alias(dev, id[i]);
|
||||
}
|
||||
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);
|
||||
|
|
|
@ -252,8 +252,12 @@ int regulator_list_hardware_vsel(struct regulator *regulator,
|
|||
/* regulator notifier block */
|
||||
int regulator_register_notifier(struct regulator *regulator,
|
||||
struct notifier_block *nb);
|
||||
int devm_regulator_register_notifier(struct regulator *regulator,
|
||||
struct notifier_block *nb);
|
||||
int regulator_unregister_notifier(struct regulator *regulator,
|
||||
struct notifier_block *nb);
|
||||
void devm_regulator_unregister_notifier(struct regulator *regulator,
|
||||
struct notifier_block *nb);
|
||||
|
||||
/* driver data - core doesn't touch */
|
||||
void *regulator_get_drvdata(struct regulator *regulator);
|
||||
|
@ -515,12 +519,24 @@ static inline int regulator_register_notifier(struct regulator *regulator,
|
|||
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,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return NULL;
|
||||
|
|
|
@ -1554,7 +1554,6 @@ static int wm2200_probe(struct snd_soc_codec *codec)
|
|||
int ret;
|
||||
|
||||
wm2200->codec = codec;
|
||||
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
|
||||
|
||||
ret = snd_soc_add_codec_controls(codec, wm_adsp1_fw_controls, 2);
|
||||
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 _fll_div factors;
|
||||
int ret, i, timeout;
|
||||
unsigned long time_left;
|
||||
|
||||
if (!Fout) {
|
||||
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 */
|
||||
for (i = 0; i < timeout; i++) {
|
||||
if (i2c->irq) {
|
||||
ret = wait_for_completion_timeout(&wm2200->fll_lock,
|
||||
msecs_to_jiffies(25));
|
||||
if (ret > 0)
|
||||
time_left = wait_for_completion_timeout(
|
||||
&wm2200->fll_lock,
|
||||
msecs_to_jiffies(25));
|
||||
if (time_left > 0)
|
||||
break;
|
||||
} else {
|
||||
msleep(1);
|
||||
|
|
|
@ -1762,6 +1762,7 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
|
|||
struct _fll_div factors;
|
||||
struct wm5100_fll *fll;
|
||||
int ret, base, lock, i, timeout;
|
||||
unsigned long time_left;
|
||||
|
||||
switch (fll_id) {
|
||||
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 */
|
||||
for (i = 0; i < timeout; i++) {
|
||||
if (i2c->irq) {
|
||||
ret = wait_for_completion_timeout(&fll->lock,
|
||||
msecs_to_jiffies(25));
|
||||
if (ret > 0)
|
||||
time_left = wait_for_completion_timeout(&fll->lock,
|
||||
msecs_to_jiffies(25));
|
||||
if (time_left > 0)
|
||||
break;
|
||||
} else {
|
||||
msleep(1);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/spi/spi.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/clk.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
@ -45,6 +46,7 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
|
|||
/* codec private data */
|
||||
struct wm8731_priv {
|
||||
struct regmap *regmap;
|
||||
struct clk *mclk;
|
||||
struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
|
||||
const struct snd_pcm_hw_constraint_list *constraints;
|
||||
unsigned int sysclk;
|
||||
|
@ -390,6 +392,8 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
|||
switch (clk_id) {
|
||||
case WM8731_SYSCLK_XTAL:
|
||||
case WM8731_SYSCLK_MCLK:
|
||||
if (wm8731->mclk && clk_set_rate(wm8731->mclk, freq))
|
||||
return -EINVAL;
|
||||
wm8731->sysclk_type = clk_id;
|
||||
break;
|
||||
default:
|
||||
|
@ -491,6 +495,8 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
|
|||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
if (wm8731->mclk)
|
||||
clk_prepare_enable(wm8731->mclk);
|
||||
break;
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
break;
|
||||
|
@ -509,6 +515,8 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
|
|||
snd_soc_write(codec, WM8731_PWR, reg | 0x0040);
|
||||
break;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
if (wm8731->mclk)
|
||||
clk_disable_unprepare(wm8731->mclk);
|
||||
snd_soc_write(codec, WM8731_PWR, 0xffff);
|
||||
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
|
||||
wm8731->supplies);
|
||||
|
@ -667,6 +675,19 @@ static int wm8731_spi_probe(struct spi_device *spi)
|
|||
if (wm8731 == NULL)
|
||||
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);
|
||||
|
||||
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)
|
||||
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);
|
||||
|
||||
wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap);
|
||||
|
|
|
@ -50,6 +50,7 @@ static struct i2c_driver wm8804_i2c_driver = {
|
|||
.driver = {
|
||||
.name = "wm8804",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &wm8804_pm,
|
||||
.of_match_table = wm8804_of_match,
|
||||
},
|
||||
.probe = wm8804_i2c_probe,
|
||||
|
|
|
@ -43,6 +43,7 @@ static struct spi_driver wm8804_spi_driver = {
|
|||
.driver = {
|
||||
.name = "wm8804",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &wm8804_pm,
|
||||
.of_match_table = wm8804_of_match,
|
||||
},
|
||||
.probe = wm8804_spi_probe,
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -24,6 +26,7 @@
|
|||
#include <sound/soc.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
|
||||
#include "wm8804.h"
|
||||
|
||||
|
@ -57,18 +60,23 @@ static const struct reg_default wm8804_reg_defaults[] = {
|
|||
};
|
||||
|
||||
struct wm8804_priv {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES];
|
||||
struct notifier_block disable_nb[WM8804_NUM_SUPPLIES];
|
||||
int mclk_div;
|
||||
};
|
||||
|
||||
static int txsrc_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
struct gpio_desc *reset;
|
||||
|
||||
int aif_pwr;
|
||||
};
|
||||
|
||||
static int txsrc_put(struct snd_kcontrol *kcontrol,
|
||||
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
|
||||
* 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)
|
||||
|
||||
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[] = {
|
||||
SOC_ENUM_EXT("Input Source", txsrc, txsrc_get, txsrc_put),
|
||||
SOC_SINGLE("TX Playback Switch", WM8804_PWRDN, 2, 1, 1),
|
||||
SOC_SINGLE("AIF Playback Switch", WM8804_PWRDN, 4, 1, 1)
|
||||
static const struct snd_kcontrol_new wm8804_tx_source_mux[] = {
|
||||
SOC_DAPM_ENUM_EXT("Input Source", txsrc,
|
||||
snd_soc_dapm_get_enum_double, txsrc_put),
|
||||
};
|
||||
|
||||
static int txsrc_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec;
|
||||
unsigned int src;
|
||||
static const struct snd_soc_dapm_widget wm8804_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_OUTPUT("SPDIF Out"),
|
||||
SND_SOC_DAPM_INPUT("SPDIF In"),
|
||||
|
||||
codec = snd_soc_kcontrol_codec(kcontrol);
|
||||
src = snd_soc_read(codec, WM8804_SPDTX4);
|
||||
if (src & 0x40)
|
||||
ucontrol->value.integer.value[0] = 1;
|
||||
else
|
||||
ucontrol->value.integer.value[0] = 0;
|
||||
SND_SOC_DAPM_PGA("SPDIFTX", WM8804_PWRDN, 2, 1, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("SPDIFRX", WM8804_PWRDN, 1, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_MUX("Tx Source", SND_SOC_NOPM, 6, 0, wm8804_tx_source_mux),
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -117,48 +161,33 @@ static int txsrc_get(struct snd_kcontrol *kcontrol,
|
|||
static int txsrc_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec;
|
||||
unsigned int src, txpwr;
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
|
||||
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 (ucontrol->value.integer.value[0] != 0
|
||||
&& ucontrol->value.integer.value[0] != 1)
|
||||
if (val != 0 && val != mask)
|
||||
return -EINVAL;
|
||||
|
||||
src = snd_soc_read(codec, WM8804_SPDTX4);
|
||||
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;
|
||||
snd_soc_dapm_mutex_lock(dapm);
|
||||
|
||||
if (snd_soc_test_bits(codec, e->reg, mask, val)) {
|
||||
/* save the current power state of the transmitter */
|
||||
txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4;
|
||||
|
||||
/* power down the transmitter */
|
||||
snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4);
|
||||
|
||||
/* set the tx source */
|
||||
snd_soc_update_bits(codec, e->reg, mask, val);
|
||||
|
||||
/* restore the transmitter's configuration */
|
||||
snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr);
|
||||
}
|
||||
|
||||
/* save the current power state of the transmitter */
|
||||
txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4;
|
||||
/* power down the transmitter */
|
||||
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]) {
|
||||
/* power down the receiver */
|
||||
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 */
|
||||
snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr);
|
||||
snd_soc_dapm_mutex_unlock(dapm);
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -376,19 +405,19 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
|
|||
int source, unsigned int freq_in,
|
||||
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) {
|
||||
/* disable the PLL */
|
||||
snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1);
|
||||
return 0;
|
||||
regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN,
|
||||
0x1, 0x1, &change);
|
||||
if (change)
|
||||
pm_runtime_put(wm8804->dev);
|
||||
} else {
|
||||
int ret;
|
||||
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,
|
||||
wm8804->mclk_div);
|
||||
|
@ -396,7 +425,10 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
|
|||
return ret;
|
||||
|
||||
/* 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 */
|
||||
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;
|
||||
}
|
||||
|
||||
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 = {
|
||||
.hw_params = wm8804_hw_params,
|
||||
.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 = {
|
||||
.set_bias_level = wm8804_set_bias_level,
|
||||
.idle_bias_off = true,
|
||||
|
||||
.controls = wm8804_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(wm8804_snd_controls),
|
||||
.dapm_widgets = wm8804_dapm_widgets,
|
||||
.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 = {
|
||||
|
@ -584,8 +576,17 @@ int wm8804_probe(struct device *dev, struct regmap *regmap)
|
|||
|
||||
dev_set_drvdata(dev, wm8804);
|
||||
|
||||
wm8804->dev = dev;
|
||||
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++)
|
||||
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 */
|
||||
for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) {
|
||||
ret = regulator_register_notifier(wm8804->supplies[i].consumer,
|
||||
&wm8804->disable_nb[i]);
|
||||
struct regulator *regulator = wm8804->supplies[i].consumer;
|
||||
|
||||
ret = devm_regulator_register_notifier(regulator,
|
||||
&wm8804->disable_nb[i]);
|
||||
if (ret != 0) {
|
||||
dev_err(dev,
|
||||
"Failed to register regulator notifier: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -614,9 +618,12 @@ int wm8804_probe(struct device *dev, struct regmap *regmap)
|
|||
wm8804->supplies);
|
||||
if (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);
|
||||
if (ret < 0) {
|
||||
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');
|
||||
|
||||
ret = wm8804_reset(wm8804);
|
||||
if (!wm8804->reset) {
|
||||
ret = wm8804_soft_reset(wm8804);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to issue reset: %d\n", ret);
|
||||
goto err_reg_enable;
|
||||
}
|
||||
}
|
||||
|
||||
ret = snd_soc_register_codec(dev, &soc_codec_dev_wm8804,
|
||||
&wm8804_dai, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to issue reset: %d\n", ret);
|
||||
dev_err(dev, "Failed to register CODEC: %d\n", ret);
|
||||
goto err_reg_enable;
|
||||
}
|
||||
|
||||
return snd_soc_register_codec(dev, &soc_codec_dev_wm8804,
|
||||
&wm8804_dai, 1);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_idle(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg_enable:
|
||||
regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies);
|
||||
|
@ -662,19 +681,51 @@ EXPORT_SYMBOL_GPL(wm8804_probe);
|
|||
|
||||
void wm8804_remove(struct device *dev)
|
||||
{
|
||||
struct wm8804_priv *wm8804;
|
||||
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]);
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
snd_soc_unregister_codec(dev);
|
||||
}
|
||||
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_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#define WM8804_MCLKDIV_128FS 1
|
||||
|
||||
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);
|
||||
void wm8804_remove(struct device *dev);
|
||||
|
|
|
@ -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 i2c_client *i2c = to_i2c_client(codec->dev);
|
||||
struct _fll_div fll_div;
|
||||
unsigned long timeout;
|
||||
unsigned long timeout, time_left;
|
||||
int ret, reg, retry;
|
||||
|
||||
/* Any change? */
|
||||
|
@ -2110,13 +2110,15 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
|
|||
if (i2c->irq)
|
||||
timeout *= 10;
|
||||
else
|
||||
timeout /= 2;
|
||||
/* ensure timeout of atleast 1 jiffies */
|
||||
timeout = timeout/2 ? : 1;
|
||||
|
||||
for (retry = 0; retry < 10; retry++) {
|
||||
ret = wait_for_completion_timeout(&wm8996->fll_lock,
|
||||
timeout);
|
||||
if (ret != 0) {
|
||||
time_left = wait_for_completion_timeout(&wm8996->fll_lock,
|
||||
timeout);
|
||||
if (time_left != 0) {
|
||||
WARN_ON(!i2c->irq);
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue