FAQ Search Today's Posts Mark Forums Read
» Video Reviews

» Linux Archive

Linux-archive is a website aiming to archive linux email lists and to make them easily accessible for linux users/developers.


» Sponsor

» Partners

» Sponsor

Go Back   Linux Archive > Ubuntu > Ubuntu Kernel Team

 
 
LinkBack Thread Tools
 
Old 06-27-2012, 06:26 PM
 
Default ALSA: hda/realtek - Finer tuning of auto-parser with badness evaluation

From: Joseph Salisbury <joseph.salisbury@canonical.com>

BugLink: http://bugs.launchpad.net/bugs/994685

== Precise SRU Justification ==

This bug is a regression from Oneiric. This bug prevents end users from using the headphone jack.

== Fix ==

commit 1c4a54b4513c175ba1a56d0aba8d9cf8f231d407
Author: Takashi Iwai <tiwai@suse.de>
Date: Thu Feb 16 16:45:59 2012 +0100

ALSA: hda/realtek - Finer tuning of auto-parser with badness evaluation


commit 07b18f69a766375736a5313c29d808e59b1e13e9
Author: Takashi Iwai <tiwai@suse.de>
Date: Thu Nov 10 15:42:54 2011 +0100

ALSA: hda/realtek - Create multi-io jacks more aggresively

Commit 07b18f6 allows us to cleanly cherry-pick the actual fix, commit 1c4a54b.
Both commits had to be backported to apply cleanly to Precise.


== Impact ==

Without this patch, the headphone jack does not work for a set of users.

== Test Case ==
A test kernel was built with these patchs and tested by the original bug reporter.




Takashi Iwai (2):
ALSA: hda/realtek - Create multi-io jacks more aggresively
ALSA: hda/realtek - Finer tuning of auto-parser with badness
evaluation

sound/pci/hda/patch_realtek.c | 412 +++++++++++++++++++++++++++++++++--------
1 file changed, 330 insertions(+), 82 deletions(-)

--
1.7.9.5


--
kernel-team mailing list
kernel-team@lists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/kernel-team
 
Old 06-27-2012, 06:26 PM
 
Default ALSA: hda/realtek - Finer tuning of auto-parser with badness evaluation

From: Takashi Iwai <tiwai@suse.de>

BugLink: http://bugs.launchpad.net/bugs/994685

This patch improves the Realtek auto-parser for assigning the DACs and
mixers in more suitable ways by evaluating the assignment with "badness"
calculations.

When assigning a DAC hinders the assignment of individual DACs for
other pins, some badness point is given. Similarly, when it blocks the
assignment of unique mixer controls, another badness point is added.
Also, if no DAC, even shared DAC, can be assigned, more badness is
pointed. Finally, comparing the accumulated badness, the best route is
chosen among several trials.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Joseph Salisbury <joseph.salisbury@canonical.com>
(backported from commit 1c4a54b4513c175ba1a56d0aba8d9cf8f231d407)
---
sound/pci/hda/patch_realtek.c | 381 ++++++++++++++++++++++++++++++++---------
1 file changed, 296 insertions(+), 85 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index a447013..b8d19d8 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2931,74 +2931,191 @@ static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
return 0;
}

