[alsa-devel] [PATCH 2/3] ASoC: Add WM8990 driver
Takashi Iwai
tiwai at suse.de
Thu Jun 5 13:08:08 CEST 2008
At Thu, 5 Jun 2008 09:54:06 +0100,
Mark Brown wrote:
>
> The WM8990 is a highly integrated ultra-low power hi-fi codec designed
> for handsets rich in multimedia features such as mobile TV, digital
> audio playback and gaming.
>
> The bulk of this driver was written by Liam Girdwood with some
> additional development and updates for new ASoC APIs by me.
>
> Signed-off-by: Liam Girdwood <lg at opensource.wolfsonmicro.com>
> Signed-off-by: Mark Brown <broonie at opensource.wolfsonmicro.com>
Hm, I obviously didn't review it in the last cycle.
> +#include <linux/kernel.h>
Heh, the same issue as wm8510.
> +/*
> + * wm8990 register cache. Note that register 0 is not included in the
> + * cache.
> + */
> +static const u16 wm8990_reg[] = {
> + /*0x8990,*/ /* R0 - Reset */
Ditto.
> +/*
> + * read wm8990 register cache
> + */
> +static inline unsigned int wm8990_read_reg_cache(struct snd_soc_codec *codec,
> + unsigned int reg)
> +{
> + u16 *cache = codec->reg_cache;
> + BUG_ON(reg < 1 || reg > (ARRAY_SIZE(wm8990_reg) + 1));
Isn't it
BUG_ON(reg < 1 || reg > ARRAY_SIZE(wm8990_reg));
??
But at the first place this is bad, because codec_reg_show() in
soc-core.c always loops from 0 to codec->reg_cache_size-1.
At least, reg == 0 should be simply ignored.
> + return cache[reg - 1];
> +}
> +
> +/*
> + * write wm8990 register cache
> + */
> +static inline void wm8990_write_reg_cache(struct snd_soc_codec *codec,
> + unsigned int reg, unsigned int value)
> +{
> + u16 *cache = codec->reg_cache;
> + BUG_ON(reg < 1 || reg > (ARRAY_SIZE(wm8990_reg) + 1));
Ditto.
> +static int wm8990_set_bias_level(struct snd_soc_codec *codec,
> + enum snd_soc_bias_level level)
(snip)
> + case SND_SOC_BIAS_STANDBY:
> + if (codec->bias_level == SND_SOC_BIAS_OFF) {
> + /* Enable all output discharge bits */
> + wm8990_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
> + WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
> + WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
> + WM8990_DIS_ROUT);
> +
> + /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
> + wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
> + WM8990_BUFDCOPEN | WM8990_POBCTRL |
> + WM8990_VMIDTOG);
> +
> + /* Delay to allow output caps to discharge */
> + schedule_timeout_interruptible(msecs_to_jiffies(300));
Why interruptible? Maybe msleep() is better for such a purpose?
> +
> + /* Disable VMIDTOG */
> + wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
> + WM8990_BUFDCOPEN | WM8990_POBCTRL);
> +
> + /* disable all output discharge bits */
> + wm8990_write(codec, WM8990_ANTIPOP1, 0);
> +
> + /* Enable outputs */
> + wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1b00);
> +
> + schedule_timeout_interruptible(msecs_to_jiffies(50));
Ditto.
> +
> + /* Enable VMID at 2x50k */
> + wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f02);
> +
> + schedule_timeout_interruptible(msecs_to_jiffies(100));
> +
> + /* Enable VREF */
> + wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
> +
> + schedule_timeout_interruptible(msecs_to_jiffies(600));
> +
> + /* Enable BUFIOEN */
> + wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
> + WM8990_BUFDCOPEN | WM8990_POBCTRL |
> + WM8990_BUFIOEN);
> +
> + /* Disable outputs */
> + wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x3);
> +
> + /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
> + wm8990_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
> + } else {
> + /* ON -> standby */
> +
> + }
> + break;
> +
> + case SND_SOC_BIAS_OFF:
> + /* Enable POBCTRL and SOFT_ST */
> + wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
> + WM8990_POBCTRL | WM8990_BUFIOEN);
> +
> + /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
> + wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
> + WM8990_BUFDCOPEN | WM8990_POBCTRL |
> + WM8990_BUFIOEN);
> +
> + /* mute DAC */
> + val = wm8990_read_reg_cache(codec, WM8990_DAC_CTRL);
> + wm8990_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
> +
> + /* Enable any disabled outputs */
> + wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
> +
> + /* Disable VMID */
> + wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f01);
> +
> + schedule_timeout_interruptible(msecs_to_jiffies(300));
Ditto.
> +static int wm8990_init(struct snd_soc_device *socdev)
> +{
> + struct snd_soc_codec *codec = socdev->codec;
> + u16 reg;
> + int ret = 0;
> +
> + codec->name = "WM8990";
> + codec->owner = THIS_MODULE;
> + codec->read = wm8990_read_reg_cache;
> + codec->write = wm8990_write;
> + codec->set_bias_level = wm8990_set_bias_level;
> + codec->dai = &wm8990_dai;
> + codec->num_dai = 2;
> + codec->reg_cache_size = sizeof(wm8990_reg);
A wrong value assignment in this file, too. But, ARRAY_SIZE() is also
wrong since the max reg value is ARRAY_SIZE(). ARRAY_SIZE()+1 is the
right value, but then it's really unclear what it means...
thanks,
Takashi
More information about the Alsa-devel
mailing list