Loading sound/soc/codecs/wm8995.c +97 −5 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/spi/spi.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> Loading @@ -30,6 +31,18 @@ #include "wm8995.h" #define WM8995_NUM_SUPPLIES 8 static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = { "DCVDD", "DBVDD1", "DBVDD2", "DBVDD3", "AVDD1", "AVDD2", "CPVDD", "MICVDD" }; static const u16 wm8995_reg_defs[WM8995_MAX_REGISTER + 1] = { [0] = 0x8995, [5] = 0x0100, [16] = 0x000b, [17] = 0x000b, [24] = 0x02c0, [25] = 0x02c0, [26] = 0x02c0, [27] = 0x02c0, Loading Loading @@ -126,8 +139,37 @@ struct wm8995_priv { int mclk[2]; int aifclk[2]; struct fll_config fll[2], fll_suspend[2]; struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES]; struct notifier_block disable_nb[WM8995_NUM_SUPPLIES]; struct snd_soc_codec *codec; }; /* * 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 * except container_of(). */ #define WM8995_REGULATOR_EVENT(n) \ static int wm8995_regulator_event_##n(struct notifier_block *nb, \ unsigned long event, void *data) \ { \ struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \ disable_nb[n]); \ if (event & REGULATOR_EVENT_DISABLE) { \ wm8995->codec->cache_sync = 1; \ } \ return 0; \ } WM8995_REGULATOR_EVENT(0) WM8995_REGULATOR_EVENT(1) WM8995_REGULATOR_EVENT(2) WM8995_REGULATOR_EVENT(3) WM8995_REGULATOR_EVENT(4) WM8995_REGULATOR_EVENT(5) WM8995_REGULATOR_EVENT(6) WM8995_REGULATOR_EVENT(7) static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0); static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0); Loading Loading @@ -1483,6 +1525,11 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); if (ret) return ret; ret = snd_soc_cache_sync(codec); if (ret) { dev_err(codec->dev, Loading @@ -1492,13 +1539,13 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1, WM8995_BG_ENA_MASK, WM8995_BG_ENA); } break; case SND_SOC_BIAS_OFF: snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1, WM8995_BG_ENA_MASK, 0); codec->cache_sync = 1; regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); break; } Loading Loading @@ -1537,10 +1584,12 @@ static int wm8995_remove(struct snd_soc_codec *codec) static int wm8995_probe(struct snd_soc_codec *codec) { struct wm8995_priv *wm8995; int i; int ret; codec->dapm.idle_bias_off = 1; wm8995 = snd_soc_codec_get_drvdata(codec); wm8995->codec = codec; ret = snd_soc_codec_set_cache_io(codec, 16, 16, wm8995->control_type); if (ret < 0) { Loading @@ -1548,21 +1597,58 @@ static int wm8995_probe(struct snd_soc_codec *codec) return ret; } for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) wm8995->supplies[i].supply = wm8995_supply_names[i]; ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8995->supplies), wm8995->supplies); if (ret) { dev_err(codec->dev, "Failed to request supplies: %d\n", ret); return ret; } wm8995->disable_nb[0].notifier_call = wm8995_regulator_event_0; wm8995->disable_nb[1].notifier_call = wm8995_regulator_event_1; wm8995->disable_nb[2].notifier_call = wm8995_regulator_event_2; wm8995->disable_nb[3].notifier_call = wm8995_regulator_event_3; wm8995->disable_nb[4].notifier_call = wm8995_regulator_event_4; wm8995->disable_nb[5].notifier_call = wm8995_regulator_event_5; wm8995->disable_nb[6].notifier_call = wm8995_regulator_event_6; wm8995->disable_nb[7].notifier_call = wm8995_regulator_event_7; /* This should really be moved into the regulator core */ for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) { ret = regulator_register_notifier(wm8995->supplies[i].consumer, &wm8995->disable_nb[i]); if (ret) { dev_err(codec->dev, "Failed to register regulator notifier: %d\n", ret); } } ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); if (ret) { dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); goto err_reg_get; } ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET); if (ret < 0) { dev_err(codec->dev, "Failed to read device ID: %d\n", ret); return ret; goto err_reg_enable; } if (ret != 0x8995) { dev_err(codec->dev, "Invalid device ID: %#x\n", ret); return -EINVAL; goto err_reg_enable; } ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0); if (ret < 0) { dev_err(codec->dev, "Failed to issue reset: %d\n", ret); return ret; goto err_reg_enable; } wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY); Loading Loading @@ -1597,6 +1683,12 @@ static int wm8995_probe(struct snd_soc_codec *codec) ARRAY_SIZE(wm8995_intercon)); return 0; err_reg_enable: regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); err_reg_get: regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); return ret; } #define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ Loading Loading
sound/soc/codecs/wm8995.c +97 −5 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/spi/spi.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> Loading @@ -30,6 +31,18 @@ #include "wm8995.h" #define WM8995_NUM_SUPPLIES 8 static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = { "DCVDD", "DBVDD1", "DBVDD2", "DBVDD3", "AVDD1", "AVDD2", "CPVDD", "MICVDD" }; static const u16 wm8995_reg_defs[WM8995_MAX_REGISTER + 1] = { [0] = 0x8995, [5] = 0x0100, [16] = 0x000b, [17] = 0x000b, [24] = 0x02c0, [25] = 0x02c0, [26] = 0x02c0, [27] = 0x02c0, Loading Loading @@ -126,8 +139,37 @@ struct wm8995_priv { int mclk[2]; int aifclk[2]; struct fll_config fll[2], fll_suspend[2]; struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES]; struct notifier_block disable_nb[WM8995_NUM_SUPPLIES]; struct snd_soc_codec *codec; }; /* * 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 * except container_of(). */ #define WM8995_REGULATOR_EVENT(n) \ static int wm8995_regulator_event_##n(struct notifier_block *nb, \ unsigned long event, void *data) \ { \ struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \ disable_nb[n]); \ if (event & REGULATOR_EVENT_DISABLE) { \ wm8995->codec->cache_sync = 1; \ } \ return 0; \ } WM8995_REGULATOR_EVENT(0) WM8995_REGULATOR_EVENT(1) WM8995_REGULATOR_EVENT(2) WM8995_REGULATOR_EVENT(3) WM8995_REGULATOR_EVENT(4) WM8995_REGULATOR_EVENT(5) WM8995_REGULATOR_EVENT(6) WM8995_REGULATOR_EVENT(7) static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0); static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0); Loading Loading @@ -1483,6 +1525,11 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); if (ret) return ret; ret = snd_soc_cache_sync(codec); if (ret) { dev_err(codec->dev, Loading @@ -1492,13 +1539,13 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1, WM8995_BG_ENA_MASK, WM8995_BG_ENA); } break; case SND_SOC_BIAS_OFF: snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1, WM8995_BG_ENA_MASK, 0); codec->cache_sync = 1; regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); break; } Loading Loading @@ -1537,10 +1584,12 @@ static int wm8995_remove(struct snd_soc_codec *codec) static int wm8995_probe(struct snd_soc_codec *codec) { struct wm8995_priv *wm8995; int i; int ret; codec->dapm.idle_bias_off = 1; wm8995 = snd_soc_codec_get_drvdata(codec); wm8995->codec = codec; ret = snd_soc_codec_set_cache_io(codec, 16, 16, wm8995->control_type); if (ret < 0) { Loading @@ -1548,21 +1597,58 @@ static int wm8995_probe(struct snd_soc_codec *codec) return ret; } for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) wm8995->supplies[i].supply = wm8995_supply_names[i]; ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8995->supplies), wm8995->supplies); if (ret) { dev_err(codec->dev, "Failed to request supplies: %d\n", ret); return ret; } wm8995->disable_nb[0].notifier_call = wm8995_regulator_event_0; wm8995->disable_nb[1].notifier_call = wm8995_regulator_event_1; wm8995->disable_nb[2].notifier_call = wm8995_regulator_event_2; wm8995->disable_nb[3].notifier_call = wm8995_regulator_event_3; wm8995->disable_nb[4].notifier_call = wm8995_regulator_event_4; wm8995->disable_nb[5].notifier_call = wm8995_regulator_event_5; wm8995->disable_nb[6].notifier_call = wm8995_regulator_event_6; wm8995->disable_nb[7].notifier_call = wm8995_regulator_event_7; /* This should really be moved into the regulator core */ for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) { ret = regulator_register_notifier(wm8995->supplies[i].consumer, &wm8995->disable_nb[i]); if (ret) { dev_err(codec->dev, "Failed to register regulator notifier: %d\n", ret); } } ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); if (ret) { dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); goto err_reg_get; } ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET); if (ret < 0) { dev_err(codec->dev, "Failed to read device ID: %d\n", ret); return ret; goto err_reg_enable; } if (ret != 0x8995) { dev_err(codec->dev, "Invalid device ID: %#x\n", ret); return -EINVAL; goto err_reg_enable; } ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0); if (ret < 0) { dev_err(codec->dev, "Failed to issue reset: %d\n", ret); return ret; goto err_reg_enable; } wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY); Loading Loading @@ -1597,6 +1683,12 @@ static int wm8995_probe(struct snd_soc_codec *codec) ARRAY_SIZE(wm8995_intercon)); return 0; err_reg_enable: regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); err_reg_get: regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); return ret; } #define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ Loading