diff options
author | Alan Tull <r80115@freescale.com> | 2009-03-05 15:36:39 -0600 |
---|---|---|
committer | Alan Tull <r80115@freescale.com> | 2009-03-31 10:05:04 -0500 |
commit | ad23bfcb4fc5e118b847abc649a2eaa423a086c5 (patch) | |
tree | 6b08daa95c8704a64a10c06c19e15cfc3e9ed878 /sound/soc/codecs | |
parent | b8d4595156bb85cf9ad3ed60598a5e2176d1ee1b (diff) |
ENGR00109155 sgtl5000: add reg_cache
Add reg_cache which is a standard feature of soc codecs.
Signed-off-by: Alan Tull <r80115@freescale.com>
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r-- | sound/soc/codecs/sgtl5000.c | 85 |
1 files changed, 82 insertions, 3 deletions
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index de00018fdcdd..962491cd3be7 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -35,7 +35,21 @@ struct sgtl5000_priv { static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level); -static unsigned int sgtl5000_read(struct snd_soc_codec *codec, unsigned int reg) +#define SGTL5000_MAX_CACHED_REG SGTL5000_CHIP_SHORT_CTRL +static u16 sgtl5000_regs[(SGTL5000_MAX_CACHED_REG >> 1) + 1]; + +static unsigned int sgtl5000_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + unsigned int offset = reg >> 1; + if (offset >= ARRAY_SIZE(sgtl5000_regs)) + return -EINVAL; + return cache[offset]; +} + +static unsigned int sgtl5000_hw_read(struct snd_soc_codec *codec, + unsigned int reg) { struct i2c_client *client = codec->control_data; int i2c_ret; @@ -62,6 +76,26 @@ static unsigned int sgtl5000_read(struct snd_soc_codec *codec, unsigned int reg) return value; } +static unsigned int sgtl5000_read(struct snd_soc_codec *codec, unsigned int reg) +{ + if ((reg == SGTL5000_CHIP_ID) || + (reg == SGTL5000_CHIP_ADCDAC_CTRL) || + (reg == SGTL5000_CHIP_ANA_STATUS) || + (reg > SGTL5000_MAX_CACHED_REG)) + return sgtl5000_hw_read(codec, reg); + else + return sgtl5000_read_reg_cache(codec, reg); +} + +static inline void sgtl5000_write_reg_cache(struct snd_soc_codec *codec, + u16 reg, unsigned int value) +{ + u16 *cache = codec->reg_cache; + unsigned int offset = reg >> 1; + if (offset < ARRAY_SIZE(sgtl5000_regs)) + cache[offset] = value; +} + static int sgtl5000_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { @@ -72,6 +106,7 @@ static int sgtl5000_write(struct snd_soc_codec *codec, unsigned int reg, int i2c_ret; struct i2c_msg msg = { addr, flags, 4, buf }; + sgtl5000_write_reg_cache(codec, reg, value); pr_debug("w r:%02x,v:%04x\n", reg, value); buf[0] = (reg & 0xff00) >> 8; buf[1] = reg & 0xff; @@ -88,7 +123,27 @@ static int sgtl5000_write(struct snd_soc_codec *codec, unsigned int reg, return i2c_ret; } -#ifdef DEBUG +static void sgtl5000_sync_reg_cache(struct snd_soc_codec *codec) +{ + int reg; + for (reg = 0; reg <= SGTL5000_MAX_CACHED_REG; reg += 2) + sgtl5000_write_reg_cache(codec, reg, + sgtl5000_hw_read(codec, reg)); +} + +static int sgtl5000_restore_reg(struct snd_soc_codec *codec, unsigned int reg) +{ + unsigned int cached_val, hw_val; + + cached_val = sgtl5000_read_reg_cache(codec, reg); + hw_val = sgtl5000_hw_read(codec, reg); + + if (hw_val != cached_val) + return sgtl5000_write(codec, reg, cached_val); + + return 0; +} + static int all_reg[] = { SGTL5000_CHIP_ID, SGTL5000_CHIP_DIG_POWER, @@ -113,6 +168,7 @@ static int all_reg[] = { SGTL5000_CHIP_SHORT_CTRL, }; +#ifdef DEBUG static void dump_reg(struct snd_soc_codec *codec) { int i, reg; @@ -703,6 +759,20 @@ static int sgtl5000_resume(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->codec; + unsigned int i; + + /* Restore refs first in same order as in sgtl5000_init */ + sgtl5000_restore_reg(codec, SGTL5000_CHIP_LINREG_CTRL); + sgtl5000_restore_reg(codec, SGTL5000_CHIP_ANA_POWER); + msleep(10); + sgtl5000_restore_reg(codec, SGTL5000_CHIP_REF_CTRL); + sgtl5000_restore_reg(codec, SGTL5000_CHIP_LINE_OUT_CTRL); + + /* Restore everythine else */ + for (i = 1; i < sizeof(all_reg) / sizeof(int); i++) + sgtl5000_restore_reg(codec, all_reg[i]); + + sgtl5000_write(codec, SGTL5000_DAP_CTRL, 0); /* Bring the codec back up to standby first to minimise pop/clicks */ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -743,12 +813,19 @@ static int sgtl5000_init(struct snd_soc_device *socdev) codec->name = "SGTL5000"; codec->owner = THIS_MODULE; - codec->read = sgtl5000_read; + codec->read = sgtl5000_read_reg_cache; codec->write = sgtl5000_write; codec->bias_level = SND_SOC_BIAS_OFF; codec->set_bias_level = sgtl5000_set_bias_level; codec->dai = &sgtl5000_dai; codec->num_dai = 1; + codec->reg_cache_size = sizeof(sgtl5000_regs); + codec->reg_cache_step = 2; + codec->reg_cache = (void *)&sgtl5000_regs; + if (codec->reg_cache == NULL) { + dev_err(&client->dev, "Failed to allocate register cache\n"); + return -ENOMEM; + } /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); @@ -757,6 +834,8 @@ static int sgtl5000_init(struct snd_soc_device *socdev) return ret; } + sgtl5000_sync_reg_cache(codec); + /* reset value */ ana_pwr = SGTL5000_DAC_STERO | SGTL5000_LINREG_SIMPLE_POWERUP | |