Merge branch 'topic/hda-regmap' into for-next
This merges the support of regmap in HD-audio infrastructure. Many in-house cache codes in HD-audio driver are relaced with the more standard regmap base now. Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
commit
664bc5c559
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* HD-audio regmap helpers
|
||||
*/
|
||||
|
||||
#ifndef __SOUND_HDA_REGMAP_H
|
||||
#define __SOUND_HDA_REGMAP_H
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/hdaudio.h>
|
||||
|
||||
int snd_hdac_regmap_init(struct hdac_device *codec);
|
||||
void snd_hdac_regmap_exit(struct hdac_device *codec);
|
||||
int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec,
|
||||
unsigned int verb);
|
||||
int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
|
||||
unsigned int *val);
|
||||
int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
|
||||
unsigned int val);
|
||||
int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
|
||||
unsigned int mask, unsigned int val);
|
||||
|
||||
/**
|
||||
* snd_hdac_regmap_encode_verb - encode the verb to a pseudo register
|
||||
* @nid: widget NID
|
||||
* @verb: codec verb
|
||||
*
|
||||
* Returns an encoded pseudo register.
|
||||
*/
|
||||
#define snd_hdac_regmap_encode_verb(nid, verb) \
|
||||
(((verb) << 8) | 0x80000 | ((unsigned int)(nid) << 20))
|
||||
|
||||
/**
|
||||
* snd_hdac_regmap_encode_amp - encode the AMP verb to a pseudo register
|
||||
* @nid: widget NID
|
||||
* @ch: channel (left = 0, right = 1)
|
||||
* @dir: direction (#HDA_INPUT, #HDA_OUTPUT)
|
||||
* @idx: input index value
|
||||
*
|
||||
* Returns an encoded pseudo register.
|
||||
*/
|
||||
#define snd_hdac_regmap_encode_amp(nid, ch, dir, idx) \
|
||||
(snd_hdac_regmap_encode_verb(nid, AC_VERB_GET_AMP_GAIN_MUTE) | \
|
||||
((ch) ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT) | \
|
||||
((dir) == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT) | \
|
||||
(idx))
|
||||
|
||||
/**
|
||||
* snd_hdac_regmap_encode_amp_stereo - encode a pseudo register for stereo AMPs
|
||||
* @nid: widget NID
|
||||
* @dir: direction (#HDA_INPUT, #HDA_OUTPUT)
|
||||
* @idx: input index value
|
||||
*
|
||||
* Returns an encoded pseudo register.
|
||||
*/
|
||||
#define snd_hdac_regmap_encode_amp_stereo(nid, dir, idx) \
|
||||
(snd_hdac_regmap_encode_verb(nid, AC_VERB_GET_AMP_GAIN_MUTE) | \
|
||||
AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT | /* both bits set! */ \
|
||||
((dir) == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT) | \
|
||||
(idx))
|
||||
|
||||
/**
|
||||
* snd_hdac_regmap_write - Write a verb with caching
|
||||
* @nid: codec NID
|
||||
* @reg: verb to write
|
||||
* @val: value to write
|
||||
*
|
||||
* For writing an amp value, use snd_hda_regmap_amp_update().
|
||||
*/
|
||||
static inline int
|
||||
snd_hdac_regmap_write(struct hdac_device *codec, hda_nid_t nid,
|
||||
unsigned int verb, unsigned int val)
|
||||
{
|
||||
unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb);
|
||||
|
||||
return snd_hdac_regmap_write_raw(codec, cmd, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_regmap_update - Update a verb value with caching
|
||||
* @nid: codec NID
|
||||
* @verb: verb to update
|
||||
* @mask: bit mask to update
|
||||
* @val: value to update
|
||||
*
|
||||
* For updating an amp value, use snd_hda_regmap_amp_update().
|
||||
*/
|
||||
static inline int
|
||||
snd_hdac_regmap_update(struct hdac_device *codec, hda_nid_t nid,
|
||||
unsigned int verb, unsigned int mask,
|
||||
unsigned int val)
|
||||
{
|
||||
unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb);
|
||||
|
||||
return snd_hdac_regmap_update_raw(codec, cmd, mask, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_regmap_read - Read a verb with caching
|
||||
* @nid: codec NID
|
||||
* @verb: verb to read
|
||||
* @val: pointer to store the value
|
||||
*
|
||||
* For reading an amp value, use snd_hda_regmap_get_amp().
|
||||
*/
|
||||
static inline int
|
||||
snd_hdac_regmap_read(struct hdac_device *codec, hda_nid_t nid,
|
||||
unsigned int verb, unsigned int *val)
|
||||
{
|
||||
unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb);
|
||||
|
||||
return snd_hdac_regmap_read_raw(codec, cmd, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_regmap_get_amp - Read AMP value
|
||||
* @codec: HD-audio codec
|
||||
* @nid: NID to read the AMP value
|
||||
* @ch: channel (left=0 or right=1)
|
||||
* @direction: #HDA_INPUT or #HDA_OUTPUT
|
||||
* @index: the index value (only for input direction)
|
||||
* @val: the pointer to store the value
|
||||
*
|
||||
* Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit.
|
||||
* Returns the value or a negative error.
|
||||
*/
|
||||
static inline int
|
||||
snd_hdac_regmap_get_amp(struct hdac_device *codec, hda_nid_t nid,
|
||||
int ch, int dir, int idx)
|
||||
{
|
||||
unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
|
||||
int err, val;
|
||||
|
||||
err = snd_hdac_regmap_read_raw(codec, cmd, &val);
|
||||
return err < 0 ? err : val;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_regmap_update_amp - update the AMP value
|
||||
* @codec: HD-audio codec
|
||||
* @nid: NID to read the AMP value
|
||||
* @ch: channel (left=0 or right=1)
|
||||
* @direction: #HDA_INPUT or #HDA_OUTPUT
|
||||
* @idx: the index value (only for input direction)
|
||||
* @mask: bit mask to set
|
||||
* @val: the bits value to set
|
||||
*
|
||||
* Update the AMP value with a bit mask.
|
||||
* Returns 0 if the value is unchanged, 1 if changed, or a negative error.
|
||||
*/
|
||||
static inline int
|
||||
snd_hdac_regmap_update_amp(struct hdac_device *codec, hda_nid_t nid,
|
||||
int ch, int dir, int idx, int mask, int val)
|
||||
{
|
||||
unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
|
||||
|
||||
return snd_hdac_regmap_update_raw(codec, cmd, mask, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_regmap_get_amp_stereo - Read stereo AMP values
|
||||
* @codec: HD-audio codec
|
||||
* @nid: NID to read the AMP value
|
||||
* @ch: channel (left=0 or right=1)
|
||||
* @direction: #HDA_INPUT or #HDA_OUTPUT
|
||||
* @index: the index value (only for input direction)
|
||||
* @val: the pointer to store the value
|
||||
*
|
||||
* Read stereo AMP values. The lower byte is left, the upper byte is right.
|
||||
* Returns the value or a negative error.
|
||||
*/
|
||||
static inline int
|
||||
snd_hdac_regmap_get_amp_stereo(struct hdac_device *codec, hda_nid_t nid,
|
||||
int dir, int idx)
|
||||
{
|
||||
unsigned int cmd = snd_hdac_regmap_encode_amp_stereo(nid, dir, idx);
|
||||
int err, val;
|
||||
|
||||
err = snd_hdac_regmap_read_raw(codec, cmd, &val);
|
||||
return err < 0 ? err : val;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_regmap_update_amp_stereo - update the stereo AMP value
|
||||
* @codec: HD-audio codec
|
||||
* @nid: NID to read the AMP value
|
||||
* @direction: #HDA_INPUT or #HDA_OUTPUT
|
||||
* @idx: the index value (only for input direction)
|
||||
* @mask: bit mask to set
|
||||
* @val: the bits value to set
|
||||
*
|
||||
* Update the stereo AMP value with a bit mask.
|
||||
* The lower byte is left, the upper byte is right.
|
||||
* Returns 0 if the value is unchanged, 1 if changed, or a negative error.
|
||||
*/
|
||||
static inline int
|
||||
snd_hdac_regmap_update_amp_stereo(struct hdac_device *codec, hda_nid_t nid,
|
||||
int dir, int idx, int mask, int val)
|
||||
{
|
||||
unsigned int cmd = snd_hdac_regmap_encode_amp_stereo(nid, dir, idx);
|
||||
|
||||
return snd_hdac_regmap_update_raw(codec, cmd, mask, val);
|
||||
}
|
||||
|
||||
#endif /* __SOUND_HDA_REGMAP_H */
|
|
@ -21,6 +21,17 @@ struct hdac_widget_tree;
|
|||
*/
|
||||
extern struct bus_type snd_hda_bus_type;
|
||||
|
||||
/*
|
||||
* generic arrays
|
||||
*/
|
||||
struct snd_array {
|
||||
unsigned int used;
|
||||
unsigned int alloced;
|
||||
unsigned int elem_size;
|
||||
unsigned int alloc_align;
|
||||
void *list;
|
||||
};
|
||||
|
||||
/*
|
||||
* HD-audio codec base device
|
||||
*/
|
||||
|
@ -61,6 +72,13 @@ struct hdac_device {
|
|||
|
||||
/* sysfs */
|
||||
struct hdac_widget_tree *widgets;
|
||||
|
||||
/* regmap */
|
||||
struct regmap *regmap;
|
||||
struct snd_array vendor_verbs;
|
||||
bool lazy_cache:1; /* don't wake up for writes */
|
||||
bool caps_overwriting:1; /* caps overwrite being in process */
|
||||
bool cache_coef:1; /* cache COEF read/write too */
|
||||
};
|
||||
|
||||
/* device/driver type used for matching */
|
||||
|
@ -90,12 +108,34 @@ int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
|
|||
unsigned int flags, unsigned int *res);
|
||||
int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
|
||||
unsigned int verb, unsigned int parm, unsigned int *res);
|
||||
int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm);
|
||||
int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
|
||||
unsigned int *res);
|
||||
int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
|
||||
int parm);
|
||||
int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
|
||||
unsigned int parm, unsigned int val);
|
||||
int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid,
|
||||
hda_nid_t *conn_list, int max_conns);
|
||||
int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
|
||||
hda_nid_t *start_id);
|
||||
|
||||
/**
|
||||
* snd_hdac_read_parm - read a codec parameter
|
||||
* @codec: the codec object
|
||||
* @nid: NID to read a parameter
|
||||
* @parm: parameter to read
|
||||
*
|
||||
* Returns -1 for error. If you need to distinguish the error more
|
||||
* strictly, use _snd_hdac_read_parm() directly.
|
||||
*/
|
||||
static inline int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid,
|
||||
int parm)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
return _snd_hdac_read_parm(codec, nid, parm, &val) < 0 ? -1 : val;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
void snd_hdac_power_up(struct hdac_device *codec);
|
||||
void snd_hdac_power_down(struct hdac_device *codec);
|
||||
|
@ -178,4 +218,26 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec)
|
|||
clear_bit(codec->addr, &codec->bus->codec_powered);
|
||||
}
|
||||
|
||||
/*
|
||||
* generic array helpers
|
||||
*/
|
||||
void *snd_array_new(struct snd_array *array);
|
||||
void snd_array_free(struct snd_array *array);
|
||||
static inline void snd_array_init(struct snd_array *array, unsigned int size,
|
||||
unsigned int align)
|
||||
{
|
||||
array->elem_size = size;
|
||||
array->alloc_align = align;
|
||||
}
|
||||
|
||||
static inline void *snd_array_elem(struct snd_array *array, unsigned int idx)
|
||||
{
|
||||
return array->list + idx * array->elem_size;
|
||||
}
|
||||
|
||||
static inline unsigned int snd_array_index(struct snd_array *array, void *ptr)
|
||||
{
|
||||
return (unsigned long)(ptr - array->list) / array->elem_size;
|
||||
}
|
||||
|
||||
#endif /* __SOUND_HDAUDIO_H */
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
config SND_HDA_CORE
|
||||
tristate
|
||||
select REGMAP
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o
|
||||
snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
|
||||
hdac_regmap.o array.o
|
||||
|
||||
snd-hda-core-objs += trace.o
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* generic arrays
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/hdaudio.h>
|
||||
|
||||
/**
|
||||
* snd_array_new - get a new element from the given array
|
||||
* @array: the array object
|
||||
*
|
||||
* Get a new element from the given array. If it exceeds the
|
||||
* pre-allocated array size, re-allocate the array.
|
||||
*
|
||||
* Returns NULL if allocation failed.
|
||||
*/
|
||||
void *snd_array_new(struct snd_array *array)
|
||||
{
|
||||
if (snd_BUG_ON(!array->elem_size))
|
||||
return NULL;
|
||||
if (array->used >= array->alloced) {
|
||||
int num = array->alloced + array->alloc_align;
|
||||
int size = (num + 1) * array->elem_size;
|
||||
void *nlist;
|
||||
if (snd_BUG_ON(num >= 4096))
|
||||
return NULL;
|
||||
nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO);
|
||||
if (!nlist)
|
||||
return NULL;
|
||||
array->list = nlist;
|
||||
array->alloced = num;
|
||||
}
|
||||
return snd_array_elem(array, array->used++);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_array_new);
|
||||
|
||||
/**
|
||||
* snd_array_free - free the given array elements
|
||||
* @array: the array object
|
||||
*/
|
||||
void snd_array_free(struct snd_array *array)
|
||||
{
|
||||
kfree(array->list);
|
||||
array->used = 0;
|
||||
array->alloced = 0;
|
||||
array->list = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_array_free);
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include <sound/hda_regmap.h>
|
||||
#include "local.h"
|
||||
|
||||
static void setup_fg_nodes(struct hdac_device *codec);
|
||||
|
@ -234,7 +235,22 @@ int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
|
|||
EXPORT_SYMBOL_GPL(snd_hdac_read);
|
||||
|
||||
/**
|
||||
* snd_hdac_read_parm - read a codec parameter
|
||||
* _snd_hdac_read_parm - read a parmeter
|
||||
*
|
||||
* This function returns zero or an error unlike snd_hdac_read_parm().
|
||||
*/
|
||||
int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
|
||||
unsigned int *res)
|
||||
{
|
||||
unsigned int cmd;
|
||||
|
||||
cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
|
||||
return snd_hdac_regmap_read_raw(codec, cmd, res);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(_snd_hdac_read_parm);
|
||||
|
||||
/**
|
||||
* snd_hdac_read_parm_uncached - read a codec parameter without caching
|
||||
* @codec: the codec object
|
||||
* @nid: NID to read a parameter
|
||||
* @parm: parameter to read
|
||||
|
@ -242,15 +258,42 @@ EXPORT_SYMBOL_GPL(snd_hdac_read);
|
|||
* Returns -1 for error. If you need to distinguish the error more
|
||||
* strictly, use snd_hdac_read() directly.
|
||||
*/
|
||||
int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm)
|
||||
int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
|
||||
int parm)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (snd_hdac_read(codec, nid, AC_VERB_PARAMETERS, parm, &val))
|
||||
return -1;
|
||||
if (codec->regmap)
|
||||
regcache_cache_bypass(codec->regmap, true);
|
||||
val = snd_hdac_read_parm(codec, nid, parm);
|
||||
if (codec->regmap)
|
||||
regcache_cache_bypass(codec->regmap, false);
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_read_parm);
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached);
|
||||
|
||||
/**
|
||||
* snd_hdac_override_parm - override read-only parameters
|
||||
* @codec: the codec object
|
||||
* @nid: NID for the parameter
|
||||
* @parm: the parameter to change
|
||||
* @val: the parameter value to overwrite
|
||||
*/
|
||||
int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
|
||||
unsigned int parm, unsigned int val)
|
||||
{
|
||||
unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm;
|
||||
int err;
|
||||
|
||||
if (!codec->regmap)
|
||||
return -EINVAL;
|
||||
|
||||
codec->caps_overwriting = true;
|
||||
err = snd_hdac_regmap_write_raw(codec, verb, val);
|
||||
codec->caps_overwriting = false;
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_override_parm);
|
||||
|
||||
/**
|
||||
* snd_hdac_get_sub_nodes - get start NID and number of subtree nodes
|
||||
|
@ -259,13 +302,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_read_parm);
|
|||
* @start_id: the pointer to store the starting NID
|
||||
*
|
||||
* Returns the number of subtree nodes or zero if not found.
|
||||
* This function reads parameters always without caching.
|
||||
*/
|
||||
int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
|
||||
hda_nid_t *start_id)
|
||||
{
|
||||
unsigned int parm;
|
||||
|
||||
parm = snd_hdac_read_parm(codec, nid, AC_PAR_NODE_COUNT);
|
||||
parm = snd_hdac_read_parm_uncached(codec, nid, AC_PAR_NODE_COUNT);
|
||||
if (parm == -1) {
|
||||
*start_id = 0;
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,472 @@
|
|||
/*
|
||||
* 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 <linux/pm_runtime.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include <sound/hda_regmap.h>
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#define codec_is_running(codec) \
|
||||
(atomic_read(&(codec)->in_pm) || \
|
||||
!pm_runtime_suspended(&(codec)->dev))
|
||||
#else
|
||||
#define codec_is_running(codec) true
|
||||
#endif
|
||||
|
||||
#define get_verb(reg) (((reg) >> 8) & 0xfff)
|
||||
|
||||
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);
|
||||
|
||||
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 */
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hda_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct hdac_device *codec = dev_to_hdac_dev(dev);
|
||||
unsigned int verb = get_verb(reg);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < codec->vendor_verbs.used; i++) {
|
||||
unsigned int *v = snd_array_elem(&codec->vendor_verbs, i);
|
||||
if (verb == *v)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (codec->caps_overwriting)
|
||||
return true;
|
||||
|
||||
switch (verb & 0xf00) {
|
||||
case AC_VERB_GET_STREAM_FORMAT:
|
||||
case AC_VERB_GET_AMP_GAIN_MUTE:
|
||||
return true;
|
||||
case AC_VERB_GET_PROC_COEF:
|
||||
return codec->cache_coef;
|
||||
case 0xf00:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
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:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hda_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct hdac_device *codec = dev_to_hdac_dev(dev);
|
||||
unsigned int verb = get_verb(reg);
|
||||
|
||||
if (codec->caps_overwriting)
|
||||
return true;
|
||||
|
||||
switch (verb) {
|
||||
case AC_VERB_PARAMETERS:
|
||||
case AC_VERB_GET_CONNECT_LIST:
|
||||
case AC_VERB_GET_SUBSYSTEM_ID:
|
||||
return true;
|
||||
/* 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 */
|
||||
return true;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
static bool is_stereo_amp_verb(unsigned int reg)
|
||||
{
|
||||
if (((reg >> 8) & 0x700) != AC_VERB_SET_AMP_GAIN_MUTE)
|
||||
return false;
|
||||
return (reg & (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT)) ==
|
||||
(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
unsigned int left, right;
|
||||
int err;
|
||||
|
||||
reg &= ~(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
|
||||
err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_LEFT, 0, &left);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_RIGHT, 0, &right);
|
||||
if (err < 0)
|
||||
return err;
|
||||
*val = left | (right << 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
int err;
|
||||
unsigned int verb, left, right;
|
||||
|
||||
verb = AC_VERB_SET_AMP_GAIN_MUTE << 8;
|
||||
if (reg & AC_AMP_GET_OUTPUT)
|
||||
verb |= AC_AMP_SET_OUTPUT;
|
||||
else
|
||||
verb |= AC_AMP_SET_INPUT | ((reg & 0xf) << 8);
|
||||
reg = (reg & ~0xfffff) | verb;
|
||||
|
||||
left = val & 0xff;
|
||||
right = (val >> 8) & 0xff;
|
||||
if (left == right) {
|
||||
reg |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
|
||||
return snd_hdac_exec_verb(codec, reg | left, 0, NULL);
|
||||
}
|
||||
|
||||
err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_LEFT | left, 0, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_RIGHT | right, 0, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
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)
|
||||
{
|
||||
struct hdac_device *codec = context;
|
||||
int verb = get_verb(reg);
|
||||
int err;
|
||||
|
||||
if (!codec_is_running(codec))
|
||||
return -EAGAIN;
|
||||
reg |= (codec->addr << 28);
|
||||
if (is_stereo_amp_verb(reg))
|
||||
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);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* special handling for asymmetric reads */
|
||||
if (verb == AC_VERB_GET_POWER_STATE) {
|
||||
if (*val & AC_PWRST_ERROR)
|
||||
*val = -1;
|
||||
else /* take only the actual state */
|
||||
*val = (*val >> 4) & 0x0f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct hdac_device *codec = context;
|
||||
unsigned int verb;
|
||||
int i, bytes, err;
|
||||
|
||||
if (!codec_is_running(codec))
|
||||
return codec->lazy_cache ? 0 : -EAGAIN;
|
||||
|
||||
reg &= ~0x00080000U; /* drop GET bit */
|
||||
reg |= (codec->addr << 28);
|
||||
|
||||
if (is_stereo_amp_verb(reg))
|
||||
return hda_reg_write_stereo_amp(codec, reg, val);
|
||||
|
||||
verb = get_verb(reg);
|
||||
if (verb == AC_VERB_SET_PROC_COEF)
|
||||
return hda_reg_write_coef(codec, reg, val);
|
||||
|
||||
switch (verb & 0xf00) {
|
||||
case AC_VERB_SET_AMP_GAIN_MUTE:
|
||||
verb = AC_VERB_SET_AMP_GAIN_MUTE;
|
||||
if (reg & AC_AMP_GET_LEFT)
|
||||
verb |= AC_AMP_SET_LEFT >> 8;
|
||||
else
|
||||
verb |= AC_AMP_SET_RIGHT >> 8;
|
||||
if (reg & AC_AMP_GET_OUTPUT) {
|
||||
verb |= AC_AMP_SET_OUTPUT >> 8;
|
||||
} else {
|
||||
verb |= AC_AMP_SET_INPUT >> 8;
|
||||
verb |= reg & 0xf;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (verb) {
|
||||
case AC_VERB_SET_DIGI_CONVERT_1:
|
||||
bytes = 2;
|
||||
break;
|
||||
case AC_VERB_SET_CONFIG_DEFAULT_BYTES_0:
|
||||
bytes = 4;
|
||||
break;
|
||||
default:
|
||||
bytes = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < bytes; i++) {
|
||||
reg &= ~0xfffff;
|
||||
reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff);
|
||||
err = snd_hdac_exec_verb(codec, reg, 0, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config hda_regmap_cfg = {
|
||||
.name = "hdaudio",
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.max_register = 0xfffffff,
|
||||
.writeable_reg = hda_writeable_reg,
|
||||
.readable_reg = hda_readable_reg,
|
||||
.volatile_reg = hda_volatile_reg,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.reg_read = hda_reg_read,
|
||||
.reg_write = hda_reg_write,
|
||||
.use_single_rw = true,
|
||||
};
|
||||
|
||||
int snd_hdac_regmap_init(struct hdac_device *codec)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = regmap_init(&codec->dev, NULL, codec, &hda_regmap_cfg);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
codec->regmap = regmap;
|
||||
snd_array_init(&codec->vendor_verbs, sizeof(unsigned int), 8);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
|
||||
|
||||
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,
|
||||
unsigned int verb)
|
||||
{
|
||||
unsigned int *p = snd_array_new(&codec->vendor_verbs);
|
||||
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
*p = verb;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
if (!codec->regmap)
|
||||
return hda_reg_write(codec, reg, val);
|
||||
else
|
||||
return regmap_write(codec->regmap, reg, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = reg_raw_write(codec, reg, val);
|
||||
if (err == -EAGAIN) {
|
||||
snd_hdac_power_up(codec);
|
||||
err = reg_raw_write(codec, reg, val);
|
||||
snd_hdac_power_down(codec);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
|
||||
|
||||
static int reg_raw_read(struct hdac_device *codec, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
if (!codec->regmap)
|
||||
return hda_reg_read(codec, reg, val);
|
||||
else
|
||||
return regmap_read(codec->regmap, reg, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = reg_raw_read(codec, reg, val);
|
||||
if (err == -EAGAIN) {
|
||||
snd_hdac_power_up(codec);
|
||||
err = reg_raw_read(codec, reg, val);
|
||||
snd_hdac_power_down(codec);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw);
|
||||
|
||||
/**
|
||||
* snd_hdac_regmap_update_raw - update a pseudo register with power mgmt
|
||||
* @codec: the codec object
|
||||
* @reg: pseudo register
|
||||
* @mask: bit mask to udpate
|
||||
* @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)
|
||||
{
|
||||
unsigned int orig;
|
||||
int err;
|
||||
|
||||
val &= mask;
|
||||
err = snd_hdac_regmap_read_raw(codec, reg, &orig);
|
||||
if (err < 0)
|
||||
return err;
|
||||
val |= orig & ~mask;
|
||||
if (val == orig)
|
||||
return 0;
|
||||
err = snd_hdac_regmap_write_raw(codec, reg, val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);
|
|
@ -73,6 +73,9 @@ static int hda_codec_driver_probe(struct device *dev)
|
|||
return -EINVAL;
|
||||
|
||||
err = codec_refresh_name(codec, codec->preset->name);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = snd_hdac_regmap_init(&codec->core);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
|
@ -98,6 +101,7 @@ static int hda_codec_driver_probe(struct device *dev)
|
|||
snd_hda_codec_register(codec);
|
||||
}
|
||||
|
||||
codec->core.lazy_cache = true;
|
||||
return 0;
|
||||
|
||||
error_module:
|
||||
|
|
|
@ -586,8 +586,8 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
|
|||
return -ENOMEM;
|
||||
nid = codec->core.start_nid;
|
||||
for (i = 0; i < codec->core.num_nodes; i++, nid++)
|
||||
codec->wcaps[i] = snd_hda_param_read(codec, nid,
|
||||
AC_PAR_AUDIO_WIDGET_CAP);
|
||||
codec->wcaps[i] = snd_hdac_read_parm_uncached(&codec->core,
|
||||
nid, AC_PAR_AUDIO_WIDGET_CAP);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -807,10 +807,6 @@ static void hda_jackpoll_work(struct work_struct *work)
|
|||
codec->jackpoll_interval);
|
||||
}
|
||||
|
||||
static void init_hda_cache(struct hda_cache_rec *cache,
|
||||
unsigned int record_size);
|
||||
static void free_hda_cache(struct hda_cache_rec *cache);
|
||||
|
||||
/* release all pincfg lists */
|
||||
static void free_init_pincfgs(struct hda_codec *codec)
|
||||
{
|
||||
|
@ -929,11 +925,6 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
|
|||
codec->proc_widget_hook = NULL;
|
||||
codec->spec = NULL;
|
||||
|
||||
free_hda_cache(&codec->amp_cache);
|
||||
free_hda_cache(&codec->cmd_cache);
|
||||
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
|
||||
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
|
||||
|
||||
/* free only driver_pins so that init_pins + user_pins are restored */
|
||||
snd_array_free(&codec->driver_pins);
|
||||
snd_array_free(&codec->cvt_setups);
|
||||
|
@ -945,6 +936,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
|
|||
snd_array_free(&codec->mixers);
|
||||
snd_array_free(&codec->nids);
|
||||
remove_conn_list(codec);
|
||||
snd_hdac_regmap_exit(&codec->core);
|
||||
}
|
||||
|
||||
static unsigned int hda_set_power_state(struct hda_codec *codec,
|
||||
|
@ -995,8 +987,6 @@ static void snd_hda_codec_dev_release(struct device *dev)
|
|||
free_init_pincfgs(codec);
|
||||
snd_hdac_device_exit(&codec->core);
|
||||
snd_hda_sysfs_clear(codec);
|
||||
free_hda_cache(&codec->amp_cache);
|
||||
free_hda_cache(&codec->cmd_cache);
|
||||
kfree(codec->modelname);
|
||||
kfree(codec->wcaps);
|
||||
kfree(codec);
|
||||
|
@ -1049,9 +1039,6 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
|
|||
codec->addr = codec_addr;
|
||||
mutex_init(&codec->spdif_mutex);
|
||||
mutex_init(&codec->control_mutex);
|
||||
mutex_init(&codec->hash_mutex);
|
||||
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
|
||||
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
|
||||
snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
|
||||
snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32);
|
||||
snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
|
||||
|
@ -1319,127 +1306,6 @@ static void hda_cleanup_all_streams(struct hda_codec *codec)
|
|||
* amp access functions
|
||||
*/
|
||||
|
||||
/* FIXME: more better hash key? */
|
||||
#define HDA_HASH_KEY(nid, dir, idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
|
||||
#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
|
||||
#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
|
||||
#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
|
||||
#define INFO_AMP_CAPS (1<<0)
|
||||
#define INFO_AMP_VOL(ch) (1 << (1 + (ch)))
|
||||
|
||||
/* initialize the hash table */
|
||||
static void init_hda_cache(struct hda_cache_rec *cache,
|
||||
unsigned int record_size)
|
||||
{
|
||||
memset(cache, 0, sizeof(*cache));
|
||||
memset(cache->hash, 0xff, sizeof(cache->hash));
|
||||
snd_array_init(&cache->buf, record_size, 64);
|
||||
}
|
||||
|
||||
static void free_hda_cache(struct hda_cache_rec *cache)
|
||||
{
|
||||
snd_array_free(&cache->buf);
|
||||
}
|
||||
|
||||
/* query the hash. allocate an entry if not found. */
|
||||
static struct hda_cache_head *get_hash(struct hda_cache_rec *cache, u32 key)
|
||||
{
|
||||
u16 idx = key % (u16)ARRAY_SIZE(cache->hash);
|
||||
u16 cur = cache->hash[idx];
|
||||
struct hda_cache_head *info;
|
||||
|
||||
while (cur != 0xffff) {
|
||||
info = snd_array_elem(&cache->buf, cur);
|
||||
if (info->key == key)
|
||||
return info;
|
||||
cur = info->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* query the hash. allocate an entry if not found. */
|
||||
static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
|
||||
u32 key)
|
||||
{
|
||||
struct hda_cache_head *info = get_hash(cache, key);
|
||||
if (!info) {
|
||||
u16 idx, cur;
|
||||
/* add a new hash entry */
|
||||
info = snd_array_new(&cache->buf);
|
||||
if (!info)
|
||||
return NULL;
|
||||
cur = snd_array_index(&cache->buf, info);
|
||||
info->key = key;
|
||||
info->val = 0;
|
||||
info->dirty = 0;
|
||||
idx = key % (u16)ARRAY_SIZE(cache->hash);
|
||||
info->next = cache->hash[idx];
|
||||
cache->hash[idx] = cur;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/* query and allocate an amp hash entry */
|
||||
static inline struct hda_amp_info *
|
||||
get_alloc_amp_hash(struct hda_codec *codec, u32 key)
|
||||
{
|
||||
return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
|
||||
}
|
||||
|
||||
/* overwrite the value with the key in the caps hash */
|
||||
static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val)
|
||||
{
|
||||
struct hda_amp_info *info;
|
||||
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
info = get_alloc_amp_hash(codec, key);
|
||||
if (!info) {
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
info->amp_caps = val;
|
||||
info->head.val |= INFO_AMP_CAPS;
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* query the value from the caps hash; if not found, fetch the current
|
||||
* value from the given function and store in the hash
|
||||
*/
|
||||
static unsigned int
|
||||
query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key,
|
||||
unsigned int (*func)(struct hda_codec *, hda_nid_t, int))
|
||||
{
|
||||
struct hda_amp_info *info;
|
||||
unsigned int val;
|
||||
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
info = get_alloc_amp_hash(codec, key);
|
||||
if (!info) {
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
return 0;
|
||||
}
|
||||
if (!(info->head.val & INFO_AMP_CAPS)) {
|
||||
mutex_unlock(&codec->hash_mutex); /* for reentrance */
|
||||
val = func(codec, nid, dir);
|
||||
write_caps_hash(codec, key, val);
|
||||
} else {
|
||||
val = info->amp_caps;
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid,
|
||||
int direction)
|
||||
{
|
||||
if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
|
||||
nid = codec->core.afg;
|
||||
return snd_hda_param_read(codec, nid,
|
||||
direction == HDA_OUTPUT ?
|
||||
AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* query_amp_caps - query AMP capabilities
|
||||
* @codec: the HD-auio codec
|
||||
|
@ -1454,9 +1320,11 @@ static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid,
|
|||
*/
|
||||
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
|
||||
{
|
||||
return query_caps_hash(codec, nid, direction,
|
||||
HDA_HASH_KEY(nid, direction, 0),
|
||||
read_amp_cap);
|
||||
if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
|
||||
nid = codec->core.afg;
|
||||
return snd_hda_param_read(codec, nid,
|
||||
direction == HDA_OUTPUT ?
|
||||
AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(query_amp_caps);
|
||||
|
||||
|
@ -1497,184 +1365,15 @@ EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps);
|
|||
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
|
||||
unsigned int caps)
|
||||
{
|
||||
return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps);
|
||||
unsigned int parm;
|
||||
|
||||
snd_hda_override_wcaps(codec, nid,
|
||||
get_wcaps(codec, nid) | AC_WCAP_AMP_OVRD);
|
||||
parm = dir == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP;
|
||||
return snd_hdac_override_parm(&codec->core, nid, parm, caps);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
|
||||
|
||||
static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid,
|
||||
int dir)
|
||||
{
|
||||
return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_query_pin_caps - Query PIN capabilities
|
||||
* @codec: the HD-auio codec
|
||||
* @nid: the NID to query
|
||||
*
|
||||
* Query PIN capabilities for the given widget.
|
||||
* Returns the obtained capability bits.
|
||||
*
|
||||
* When cap bits have been already read, this doesn't read again but
|
||||
* returns the cached value.
|
||||
*/
|
||||
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid),
|
||||
read_pin_cap);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_query_pin_caps);
|
||||
|
||||
/**
|
||||
* snd_hda_override_pin_caps - Override the pin capabilities
|
||||
* @codec: the CODEC
|
||||
* @nid: the NID to override
|
||||
* @caps: the capability bits to set
|
||||
*
|
||||
* Override the cached PIN capabilitiy bits value by the given one.
|
||||
*
|
||||
* Returns zero if successful or a negative error code.
|
||||
*/
|
||||
int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned int caps)
|
||||
{
|
||||
return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_override_pin_caps);
|
||||
|
||||
/* read or sync the hash value with the current value;
|
||||
* call within hash_mutex
|
||||
*/
|
||||
static struct hda_amp_info *
|
||||
update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
int direction, int index, bool init_only)
|
||||
{
|
||||
struct hda_amp_info *info;
|
||||
unsigned int parm, val = 0;
|
||||
bool val_read = false;
|
||||
|
||||
retry:
|
||||
info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
|
||||
if (!info)
|
||||
return NULL;
|
||||
if (!(info->head.val & INFO_AMP_VOL(ch))) {
|
||||
if (!val_read) {
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
|
||||
parm |= direction == HDA_OUTPUT ?
|
||||
AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
|
||||
parm |= index;
|
||||
val = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_AMP_GAIN_MUTE, parm);
|
||||
val &= 0xff;
|
||||
val_read = true;
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
goto retry;
|
||||
}
|
||||
info->vol[ch] = val;
|
||||
info->head.val |= INFO_AMP_VOL(ch);
|
||||
} else if (init_only)
|
||||
return NULL;
|
||||
return info;
|
||||
}
|
||||
|
||||
/*
|
||||
* write the current volume in info to the h/w
|
||||
*/
|
||||
static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps,
|
||||
hda_nid_t nid, int ch, int direction, int index,
|
||||
int val)
|
||||
{
|
||||
u32 parm;
|
||||
|
||||
parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
|
||||
parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
|
||||
parm |= index << AC_AMP_SET_INDEX_SHIFT;
|
||||
if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) &&
|
||||
(amp_caps & AC_AMPCAP_MIN_MUTE))
|
||||
; /* set the zero value as a fake mute */
|
||||
else
|
||||
parm |= val;
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_codec_amp_read - Read AMP value
|
||||
* @codec: HD-audio codec
|
||||
* @nid: NID to read the AMP value
|
||||
* @ch: channel (left=0 or right=1)
|
||||
* @direction: #HDA_INPUT or #HDA_OUTPUT
|
||||
* @index: the index value (only for input direction)
|
||||
*
|
||||
* Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit.
|
||||
*/
|
||||
int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
int direction, int index)
|
||||
{
|
||||
struct hda_amp_info *info;
|
||||
unsigned int val = 0;
|
||||
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
info = update_amp_hash(codec, nid, ch, direction, index, false);
|
||||
if (info)
|
||||
val = info->vol[ch];
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_read);
|
||||
|
||||
static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
int direction, int idx, int mask, int val,
|
||||
bool init_only, bool cache_only)
|
||||
{
|
||||
struct hda_amp_info *info;
|
||||
unsigned int caps;
|
||||
|
||||
if (snd_BUG_ON(mask & ~0xff))
|
||||
mask &= 0xff;
|
||||
val &= mask;
|
||||
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
info = update_amp_hash(codec, nid, ch, direction, idx, init_only);
|
||||
if (!info) {
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
return 0;
|
||||
}
|
||||
val |= info->vol[ch] & ~mask;
|
||||
if (info->vol[ch] == val) {
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
return 0;
|
||||
}
|
||||
info->vol[ch] = val;
|
||||
info->head.dirty |= cache_only;
|
||||
caps = info->amp_caps;
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
if (!cache_only)
|
||||
put_vol_mute(codec, caps, nid, ch, direction, idx, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_codec_amp_update - update the AMP value
|
||||
* @codec: HD-audio codec
|
||||
* @nid: NID to read the AMP value
|
||||
* @ch: channel (left=0 or right=1)
|
||||
* @direction: #HDA_INPUT or #HDA_OUTPUT
|
||||
* @idx: the index value (only for input direction)
|
||||
* @mask: bit mask to set
|
||||
* @val: the bits value to set
|
||||
*
|
||||
* Update the AMP value with a bit mask.
|
||||
* Returns 0 if the value is unchanged, 1 if changed.
|
||||
*/
|
||||
int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
int direction, int idx, int mask, int val)
|
||||
{
|
||||
return codec_amp_update(codec, nid, ch, direction, idx, mask, val,
|
||||
false, codec->cached_write);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update);
|
||||
|
||||
/**
|
||||
* snd_hda_codec_amp_stereo - update the AMP stereo values
|
||||
* @codec: HD-audio codec
|
||||
|
@ -1718,8 +1417,16 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo);
|
|||
int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
int dir, int idx, int mask, int val)
|
||||
{
|
||||
return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true,
|
||||
codec->cached_write);
|
||||
int orig;
|
||||
|
||||
if (!codec->core.regmap)
|
||||
return -EINVAL;
|
||||
regcache_cache_only(codec->core.regmap, true);
|
||||
orig = snd_hda_codec_amp_read(codec, nid, ch, dir, idx);
|
||||
regcache_cache_only(codec->core.regmap, false);
|
||||
if (orig >= 0)
|
||||
return 0;
|
||||
return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init);
|
||||
|
||||
|
@ -1748,49 +1455,6 @@ int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init_stereo);
|
||||
|
||||
/**
|
||||
* snd_hda_codec_resume_amp - Resume all AMP commands from the cache
|
||||
* @codec: HD-audio codec
|
||||
*
|
||||
* Resume the all amp commands from the cache.
|
||||
*/
|
||||
void snd_hda_codec_resume_amp(struct hda_codec *codec)
|
||||
{
|
||||
int i;
|
||||
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
codec->cached_write = 0;
|
||||
for (i = 0; i < codec->amp_cache.buf.used; i++) {
|
||||
struct hda_amp_info *buffer;
|
||||
u32 key;
|
||||
hda_nid_t nid;
|
||||
unsigned int idx, dir, ch;
|
||||
struct hda_amp_info info;
|
||||
|
||||
buffer = snd_array_elem(&codec->amp_cache.buf, i);
|
||||
if (!buffer->head.dirty)
|
||||
continue;
|
||||
buffer->head.dirty = 0;
|
||||
info = *buffer;
|
||||
key = info.head.key;
|
||||
if (!key)
|
||||
continue;
|
||||
nid = key & 0xff;
|
||||
idx = (key >> 16) & 0xff;
|
||||
dir = (key >> 24) & 0xff;
|
||||
for (ch = 0; ch < 2; ch++) {
|
||||
if (!(info.head.val & INFO_AMP_VOL(ch)))
|
||||
continue;
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx,
|
||||
info.vol[ch]);
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_codec_resume_amp);
|
||||
|
||||
static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
|
||||
unsigned int ofs)
|
||||
{
|
||||
|
@ -1861,8 +1525,8 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
|
|||
maxval = get_amp_max_value(codec, nid, dir, 0);
|
||||
if (val > maxval)
|
||||
val = maxval;
|
||||
return codec_amp_update(codec, nid, ch, dir, idx, HDA_AMP_VOLMASK, val,
|
||||
false, !hda_codec_is_power_on(codec));
|
||||
return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
|
||||
HDA_AMP_VOLMASK, val);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2545,17 +2209,15 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
|
|||
int change = 0;
|
||||
|
||||
if (chs & 1) {
|
||||
change = codec_amp_update(codec, nid, 0, dir, idx,
|
||||
HDA_AMP_MUTE,
|
||||
*valp ? 0 : HDA_AMP_MUTE, false,
|
||||
!hda_codec_is_power_on(codec));
|
||||
change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
|
||||
HDA_AMP_MUTE,
|
||||
*valp ? 0 : HDA_AMP_MUTE);
|
||||
valp++;
|
||||
}
|
||||
if (chs & 2)
|
||||
change |= codec_amp_update(codec, nid, 1, dir, idx,
|
||||
HDA_AMP_MUTE,
|
||||
*valp ? 0 : HDA_AMP_MUTE, false,
|
||||
!hda_codec_is_power_on(codec));
|
||||
change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
|
||||
HDA_AMP_MUTE,
|
||||
*valp ? 0 : HDA_AMP_MUTE);
|
||||
hda_call_check_power_status(codec, nid);
|
||||
return change;
|
||||
}
|
||||
|
@ -2857,25 +2519,35 @@ static unsigned int convert_to_spdif_status(unsigned short val)
|
|||
|
||||
/* set digital convert verbs both for the given NID and its slaves */
|
||||
static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
|
||||
int verb, int val)
|
||||
int mask, int val)
|
||||
{
|
||||
const hda_nid_t *d;
|
||||
|
||||
snd_hda_codec_write_cache(codec, nid, 0, verb, val);
|
||||
snd_hdac_regmap_update(&codec->core, nid, AC_VERB_SET_DIGI_CONVERT_1,
|
||||
mask, val);
|
||||
d = codec->slave_dig_outs;
|
||||
if (!d)
|
||||
return;
|
||||
for (; *d; d++)
|
||||
snd_hda_codec_write_cache(codec, *d, 0, verb, val);
|
||||
snd_hdac_regmap_update(&codec->core, nid,
|
||||
AC_VERB_SET_DIGI_CONVERT_1, mask, val);
|
||||
}
|
||||
|
||||
static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid,
|
||||
int dig1, int dig2)
|
||||
{
|
||||
if (dig1 != -1)
|
||||
set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_1, dig1);
|
||||
if (dig2 != -1)
|
||||
set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, dig2);
|
||||
unsigned int mask = 0;
|
||||
unsigned int val = 0;
|
||||
|
||||
if (dig1 != -1) {
|
||||
mask |= 0xff;
|
||||
val = dig1;
|
||||
}
|
||||
if (dig2 != -1) {
|
||||
mask |= 0xff00;
|
||||
val |= dig2 << 8;
|
||||
}
|
||||
set_dig_out(codec, nid, mask, val);
|
||||
}
|
||||
|
||||
static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
|
||||
|
@ -3008,6 +2680,7 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
|
|||
struct snd_kcontrol *kctl;
|
||||
struct snd_kcontrol_new *dig_mix;
|
||||
int idx = 0;
|
||||
int val = 0;
|
||||
const int spdif_index = 16;
|
||||
struct hda_spdif_out *spdif;
|
||||
struct hda_bus *bus = codec->bus;
|
||||
|
@ -3048,8 +2721,9 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
|
|||
return err;
|
||||
}
|
||||
spdif->nid = cvt_nid;
|
||||
spdif->ctls = snd_hda_codec_read(codec, cvt_nid, 0,
|
||||
AC_VERB_GET_DIGI_CONVERT_1, 0);
|
||||
snd_hdac_regmap_read(&codec->core, cvt_nid,
|
||||
AC_VERB_GET_DIGI_CONVERT_1, &val);
|
||||
spdif->ctls = val;
|
||||
spdif->status = convert_to_spdif_status(spdif->ctls);
|
||||
return 0;
|
||||
}
|
||||
|
@ -3193,8 +2867,8 @@ static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol,
|
|||
change = codec->spdif_in_enable != val;
|
||||
if (change) {
|
||||
codec->spdif_in_enable = val;
|
||||
snd_hda_codec_write_cache(codec, nid, 0,
|
||||
AC_VERB_SET_DIGI_CONVERT_1, val);
|
||||
snd_hdac_regmap_write(&codec->core, nid,
|
||||
AC_VERB_SET_DIGI_CONVERT_1, val);
|
||||
}
|
||||
mutex_unlock(&codec->spdif_mutex);
|
||||
return change;
|
||||
|
@ -3205,10 +2879,11 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
|
|||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
hda_nid_t nid = kcontrol->private_value;
|
||||
unsigned short val;
|
||||
unsigned int val;
|
||||
unsigned int sbits;
|
||||
|
||||
val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0);
|
||||
snd_hdac_regmap_read(&codec->core, nid,
|
||||
AC_VERB_GET_DIGI_CONVERT_1, &val);
|
||||
sbits = convert_to_spdif_status(val);
|
||||
ucontrol->value.iec958.status[0] = sbits;
|
||||
ucontrol->value.iec958.status[1] = sbits >> 8;
|
||||
|
@ -3274,153 +2949,6 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_create_spdif_in_ctls);
|
||||
|
||||
/*
|
||||
* command cache
|
||||
*/
|
||||
|
||||
/* build a 31bit cache key with the widget id and the command parameter */
|
||||
#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid)
|
||||
#define get_cmd_cache_nid(key) ((key) & 0xff)
|
||||
#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff)
|
||||
|
||||
/**
|
||||
* snd_hda_codec_write_cache - send a single command with caching
|
||||
* @codec: the HDA codec
|
||||
* @nid: NID to send the command
|
||||
* @flags: optional bit flags
|
||||
* @verb: the verb to send
|
||||
* @parm: the parameter for the verb
|
||||
*
|
||||
* Send a single command without waiting for response.
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
int flags, unsigned int verb, unsigned int parm)
|
||||
{
|
||||
int err;
|
||||
struct hda_cache_head *c;
|
||||
u32 key;
|
||||
unsigned int cache_only;
|
||||
|
||||
cache_only = codec->cached_write;
|
||||
if (!cache_only) {
|
||||
err = snd_hda_codec_write(codec, nid, flags, verb, parm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* parm may contain the verb stuff for get/set amp */
|
||||
verb = verb | (parm >> 8);
|
||||
parm &= 0xff;
|
||||
key = build_cmd_cache_key(nid, verb);
|
||||
mutex_lock(&codec->bus->core.cmd_mutex);
|
||||
c = get_alloc_hash(&codec->cmd_cache, key);
|
||||
if (c) {
|
||||
c->val = parm;
|
||||
c->dirty = cache_only;
|
||||
}
|
||||
mutex_unlock(&codec->bus->core.cmd_mutex);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_codec_write_cache);
|
||||
|
||||
/**
|
||||
* snd_hda_codec_update_cache - check cache and write the cmd only when needed
|
||||
* @codec: the HDA codec
|
||||
* @nid: NID to send the command
|
||||
* @flags: optional bit flags
|
||||
* @verb: the verb to send
|
||||
* @parm: the parameter for the verb
|
||||
*
|
||||
* This function works like snd_hda_codec_write_cache(), but it doesn't send
|
||||
* command if the parameter is already identical with the cached value.
|
||||
* If not, it sends the command and refreshes the cache.
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
int flags, unsigned int verb, unsigned int parm)
|
||||
{
|
||||
struct hda_cache_head *c;
|
||||
u32 key;
|
||||
|
||||
/* parm may contain the verb stuff for get/set amp */
|
||||
verb = verb | (parm >> 8);
|
||||
parm &= 0xff;
|
||||
key = build_cmd_cache_key(nid, verb);
|
||||
mutex_lock(&codec->bus->core.cmd_mutex);
|
||||
c = get_hash(&codec->cmd_cache, key);
|
||||
if (c && c->val == parm) {
|
||||
mutex_unlock(&codec->bus->core.cmd_mutex);
|
||||
return 0;
|
||||
}
|
||||
mutex_unlock(&codec->bus->core.cmd_mutex);
|
||||
return snd_hda_codec_write_cache(codec, nid, flags, verb, parm);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_codec_update_cache);
|
||||
|
||||
/**
|
||||
* snd_hda_codec_resume_cache - Resume the all commands from the cache
|
||||
* @codec: HD-audio codec
|
||||
*
|
||||
* Execute all verbs recorded in the command caches to resume.
|
||||
*/
|
||||
void snd_hda_codec_resume_cache(struct hda_codec *codec)
|
||||
{
|
||||
int i;
|
||||
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
codec->cached_write = 0;
|
||||
for (i = 0; i < codec->cmd_cache.buf.used; i++) {
|
||||
struct hda_cache_head *buffer;
|
||||
u32 key;
|
||||
|
||||
buffer = snd_array_elem(&codec->cmd_cache.buf, i);
|
||||
key = buffer->key;
|
||||
if (!key)
|
||||
continue;
|
||||
if (!buffer->dirty)
|
||||
continue;
|
||||
buffer->dirty = 0;
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0,
|
||||
get_cmd_cache_cmd(key), buffer->val);
|
||||
mutex_lock(&codec->hash_mutex);
|
||||
}
|
||||
mutex_unlock(&codec->hash_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_codec_resume_cache);
|
||||
|
||||
/**
|
||||
* snd_hda_sequence_write_cache - sequence writes with caching
|
||||
* @codec: the HDA codec
|
||||
* @seq: VERB array to send
|
||||
*
|
||||
* Send the commands sequentially from the given array.
|
||||
* Thte commands are recorded on cache for power-save and resume.
|
||||
* The array must be terminated with NID=0.
|
||||
*/
|
||||
void snd_hda_sequence_write_cache(struct hda_codec *codec,
|
||||
const struct hda_verb *seq)
|
||||
{
|
||||
for (; seq->nid; seq++)
|
||||
snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
|
||||
seq->param);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_sequence_write_cache);
|
||||
|
||||
/**
|
||||
* snd_hda_codec_flush_cache - Execute all pending (cached) amps / verbs
|
||||
* @codec: HD-audio codec
|
||||
*/
|
||||
void snd_hda_codec_flush_cache(struct hda_codec *codec)
|
||||
{
|
||||
snd_hda_codec_resume_amp(codec);
|
||||
snd_hda_codec_resume_cache(codec);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache);
|
||||
|
||||
/**
|
||||
* snd_hda_codec_set_power_to_all - Set the power state to all widgets
|
||||
* @codec: the HDA codec
|
||||
|
@ -3621,22 +3149,6 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
|
|||
return state;
|
||||
}
|
||||
|
||||
/* mark all entries of cmd and amp caches dirty */
|
||||
static void hda_mark_cmd_cache_dirty(struct hda_codec *codec)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < codec->cmd_cache.buf.used; i++) {
|
||||
struct hda_cache_head *cmd;
|
||||
cmd = snd_array_elem(&codec->cmd_cache.buf, i);
|
||||
cmd->dirty = 1;
|
||||
}
|
||||
for (i = 0; i < codec->amp_cache.buf.used; i++) {
|
||||
struct hda_amp_info *amp;
|
||||
amp = snd_array_elem(&codec->amp_cache.buf, i);
|
||||
amp->head.dirty = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* kick up codec; used both from PM and power-save
|
||||
*/
|
||||
|
@ -3644,7 +3156,8 @@ static void hda_call_codec_resume(struct hda_codec *codec)
|
|||
{
|
||||
atomic_inc(&codec->core.in_pm);
|
||||
|
||||
hda_mark_cmd_cache_dirty(codec);
|
||||
if (codec->core.regmap)
|
||||
regcache_mark_dirty(codec->core.regmap);
|
||||
|
||||
codec->power_jiffies = jiffies;
|
||||
|
||||
|
@ -3657,8 +3170,8 @@ static void hda_call_codec_resume(struct hda_codec *codec)
|
|||
else {
|
||||
if (codec->patch_ops.init)
|
||||
codec->patch_ops.init(codec);
|
||||
snd_hda_codec_resume_amp(codec);
|
||||
snd_hda_codec_resume_cache(codec);
|
||||
if (codec->core.regmap)
|
||||
regcache_sync(codec->core.regmap);
|
||||
}
|
||||
|
||||
if (codec->jackpoll_interval)
|
||||
|
@ -3878,8 +3391,7 @@ unsigned int snd_hda_calc_stream_format(struct hda_codec *codec,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_calc_stream_format);
|
||||
|
||||
static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid,
|
||||
int dir)
|
||||
static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
unsigned int val = 0;
|
||||
if (nid != codec->core.afg &&
|
||||
|
@ -3892,14 +3404,7 @@ static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid,
|
|||
return val;
|
||||
}
|
||||
|
||||
static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return query_caps_hash(codec, nid, 0, HDA_HASH_PARPCM_KEY(nid),
|
||||
get_pcm_param);
|
||||
}
|
||||
|
||||
static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid,
|
||||
int dir)
|
||||
static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
|
||||
if (!streams || streams == -1)
|
||||
|
@ -3909,12 +3414,6 @@ static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid,
|
|||
return streams;
|
||||
}
|
||||
|
||||
static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return query_caps_hash(codec, nid, 0, HDA_HASH_PARSTR_KEY(nid),
|
||||
get_stream_param);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_query_supported_pcm - query the supported PCM rates and formats
|
||||
* @codec: the HDA codec
|
||||
|
@ -5012,52 +4511,6 @@ void snd_hda_bus_reset(struct hda_bus *bus)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_bus_reset);
|
||||
|
||||
/*
|
||||
* generic arrays
|
||||
*/
|
||||
|
||||
/**
|
||||
* snd_array_new - get a new element from the given array
|
||||
* @array: the array object
|
||||
*
|
||||
* Get a new element from the given array. If it exceeds the
|
||||
* pre-allocated array size, re-allocate the array.
|
||||
*
|
||||
* Returns NULL if allocation failed.
|
||||
*/
|
||||
void *snd_array_new(struct snd_array *array)
|
||||
{
|
||||
if (snd_BUG_ON(!array->elem_size))
|
||||
return NULL;
|
||||
if (array->used >= array->alloced) {
|
||||
int num = array->alloced + array->alloc_align;
|
||||
int size = (num + 1) * array->elem_size;
|
||||
void *nlist;
|
||||
if (snd_BUG_ON(num >= 4096))
|
||||
return NULL;
|
||||
nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO);
|
||||
if (!nlist)
|
||||
return NULL;
|
||||
array->list = nlist;
|
||||
array->alloced = num;
|
||||
}
|
||||
return snd_array_elem(array, array->used++);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_array_new);
|
||||
|
||||
/**
|
||||
* snd_array_free - free the given array elements
|
||||
* @array: the array object
|
||||
*/
|
||||
void snd_array_free(struct snd_array *array)
|
||||
{
|
||||
kfree(array->list);
|
||||
array->used = 0;
|
||||
array->alloced = 0;
|
||||
array->list = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_array_free);
|
||||
|
||||
/**
|
||||
* snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
|
||||
* @pcm: PCM caps bits
|
||||
|
|
|
@ -28,36 +28,7 @@
|
|||
#include <sound/hwdep.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include <sound/hda_verbs.h>
|
||||
|
||||
/*
|
||||
* generic arrays
|
||||
*/
|
||||
struct snd_array {
|
||||
unsigned int used;
|
||||
unsigned int alloced;
|
||||
unsigned int elem_size;
|
||||
unsigned int alloc_align;
|
||||
void *list;
|
||||
};
|
||||
|
||||
void *snd_array_new(struct snd_array *array);
|
||||
void snd_array_free(struct snd_array *array);
|
||||
static inline void snd_array_init(struct snd_array *array, unsigned int size,
|
||||
unsigned int align)
|
||||
{
|
||||
array->elem_size = size;
|
||||
array->alloc_align = align;
|
||||
}
|
||||
|
||||
static inline void *snd_array_elem(struct snd_array *array, unsigned int idx)
|
||||
{
|
||||
return array->list + idx * array->elem_size;
|
||||
}
|
||||
|
||||
static inline unsigned int snd_array_index(struct snd_array *array, void *ptr)
|
||||
{
|
||||
return (unsigned long)(ptr - array->list) / array->elem_size;
|
||||
}
|
||||
#include <sound/hda_regmap.h>
|
||||
|
||||
/*
|
||||
* Structures
|
||||
|
@ -181,25 +152,6 @@ struct hda_codec_ops {
|
|||
void (*stream_pm)(struct hda_codec *codec, hda_nid_t nid, bool on);
|
||||
};
|
||||
|
||||
/* record for amp information cache */
|
||||
struct hda_cache_head {
|
||||
u32 key:31; /* hash key */
|
||||
u32 dirty:1;
|
||||
u16 val; /* assigned value */
|
||||
u16 next;
|
||||
};
|
||||
|
||||
struct hda_amp_info {
|
||||
struct hda_cache_head head;
|
||||
u32 amp_caps; /* amp capabilities */
|
||||
u16 vol[2]; /* current volume & mute */
|
||||
};
|
||||
|
||||
struct hda_cache_rec {
|
||||
u16 hash[64]; /* hash table for index */
|
||||
struct snd_array buf; /* record entries */
|
||||
};
|
||||
|
||||
/* PCM callbacks */
|
||||
struct hda_pcm_ops {
|
||||
int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec,
|
||||
|
@ -283,14 +235,10 @@ struct hda_codec {
|
|||
struct snd_array mixers; /* list of assigned mixer elements */
|
||||
struct snd_array nids; /* list of mapped mixer elements */
|
||||
|
||||
struct hda_cache_rec amp_cache; /* cache for amp access */
|
||||
struct hda_cache_rec cmd_cache; /* cache for other commands */
|
||||
|
||||
struct list_head conn_list; /* linked-list of connection-list */
|
||||
|
||||
struct mutex spdif_mutex;
|
||||
struct mutex control_mutex;
|
||||
struct mutex hash_mutex;
|
||||
struct snd_array spdif_out;
|
||||
unsigned int spdif_in_enable; /* SPDIF input enable? */
|
||||
const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
|
||||
|
@ -395,7 +343,7 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
|
|||
int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
|
||||
unsigned int verb, unsigned int parm);
|
||||
#define snd_hda_param_read(codec, nid, param) \
|
||||
snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param)
|
||||
snd_hdac_read_parm(&(codec)->core, nid, param)
|
||||
#define snd_hda_get_sub_nodes(codec, nid, start_nid) \
|
||||
snd_hdac_get_sub_nodes(&(codec)->core, nid, start_nid)
|
||||
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
|
||||
|
@ -439,15 +387,15 @@ snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
|
|||
}
|
||||
|
||||
/* cached write */
|
||||
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
int flags, unsigned int verb, unsigned int parm);
|
||||
void snd_hda_sequence_write_cache(struct hda_codec *codec,
|
||||
const struct hda_verb *seq);
|
||||
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
int flags, unsigned int verb, unsigned int parm);
|
||||
void snd_hda_codec_resume_cache(struct hda_codec *codec);
|
||||
/* both for cmd & amp caches */
|
||||
void snd_hda_codec_flush_cache(struct hda_codec *codec);
|
||||
static inline int
|
||||
snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
int flags, unsigned int verb, unsigned int parm)
|
||||
{
|
||||
return snd_hdac_regmap_write(&codec->core, nid, verb, parm);
|
||||
}
|
||||
|
||||
#define snd_hda_codec_update_cache(codec, nid, flags, verb, parm) \
|
||||
snd_hda_codec_write_cache(codec, nid, flags, verb, parm)
|
||||
|
||||
/* the struct for codec->pin_configs */
|
||||
struct hda_pincfg {
|
||||
|
|
|
@ -3381,11 +3381,6 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
|
|||
imux = &spec->input_mux;
|
||||
adc_idx = kcontrol->id.index;
|
||||
mutex_lock(&codec->control_mutex);
|
||||
/* we use the cache-only update at first since multiple input paths
|
||||
* may shared the same amp; by updating only caches, the redundant
|
||||
* writes to hardware can be reduced.
|
||||
*/
|
||||
codec->cached_write = 1;
|
||||
for (i = 0; i < imux->num_items; i++) {
|
||||
path = get_input_path(codec, adc_idx, i);
|
||||
if (!path || !path->ctls[type])
|
||||
|
@ -3393,12 +3388,9 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
|
|||
kcontrol->private_value = path->ctls[type];
|
||||
err = func(kcontrol, ucontrol);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
break;
|
||||
}
|
||||
error:
|
||||
codec->cached_write = 0;
|
||||
mutex_unlock(&codec->control_mutex);
|
||||
snd_hda_codec_flush_cache(codec); /* flush the updates */
|
||||
if (err >= 0 && spec->cap_sync_hook)
|
||||
spec->cap_sync_hook(codec, kcontrol, ucontrol);
|
||||
return err;
|
||||
|
@ -5760,8 +5752,6 @@ int snd_hda_gen_init(struct hda_codec *codec)
|
|||
|
||||
snd_hda_apply_verbs(codec);
|
||||
|
||||
codec->cached_write = 1;
|
||||
|
||||
init_multi_out(codec);
|
||||
init_extra_out(codec);
|
||||
init_multi_io(codec);
|
||||
|
@ -5777,7 +5767,7 @@ int snd_hda_gen_init(struct hda_codec *codec)
|
|||
/* call init functions of standard auto-mute helpers */
|
||||
update_automute_all(codec);
|
||||
|
||||
snd_hda_codec_flush_cache(codec);
|
||||
regcache_sync(codec->core.regmap);
|
||||
|
||||
if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
|
||||
snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
|
||||
|
|
|
@ -127,18 +127,16 @@ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ctl_elem_value *ucontrol);
|
||||
#endif
|
||||
/* lowlevel accessor with caching; use carefully */
|
||||
int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
int direction, int index);
|
||||
int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
int direction, int idx, int mask, int val);
|
||||
#define snd_hda_codec_amp_read(codec, nid, ch, dir, idx) \
|
||||
snd_hdac_regmap_get_amp(&(codec)->core, nid, ch, dir, idx)
|
||||
#define snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val) \
|
||||
snd_hdac_regmap_update_amp(&(codec)->core, nid, ch, dir, idx, mask, val)
|
||||
int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
|
||||
int dir, int idx, int mask, int val);
|
||||
int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
int direction, int idx, int mask, int val);
|
||||
int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
|
||||
int dir, int idx, int mask, int val);
|
||||
void snd_hda_codec_resume_amp(struct hda_codec *codec);
|
||||
|
||||
void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
|
||||
unsigned int *tlv);
|
||||
struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
|
||||
|
@ -559,9 +557,41 @@ static inline void snd_hda_override_wcaps(struct hda_codec *codec,
|
|||
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
|
||||
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
|
||||
unsigned int caps);
|
||||
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
|
||||
int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned int caps);
|
||||
/**
|
||||
* snd_hda_query_pin_caps - Query PIN capabilities
|
||||
* @codec: the HD-auio codec
|
||||
* @nid: the NID to query
|
||||
*
|
||||
* Query PIN capabilities for the given widget.
|
||||
* Returns the obtained capability bits.
|
||||
*
|
||||
* When cap bits have been already read, this doesn't read again but
|
||||
* returns the cached value.
|
||||
*/
|
||||
static inline u32
|
||||
snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_override_pin_caps - Override the pin capabilities
|
||||
* @codec: the CODEC
|
||||
* @nid: the NID to override
|
||||
* @caps: the capability bits to set
|
||||
*
|
||||
* Override the cached PIN capabilitiy bits value by the given one.
|
||||
*
|
||||
* Returns zero if successful or a negative error code.
|
||||
*/
|
||||
static inline int
|
||||
snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned int caps)
|
||||
{
|
||||
return snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, caps);
|
||||
}
|
||||
|
||||
bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
|
||||
int dir, unsigned int bits);
|
||||
|
||||
|
|
|
@ -32,6 +32,10 @@ static int dump_coef = -1;
|
|||
module_param(dump_coef, int, 0644);
|
||||
MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)");
|
||||
|
||||
/* always use noncached version */
|
||||
#define param_read(codec, nid, parm) \
|
||||
snd_hdac_read_parm_uncached(&(codec)->core, nid, parm)
|
||||
|
||||
static char *bits_names(unsigned int bits, char *names[], int size)
|
||||
{
|
||||
int i, n;
|
||||
|
@ -119,9 +123,8 @@ static void print_amp_caps(struct snd_info_buffer *buffer,
|
|||
struct hda_codec *codec, hda_nid_t nid, int dir)
|
||||
{
|
||||
unsigned int caps;
|
||||
caps = snd_hda_param_read(codec, nid,
|
||||
dir == HDA_OUTPUT ?
|
||||
AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
|
||||
caps = param_read(codec, nid, dir == HDA_OUTPUT ?
|
||||
AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
|
||||
if (caps == -1 || caps == 0) {
|
||||
snd_iprintf(buffer, "N/A\n");
|
||||
return;
|
||||
|
@ -225,8 +228,8 @@ static void print_pcm_formats(struct snd_info_buffer *buffer,
|
|||
static void print_pcm_caps(struct snd_info_buffer *buffer,
|
||||
struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM);
|
||||
unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
|
||||
unsigned int pcm = param_read(codec, nid, AC_PAR_PCM);
|
||||
unsigned int stream = param_read(codec, nid, AC_PAR_STREAM);
|
||||
if (pcm == -1 || stream == -1) {
|
||||
snd_iprintf(buffer, "N/A\n");
|
||||
return;
|
||||
|
@ -273,7 +276,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
|
|||
static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
|
||||
unsigned int caps, val;
|
||||
|
||||
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
|
||||
caps = param_read(codec, nid, AC_PAR_PIN_CAP);
|
||||
snd_iprintf(buffer, " Pincap 0x%08x:", caps);
|
||||
if (caps & AC_PINCAP_IN)
|
||||
snd_iprintf(buffer, " IN");
|
||||
|
@ -401,8 +404,7 @@ static void print_pin_ctls(struct snd_info_buffer *buffer,
|
|||
static void print_vol_knob(struct snd_info_buffer *buffer,
|
||||
struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
unsigned int cap = snd_hda_param_read(codec, nid,
|
||||
AC_PAR_VOL_KNB_CAP);
|
||||
unsigned int cap = param_read(codec, nid, AC_PAR_VOL_KNB_CAP);
|
||||
snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ",
|
||||
(cap >> 7) & 1, cap & 0x7f);
|
||||
cap = snd_hda_codec_read(codec, nid, 0,
|
||||
|
@ -487,7 +489,7 @@ static void print_power_state(struct snd_info_buffer *buffer,
|
|||
[ilog2(AC_PWRST_EPSS)] = "EPSS",
|
||||
};
|
||||
|
||||
int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE);
|
||||
int sup = param_read(codec, nid, AC_PAR_POWER_STATE);
|
||||
int pwr = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_POWER_STATE, 0);
|
||||
if (sup != -1)
|
||||
|
@ -531,8 +533,7 @@ static void print_proc_caps(struct snd_info_buffer *buffer,
|
|||
struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
unsigned int i, ncoeff, oldindex;
|
||||
unsigned int proc_caps = snd_hda_param_read(codec, nid,
|
||||
AC_PAR_PROC_CAP);
|
||||
unsigned int proc_caps = param_read(codec, nid, AC_PAR_PROC_CAP);
|
||||
ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT;
|
||||
snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n",
|
||||
proc_caps & AC_PCAP_BENIGN, ncoeff);
|
||||
|
@ -597,7 +598,7 @@ static void print_gpio(struct snd_info_buffer *buffer,
|
|||
struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
unsigned int gpio =
|
||||
snd_hda_param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP);
|
||||
param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP);
|
||||
unsigned int enable, direction, wake, unsol, sticky, data;
|
||||
int i, max;
|
||||
snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
|
||||
|
@ -727,8 +728,7 @@ static void print_codec_info(struct snd_info_entry *entry,
|
|||
|
||||
for (i = 0; i < nodes; i++, nid++) {
|
||||
unsigned int wid_caps =
|
||||
snd_hda_param_read(codec, nid,
|
||||
AC_PAR_AUDIO_WIDGET_CAP);
|
||||
param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
|
||||
unsigned int wid_type = get_wcaps_type(wid_caps);
|
||||
hda_nid_t *conn = NULL;
|
||||
int conn_len = 0;
|
||||
|
|
|
@ -777,7 +777,6 @@ static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
|
|||
return 0;
|
||||
|
||||
mutex_lock(&codec->control_mutex);
|
||||
codec->cached_write = 1;
|
||||
path = snd_hda_get_path_from_idx(codec,
|
||||
spec->smux_paths[spec->cur_smux]);
|
||||
if (path)
|
||||
|
@ -786,9 +785,7 @@ static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
|
|||
if (path)
|
||||
snd_hda_activate_path(codec, path, true, true);
|
||||
spec->cur_smux = val;
|
||||
codec->cached_write = 0;
|
||||
mutex_unlock(&codec->control_mutex);
|
||||
snd_hda_codec_flush_cache(codec); /* flush the updates */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1004,18 +1001,17 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
|
|||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
struct ad198x_spec *spec = codec->spec;
|
||||
static const struct hda_verb gpio_init_verbs[] = {
|
||||
{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
|
||||
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
|
||||
{0x01, AC_VERB_SET_GPIO_DATA, 0x02},
|
||||
{},
|
||||
};
|
||||
|
||||
switch (action) {
|
||||
case HDA_FIXUP_ACT_PRE_PROBE:
|
||||
spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook;
|
||||
spec->gen.own_eapd_ctl = 1;
|
||||
snd_hda_sequence_write_cache(codec, gpio_init_verbs);
|
||||
snd_hda_codec_write_cache(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_MASK, 0x02);
|
||||
snd_hda_codec_write_cache(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_DIRECTION, 0x02);
|
||||
snd_hda_codec_write_cache(codec, 0x01, 0,
|
||||
AC_VERB_SET_GPIO_DATA, 0x02);
|
||||
break;
|
||||
case HDA_FIXUP_ACT_PROBE:
|
||||
if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
|
||||
|
|
|
@ -302,6 +302,7 @@ static void cxt_fixup_headphone_mic(struct hda_codec *codec,
|
|||
switch (action) {
|
||||
case HDA_FIXUP_ACT_PRE_PROBE:
|
||||
spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC;
|
||||
snd_hdac_regmap_add_vendor_verb(&codec->core, 0x410);
|
||||
break;
|
||||
case HDA_FIXUP_ACT_PROBE:
|
||||
spec->gen.cap_sync_hook = cxt_update_headset_mode_hook;
|
||||
|
@ -409,15 +410,11 @@ static void olpc_xo_automic(struct hda_codec *codec,
|
|||
struct hda_jack_callback *jack)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
int saved_cached_write = codec->cached_write;
|
||||
|
||||
codec->cached_write = 1;
|
||||
/* in DC mode, we don't handle automic */
|
||||
if (!spec->dc_enable)
|
||||
snd_hda_gen_mic_autoswitch(codec, jack);
|
||||
olpc_xo_update_mic_pins(codec);
|
||||
snd_hda_codec_flush_cache(codec);
|
||||
codec->cached_write = saved_cached_write;
|
||||
if (spec->dc_enable)
|
||||
olpc_xo_update_mic_boost(codec);
|
||||
}
|
||||
|
|
|
@ -2211,8 +2211,7 @@ static int generic_hdmi_resume(struct hda_codec *codec)
|
|||
int pin_idx;
|
||||
|
||||
codec->patch_ops.init(codec);
|
||||
snd_hda_codec_resume_amp(codec);
|
||||
snd_hda_codec_resume_cache(codec);
|
||||
regcache_sync(codec->core.regmap);
|
||||
|
||||
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
||||
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
||||
|
@ -2299,6 +2298,7 @@ static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
|
|||
|
||||
/* enable DP1.2 mode */
|
||||
vendor_param |= INTEL_EN_DP12;
|
||||
snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB);
|
||||
snd_hda_codec_write_cache(codec, INTEL_VENDOR_NID, 0,
|
||||
INTEL_SET_VENDOR_VERB, vendor_param);
|
||||
}
|
||||
|
|
|
@ -799,8 +799,7 @@ static int alc_resume(struct hda_codec *codec)
|
|||
if (!spec->no_depop_delay)
|
||||
msleep(150); /* to avoid pop noise */
|
||||
codec->patch_ops.init(codec);
|
||||
snd_hda_codec_resume_amp(codec);
|
||||
snd_hda_codec_resume_cache(codec);
|
||||
regcache_sync(codec->core.regmap);
|
||||
hda_call_check_power_status(codec, 0x01);
|
||||
return 0;
|
||||
}
|
||||
|
@ -3058,8 +3057,7 @@ static int alc269_resume(struct hda_codec *codec)
|
|||
msleep(200);
|
||||
}
|
||||
|
||||
snd_hda_codec_resume_amp(codec);
|
||||
snd_hda_codec_resume_cache(codec);
|
||||
regcache_sync(codec->core.regmap);
|
||||
hda_call_check_power_status(codec, 0x01);
|
||||
|
||||
/* on some machine, the BIOS will clear the codec gpio data when enter
|
||||
|
|
|
@ -222,6 +222,10 @@ static int si3054_init(struct hda_codec *codec)
|
|||
unsigned wait_count;
|
||||
u16 val;
|
||||
|
||||
if (snd_hdac_regmap_add_vendor_verb(&codec->core,
|
||||
SI3054_VERB_WRITE_NODE))
|
||||
return -ENOMEM;
|
||||
|
||||
snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0);
|
||||
snd_hda_codec_write(codec, codec->core.mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0);
|
||||
SET_REG(codec, SI3054_LINE_RATE, 9600);
|
||||
|
|
|
@ -1050,12 +1050,9 @@ static const struct hda_verb stac92hd71bxx_core_init[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const struct hda_verb stac92hd71bxx_unmute_core_init[] = {
|
||||
static const hda_nid_t stac92hd71bxx_unmute_nids[] = {
|
||||
/* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
|
||||
{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
||||
{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
||||
{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
||||
{}
|
||||
0x0f, 0x0a, 0x0d, 0
|
||||
};
|
||||
|
||||
static const struct hda_verb stac925x_core_init[] = {
|
||||
|
@ -4269,6 +4266,10 @@ static int stac_parse_auto_config(struct hda_codec *codec)
|
|||
|
||||
if (spec->aloopback_ctl &&
|
||||
snd_hda_get_bool_hint(codec, "loopback") == 1) {
|
||||
unsigned int wr_verb =
|
||||
spec->aloopback_ctl->private_value >> 16;
|
||||
if (snd_hdac_regmap_add_vendor_verb(&codec->core, wr_verb))
|
||||
return -ENOMEM;
|
||||
if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -4688,7 +4689,7 @@ static int patch_stac92hd95(struct hda_codec *codec)
|
|||
static int patch_stac92hd71bxx(struct hda_codec *codec)
|
||||
{
|
||||
struct sigmatel_spec *spec;
|
||||
const struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
|
||||
const hda_nid_t *unmute_nids = stac92hd71bxx_unmute_nids;
|
||||
int err;
|
||||
|
||||
err = alloc_stac_spec(codec);
|
||||
|
@ -4713,7 +4714,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
|
|||
switch (codec->core.vendor_id) {
|
||||
case 0x111d76b6: /* 4 Port without Analog Mixer */
|
||||
case 0x111d76b7:
|
||||
unmute_init++;
|
||||
unmute_nids++;
|
||||
break;
|
||||
case 0x111d7608: /* 5 Port with Analog Mixer */
|
||||
if ((codec->core.revision_id & 0xf) == 0 ||
|
||||
|
@ -4721,7 +4722,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
|
|||
spec->stream_delay = 40; /* 40 milliseconds */
|
||||
|
||||
/* disable VSW */
|
||||
unmute_init++;
|
||||
unmute_nids++;
|
||||
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
|
||||
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
|
||||
break;
|
||||
|
@ -4735,8 +4736,12 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
|
|||
if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB)
|
||||
snd_hda_add_verbs(codec, stac92hd71bxx_core_init);
|
||||
|
||||
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
|
||||
snd_hda_sequence_write_cache(codec, unmute_init);
|
||||
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) {
|
||||
const hda_nid_t *p;
|
||||
for (p = unmute_nids; *p; p++)
|
||||
snd_hda_codec_amp_init_stereo(codec, *p, HDA_INPUT, 0,
|
||||
0xff, 0x00);
|
||||
}
|
||||
|
||||
spec->aloopback_ctl = &stac92hd71bxx_loopback;
|
||||
spec->aloopback_mask = 0x50;
|
||||
|
|
Loading…
Reference in New Issue