ELD fixes for 3.2
cover ELD fixes for 3.2
Takashi,
Here are 2 fixes and 1 cleanup patch targeted for 3.2.
Thanks,
Fengguang
PATCH 2/3 hda - delayed ELD repoll
The Intel HDMI chips (ironlake at least) are found to have ~250ms delay between the ELD_Valid=1 hotplug event is send and the ELD buffer becomes actually readable. During the time the ELD buffer is mysteriously all 0.
Fix it by scheduling a delayed work to re-read ELD buffer after 300ms.
--- sound/pci/hda/patch_hdmi.c | 36 ++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-)
sound/pci/hda/patch_hdmi.c +29 -7
@@ -65,7 +65,10 @@ struct hdmi_spec_per_pin {hda_nid_t pin_nid;int num_mux_nids;hda_nid_t mux_nids[HDA_MAX_CONNECTIONS ]; + +struct hda_codec *codec;struct hdmi_eld sink_eld; +struct delayed_work work; };struct hdmi_spec {
@@ -745,8 +748,7 @@ static void hdmi_setup_audio_infoframe(s* Unsolicited events */ -static void hdmi_present_sense (struct hda_codec *codec,hda_nid_t pin_nid, -struct hdmi_eld *eld); +static void hdmi_present_sense (struct hdmi_spec_per_pin *per_pin,bool retry);static void hdmi_intrinsic_event (struct hda_codec *codec,unsigned int res) {
@@ -766,7 +768,7 @@ static void hdmi_intrinsic_event(structreturn ; eld = &spec->pins[pin_idx].sink_eld; -hdmi_present_sense (codec, pin_nid, eld); +hdmi_present_sense (&spec->pins[pin_idx], true);/* * HDMI sink's ELD info cannot always be retrieved for now, e.g.
@@ -968,9 +970,11 @@ static int hdmi_read_pin_conn(struct hdareturn 0 ; } -static void hdmi_present_sense (struct hda_codec *codec,hda_nid_t pin_nid, -struct hdmi_eld *eld) +static void hdmi_present_sense (struct hdmi_spec_per_pin *per_pin,bool retry) { +struct hda_codec *codec = per_pin->codec; +struct hdmi_eld *eld = &per_pin->sink_eld; +hda_nid_t pin_nid = per_pin->pin_nid;/* * Always execute a GetPinSense verb here, even when called from * hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited
@@ -992,13 +996,27 @@ static void hdmi_present_sense(struct hd"HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n" , codec->addr, pin_nid, eld->monitor_present, eld_valid); -if (eld_valid) +if (eld_valid) {if (!snd_hdmi_get_eld (eld, codec, pin_nid))snd_hdmi_show_eld (eld); +else if (retry) { +queue_delayed_work (codec->bus->workq, + &per_pin->work, +msecs_to_jiffies (300 )); + } + }snd_hda_input_jack_report (codec, pin_nid); } +static void hdmi_repoll_eld (struct work_struct *work) +{ +struct hdmi_spec_per_pin *per_pin = + container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work); + +hdmi_present_sense (per_pin, false); +} +static int hdmi_add_pin (struct hda_codec *codec,hda_nid_t pin_nid) {struct hdmi_spec *spec = codec->spec;
@@ -1227,7 +1245,7 @@ static int generic_hdmi_build_jack(strucif (err <0 )return err; -hdmi_present_sense (codec, per_pin->pin_nid, &per_pin->sink_eld); +hdmi_present_sense (per_pin, false);return 0 ; }
@@ -1278,6 +1296,8 @@ static int generic_hdmi_init(struct hda_AC_VERB_SET_UNSOLICITED_ENABLE , AC_USRSP_EN | pin_nid); + per_pin->codec = codec; +INIT_DELAYED_WORK (&per_pin->work, hdmi_repoll_eld);snd_hda_eld_proc_new (codec, eld, pin_idx); }return 0 ;
@@ -1292,10 +1312,12 @@ static void generic_hdmi_free(struct hdastruct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];struct hdmi_eld *eld = &per_pin->sink_eld; +cancel_delayed_work (&per_pin->work);snd_hda_eld_proc_free (codec, eld); }snd_hda_input_jack_free (codec); +flush_workqueue (codec->bus->workq);kfree (spec); }
PATCH 1/3 hda - fix ELD memory leak
In reply to Wu Fengguangmemset(eld) clears eld->proc_entry which will leak the struct snd_info_entry when unloading module.
Fix it by
- memset only the fields before eld->eld_buffer
- set eld->eld_valid to true _after_ all eld fields have been filled
--- sound/pci/hda/hda_eld.c | 5 +---- sound/pci/hda/hda_local.h | 3 +++ sound/pci/hda/patch_hdmi.c | 11 +++++------ 3 files changed, 9 insertions(+), 10 deletions(-)
sound/pci/hda/hda_eld.c +1 -4
@@ -297,10 +297,10 @@ static int hdmi_update_eld(struct hdmi_e buf + ELD_FIXED_BYTES + mnl + 3 * i); } + e->eld_valid = true;return 0 ; out_fail: - e->eld_ver =0 ;return -EINVAL ; }
@@ -323,9 +323,6 @@ int snd_hdmi_get_eld(struct hdmi_eld *el* ELD is valid, actual eld_size is assigned in hdmi_update_eld() */ -if (!eld->eld_valid) -return -ENOENT ; - size =snd_hdmi_get_eld_size (codec, nid);if (size ==0 ) {/* wfg: workaround for ASUS P5E-VM HDMI board */
sound/pci/hda/patch_hdmi.c +5 -6
@@ -980,20 +980,19 @@ static void hdmi_present_sense(struct hd* the unsolicited response to avoid custom WARs. */ int present =snd_hda_pin_sense (codec, pin_nid); +bool eld_valid = false; -memset (eld,0 ,sizeof (*eld)); +memset (eld,0 , offsetof(struct hdmi_eld , eld_buffer)); eld->monitor_present = !!(present &AC_PINSENSE_PRESENCE );if (eld->monitor_present) - eld->eld_valid = !!(present &AC_PINSENSE_ELDV ); -else - eld->eld_valid =0 ; + eld_valid = !!(present &AC_PINSENSE_ELDV );printk (KERN_INFO "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n" , - codec->addr, pin_nid, eld->monitor_present, eld->eld_valid); + codec->addr, pin_nid, eld->monitor_present, eld_valid); -if (eld->eld_valid) +if (eld_valid)if (!snd_hdmi_get_eld (eld, codec, pin_nid))snd_hdmi_show_eld (eld);
sound/pci/hda/hda_local.h +3 -0
@@ -651,6 +651,9 @@ struct hdmi_eld {int spk_alloc;int sad_count;struct cea_sad sad[ELD_MAX_SAD ]; +/* +* all fields above eld_buffer will be cleared before updating ELD +*/ char eld_buffer[ELD_MAX_SIZE ];#ifdef CONFIG_PROC_FS struct snd_info_entry *proc_entry;
PATCH 3/3 hda - move eld->spk_alloc fixup to hdmi_update_eld()
In reply to Wu FengguangIt looks more natural and saves two lines of code.
--- sound/pci/hda/hda_eld.c | 8 ++++++++ sound/pci/hda/patch_hdmi.c | 10 ---------- 2 files changed, 8 insertions(+), 10 deletions(-)
sound/pci/hda/hda_eld.c +8 -0
@@ -297,6 +297,14 @@ static int hdmi_update_eld(struct hdmi_e buf + ELD_FIXED_BYTES + mnl + 3 * i); } +/* +* HDMI sink's ELD info cannot always be retrieved for now, e.g. +* in console or for audio devices. Assume the highest speakers +* configuration, to _not_ prohibit multi-channel audio playback. +*/ +if (!e->spk_alloc) + e->spk_alloc =0xffff ; + e->eld_valid = true;return 0 ;
sound/pci/hda/patch_hdmi.c +0 -10
@@ -757,7 +757,6 @@ static void hdmi_intrinsic_event(structint pd = !!(res &AC_UNSOL_RES_PD );int eldv = !!(res &AC_UNSOL_RES_ELDV );int pin_idx; -struct hdmi_eld *eld; printk(KERN_INFO "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
@@ -766,17 +765,8 @@ static void hdmi_intrinsic_event(struct pin_idx =pin_nid_to_pin_index (spec, pin_nid);if (pin_idx <0 )return ; - eld = &spec->pins[pin_idx].sink_eld;hdmi_present_sense (&spec->pins[pin_idx], true); - -/* -* HDMI sink's ELD info cannot always be retrieved for now, e.g. -* in console or for audio devices. Assume the highest speakers -* configuration, to _not_ prohibit multi-channel audio playback. -*/ -if (!eld->spk_alloc) - eld->spk_alloc =0xffff ; }static void hdmi_non_intrinsic_event (struct hda_codec *codec,unsigned int res)
Takashi Iwai
AppliedAt Wed, 16 Nov 2011 16:29:45 +0800,
Quoted 4 lines · Wu Fengguang
Takashi,
Here are 2 fixes and 1 cleanup patch targeted for 3.2.
Thanks, applied all patches now.
Takashi
Re: [PATCH 2/3] hda - delayed ELD repoll
In reply to Wu FengguangQuoted 5 lines · Fengguang
The Intel HDMI chips (ironlake at least) are found to have ~250ms delay between the ELD_Valid=1 hotplug event is send and the ELD buffer becomes actually readable. During the time the ELD buffer is mysteriously all 0.
Fix it by scheduling a delayed work to re-read ELD buffer after 300ms.
Just FYI.
Up to now the 300ms looks sufficient. But if ever there comes longer delays, I've already prepared a patch for retrying it for several times ;)
Thanks,
Fengguang
--- sound/pci/hda/patch_hdmi.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
sound/pci/hda/patch_hdmi.c +11 -8
@@ -69,6 +69,7 @@ struct hdmi_spec_per_pin {struct hda_codec *codec;struct hdmi_eld sink_eld;struct delayed_work work; +int repoll_count; };struct hdmi_spec {
@@ -748,7 +749,7 @@ static void hdmi_setup_audio_infoframe(s* Unsolicited events */ -static void hdmi_present_sense (struct hdmi_spec_per_pin *per_pin,bool retry); +static void hdmi_present_sense (struct hdmi_spec_per_pin *per_pin,int repoll);static void hdmi_intrinsic_event (struct hda_codec *codec,unsigned int res) {
@@ -766,7 +767,7 @@ static void hdmi_intrinsic_event(structif (pin_idx <0 )return ; -hdmi_present_sense (&spec->pins[pin_idx], true); +hdmi_present_sense (&spec->pins[pin_idx],1 ); }static void hdmi_non_intrinsic_event (struct hda_codec *codec,unsigned int res)
@@ -960,7 +961,7 @@ static int hdmi_read_pin_conn(struct hdareturn 0 ; } -static void hdmi_present_sense (struct hdmi_spec_per_pin *per_pin,bool retry) +static void hdmi_present_sense (struct hdmi_spec_per_pin *per_pin,int repoll) {struct hda_codec *codec = per_pin->codec;struct hdmi_eld *eld = &per_pin->sink_eld;
@@ -989,10 +990,9 @@ static void hdmi_present_sense(struct hdif (eld_valid) {if (!snd_hdmi_get_eld (eld, codec, pin_nid))snd_hdmi_show_eld (eld); -else if (retry) { +else if (repoll) {queue_delayed_work (codec->bus->workq, - &per_pin->work, -msecs_to_jiffies (300 )); + &per_pin->work, repoll *HZ /3 ); } }
@@ -1004,7 +1004,10 @@ static void hdmi_repoll_eld(struct work_struct hdmi_spec_per_pin *per_pin = container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work); -hdmi_present_sense (per_pin, false); +if (per_pin->repoll_count++ >3 ) + per_pin->repoll_count =0 ; + +hdmi_present_sense (per_pin, per_pin->repoll_count); }static int hdmi_add_pin (struct hda_codec *codec,hda_nid_t pin_nid)
@@ -1235,7 +1238,7 @@ static int generic_hdmi_build_jack(strucif (err <0 )return err; -hdmi_present_sense (per_pin, false); +hdmi_present_sense (per_pin,0 );return 0 ; }
Re: [PATCH 2/3] hda - delayed ELD repoll
At Mon, 21 Nov 2011 14:48:11 +0800,
Quoted 6 lines · Wu Fengguang
Quoted 4 lines · Fengguang
The Intel HDMI chips (ironlake at least) are found to have ~250ms delay between the ELD_Valid=1 hotplug event is send and the ELD buffer becomes actually readable. During the time the ELD buffer is mysteriously all 0.
Quoted 6 lines
Quoted 1 line
Fix it by scheduling a delayed work to re-read ELD buffer after 300ms.
Just FYI.
Up to now the 300ms looks sufficient. But if ever there comes longer delays, I've already prepared a patch for retrying it for several times ;)
Thanks. I think it's safer to have a repoll mechanism. But, looking at the patch, one question remains:
sound/pci/hda/patch_hdmi.c static void hdmi_present_sense(struct hd · line 995
Quoted 10 lines
sound/pci/hda/patch_hdmi.c +2
-3@@ -989,10 +990,9 @@ static void hdmi_present_sense(struct hdif (eld_valid) {if (!snd_hdmi_get_eld (eld, codec, pin_nid))snd_hdmi_show_eld (eld); -else if (retry) { +else if (repoll) {queue_delayed_work (codec->bus->workq, - &per_pin->work, -msecs_to_jiffies (300 )); + &per_pin->work, repoll *HZ /3 );
Do you really want to wait longer with a higher repoll number?
Takashi
Re: [PATCH 2/3] hda - delayed ELD repoll
Quoted 18 lines · Takashi Iwai
At Mon, 21 Nov 2011 14:48:11 +0800,
Quoted 6 lines · Wu Fengguang
Quoted 4 lines · Fengguang
The Intel HDMI chips (ironlake at least) are found to have ~250ms delay between the ELD_Valid=1 hotplug event is send and the ELD buffer becomes actually readable. During the time the ELD buffer is mysteriously all 0.
Quoted 6 lines
Quoted 1 line
Fix it by scheduling a delayed work to re-read ELD buffer after 300ms.
Just FYI.
Up to now the 300ms looks sufficient. But if ever there comes longer delays, I've already prepared a patch for retrying it for several times ;)
Thanks. I think it's safer to have a repoll mechanism. But, looking at the patch, one question remains:
Quoted 12 lines
Review static void hdmi_present_sense(struct hd · line 995
Quoted 10 lines
(quoted diff) +2
-3@@ -989,10 +990,9 @@ static void hdmi_present_sense(struct hd if (eld_valid) { if (!snd_hdmi_get_eld(eld, codec, pin_nid)) snd_hdmi_show_eld(eld); - else if (retry) { + else if (repoll) { queue_delayed_work(codec->bus->workq, - &per_pin->work, - msecs_to_jiffies(300)); + &per_pin->work, repoll * HZ / 3);Do you really want to wait longer with a higher repoll number?
I think it would be good to keep constant repoll intervals (it's cheap after all), but at the same time lift the retry time from 3 to 6.
I did this patch when I got the graphics driver side wrong and triggered the hotplug event rather early. At the time I find that it failed until the 3rd repoll (300ms + 600ms + 900ms).
Thanks,
Fengguang
---
Subject: hda - repoll ELD content for multiple times
Date: Fri Nov 18 16:59:32 CST 2011
Improve the one-shot ELD repoll to up to 6 retries.
Up to now the 300ms looks sufficient for the test boxes. However I'm a bit worried about how well it can fit the wider user base.
--- sound/pci/hda/patch_hdmi.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
sound/pci/hda/patch_hdmi.c +10 -6
@@ -69,6 +69,7 @@ struct hdmi_spec_per_pin {struct hda_codec *codec;struct hdmi_eld sink_eld;struct delayed_work work; +int repoll_count; };struct hdmi_spec {
@@ -748,7 +749,7 @@ static void hdmi_setup_audio_infoframe(s* Unsolicited events */ -static void hdmi_present_sense (struct hdmi_spec_per_pin *per_pin,bool retry); +static void hdmi_present_sense (struct hdmi_spec_per_pin *per_pin,int repoll);static void hdmi_intrinsic_event (struct hda_codec *codec,unsigned int res) {
@@ -766,7 +767,7 @@ static void hdmi_intrinsic_event(structif (pin_idx <0 )return ; -hdmi_present_sense (&spec->pins[pin_idx], true); +hdmi_present_sense (&spec->pins[pin_idx],1 ); }static void hdmi_non_intrinsic_event (struct hda_codec *codec,unsigned int res)
@@ -960,7 +961,7 @@ static int hdmi_read_pin_conn(struct hdareturn 0 ; } -static void hdmi_present_sense (struct hdmi_spec_per_pin *per_pin,bool retry) +static void hdmi_present_sense (struct hdmi_spec_per_pin *per_pin,int repoll) {struct hda_codec *codec = per_pin->codec;struct hdmi_eld *eld = &per_pin->sink_eld;
@@ -989,7 +990,7 @@ static void hdmi_present_sense(struct hdif (eld_valid) {if (!snd_hdmi_get_eld (eld, codec, pin_nid))snd_hdmi_show_eld (eld); -else if (retry) { +else if (repoll) {queue_delayed_work (codec->bus->workq, &per_pin->work,msecs_to_jiffies (300 ));
@@ -1004,7 +1005,10 @@ static void hdmi_repoll_eld(struct work_struct hdmi_spec_per_pin *per_pin = container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work); -hdmi_present_sense (per_pin, false); +if (per_pin->repoll_count++ >6 ) + per_pin->repoll_count =0 ; + +hdmi_present_sense (per_pin, per_pin->repoll_count); }static int hdmi_add_pin (struct hda_codec *codec,hda_nid_t pin_nid)
@@ -1235,7 +1239,7 @@ static int generic_hdmi_build_jack(strucif (err <0 )return err; -hdmi_present_sense (per_pin, false); +hdmi_present_sense (per_pin,0 );return 0 ; }