-/* return 0 if no possible DAC is found, 1 if one or more found */
+/* mark up volume and mute control NIDs: used during badness parsing and
+ * at creating actual controls
+ */
+static inline unsigned int get_ctl_pos(unsigned int data)
+{
+ hda_nid_t nid = get_amp_nid_(data);
+ unsigned int dir;
+ if (snd_BUG_ON(nid >= MAX_VOL_NIDS))
+ return 0;
+ dir = get_amp_direction_(data);
+ return (nid << 1) | dir;
+}
+
+#define is_ctl_used(bits, data)
+ test_bit(get_ctl_pos(data), bits)
+#define mark_ctl_usage(bits, data)
+ set_bit(get_ctl_pos(data), bits)
+
+static void clear_vol_marks(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ memset(spec->vol_ctls, 0, sizeof(spec->vol_ctls));
+ memset(spec->sw_ctls, 0, sizeof(spec->sw_ctls));
+}
+
+/* badness definition */
+enum {
+ /* No primary DAC is found for the main output */
+ BAD_NO_PRIMARY_DAC = 0x10000,
+ /* No DAC is found for the extra output */
+ BAD_NO_DAC = 0x4000,
+ /* No individual DAC for extra output */
+ BAD_NO_EXTRA_DAC = 0x1000,
+ /* No individual DAC for extra surrounds */
+ BAD_NO_EXTRA_SURR_DAC = 0x200,
+ /* Primary DAC shared with main surrounds */
+ BAD_SHARED_SURROUND = 0x100,
+ /* Volume widget is shared */
+ BAD_SHARED_VOL = 0x10,
+ /* Primary DAC shared with main CLFE */
+ BAD_SHARED_CLFE = 0x10,
+ /* Primary DAC shared with extra surrounds */
+ BAD_SHARED_EXTRA_SURROUND = 0x10,
+ /* No possible multi-ios */
+ BAD_MULTI_IO = 0x1,
+};
+
+static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
+ hda_nid_t pin, hda_nid_t dac);
+static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
+ hda_nid_t pin, hda_nid_t dac);
+
+static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin,
+ hda_nid_t dac)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t nid;
+ unsigned int val;
+ int badness = 0;
+
+ nid = alc_look_for_out_vol_nid(codec, pin, dac);
+ if (nid) {
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ if (is_ctl_used(spec->vol_ctls, nid))
+ badness += BAD_SHARED_VOL;
+ else
+ mark_ctl_usage(spec->vol_ctls, val);
+ } else
+ badness += BAD_SHARED_VOL;
+ nid = alc_look_for_out_mute_nid(codec, pin, dac);
+ if (nid) {
+ unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
+ if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT)
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ else
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
+ if (is_ctl_used(spec->sw_ctls, val))
+ badness += BAD_SHARED_VOL;
+ else
+ mark_ctl_usage(spec->sw_ctls, val);
+ } else
+ badness += BAD_SHARED_VOL;
+ return badness;
+}
+
+/* try to assign DACs to extra pins and return the resultant badness */
static int alc_auto_fill_extra_dacs(struct hda_codec *codec, int num_outs,
const hda_nid_t *pins, hda_nid_t *dacs)
{
+ struct alc_spec *spec = codec->spec;
int i;
+ int badness = 0;
+ hda_nid_t dac;

if (num_outs && !dacs[0]) {
- dacs[0] = alc_auto_look_for_dac(codec, pins[0]);
- if (!dacs[0])
- return 0;
+ dac = dacs[0] = alc_auto_look_for_dac(codec, pins[0]);
+ if (!dacs[0]) {
+ dac = spec->private_dac_nids[0];
+ if (!alc_auto_is_dac_reachable(codec, pins[0], dac))
+ return BAD_NO_DAC;
+ badness += BAD_NO_EXTRA_DAC;
+ }
+ badness += eval_shared_vol_badness(codec, pins[0], dac);
}

for (i = 1; i < num_outs; i++)
dacs[i] = get_dac_if_single(codec, pins[i]);
for (i = 1; i < num_outs; i++) {
- if (!dacs[i])
- dacs[i] = alc_auto_look_for_dac(codec, pins[i]);
+ dac = dacs[i];
+ if (!dac)
+ dac = dacs[i] = alc_auto_look_for_dac(codec, pins[i]);
+ if (!dac) {
+ if (alc_auto_is_dac_reachable(codec, pins[i], dacs[0])) {
+ dac = dacs[0];
+ badness += BAD_SHARED_EXTRA_SURROUND;
+ } else if (alc_auto_is_dac_reachable(codec, pins[i],
+ spec->private_dac_nids[0])) {
+ dac = spec->private_dac_nids[0];
+ badness += BAD_NO_EXTRA_SURR_DAC;
+ } else
+ badness += BAD_NO_DAC;
+ }
+ if (dac)
+ badness += eval_shared_vol_badness(codec, pins[i], dac);
}
- return 1;
+ return badness;
}

