Commit 164548d3 authored by Mark Brown's avatar Mark Brown

ASoC: Implement basic WM8993 interrupt support

If an interrupt is supplied then use it for thermal warning and FLL lock
notifications. When using the interrupt raise the timeout for the FLL lock
substantially to reduce the chances of spurious warnings.
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent a4b52337
......@@ -204,9 +204,11 @@ static struct {
struct wm8993_priv {
struct wm_hubs_data hubs_data;
struct device *dev;
struct regmap *regmap;
struct regulator_bulk_data supplies[WM8993_NUM_SUPPLIES];
struct wm8993_platform_data pdata;
struct completion fll_lock;
int master;
int sysclk_source;
int tdm_slots;
......@@ -225,6 +227,7 @@ static bool wm8993_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case WM8993_SOFTWARE_RESET:
case WM8993_GPIO_CTRL_1:
case WM8993_DC_SERVO_0:
case WM8993_DC_SERVO_READBACK_0:
case WM8993_DC_SERVO_READBACK_1:
......@@ -467,8 +470,10 @@ static int _wm8993_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
unsigned int Fref, unsigned int Fout)
{
struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *i2c = to_i2c_client(codec->dev);
u16 reg1, reg4, reg5;
struct _fll_div fll_div;
unsigned int timeout;
int ret;
/* Any change? */
......@@ -539,14 +544,22 @@ static int _wm8993_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
reg5 |= fll_div.fll_clk_ref_div << WM8993_FLL_CLK_REF_DIV_SHIFT;
snd_soc_write(codec, WM8993_FLL_CONTROL_5, reg5);
/* If we've got an interrupt wired up make sure we get it */
if (i2c->irq)
timeout = msecs_to_jiffies(20);
else if (Fref < 1000000)
timeout = msecs_to_jiffies(3);
else
timeout = msecs_to_jiffies(1);
try_wait_for_completion(&wm8993->fll_lock);
/* Enable the FLL */
snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA);
/* Both overestimates */
if (Fref < 1000000)
msleep(3);
else
msleep(1);
timeout = wait_for_completion_timeout(&wm8993->fll_lock, timeout);
if (i2c->irq && !timeout)
dev_warn(codec->dev, "Timed out waiting for FLL\n");
dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
......@@ -1471,6 +1484,45 @@ static int wm8993_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
static irqreturn_t wm8993_irq(int irq, void *data)
{
struct wm8993_priv *wm8993 = data;
int mask, val, ret;
ret = regmap_read(wm8993->regmap, WM8993_GPIO_CTRL_1, &val);
if (ret != 0) {
dev_err(wm8993->dev, "Failed to read interrupt status: %d\n",
ret);
return IRQ_NONE;
}
ret = regmap_read(wm8993->regmap, WM8993_GPIOCTRL_2, &mask);
if (ret != 0) {
dev_err(wm8993->dev, "Failed to read interrupt mask: %d\n",
ret);
return IRQ_NONE;
}
/* The IRQ pin status is visible in the register too */
val &= ~(mask | WM8993_IRQ);
if (!val)
return IRQ_NONE;
if (val & WM8993_TEMPOK_EINT)
dev_crit(wm8993->dev, "Thermal warning\n");
if (val & WM8993_FLL_LOCK_EINT) {
dev_dbg(wm8993->dev, "FLL locked\n");
complete(&wm8993->fll_lock);
}
ret = regmap_write(wm8993->regmap, WM8993_GPIO_CTRL_1, val);
if (ret != 0)
dev_err(wm8993->dev, "Failed to ack interrupt: %d\n", ret);
return IRQ_HANDLED;
}
static const struct snd_soc_dai_ops wm8993_ops = {
.set_sysclk = wm8993_set_sysclk,
.set_fmt = wm8993_set_dai_fmt,
......@@ -1671,6 +1723,9 @@ static __devinit int wm8993_i2c_probe(struct i2c_client *i2c,
if (wm8993 == NULL)
return -ENOMEM;
wm8993->dev = &i2c->dev;
init_completion(&wm8993->fll_lock);
wm8993->regmap = regmap_init_i2c(i2c, &wm8993_regmap);
if (IS_ERR(wm8993->regmap)) {
ret = PTR_ERR(wm8993->regmap);
......@@ -1713,6 +1768,22 @@ static __devinit int wm8993_i2c_probe(struct i2c_client *i2c,
if (ret != 0)
goto err_enable;
if (i2c->irq) {
/* Put GPIO1 into interrupt mode (only GPIO1 can output IRQ) */
ret = regmap_update_bits(wm8993->regmap, WM8993_GPIO1,
WM8993_GPIO1_PD |
WM8993_GPIO1_SEL_MASK, 7);
if (ret != 0)
goto err_enable;
ret = request_threaded_irq(i2c->irq, NULL, wm8993_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"wm8993", wm8993);
if (ret != 0)
goto err_enable;
}
regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
regcache_cache_only(wm8993->regmap, true);
......@@ -1721,11 +1792,14 @@ static __devinit int wm8993_i2c_probe(struct i2c_client *i2c,
&soc_codec_dev_wm8993, &wm8993_dai, 1);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
goto err_enable;
goto err_irq;
}
return 0;
err_irq:
if (i2c->irq)
free_irq(i2c->irq, wm8993);
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
err_get:
......@@ -1735,11 +1809,13 @@ static __devinit int wm8993_i2c_probe(struct i2c_client *i2c,
return ret;
}
static __devexit int wm8993_i2c_remove(struct i2c_client *client)
static __devexit int wm8993_i2c_remove(struct i2c_client *i2c)
{
struct wm8993_priv *wm8993 = i2c_get_clientdata(client);
struct wm8993_priv *wm8993 = i2c_get_clientdata(i2c);
snd_soc_unregister_codec(&client->dev);
snd_soc_unregister_codec(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm8993);
regmap_exit(wm8993->regmap);
regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
regulator_bulk_free(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment