diff options
author | David Henningsson <david.henningsson@canonical.com> | 2013-02-19 10:11:25 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2013-02-19 12:28:30 -0500 |
commit | 4bd038f9d80216a6e95c5c36fae5054a83ea75d7 (patch) | |
tree | a2347fc3bb5bdbb84527858956667b9d9882ba0c /sound | |
parent | 1613d6b46b433f07f1d2703e4bd102802dcd75a4 (diff) |
ALSA: hda - hdmi: Protect ELD buffer
Because the eld buffer can be simultaneously accessed from both
workqueue context (updating) and process context (kcontrol read),
we need to protect it with a mutex to guarantee consistency.
To avoid holding the mutex while reading the ELD info from the
codec, we introduce a temporary eld buffer.
Signed-off-by: David Henningsson <david.henningsson@canonical.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/hda/hda_eld.c | 8 | ||||
-rw-r--r-- | sound/pci/hda/hda_local.h | 1 | ||||
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 51 |
3 files changed, 47 insertions, 13 deletions
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 16066d7763ec..7dd846380a50 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c | |||
@@ -500,10 +500,13 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry, | |||
500 | [4 ... 7] = "reserved" | 500 | [4 ... 7] = "reserved" |
501 | }; | 501 | }; |
502 | 502 | ||
503 | mutex_lock(&eld->lock); | ||
503 | snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present); | 504 | snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present); |
504 | snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid); | 505 | snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid); |
505 | if (!eld->eld_valid) | 506 | if (!eld->eld_valid) { |
507 | mutex_unlock(&eld->lock); | ||
506 | return; | 508 | return; |
509 | } | ||
507 | snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); | 510 | snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); |
508 | snd_iprintf(buffer, "connection_type\t\t%s\n", | 511 | snd_iprintf(buffer, "connection_type\t\t%s\n", |
509 | eld_connection_type_names[e->conn_type]); | 512 | eld_connection_type_names[e->conn_type]); |
@@ -525,6 +528,7 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry, | |||
525 | 528 | ||
526 | for (i = 0; i < e->sad_count; i++) | 529 | for (i = 0; i < e->sad_count; i++) |
527 | hdmi_print_sad_info(i, e->sad + i, buffer); | 530 | hdmi_print_sad_info(i, e->sad + i, buffer); |
531 | mutex_unlock(&eld->lock); | ||
528 | } | 532 | } |
529 | 533 | ||
530 | static void hdmi_write_eld_info(struct snd_info_entry *entry, | 534 | static void hdmi_write_eld_info(struct snd_info_entry *entry, |
@@ -538,6 +542,7 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, | |||
538 | long long val; | 542 | long long val; |
539 | unsigned int n; | 543 | unsigned int n; |
540 | 544 | ||
545 | mutex_lock(&eld->lock); | ||
541 | while (!snd_info_get_line(buffer, line, sizeof(line))) { | 546 | while (!snd_info_get_line(buffer, line, sizeof(line))) { |
542 | if (sscanf(line, "%s %llx", name, &val) != 2) | 547 | if (sscanf(line, "%s %llx", name, &val) != 2) |
543 | continue; | 548 | continue; |
@@ -589,6 +594,7 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, | |||
589 | e->sad_count = n + 1; | 594 | e->sad_count = n + 1; |
590 | } | 595 | } |
591 | } | 596 | } |
597 | mutex_unlock(&eld->lock); | ||
592 | } | 598 | } |
593 | 599 | ||
594 | 600 | ||
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 363cd487266b..83b7486c8eff 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h | |||
@@ -739,6 +739,7 @@ struct hdmi_eld { | |||
739 | int eld_size; | 739 | int eld_size; |
740 | char eld_buffer[ELD_MAX_SIZE]; | 740 | char eld_buffer[ELD_MAX_SIZE]; |
741 | struct parsed_hdmi_eld info; | 741 | struct parsed_hdmi_eld info; |
742 | struct mutex lock; | ||
742 | #ifdef CONFIG_PROC_FS | 743 | #ifdef CONFIG_PROC_FS |
743 | struct snd_info_entry *proc_entry; | 744 | struct snd_info_entry *proc_entry; |
744 | #endif | 745 | #endif |
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 1e381918eb82..e77735d804a3 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c | |||
@@ -91,6 +91,7 @@ struct hdmi_spec { | |||
91 | struct hda_pcm pcm_rec[MAX_HDMI_PINS]; | 91 | struct hda_pcm pcm_rec[MAX_HDMI_PINS]; |
92 | unsigned int channels_max; /* max over all cvts */ | 92 | unsigned int channels_max; /* max over all cvts */ |
93 | 93 | ||
94 | struct hdmi_eld temp_eld; | ||
94 | /* | 95 | /* |
95 | * Non-generic ATI/NVIDIA specific | 96 | * Non-generic ATI/NVIDIA specific |
96 | */ | 97 | */ |
@@ -352,7 +353,9 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, | |||
352 | pin_idx = kcontrol->private_value; | 353 | pin_idx = kcontrol->private_value; |
353 | eld = &spec->pins[pin_idx].sink_eld; | 354 | eld = &spec->pins[pin_idx].sink_eld; |
354 | 355 | ||
356 | mutex_lock(&eld->lock); | ||
355 | uinfo->count = eld->eld_valid ? eld->eld_size : 0; | 357 | uinfo->count = eld->eld_valid ? eld->eld_size : 0; |
358 | mutex_unlock(&eld->lock); | ||
356 | 359 | ||
357 | return 0; | 360 | return 0; |
358 | } | 361 | } |
@@ -368,7 +371,9 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, | |||
368 | pin_idx = kcontrol->private_value; | 371 | pin_idx = kcontrol->private_value; |
369 | eld = &spec->pins[pin_idx].sink_eld; | 372 | eld = &spec->pins[pin_idx].sink_eld; |
370 | 373 | ||
374 | mutex_lock(&eld->lock); | ||
371 | if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) { | 375 | if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) { |
376 | mutex_unlock(&eld->lock); | ||
372 | snd_BUG(); | 377 | snd_BUG(); |
373 | return -EINVAL; | 378 | return -EINVAL; |
374 | } | 379 | } |
@@ -378,6 +383,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, | |||
378 | if (eld->eld_valid) | 383 | if (eld->eld_valid) |
379 | memcpy(ucontrol->value.bytes.data, eld->eld_buffer, | 384 | memcpy(ucontrol->value.bytes.data, eld->eld_buffer, |
380 | eld->eld_size); | 385 | eld->eld_size); |
386 | mutex_unlock(&eld->lock); | ||
381 | 387 | ||
382 | return 0; | 388 | return 0; |
383 | } | 389 | } |
@@ -1164,7 +1170,9 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) | |||
1164 | static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) | 1170 | static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) |
1165 | { | 1171 | { |
1166 | struct hda_codec *codec = per_pin->codec; | 1172 | struct hda_codec *codec = per_pin->codec; |
1167 | struct hdmi_eld *eld = &per_pin->sink_eld; | 1173 | struct hdmi_spec *spec = codec->spec; |
1174 | struct hdmi_eld *eld = &spec->temp_eld; | ||
1175 | struct hdmi_eld *pin_eld = &per_pin->sink_eld; | ||
1168 | hda_nid_t pin_nid = per_pin->pin_nid; | 1176 | hda_nid_t pin_nid = per_pin->pin_nid; |
1169 | /* | 1177 | /* |
1170 | * Always execute a GetPinSense verb here, even when called from | 1178 | * Always execute a GetPinSense verb here, even when called from |
@@ -1175,38 +1183,56 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) | |||
1175 | * the unsolicited response to avoid custom WARs. | 1183 | * the unsolicited response to avoid custom WARs. |
1176 | */ | 1184 | */ |
1177 | int present = snd_hda_pin_sense(codec, pin_nid); | 1185 | int present = snd_hda_pin_sense(codec, pin_nid); |
1178 | bool eld_valid = false; | 1186 | bool update_eld = false; |
1187 | bool eld_changed = false; | ||
1179 | 1188 | ||
1180 | eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); | 1189 | pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); |
1181 | if (eld->monitor_present) | 1190 | if (pin_eld->monitor_present) |
1182 | eld_valid = !!(present & AC_PINSENSE_ELDV); | 1191 | eld->eld_valid = !!(present & AC_PINSENSE_ELDV); |
1192 | else | ||
1193 | eld->eld_valid = false; | ||
1183 | 1194 | ||
1184 | _snd_printd(SND_PR_VERBOSE, | 1195 | _snd_printd(SND_PR_VERBOSE, |
1185 | "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", | 1196 | "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", |
1186 | codec->addr, pin_nid, eld->monitor_present, eld_valid); | 1197 | codec->addr, pin_nid, eld->monitor_present, eld->eld_valid); |
1187 | 1198 | ||
1188 | eld->eld_valid = false; | 1199 | if (eld->eld_valid) { |
1189 | if (eld_valid) { | ||
1190 | if (snd_hdmi_get_eld(codec, pin_nid, eld->eld_buffer, | 1200 | if (snd_hdmi_get_eld(codec, pin_nid, eld->eld_buffer, |
1191 | &eld->eld_size) < 0) | 1201 | &eld->eld_size) < 0) |
1192 | eld_valid = false; | 1202 | eld->eld_valid = false; |
1193 | else { | 1203 | else { |
1194 | memset(&eld->info, 0, sizeof(struct parsed_hdmi_eld)); | 1204 | memset(&eld->info, 0, sizeof(struct parsed_hdmi_eld)); |
1195 | if (snd_hdmi_parse_eld(&eld->info, eld->eld_buffer, | 1205 | if (snd_hdmi_parse_eld(&eld->info, eld->eld_buffer, |
1196 | eld->eld_size) < 0) | 1206 | eld->eld_size) < 0) |
1197 | eld_valid = false; | 1207 | eld->eld_valid = false; |
1198 | } | 1208 | } |
1199 | 1209 | ||
1200 | if (eld_valid) { | 1210 | if (eld->eld_valid) { |
1201 | snd_hdmi_show_eld(&eld->info); | 1211 | snd_hdmi_show_eld(&eld->info); |
1202 | eld->eld_valid = true; | 1212 | update_eld = true; |
1203 | } | 1213 | } |
1204 | else if (repoll) { | 1214 | else if (repoll) { |
1205 | queue_delayed_work(codec->bus->workq, | 1215 | queue_delayed_work(codec->bus->workq, |
1206 | &per_pin->work, | 1216 | &per_pin->work, |
1207 | msecs_to_jiffies(300)); | 1217 | msecs_to_jiffies(300)); |
1218 | return; | ||
1208 | } | 1219 | } |
1209 | } | 1220 | } |
1221 | |||
1222 | mutex_lock(&pin_eld->lock); | ||
1223 | if (pin_eld->eld_valid && !eld->eld_valid) | ||
1224 | update_eld = true; | ||
1225 | if (update_eld) { | ||
1226 | pin_eld->eld_valid = eld->eld_valid; | ||
1227 | eld_changed = memcmp(pin_eld->eld_buffer, eld->eld_buffer, | ||
1228 | eld->eld_size) != 0; | ||
1229 | if (eld_changed) | ||
1230 | memcpy(pin_eld->eld_buffer, eld->eld_buffer, | ||
1231 | eld->eld_size); | ||
1232 | pin_eld->eld_size = eld->eld_size; | ||
1233 | pin_eld->info = eld->info; | ||
1234 | } | ||
1235 | mutex_unlock(&pin_eld->lock); | ||
1210 | } | 1236 | } |
1211 | 1237 | ||
1212 | static void hdmi_repoll_eld(struct work_struct *work) | 1238 | static void hdmi_repoll_eld(struct work_struct *work) |
@@ -1674,6 +1700,7 @@ static int generic_hdmi_init_per_pins(struct hda_codec *codec) | |||
1674 | struct hdmi_eld *eld = &per_pin->sink_eld; | 1700 | struct hdmi_eld *eld = &per_pin->sink_eld; |
1675 | 1701 | ||
1676 | per_pin->codec = codec; | 1702 | per_pin->codec = codec; |
1703 | mutex_init(&eld->lock); | ||
1677 | INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld); | 1704 | INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld); |
1678 | snd_hda_eld_proc_new(codec, eld, pin_idx); | 1705 | snd_hda_eld_proc_new(codec, eld, pin_idx); |
1679 | } | 1706 | } |