static int alc_auto_fill_multi_ios(struct hda_codec *codec,
unsigned int location, int offset);

/* fill in the dac_nids table from the parsed pin configuration */
-static int alc_auto_fill_dac_nids(struct hda_codec *codec)
+static int fill_and_eval_dacs(struct hda_codec *codec,
+ bool fill_hardwired)
{
struct alc_spec *spec = codec->spec;
const struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int location, defcfg;
- int num_pins;
- bool redone = false;
- int i;
+ int i, err, badness;

- again:
/* set num_dacs once to full for alc_auto_look_for_dac() */
spec->multiout.num_dacs = cfg->line_outs;
- spec->multiout.hp_out_nid[0] = 0;
- spec->multiout.extra_out_nid[0] = 0;
- memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
spec->multiout.dac_nids = spec->private_dac_nids;
+ memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
+ memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid));
+ memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
spec->multi_ios = 0;
+ clear_vol_marks(codec);
+ badness = 0;

/* fill hard-wired DACs first */
- if (!redone) {
+ if (fill_hardwired) {
for (i = 0; i < cfg->line_outs; i++)
spec->private_dac_nids[i] =
get_dac_if_single(codec, cfg->line_out_pins[i]);
- if (cfg->hp_outs)
- spec->multiout.hp_out_nid[0] =
- get_dac_if_single(codec, cfg->hp_pins[0]);
- if (cfg->speaker_outs)
- spec->multiout.extra_out_nid[0] =
- get_dac_if_single(codec, cfg->speaker_pins[0]);
+ for (i = 0; i < cfg->hp_outs; i++)
+ spec->multiout.hp_out_nid[i] =
+ get_dac_if_single(codec, cfg->hp_pins[i]);
+ for (i = 0; i < cfg->speaker_outs; i++)
+ spec->multiout.extra_out_nid[i] =
+ get_dac_if_single(codec, cfg->speaker_pins[i]);
}

for (i = 0; i < cfg->line_outs; i++) {
hda_nid_t pin = cfg->line_out_pins[i];
- if (spec->private_dac_nids[i])
- continue;
- spec->private_dac_nids[i] = alc_auto_look_for_dac(codec, pin);
- if (!spec->private_dac_nids[i] && !redone) {
- /* if we can't find primary DACs, re-probe without
- * checking the hard-wired DACs
- */
- redone = true;
- goto again;
+ hda_nid_t dac;
+ if (!spec->private_dac_nids[i])
+ spec->private_dac_nids[i] =
+ alc_auto_look_for_dac(codec, pin);
+ dac = spec->private_dac_nids[i];
+ if (!dac) {
+ if (!i)
+ badness += BAD_NO_PRIMARY_DAC;
+ else if (alc_auto_is_dac_reachable(codec, pin,
+ spec->private_dac_nids[0])) {
+ if (i == 1)
+ badness += BAD_SHARED_SURROUND;
+ else
+ badness += BAD_SHARED_CLFE;
+ dac = spec->private_dac_nids[0];
+ } else
+ badness += BAD_NO_DAC;
}
+ if (dac)
+ badness += eval_shared_vol_badness(codec, pin, dac);
}

/* re-count num_dacs and squash invalid entries */
@@ -3018,26 +3135,114 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec)
/* try to fill multi-io first */
defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]);
location = get_defcfg_location(defcfg);
-
- num_pins = alc_auto_fill_multi_ios(codec, location, 0);
- if (num_pins > 0) {
- spec->multi_ios = num_pins;
- spec->ext_channel_count = 2;
- spec->multiout.num_dacs = num_pins + 1;
- }
+ err = alc_auto_fill_multi_ios(codec, location, 0);
+ if (err < 0)
+ return err;
+ badness += err;
}

