Commit 40ba66a7 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: hda - Add cache support for COEF read/write

The 16bit COEF read/write is pretty standard for many codecs, and they
can be cached in most cases -- more importantly, they need to be
restored at resume.  For making this easier, add the cache support to
regmap.  If the codec driver wants to cache the COEF access, set
codec->cache_coef flag and issue AC_VERB_GET_PROC_COEF with the coef
index in LSB 8 bits.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 33f81940
...@@ -78,6 +78,7 @@ struct hdac_device { ...@@ -78,6 +78,7 @@ struct hdac_device {
struct snd_array vendor_verbs; struct snd_array vendor_verbs;
bool lazy_cache:1; /* don't wake up for writes */ bool lazy_cache:1; /* don't wake up for writes */
bool caps_overwriting:1; /* caps overwrite being in process */ bool caps_overwriting:1; /* caps overwrite being in process */
bool cache_coef:1; /* cache COEF read/write too */
}; };
/* device/driver type used for matching */ /* device/driver type used for matching */
......
...@@ -33,10 +33,12 @@ ...@@ -33,10 +33,12 @@
static bool hda_volatile_reg(struct device *dev, unsigned int reg) static bool hda_volatile_reg(struct device *dev, unsigned int reg)
{ {
struct hdac_device *codec = dev_to_hdac_dev(dev);
unsigned int verb = get_verb(reg); unsigned int verb = get_verb(reg);
switch (verb) { switch (verb) {
case AC_VERB_GET_PROC_COEF: case AC_VERB_GET_PROC_COEF:
return !codec->cache_coef;
case AC_VERB_GET_COEF_INDEX: case AC_VERB_GET_COEF_INDEX:
case AC_VERB_GET_PROC_STATE: case AC_VERB_GET_PROC_STATE:
case AC_VERB_GET_POWER_STATE: case AC_VERB_GET_POWER_STATE:
...@@ -75,6 +77,8 @@ static bool hda_writeable_reg(struct device *dev, unsigned int reg) ...@@ -75,6 +77,8 @@ static bool hda_writeable_reg(struct device *dev, unsigned int reg)
case AC_VERB_GET_STREAM_FORMAT: case AC_VERB_GET_STREAM_FORMAT:
case AC_VERB_GET_AMP_GAIN_MUTE: case AC_VERB_GET_AMP_GAIN_MUTE:
return true; return true;
case AC_VERB_GET_PROC_COEF:
return codec->cache_coef;
case 0xf00: case 0xf00:
break; break;
default: default:
...@@ -188,9 +192,47 @@ static int hda_reg_write_stereo_amp(struct hdac_device *codec, ...@@ -188,9 +192,47 @@ static int hda_reg_write_stereo_amp(struct hdac_device *codec,
return 0; return 0;
} }
/* read a pseudo coef register (16bit) */
static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg,
unsigned int *val)
{
unsigned int verb;
int err;
if (!codec->cache_coef)
return -EINVAL;
/* LSB 8bit = coef index */
verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
err = snd_hdac_exec_verb(codec, verb, 0, NULL);
if (err < 0)
return err;
verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8);
return snd_hdac_exec_verb(codec, verb, 0, val);
}
/* write a pseudo coef register (16bit) */
static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg,
unsigned int val)
{
unsigned int verb;
int err;
if (!codec->cache_coef)
return -EINVAL;
/* LSB 8bit = coef index */
verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
err = snd_hdac_exec_verb(codec, verb, 0, NULL);
if (err < 0)
return err;
verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) |
(val & 0xffff);
return snd_hdac_exec_verb(codec, verb, 0, NULL);
}
static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
{ {
struct hdac_device *codec = context; struct hdac_device *codec = context;
int verb = get_verb(reg);
int err; int err;
if (!codec_is_running(codec)) if (!codec_is_running(codec))
...@@ -198,11 +240,13 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) ...@@ -198,11 +240,13 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
reg |= (codec->addr << 28); reg |= (codec->addr << 28);
if (is_stereo_amp_verb(reg)) if (is_stereo_amp_verb(reg))
return hda_reg_read_stereo_amp(codec, reg, val); return hda_reg_read_stereo_amp(codec, reg, val);
if (verb == AC_VERB_GET_PROC_COEF)
return hda_reg_read_coef(codec, reg, val);
err = snd_hdac_exec_verb(codec, reg, 0, val); err = snd_hdac_exec_verb(codec, reg, 0, val);
if (err < 0) if (err < 0)
return err; return err;
/* special handling for asymmetric reads */ /* special handling for asymmetric reads */
if (get_verb(reg) == AC_VERB_GET_POWER_STATE) { if (verb == AC_VERB_GET_POWER_STATE) {
if (*val & AC_PWRST_ERROR) if (*val & AC_PWRST_ERROR)
*val = -1; *val = -1;
else /* take only the actual state */ else /* take only the actual state */
...@@ -227,6 +271,9 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) ...@@ -227,6 +271,9 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
return hda_reg_write_stereo_amp(codec, reg, val); return hda_reg_write_stereo_amp(codec, reg, val);
verb = get_verb(reg); verb = get_verb(reg);
if (verb == AC_VERB_SET_PROC_COEF)
return hda_reg_write_coef(codec, reg, val);
switch (verb & 0xf00) { switch (verb & 0xf00) {
case AC_VERB_SET_AMP_GAIN_MUTE: case AC_VERB_SET_AMP_GAIN_MUTE:
verb = AC_VERB_SET_AMP_GAIN_MUTE; verb = AC_VERB_SET_AMP_GAIN_MUTE;
......
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