aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/hda_eld.c
diff options
context:
space:
mode:
authorAnssi Hannula <anssi.hannula@iki.fi>2013-10-24 14:10:36 -0400
committerTakashi Iwai <tiwai@suse.de>2013-10-24 16:54:13 -0400
commit89250f84644676c1ed6a09acef1033e14596a402 (patch)
tree2af9f7e5277faad79f336ab27ad6fe89859478d7 /sound/pci/hda/hda_eld.c
parent5a61358433b1f89b500f2c365746c73cb7a27e2f (diff)
ALSA: hda - hdmi: Add ELD emulation for ATI/AMD codecs
ATI/AMD HDMI/DP codecs do not include standard HDA ELD (EDID-like data) support. In place of providing access to an ELD buffer, various vendor-specific verbs are provided to provide the relevant information. Revision ID 3 and later (0x100300 as reported by procfs codec#X) have support for providing more information than the previous revisions (but only if supported by the display driver). Generate ELD from the information provided by the vendor-specific verbs on ATI/AMD codecs. The specification is available at: http://www.x.org/docs/AMD/AMD_HDA_verbs_v2.pdf v2: moved code to hda_eld.c and cleaned it up v3: adapted to hdmi_ops infrastructure Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi> Tested-by: Peter Frühberger <fritsch@xbmc.org> # v2 Tested-by: Olivier Langlois <olivier@trillion01.com> # v2 Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_eld.c')
-rw-r--r--sound/pci/hda/hda_eld.c151
1 files changed, 151 insertions, 0 deletions
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index f62356c2f54c..32d3e3855a6e 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -2,6 +2,7 @@
2 * Generic routines and proc interface for ELD(EDID Like Data) information 2 * Generic routines and proc interface for ELD(EDID Like Data) information
3 * 3 *
4 * Copyright(c) 2008 Intel Corporation. 4 * Copyright(c) 2008 Intel Corporation.
5 * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
5 * 6 *
6 * Authors: 7 * Authors:
7 * Wu Fengguang <wfg@linux.intel.com> 8 * Wu Fengguang <wfg@linux.intel.com>
@@ -633,3 +634,153 @@ void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
633 hinfo->maxbps = min(hinfo->maxbps, maxbps); 634 hinfo->maxbps = min(hinfo->maxbps, maxbps);
634 hinfo->channels_max = min(hinfo->channels_max, channels_max); 635 hinfo->channels_max = min(hinfo->channels_max, channels_max);
635} 636}
637
638
639/* ATI/AMD specific stuff (ELD emulation) */
640
641#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776
642#define ATI_VERB_SET_SINK_INFO_INDEX 0x780
643#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70
644#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76
645#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b
646#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80
647#define ATI_VERB_GET_SINK_INFO_DATA 0xf81
648
649#define ATI_SPKALLOC_SPKALLOC 0x007f
650#define ATI_SPKALLOC_TYPE_HDMI 0x0100
651#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200
652
653/* first three bytes are just standard SAD */
654#define ATI_AUDIODESC_CHANNELS 0x00000007
655#define ATI_AUDIODESC_RATES 0x0000ff00
656#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000
657
658/* in standard HDMI VSDB format */
659#define ATI_DELAY_VIDEO_LATENCY 0x000000ff
660#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00
661
662enum ati_sink_info_idx {
663 ATI_INFO_IDX_MANUFACTURER_ID = 0,
664 ATI_INFO_IDX_PRODUCT_ID = 1,
665 ATI_INFO_IDX_SINK_DESC_LEN = 2,
666 ATI_INFO_IDX_PORT_ID_LOW = 3,
667 ATI_INFO_IDX_PORT_ID_HIGH = 4,
668 ATI_INFO_IDX_SINK_DESC_FIRST = 5,
669 ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */
670};
671
672int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
673 unsigned char *buf, int *eld_size, bool rev3_or_later)
674{
675 int spkalloc, ati_sad, aud_synch;
676 int sink_desc_len = 0;
677 int pos, i;
678
679 /* ATI/AMD does not have ELD, emulate it */
680
681 spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0);
682
683 if (!spkalloc) {
684 snd_printd(KERN_INFO "HDMI ATI/AMD: no speaker allocation for ELD\n");
685 return -EINVAL;
686 }
687
688 memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3);
689
690 /* version */
691 buf[0] = ELD_VER_CEA_861D << 3;
692
693 /* speaker allocation from EDID */
694 buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC;
695
696 /* is DisplayPort? */
697 if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT)
698 buf[5] |= 0x04;
699
700 pos = ELD_FIXED_BYTES;
701
702 if (rev3_or_later) {
703 int sink_info;
704
705 snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW);
706 sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
707 put_unaligned_le32(sink_info, buf + 8);
708
709 snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH);
710 sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
711 put_unaligned_le32(sink_info, buf + 12);
712
713 snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID);
714 sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
715 put_unaligned_le16(sink_info, buf + 16);
716
717 snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID);
718 sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
719 put_unaligned_le16(sink_info, buf + 18);
720
721 snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN);
722 sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
723
724 if (sink_desc_len > ELD_MAX_MNL) {
725 snd_printd(KERN_INFO "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n",
726 sink_desc_len);
727 sink_desc_len = ELD_MAX_MNL;
728 }
729
730 buf[4] |= sink_desc_len;
731
732 for (i = 0; i < sink_desc_len; i++) {
733 snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i);
734 buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
735 }
736 }
737
738 for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) {
739 if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST)
740 continue; /* not handled by ATI/AMD */
741
742 snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3);
743 ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0);
744
745 if (ati_sad & ATI_AUDIODESC_RATES) {
746 /* format is supported, copy SAD as-is */
747 buf[pos++] = (ati_sad & 0x0000ff) >> 0;
748 buf[pos++] = (ati_sad & 0x00ff00) >> 8;
749 buf[pos++] = (ati_sad & 0xff0000) >> 16;
750 }
751
752 if (i == AUDIO_CODING_TYPE_LPCM
753 && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES)
754 && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) {
755 /* for PCM there is a separate stereo rate mask */
756 buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1;
757 /* rates from the extra byte */
758 buf[pos++] = (ati_sad & 0xff000000) >> 24;
759 buf[pos++] = (ati_sad & 0x00ff0000) >> 16;
760 }
761 }
762
763 if (pos == ELD_FIXED_BYTES + sink_desc_len) {
764 snd_printd(KERN_INFO "HDMI ATI/AMD: no audio descriptors for ELD\n");
765 return -EINVAL;
766 }
767
768 aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0);
769 if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) {
770 int video_latency = (aud_synch & ATI_DELAY_VIDEO_LATENCY) - 1;
771 int audio_latency = ((aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8) - 1;
772
773 if (video_latency > audio_latency)
774 buf[6] = min(video_latency - audio_latency, 0xfa);
775 }
776
777 /* Baseline length */
778 buf[2] = pos - 4;
779
780 /* SAD count */
781 buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4;
782
783 *eld_size = pos;
784
785 return 0;
786}