// SPDX-License-Identifier: GPL-2.0-only /* * Regmap support for HD-audio verbs * * A virtual register is translated to one or more hda verbs for write, * vice versa for read. * * A few limitations: * - Provided for not all verbs but only subset standard non-volatile verbs. * - For reading, only AC_VERB_GET_* variants can be used. * - For writing, mapped to the *corresponding* AC_VERB_SET_* variants, * so can't handle asymmetric verbs for read and write
*/
switch (verb) { case AC_VERB_GET_PROC_COEF: return !codec->cache_coef; case AC_VERB_GET_COEF_INDEX: case AC_VERB_GET_PROC_STATE: case AC_VERB_GET_POWER_STATE: case AC_VERB_GET_PIN_SENSE: case AC_VERB_GET_HDMI_DIP_SIZE: case AC_VERB_GET_HDMI_ELDD: case AC_VERB_GET_HDMI_DIP_INDEX: case AC_VERB_GET_HDMI_DIP_DATA: case AC_VERB_GET_HDMI_DIP_XMIT: case AC_VERB_GET_HDMI_CP_CTRL: case AC_VERB_GET_HDMI_CHAN_SLOT: case AC_VERB_GET_DEVICE_SEL: case AC_VERB_GET_DEVICE_LIST: /* read-only volatile */ returntrue;
}
snd_array_for_each(&codec->vendor_verbs, i, v) { if (verb == *v) returntrue;
}
if (codec->caps_overwriting) returntrue;
switch (verb & 0xf00) { case AC_VERB_GET_STREAM_FORMAT: case AC_VERB_GET_AMP_GAIN_MUTE: returntrue; case AC_VERB_GET_PROC_COEF: return codec->cache_coef; case 0xf00: break; default: returnfalse;
}
switch (verb) { case AC_VERB_GET_CONNECT_SEL: case AC_VERB_GET_SDI_SELECT: case AC_VERB_GET_PIN_WIDGET_CONTROL: case AC_VERB_GET_UNSOLICITED_RESPONSE: /* only as SET_UNSOLICITED_ENABLE */ case AC_VERB_GET_BEEP_CONTROL: case AC_VERB_GET_EAPD_BTLENABLE: case AC_VERB_GET_DIGI_CONVERT_1: case AC_VERB_GET_DIGI_CONVERT_2: /* only for beep control */ case AC_VERB_GET_VOLUME_KNOB_CONTROL: case AC_VERB_GET_GPIO_MASK: case AC_VERB_GET_GPIO_DIRECTION: case AC_VERB_GET_GPIO_DATA: /* not for volatile read */ case AC_VERB_GET_GPIO_WAKE_MASK: case AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK: case AC_VERB_GET_GPIO_STICKY_MASK: returntrue;
}
switch (verb) { case AC_VERB_PARAMETERS: case AC_VERB_GET_CONNECT_LIST: case AC_VERB_GET_SUBSYSTEM_ID: returntrue; /* below are basically writable, but disabled for reducing unnecessary * writes at sync
*/ case AC_VERB_GET_CONFIG_DEFAULT: /* usually just read */ case AC_VERB_GET_CONV: /* managed in PCM code */ case AC_VERB_GET_CVT_CHAN_COUNT: /* managed in HDMI CA code */ returntrue;
}
return hda_writeable_reg(dev, reg);
}
/* * Stereo amp pseudo register: * for making easier to handle the stereo volume control, we provide a * fake register to deal both left and right channels by a single * (pseudo) register access. A verb consisting of SET_AMP_GAIN with * *both* SET_LEFT and SET_RIGHT bits takes a 16bit value, the lower 8bit * for the left and the upper 8bit for the right channel.
*/ staticbool is_stereo_amp_verb(unsignedint reg)
{ if (((reg >> 8) & 0x700) != AC_VERB_SET_AMP_GAIN_MUTE) returnfalse; return (reg & (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT)) ==
(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
}
/** * snd_hdac_regmap_init - Initialize regmap for HDA register accesses * @codec: the codec object * * Returns zero for success or a negative error code.
*/ int snd_hdac_regmap_init(struct hdac_device *codec)
{ struct regmap *regmap;
/** * snd_hdac_regmap_exit - Release the regmap from HDA codec * @codec: the codec object
*/ void snd_hdac_regmap_exit(struct hdac_device *codec)
{ if (codec->regmap) {
regmap_exit(codec->regmap);
codec->regmap = NULL;
snd_array_free(&codec->vendor_verbs);
}
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit);
/** * snd_hdac_regmap_add_vendor_verb - add a vendor-specific verb to regmap * @codec: the codec object * @verb: verb to allow accessing via regmap * * Returns zero for success or a negative error code.
*/ int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec, unsignedint verb)
{ unsignedint *p = snd_array_new(&codec->vendor_verbs);
if (!p) return -ENOMEM;
*p = verb | 0x800; /* set GET bit */ return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb);
/* * helper functions
*/
/* write a pseudo-register value (w/o power sequence) */ staticint reg_raw_write(struct hdac_device *codec, unsignedint reg, unsignedint val)
{ int err;
/* a helper macro to call @func_call; retry with power-up if failed */ #define CALL_RAW_FUNC(codec, func_call) \
({ \ int _err = func_call; \ if (_err == -EAGAIN) { \
_err = snd_hdac_power_up_pm(codec); \ if (_err >= 0) \
_err = func_call; \
snd_hdac_power_down_pm(codec); \
} \
_err;})
/** * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt * @codec: the codec object * @reg: pseudo register * @val: value to write * * Returns zero if successful or a negative error code.
*/ int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsignedint reg, unsignedint val)
{ return CALL_RAW_FUNC(codec, reg_raw_write(codec, reg, val));
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
/** * snd_hdac_regmap_read_raw - read a pseudo register with power mgmt * @codec: the codec object * @reg: pseudo register * @val: pointer to store the read value * * Returns zero if successful or a negative error code.
*/ int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsignedint reg, unsignedint *val)
{ return __snd_hdac_regmap_read_raw(codec, reg, val, false);
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw);
/* Works like snd_hdac_regmap_read_raw(), but this doesn't read from the * cache but always via hda verbs.
*/ int snd_hdac_regmap_read_raw_uncached(struct hdac_device *codec, unsignedint reg, unsignedint *val)
{ return __snd_hdac_regmap_read_raw(codec, reg, val, true);
}
mutex_lock(&codec->regmap_lock); if (codec->regmap) {
err = regmap_update_bits_check(codec->regmap, reg, mask, val,
&change); if (!err)
err = change ? 1 : 0;
} else {
err = hda_reg_read(codec, reg, &orig); if (!err) {
val &= mask;
val |= orig & ~mask; if (val != orig) {
err = hda_reg_write(codec, reg, val); if (!err)
err = 1;
}
}
}
mutex_unlock(&codec->regmap_lock); return err;
}
/** * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt * @codec: the codec object * @reg: pseudo register * @mask: bit mask to update * @val: value to update * * Returns zero if successful or a negative error code.
*/ int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsignedint reg, unsignedint mask, unsignedint val)
{ return CALL_RAW_FUNC(codec, reg_raw_update(codec, reg, mask, val));
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);
if (!codec->regmap) return reg_raw_update(codec, reg, mask, val);
mutex_lock(&codec->regmap_lock); /* Discard any updates to already initialised registers. */ if (!regcache_reg_cached(codec->regmap, reg))
err = regmap_update_bits(codec->regmap, reg, mask, val);
mutex_unlock(&codec->regmap_lock); return err;
}
/** * snd_hdac_regmap_update_raw_once - initialize the register value only once * @codec: the codec object * @reg: pseudo register * @mask: bit mask to update * @val: value to update * * Performs the update of the register bits only once when the register * hasn't been initialized yet. Used in HD-audio legacy driver. * Returns zero if successful or a negative error code
*/ int snd_hdac_regmap_update_raw_once(struct hdac_device *codec, unsignedint reg, unsignedint mask, unsignedint val)
{ return CALL_RAW_FUNC(codec, reg_raw_update_once(codec, reg, mask, val));
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw_once);
/** * snd_hdac_regmap_sync - sync out the cached values for PM resume * @codec: the codec object
*/ void snd_hdac_regmap_sync(struct hdac_device *codec)
{
mutex_lock(&codec->regmap_lock); if (codec->regmap)
regcache_sync(codec->regmap);
mutex_unlock(&codec->regmap_lock);
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_sync);
Messung V0.5
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.