- if (cfg->line_out_type != AUTO_PIN_HP_OUT)
- alc_auto_fill_extra_dacs(codec, cfg->hp_outs, cfg->hp_pins,
- spec->multiout.hp_out_nid);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = alc_auto_fill_extra_dacs(codec, cfg->hp_outs,
+ cfg->hp_pins,
+ spec->multiout.hp_out_nid);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- int err = alc_auto_fill_extra_dacs(codec, cfg->speaker_outs,
- cfg->speaker_pins,
- spec->multiout.extra_out_nid);
- /* if no speaker volume is assigned, try again as the primary
- * output
- */
- if (!err && cfg->speaker_outs > 0 &&
+ err = alc_auto_fill_extra_dacs(codec, cfg->speaker_outs,
+ cfg->speaker_pins,
+ spec->multiout.extra_out_nid);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+ if (!spec->multi_ios &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ cfg->hp_outs) {
+ /* try multi-ios with HP + inputs */
+ defcfg = snd_hda_codec_get_pincfg(codec, cfg->hp_pins[0]);
+ location = get_defcfg_location(defcfg);
+ err = alc_auto_fill_multi_ios(codec, location, 1);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+
+ return badness;
+}
+
+#define DEBUG_BADNESS
+
+#ifdef DEBUG_BADNESS
+#define debug_badness snd_printdd
+#else
+#define debug_badness(...)
+#endif
+
+static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg)
+{
+ debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x
",
+ cfg->line_out_pins[0], cfg->line_out_pins[1],
+ cfg->line_out_pins[2], cfg->line_out_pins[2],
+ spec->multiout.dac_nids[0],
+ spec->multiout.dac_nids[1],
+ spec->multiout.dac_nids[2],
+ spec->multiout.dac_nids[3]);
+ debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x
",
+ cfg->hp_pins[0], cfg->hp_pins[1],
+ cfg->hp_pins[2], cfg->hp_pins[2],
+ spec->multiout.hp_out_nid[0],
+ spec->multiout.hp_out_nid[1],
+ spec->multiout.hp_out_nid[2],
+ spec->multiout.hp_out_nid[3]);
+ debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x
",
+ cfg->speaker_pins[0], cfg->speaker_pins[1],
+ cfg->speaker_pins[2], cfg->speaker_pins[3],
+ spec->multiout.extra_out_nid[0],
+ spec->multiout.extra_out_nid[1],
+ spec->multiout.extra_out_nid[2],
+ spec->multiout.extra_out_nid[3]);
+}
+
+static int alc_auto_fill_dac_nids(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct auto_pin_cfg *best_cfg;
+ int best_badness = INT_MAX;
+ int badness;
+ bool fill_hardwired = true;
+ bool best_wired = true;
+ bool hp_spk_swapped = false;
+
+ best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL);
+ if (!best_cfg)
+ return -ENOMEM;
+ *best_cfg = *cfg;
+
+ for (; {
+ badness = fill_and_eval_dacs(codec, fill_hardwired);
+ if (badness < 0)
+ return badness;
+ debug_badness("==> lo_type=%d, wired=%d, badness=0x%x
",
+ cfg->line_out_type, fill_hardwired, badness);
+ debug_show_configs(spec, cfg);
+ if (badness < best_badness) {
+ best_badness = badness;
+ *best_cfg = *cfg;
+ best_wired = fill_hardwired;
+ }
+ if (!badness)
+ break;
+ if (fill_hardwired) {
+ fill_hardwired = false;
+ continue;
+ }
+ if (hp_spk_swapped)
+ break;
+ hp_spk_swapped = true;
+ if (cfg->speaker_outs > 0 &&
cfg->line_out_type == AUTO_PIN_HP_OUT) {
cfg->hp_outs = cfg->line_outs;
memcpy(cfg->hp_pins, cfg->line_out_pins,
@@ -3048,49 +3253,46 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec)
cfg->speaker_outs = 0;
memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
- redone = false;
- goto again;
- }
+ fill_hardwired = true;
+ continue;
+ }
+ if (cfg->hp_outs > 0 &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ cfg->speaker_outs = cfg->line_outs;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ fill_hardwired = true;
+ continue;
+ }
+ break;
}

