diff options
Diffstat (limited to 'sound/pci/hda/patch_hdmi.c')
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 308 |
1 files changed, 303 insertions, 5 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 333d5338c15c..4a5d24fe8adf 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <sound/core.h> | 35 | #include <sound/core.h> |
36 | #include <sound/jack.h> | 36 | #include <sound/jack.h> |
37 | #include <sound/asoundef.h> | 37 | #include <sound/asoundef.h> |
38 | #include <sound/tlv.h> | ||
38 | #include "hda_codec.h" | 39 | #include "hda_codec.h" |
39 | #include "hda_local.h" | 40 | #include "hda_local.h" |
40 | #include "hda_jack.h" | 41 | #include "hda_jack.h" |
@@ -73,6 +74,8 @@ struct hdmi_spec_per_pin { | |||
73 | struct delayed_work work; | 74 | struct delayed_work work; |
74 | int repoll_count; | 75 | int repoll_count; |
75 | bool non_pcm; | 76 | bool non_pcm; |
77 | bool chmap_set; /* channel-map override by ALSA API? */ | ||
78 | unsigned char chmap[8]; /* ALSA API channel-map */ | ||
76 | }; | 79 | }; |
77 | 80 | ||
78 | struct hdmi_spec { | 81 | struct hdmi_spec { |
@@ -82,6 +85,7 @@ struct hdmi_spec { | |||
82 | int num_pins; | 85 | int num_pins; |
83 | struct hdmi_spec_per_pin pins[MAX_HDMI_PINS]; | 86 | struct hdmi_spec_per_pin pins[MAX_HDMI_PINS]; |
84 | struct hda_pcm pcm_rec[MAX_HDMI_PINS]; | 87 | struct hda_pcm pcm_rec[MAX_HDMI_PINS]; |
88 | unsigned int channels_max; /* max over all cvts */ | ||
85 | 89 | ||
86 | /* | 90 | /* |
87 | * Non-generic ATI/NVIDIA specific | 91 | * Non-generic ATI/NVIDIA specific |
@@ -548,7 +552,7 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, | |||
548 | } | 552 | } |
549 | 553 | ||
550 | 554 | ||
551 | static void hdmi_setup_channel_mapping(struct hda_codec *codec, | 555 | static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, |
552 | hda_nid_t pin_nid, | 556 | hda_nid_t pin_nid, |
553 | bool non_pcm, | 557 | bool non_pcm, |
554 | int ca) | 558 | int ca) |
@@ -588,6 +592,136 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, | |||
588 | hdmi_debug_channel_mapping(codec, pin_nid); | 592 | hdmi_debug_channel_mapping(codec, pin_nid); |
589 | } | 593 | } |
590 | 594 | ||
595 | struct channel_map_table { | ||
596 | unsigned char map; /* ALSA API channel map position */ | ||
597 | unsigned char cea_slot; /* CEA slot value */ | ||
598 | int spk_mask; /* speaker position bit mask */ | ||
599 | }; | ||
600 | |||
601 | static struct channel_map_table map_tables[] = { | ||
602 | { SNDRV_CHMAP_FL, 0x00, FL }, | ||
603 | { SNDRV_CHMAP_FR, 0x01, FR }, | ||
604 | { SNDRV_CHMAP_RL, 0x04, RL }, | ||
605 | { SNDRV_CHMAP_RR, 0x05, RR }, | ||
606 | { SNDRV_CHMAP_LFE, 0x02, LFE }, | ||
607 | { SNDRV_CHMAP_FC, 0x03, FC }, | ||
608 | { SNDRV_CHMAP_RLC, 0x06, RLC }, | ||
609 | { SNDRV_CHMAP_RRC, 0x07, RRC }, | ||
610 | {} /* terminator */ | ||
611 | }; | ||
612 | |||
613 | /* from ALSA API channel position to speaker bit mask */ | ||
614 | static int to_spk_mask(unsigned char c) | ||
615 | { | ||
616 | struct channel_map_table *t = map_tables; | ||
617 | for (; t->map; t++) { | ||
618 | if (t->map == c) | ||
619 | return t->spk_mask; | ||
620 | } | ||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | /* from ALSA API channel position to CEA slot */ | ||
625 | static int to_cea_slot(unsigned char c) | ||
626 | { | ||
627 | struct channel_map_table *t = map_tables; | ||
628 | for (; t->map; t++) { | ||
629 | if (t->map == c) | ||
630 | return t->cea_slot; | ||
631 | } | ||
632 | return 0x0f; | ||
633 | } | ||
634 | |||
635 | /* from CEA slot to ALSA API channel position */ | ||
636 | static int from_cea_slot(unsigned char c) | ||
637 | { | ||
638 | struct channel_map_table *t = map_tables; | ||
639 | for (; t->map; t++) { | ||
640 | if (t->cea_slot == c) | ||
641 | return t->map; | ||
642 | } | ||
643 | return 0; | ||
644 | } | ||
645 | |||
646 | /* from speaker bit mask to ALSA API channel position */ | ||
647 | static int spk_to_chmap(int spk) | ||
648 | { | ||
649 | struct channel_map_table *t = map_tables; | ||
650 | for (; t->map; t++) { | ||
651 | if (t->spk_mask == spk) | ||
652 | return t->map; | ||
653 | } | ||
654 | return 0; | ||
655 | } | ||
656 | |||
657 | /* get the CA index corresponding to the given ALSA API channel map */ | ||
658 | static int hdmi_manual_channel_allocation(int chs, unsigned char *map) | ||
659 | { | ||
660 | int i, spks = 0, spk_mask = 0; | ||
661 | |||
662 | for (i = 0; i < chs; i++) { | ||
663 | int mask = to_spk_mask(map[i]); | ||
664 | if (mask) { | ||
665 | spk_mask |= mask; | ||
666 | spks++; | ||
667 | } | ||
668 | } | ||
669 | |||
670 | for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { | ||
671 | if ((chs == channel_allocations[i].channels || | ||
672 | spks == channel_allocations[i].channels) && | ||
673 | (spk_mask & channel_allocations[i].spk_mask) == | ||
674 | channel_allocations[i].spk_mask) | ||
675 | return channel_allocations[i].ca_index; | ||
676 | } | ||
677 | return -1; | ||
678 | } | ||
679 | |||
680 | /* set up the channel slots for the given ALSA API channel map */ | ||
681 | static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, | ||
682 | hda_nid_t pin_nid, | ||
683 | int chs, unsigned char *map) | ||
684 | { | ||
685 | int i; | ||
686 | for (i = 0; i < 8; i++) { | ||
687 | int val, err; | ||
688 | if (i < chs) | ||
689 | val = to_cea_slot(map[i]); | ||
690 | else | ||
691 | val = 0xf; | ||
692 | val |= (i << 4); | ||
693 | err = snd_hda_codec_write(codec, pin_nid, 0, | ||
694 | AC_VERB_SET_HDMI_CHAN_SLOT, val); | ||
695 | if (err) | ||
696 | return -EINVAL; | ||
697 | } | ||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | /* store ALSA API channel map from the current default map */ | ||
702 | static void hdmi_setup_fake_chmap(unsigned char *map, int ca) | ||
703 | { | ||
704 | int i; | ||
705 | for (i = 0; i < 8; i++) { | ||
706 | if (i < channel_allocations[ca].channels) | ||
707 | map[i] = from_cea_slot((hdmi_channel_mapping[ca][i] >> 4) & 0x0f); | ||
708 | else | ||
709 | map[i] = 0; | ||
710 | } | ||
711 | } | ||
712 | |||
713 | static void hdmi_setup_channel_mapping(struct hda_codec *codec, | ||
714 | hda_nid_t pin_nid, bool non_pcm, int ca, | ||
715 | int channels, unsigned char *map) | ||
716 | { | ||
717 | if (!non_pcm && map) { | ||
718 | hdmi_manual_setup_channel_mapping(codec, pin_nid, | ||
719 | channels, map); | ||
720 | } else { | ||
721 | hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca); | ||
722 | hdmi_setup_fake_chmap(map, ca); | ||
723 | } | ||
724 | } | ||
591 | 725 | ||
592 | /* | 726 | /* |
593 | * Audio InfoFrame routines | 727 | * Audio InfoFrame routines |
@@ -726,7 +860,12 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, | |||
726 | if (!eld->monitor_present) | 860 | if (!eld->monitor_present) |
727 | return; | 861 | return; |
728 | 862 | ||
729 | ca = hdmi_channel_allocation(eld, channels); | 863 | if (!non_pcm && per_pin->chmap_set) |
864 | ca = hdmi_manual_channel_allocation(channels, per_pin->chmap); | ||
865 | else | ||
866 | ca = hdmi_channel_allocation(eld, channels); | ||
867 | if (ca < 0) | ||
868 | ca = 0; | ||
730 | 869 | ||
731 | memset(&ai, 0, sizeof(ai)); | 870 | memset(&ai, 0, sizeof(ai)); |
732 | if (eld->conn_type == 0) { /* HDMI */ | 871 | if (eld->conn_type == 0) { /* HDMI */ |
@@ -763,7 +902,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, | |||
763 | "pin=%d channels=%d\n", | 902 | "pin=%d channels=%d\n", |
764 | pin_nid, | 903 | pin_nid, |
765 | channels); | 904 | channels); |
766 | hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca); | 905 | hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, |
906 | channels, per_pin->chmap); | ||
767 | hdmi_stop_infoframe_trans(codec, pin_nid); | 907 | hdmi_stop_infoframe_trans(codec, pin_nid); |
768 | hdmi_fill_audio_infoframe(codec, pin_nid, | 908 | hdmi_fill_audio_infoframe(codec, pin_nid, |
769 | ai.bytes, sizeof(ai)); | 909 | ai.bytes, sizeof(ai)); |
@@ -772,7 +912,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, | |||
772 | /* For non-pcm audio switch, setup new channel mapping | 912 | /* For non-pcm audio switch, setup new channel mapping |
773 | * accordingly */ | 913 | * accordingly */ |
774 | if (per_pin->non_pcm != non_pcm) | 914 | if (per_pin->non_pcm != non_pcm) |
775 | hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca); | 915 | hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, |
916 | channels, per_pin->chmap); | ||
776 | } | 917 | } |
777 | 918 | ||
778 | per_pin->non_pcm = non_pcm; | 919 | per_pin->non_pcm = non_pcm; |
@@ -1098,8 +1239,11 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) | |||
1098 | 1239 | ||
1099 | per_cvt->cvt_nid = cvt_nid; | 1240 | per_cvt->cvt_nid = cvt_nid; |
1100 | per_cvt->channels_min = 2; | 1241 | per_cvt->channels_min = 2; |
1101 | if (chans <= 16) | 1242 | if (chans <= 16) { |
1102 | per_cvt->channels_max = chans; | 1243 | per_cvt->channels_max = chans; |
1244 | if (chans > spec->channels_max) | ||
1245 | spec->channels_max = chans; | ||
1246 | } | ||
1103 | 1247 | ||
1104 | err = snd_hda_query_supported_pcm(codec, cvt_nid, | 1248 | err = snd_hda_query_supported_pcm(codec, cvt_nid, |
1105 | &per_cvt->rates, | 1249 | &per_cvt->rates, |
@@ -1250,7 +1394,10 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, | |||
1250 | AC_VERB_SET_PIN_WIDGET_CONTROL, | 1394 | AC_VERB_SET_PIN_WIDGET_CONTROL, |
1251 | pinctl & ~PIN_OUT); | 1395 | pinctl & ~PIN_OUT); |
1252 | snd_hda_spdif_ctls_unassign(codec, pin_idx); | 1396 | snd_hda_spdif_ctls_unassign(codec, pin_idx); |
1397 | per_pin->chmap_set = false; | ||
1398 | memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); | ||
1253 | } | 1399 | } |
1400 | |||
1254 | return 0; | 1401 | return 0; |
1255 | } | 1402 | } |
1256 | 1403 | ||
@@ -1261,6 +1408,135 @@ static const struct hda_pcm_ops generic_ops = { | |||
1261 | .cleanup = generic_hdmi_playback_pcm_cleanup, | 1408 | .cleanup = generic_hdmi_playback_pcm_cleanup, |
1262 | }; | 1409 | }; |
1263 | 1410 | ||
1411 | /* | ||
1412 | * ALSA API channel-map control callbacks | ||
1413 | */ | ||
1414 | static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, | ||
1415 | struct snd_ctl_elem_info *uinfo) | ||
1416 | { | ||
1417 | struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); | ||
1418 | struct hda_codec *codec = info->private_data; | ||
1419 | struct hdmi_spec *spec = codec->spec; | ||
1420 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1421 | uinfo->count = spec->channels_max; | ||
1422 | uinfo->value.integer.min = 0; | ||
1423 | uinfo->value.integer.max = SNDRV_CHMAP_LAST; | ||
1424 | return 0; | ||
1425 | } | ||
1426 | |||
1427 | static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, | ||
1428 | unsigned int size, unsigned int __user *tlv) | ||
1429 | { | ||
1430 | struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); | ||
1431 | struct hda_codec *codec = info->private_data; | ||
1432 | struct hdmi_spec *spec = codec->spec; | ||
1433 | const unsigned int valid_mask = | ||
1434 | FL | FR | RL | RR | LFE | FC | RLC | RRC; | ||
1435 | unsigned int __user *dst; | ||
1436 | int chs, count = 0; | ||
1437 | |||
1438 | if (size < 8) | ||
1439 | return -ENOMEM; | ||
1440 | if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) | ||
1441 | return -EFAULT; | ||
1442 | size -= 8; | ||
1443 | dst = tlv + 2; | ||
1444 | for (chs = 2; chs <= spec->channels_max; chs += 2) { | ||
1445 | int i, c; | ||
1446 | struct cea_channel_speaker_allocation *cap; | ||
1447 | cap = channel_allocations; | ||
1448 | for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { | ||
1449 | int chs_bytes = chs * 4; | ||
1450 | if (cap->channels != chs) | ||
1451 | continue; | ||
1452 | if (cap->spk_mask & ~valid_mask) | ||
1453 | continue; | ||
1454 | if (size < 8) | ||
1455 | return -ENOMEM; | ||
1456 | if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) || | ||
1457 | put_user(chs_bytes, dst + 1)) | ||
1458 | return -EFAULT; | ||
1459 | dst += 2; | ||
1460 | size -= 8; | ||
1461 | count += 8; | ||
1462 | if (size < chs_bytes) | ||
1463 | return -ENOMEM; | ||
1464 | size -= chs_bytes; | ||
1465 | count += chs_bytes; | ||
1466 | for (c = 7; c >= 0; c--) { | ||
1467 | int spk = cap->speakers[c]; | ||
1468 | if (!spk) | ||
1469 | continue; | ||
1470 | if (put_user(spk_to_chmap(spk), dst)) | ||
1471 | return -EFAULT; | ||
1472 | dst++; | ||
1473 | } | ||
1474 | } | ||
1475 | } | ||
1476 | if (put_user(count, tlv + 1)) | ||
1477 | return -EFAULT; | ||
1478 | return 0; | ||
1479 | } | ||
1480 | |||
1481 | static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, | ||
1482 | struct snd_ctl_elem_value *ucontrol) | ||
1483 | { | ||
1484 | struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); | ||
1485 | struct hda_codec *codec = info->private_data; | ||
1486 | struct hdmi_spec *spec = codec->spec; | ||
1487 | int pin_idx = kcontrol->private_value; | ||
1488 | struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; | ||
1489 | int i; | ||
1490 | |||
1491 | for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++) | ||
1492 | ucontrol->value.integer.value[i] = per_pin->chmap[i]; | ||
1493 | return 0; | ||
1494 | } | ||
1495 | |||
1496 | static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, | ||
1497 | struct snd_ctl_elem_value *ucontrol) | ||
1498 | { | ||
1499 | struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); | ||
1500 | struct hda_codec *codec = info->private_data; | ||
1501 | struct hdmi_spec *spec = codec->spec; | ||
1502 | int pin_idx = kcontrol->private_value; | ||
1503 | struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; | ||
1504 | unsigned int ctl_idx; | ||
1505 | struct snd_pcm_substream *substream; | ||
1506 | unsigned char chmap[8]; | ||
1507 | int i, ca, prepared = 0; | ||
1508 | |||
1509 | ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); | ||
1510 | substream = snd_pcm_chmap_substream(info, ctl_idx); | ||
1511 | if (!substream || !substream->runtime) | ||
1512 | return -EBADFD; | ||
1513 | switch (substream->runtime->status->state) { | ||
1514 | case SNDRV_PCM_STATE_OPEN: | ||
1515 | case SNDRV_PCM_STATE_SETUP: | ||
1516 | break; | ||
1517 | case SNDRV_PCM_STATE_PREPARED: | ||
1518 | prepared = 1; | ||
1519 | break; | ||
1520 | default: | ||
1521 | return -EBUSY; | ||
1522 | } | ||
1523 | memset(chmap, 0, sizeof(chmap)); | ||
1524 | for (i = 0; i < ARRAY_SIZE(chmap); i++) | ||
1525 | chmap[i] = ucontrol->value.integer.value[i]; | ||
1526 | if (!memcmp(chmap, per_pin->chmap, sizeof(chmap))) | ||
1527 | return 0; | ||
1528 | ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); | ||
1529 | if (ca < 0) | ||
1530 | return -EINVAL; | ||
1531 | per_pin->chmap_set = true; | ||
1532 | memcpy(per_pin->chmap, chmap, sizeof(chmap)); | ||
1533 | if (prepared) | ||
1534 | hdmi_setup_audio_infoframe(codec, pin_idx, per_pin->non_pcm, | ||
1535 | substream); | ||
1536 | |||
1537 | return 0; | ||
1538 | } | ||
1539 | |||
1264 | static int generic_hdmi_build_pcms(struct hda_codec *codec) | 1540 | static int generic_hdmi_build_pcms(struct hda_codec *codec) |
1265 | { | 1541 | { |
1266 | struct hdmi_spec *spec = codec->spec; | 1542 | struct hdmi_spec *spec = codec->spec; |
@@ -1273,6 +1549,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) | |||
1273 | info = &spec->pcm_rec[pin_idx]; | 1549 | info = &spec->pcm_rec[pin_idx]; |
1274 | info->name = get_hdmi_pcm_name(pin_idx); | 1550 | info->name = get_hdmi_pcm_name(pin_idx); |
1275 | info->pcm_type = HDA_PCM_TYPE_HDMI; | 1551 | info->pcm_type = HDA_PCM_TYPE_HDMI; |
1552 | info->own_chmap = true; | ||
1276 | 1553 | ||
1277 | pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; | 1554 | pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; |
1278 | pstr->substreams = 1; | 1555 | pstr->substreams = 1; |
@@ -1330,6 +1607,27 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) | |||
1330 | hdmi_present_sense(per_pin, 0); | 1607 | hdmi_present_sense(per_pin, 0); |
1331 | } | 1608 | } |
1332 | 1609 | ||
1610 | /* add channel maps */ | ||
1611 | for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { | ||
1612 | struct snd_pcm_chmap *chmap; | ||
1613 | struct snd_kcontrol *kctl; | ||
1614 | int i; | ||
1615 | err = snd_pcm_add_chmap_ctls(codec->pcm_info[pin_idx].pcm, | ||
1616 | SNDRV_PCM_STREAM_PLAYBACK, | ||
1617 | NULL, 0, pin_idx, &chmap); | ||
1618 | if (err < 0) | ||
1619 | return err; | ||
1620 | /* override handlers */ | ||
1621 | chmap->private_data = codec; | ||
1622 | kctl = chmap->kctl; | ||
1623 | for (i = 0; i < kctl->count; i++) | ||
1624 | kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; | ||
1625 | kctl->info = hdmi_chmap_ctl_info; | ||
1626 | kctl->get = hdmi_chmap_ctl_get; | ||
1627 | kctl->put = hdmi_chmap_ctl_put; | ||
1628 | kctl->tlv.c = hdmi_chmap_ctl_tlv; | ||
1629 | } | ||
1630 | |||
1333 | return 0; | 1631 | return 0; |
1334 | } | 1632 | } |
1335 | 1633 | ||