Skip to content

Commit

Permalink
Merge branch 'test/hda-pincfg' into topic/hda
Browse files Browse the repository at this point in the history
  • Loading branch information
Takashi Iwai committed Feb 23, 2009
2 parents 13c989b + 39c2871 commit 209b140
Show file tree
Hide file tree
Showing 10 changed files with 332 additions and 291 deletions.
23 changes: 22 additions & 1 deletion Documentation/sound/alsa/HD-Audio.txt
Original file line number Diff line number Diff line change
Expand Up @@ -365,10 +365,23 @@ modelname::
to this file.
init_verbs::
The extra verbs to execute at initialization. You can add a verb by
writing to this file. Pass tree numbers, nid, verb and parameter.
writing to this file. Pass three numbers: nid, verb and parameter.
hints::
Shows hint strings for codec parsers for any use. Right now it's
not used.
init_pin_configs::
Shows the initial pin default config values set by BIOS.
driver_pin_configs::
Shows the pin default values set by the codec parser explicitly.
This doesn't show all pin values but only the changed values by
the parser. That is, if the parser doesn't change the pin default
config values by itself, this will contain nothing.
user_pin_configs::
Shows the pin default config values to override the BIOS setup.
Writing this (with two numbers, NID and value) appends the new
value. The given will be used instead of the initial BIOS value at
the next reconfiguration time. Note that this config will override
even the driver pin configs, too.
reconfig::
Triggers the codec re-configuration. When any value is written to
this file, the driver re-initialize and parses the codec tree
Expand All @@ -378,6 +391,14 @@ clear::
Resets the codec, removes the mixer elements and PCM stuff of the
specified codec, and clear all init verbs and hints.

For example, when you want to change the pin default configuration
value of the pin widget 0x14 to 0x9993013f, and let the driver
re-configure based on that state, run like below:
------------------------------------------------------------------------
# echo 0x14 0x9993013f > /sys/class/sound/hwC0D0/user_pin_configs
# echo 1 > /sys/class/sound/hwC0D0/reconfig
------------------------------------------------------------------------


Power-Saving
~~~~~~~~~~~~
Expand Down
162 changes: 151 additions & 11 deletions sound/pci/hda/hda_codec.c
Original file line number Diff line number Diff line change
Expand Up @@ -682,18 +682,148 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
return 0;
}

/* read all pin default configurations and save codec->init_pins */
static int read_pin_defaults(struct hda_codec *codec)
{
int i;
hda_nid_t nid = codec->start_nid;

for (i = 0; i < codec->num_nodes; i++, nid++) {
struct hda_pincfg *pin;
unsigned int wcaps = get_wcaps(codec, nid);
unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
AC_WCAP_TYPE_SHIFT;
if (wid_type != AC_WID_PIN)
continue;
pin = snd_array_new(&codec->init_pins);
if (!pin)
return -ENOMEM;
pin->nid = nid;
pin->cfg = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONFIG_DEFAULT, 0);
}
return 0;
}

/* look up the given pin config list and return the item matching with NID */
static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec,
struct snd_array *array,
hda_nid_t nid)
{
int i;
for (i = 0; i < array->used; i++) {
struct hda_pincfg *pin = snd_array_elem(array, i);
if (pin->nid == nid)
return pin;
}
return NULL;
}

/* write a config value for the given NID */
static void set_pincfg(struct hda_codec *codec, hda_nid_t nid,
unsigned int cfg)
{
int i;
for (i = 0; i < 4; i++) {
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i,
cfg & 0xff);
cfg >>= 8;
}
}

/* set the current pin config value for the given NID.
* the value is cached, and read via snd_hda_codec_get_pincfg()
*/
int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
hda_nid_t nid, unsigned int cfg)
{
struct hda_pincfg *pin;
unsigned int oldcfg;

oldcfg = snd_hda_codec_get_pincfg(codec, nid);
pin = look_up_pincfg(codec, list, nid);
if (!pin) {
pin = snd_array_new(list);
if (!pin)
return -ENOMEM;
pin->nid = nid;
}
pin->cfg = cfg;

/* change only when needed; e.g. if the pincfg is already present
* in user_pins[], don't write it
*/
cfg = snd_hda_codec_get_pincfg(codec, nid);
if (oldcfg != cfg)
set_pincfg(codec, nid, cfg);
return 0;
}

int snd_hda_codec_set_pincfg(struct hda_codec *codec,
hda_nid_t nid, unsigned int cfg)
{
return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg);
}
EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg);

/* get the current pin config value of the given pin NID */
unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_pincfg *pin;

#ifdef CONFIG_SND_HDA_HWDEP
pin = look_up_pincfg(codec, &codec->user_pins, nid);
if (pin)
return pin->cfg;
#endif
pin = look_up_pincfg(codec, &codec->driver_pins, nid);
if (pin)
return pin->cfg;
pin = look_up_pincfg(codec, &codec->init_pins, nid);
if (pin)
return pin->cfg;
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg);

