// 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 */ #include <linux/slab.h> #include <linux/device.h> #include <linux/regmap.h> #include <linux/export.h> #include <linux/pm.h> #include <sound/core.h> #include <sound/hdaudio.h> #include <sound/hda_regmap.h> #include "local.h" static int codec_pm_lock(struct hdac_device *codec) { … } static void codec_pm_unlock(struct hdac_device *codec, int lock) { … } #define get_verb(reg) … static bool hda_volatile_reg(struct device *dev, unsigned int reg) { … } static bool hda_writeable_reg(struct device *dev, unsigned int reg) { … } static bool hda_readable_reg(struct device *dev, unsigned int 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. */ static bool is_stereo_amp_verb(unsigned int reg) { … } /* read a pseudo stereo amp register (16bit left+right) */ static int hda_reg_read_stereo_amp(struct hdac_device *codec, unsigned int reg, unsigned int *val) { … } /* write a pseudo stereo amp register (16bit left+right) */ static int hda_reg_write_stereo_amp(struct hdac_device *codec, unsigned int reg, unsigned int val) { … } /* read a pseudo coef register (16bit) */ static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg, unsigned int *val) { … } /* write a pseudo coef register (16bit) */ static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg, unsigned int val) { … } static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) { … } static int hda_reg_write(void *context, unsigned int reg, unsigned int val) { … } static const struct regmap_config hda_regmap_cfg = …; /** * 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) { … } EXPORT_SYMBOL_GPL(…); /** * snd_hdac_regmap_exit - Release the regmap from HDA codec * @codec: the codec object */ void snd_hdac_regmap_exit(struct hdac_device *codec) { … } EXPORT_SYMBOL_GPL(…); /** * 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, unsigned int verb) { … } EXPORT_SYMBOL_GPL(…); /* * helper functions */ /* write a pseudo-register value (w/o power sequence) */ static int reg_raw_write(struct hdac_device *codec, unsigned int reg, unsigned int val) { … } /* a helper macro to call @func_call; retry with power-up if failed */ #define CALL_RAW_FUNC(codec, func_call) … /** * 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, unsigned int reg, unsigned int val) { … } EXPORT_SYMBOL_GPL(…); static int reg_raw_read(struct hdac_device *codec, unsigned int reg, unsigned int *val, bool uncached) { … } static int __snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg, unsigned int *val, bool uncached) { … } /** * 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, unsigned int reg, unsigned int *val) { … } EXPORT_SYMBOL_GPL(…); /* 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, unsigned int reg, unsigned int *val) { … } static int reg_raw_update(struct hdac_device *codec, unsigned int reg, unsigned int mask, unsigned int val) { … } /** * 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, unsigned int reg, unsigned int mask, unsigned int val) { … } EXPORT_SYMBOL_GPL(…); static int reg_raw_update_once(struct hdac_device *codec, unsigned int reg, unsigned int mask, unsigned int val) { … } /** * 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, unsigned int reg, unsigned int mask, unsigned int val) { … } EXPORT_SYMBOL_GPL(…); /** * 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) { … } EXPORT_SYMBOL_GPL(…);