- if (cfg->line_out_pins[0])
+
+ if (badness) {
+ *cfg = *best_cfg;
+ fill_and_eval_dacs(codec, best_wired);
+ }
+ debug_badness("==> Best config: lo_type=%d, wired=%d
",
+ cfg->line_out_type, best_wired);
+ debug_show_configs(spec, cfg);
+
+if (cfg->line_out_pins[0])
spec->vmaster_nid =
alc_look_for_out_vol_nid(codec, cfg->line_out_pins[0],
spec->multiout.dac_nids[0]);

- if (!spec->multi_ios &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
- cfg->hp_outs) {
- /* try multi-ios with HP + inputs */
- defcfg = snd_hda_codec_get_pincfg(codec, cfg->hp_pins[0]);
- location = get_defcfg_location(defcfg);
-
- num_pins = alc_auto_fill_multi_ios(codec, location, 1);
- if (num_pins > 0) {
- spec->multi_ios = num_pins;
- spec->ext_channel_count = 2;
- spec->multiout.num_dacs = num_pins + 1;
- }
- }
-
+ /* clear the bitmap flags for creating controls */
+ clear_vol_marks(codec);
+ kfree(best_cfg);
return 0;
}

-static inline unsigned int get_ctl_pos(unsigned int data)
-{
- hda_nid_t nid = get_amp_nid_(data);
- unsigned int dir;
- if (snd_BUG_ON(nid >= MAX_VOL_NIDS))
- return 0;
- dir = get_amp_direction_(data);
- return (nid << 1) | dir;
-}
-
-#define is_ctl_used(bits, data)
- test_bit(get_ctl_pos(data), bits)
-#define mark_ctl_usage(bits, data)
- set_bit(get_ctl_pos(data), bits)
-
static int alc_auto_add_vol_ctl(struct hda_codec *codec,
const char *pfx, int cidx,
hda_nid_t nid, unsigned int chs)
@@ -3474,6 +3676,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec,
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t prime_dac = spec->private_dac_nids[0];
int type, i, dacs, num_pins = 0;
+ int badness = 0;

dacs = spec->multiout.num_dacs;
for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
@@ -3498,12 +3701,16 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec,
}
if (!dac)
dac = alc_auto_look_for_dac(codec, nid);
- if (!dac)
+ if (!dac) {
+ badness += BAD_MULTI_IO;
continue;
+ }
spec->multi_io[num_pins].pin = nid;
spec->multi_io[num_pins].dac = dac;
num_pins++;
spec->private_dac_nids[spec->multiout.num_dacs++] = dac;
+ if (num_pins >= 2)
+ break;
}
}
spec->multiout.num_dacs = dacs;
@@ -3512,9 +3719,13 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec,
memset(spec->private_dac_nids + dacs, 0,
sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - dacs));
spec->private_dac_nids[0] = prime_dac;
- return 0;
+ return badness;
}
- return num_pins;
+
+ spec->multi_ios = num_pins;
+ spec->ext_channel_count = 2;
+ spec->multiout.num_dacs = num_pins + 1;
+ return 0;
}

static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol,
--
1.7.9.5


--
kernel-team mailing list
kernel-team@lists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/kernel-team
 

Thread Tools




All times are GMT. The time now is 09:43 AM.

VBulletin, Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
Content Relevant URLs by vBSEO ©2007, Crawlability, Inc.
Copyright 2007 - 2008, www.linux-archive.org