/* restore all current pin configs */
static void restore_pincfgs(struct hda_codec *codec)
{
int i;
for (i = 0; i < codec->init_pins.used; i++) {
struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
set_pincfg(codec, pin->nid,
snd_hda_codec_get_pincfg(codec, pin->nid));
}
}

static void init_hda_cache(struct hda_cache_rec *cache,
unsigned int record_size);
static void free_hda_cache(struct hda_cache_rec *cache);

/* restore the initial pin cfgs and release all pincfg lists */
static void restore_init_pincfgs(struct hda_codec *codec)
{
/* first free driver_pins and user_pins, then call restore_pincfg
* so that only the values in init_pins are restored
*/
snd_array_free(&codec->driver_pins);
#ifdef CONFIG_SND_HDA_HWDEP
snd_array_free(&codec->user_pins);
#endif
restore_pincfgs(codec);
snd_array_free(&codec->init_pins);
}

/*
* codec destructor
*/
static void snd_hda_codec_free(struct hda_codec *codec)
{
if (!codec)
return;
restore_init_pincfgs(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
cancel_delayed_work(&codec->power_work);
flush_workqueue(codec->bus->workq);
Expand Down Expand Up @@ -751,6 +881,8 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
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 snd_kcontrol *), 32);
snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
if (codec->bus->modelname) {
codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
if (!codec->modelname) {
Expand Down Expand Up @@ -787,15 +919,18 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
setup_fg_nodes(codec);
if (!codec->afg && !codec->mfg) {
snd_printdd("hda_codec: no AFG or MFG node found\n");
snd_hda_codec_free(codec);
return -ENODEV;
err = -ENODEV;
goto error;
}

if (read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg) < 0) {
err = read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg);
if (err < 0) {
snd_printk(KERN_ERR "hda_codec: cannot malloc\n");
snd_hda_codec_free(codec);
return -ENOMEM;
goto error;
}
err = read_pin_defaults(codec);
if (err < 0)
goto error;

if (!codec->subsystem_id) {
hda_nid_t nid = codec->afg ? codec->afg : codec->mfg;
Expand All @@ -808,10 +943,8 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr

if (do_init) {
err = snd_hda_codec_configure(codec);
if (err < 0) {
snd_hda_codec_free(codec);
return err;
}
if (err < 0)
goto error;
}
snd_hda_codec_proc_new(codec);

Expand All @@ -824,6 +957,10 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
if (codecp)
*codecp = codec;
return 0;

error:
snd_hda_codec_free(codec);
return err;
}
EXPORT_SYMBOL_HDA(snd_hda_codec_new);

Expand Down Expand Up @@ -1334,6 +1471,9 @@ void snd_hda_codec_reset(struct hda_codec *codec)
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);
restore_pincfgs(codec);
codec->num_pcms = 0;
codec->pcm_info = NULL;
codec->preset = NULL;
Expand Down Expand Up @@ -2175,6 +2315,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
hda_set_power_state(codec,
codec->afg ? codec->afg : codec->mfg,
AC_PWRST_D0);
restore_pincfgs(codec); /* restore all current pin configs */
hda_exec_init_verbs(codec);
if (codec->patch_ops.resume)
codec->patch_ops.resume(codec);
Expand Down Expand Up @@ -3355,8 +3496,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
if (ignore_nids && is_in_nid_list(nid, ignore_nids))
continue;

def_conf = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONFIG_DEFAULT, 0);
def_conf = snd_hda_codec_get_pincfg(codec, nid);
if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
continue;
loc = get_defcfg_location(def_conf);
Expand Down
15 changes: 15 additions & 0 deletions sound/pci/hda/hda_codec.h
Original file line number Diff line number Diff line change
Expand Up @@ -778,11 +778,14 @@ struct hda_codec {
unsigned short spdif_ctls; /* SPDIF control bits */
unsigned int spdif_in_enable; /* SPDIF input enable? */
hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
struct snd_array init_pins; /* initial (BIOS) pin configurations */
struct snd_array driver_pins; /* pin configs set by codec parser */

#ifdef CONFIG_SND_HDA_HWDEP
struct snd_hwdep *hwdep; /* assigned hwdep device */
struct snd_array init_verbs; /* additional init verbs */
struct snd_array hints; /* additional hints */
struct snd_array user_pins; /* default pin configs to override */
#endif

/* misc flags */
Expand Down Expand Up @@ -855,6 +858,18 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec);
#define snd_hda_sequence_write_cache snd_hda_sequence_write
#endif

/* the struct for codec->pin_configs */
struct hda_pincfg {
hda_nid_t nid;
unsigned int cfg;
};

unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid,
unsigned int cfg);
int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
hda_nid_t nid, unsigned int cfg); /* for hwdep */

/*
* Mixer
*/
Expand Down
2 changes: 1 addition & 1 deletion sound/pci/hda/hda_generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid
if (node->type == AC_WID_PIN) {
node->pin_caps = snd_hda_param_read(codec, node->nid, AC_PAR_PIN_CAP);
node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
node->def_cfg = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid);
}

if (node->wid_caps & AC_WCAP_OUT_AMP) {
Expand Down
Loading

0 comments on commit 209b140

Please sign in to comment.