/* * Copyright © 2012 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: * Paulo Zanoni * */ #include #include #include #include #include #include "intel_io.h" #include "intel_chipset.h" #include "drmtest.h" typedef enum { TRANSC_A = 0, TRANSC_B = 1, TRANSC_C = 2, TRANSC_INVALID } Transcoder; typedef enum { REG_HDMIB_GEN4 = 0x61140, REG_HDMIC_GEN4 = 0x61160, REG_HDMIB_VLV = 0x1e1140, REG_HDMIC_VLV = 0x1e1160, REG_HDMIB_PCH = 0xe1140, REG_HDMIC_PCH = 0xe1150, REG_HDMID_PCH = 0xe1160, REG_DIP_CTL_GEN4 = 0x61170, REG_DIP_CTL_A_VLV = 0x1e0200, REG_DIP_CTL_B_VLV = 0x1e1170, REG_DIP_CTL_A = 0xe0200, REG_DIP_CTL_B = 0xe1200, REG_DIP_CTL_C = 0xe2200, REG_DIP_DATA_GEN4 = 0x61178, REG_DIP_DATA_A_VLV = 0x1e0208, REG_DIP_DATA_B_VLV = 0x1e1174, REG_DIP_DATA_A = 0xe0208, REG_DIP_DATA_B = 0xe1208, REG_DIP_DATA_C = 0xe2208, } Register; typedef enum { DIP_AVI = 0, DIP_VENDOR = 1, DIP_GAMUT = 2, DIP_SPD = 3, DIP_INVALID, } DipType; typedef enum { DIP_FREQ_ONCE = 0, DIP_FREQ_EVERY_VSYNC = 1, DIP_FREQ_EVERY_OTHER_VSYNC = 2, DIP_FREQ_RESERVED = 3, } DipFrequency; typedef enum { SOURCE_DEVICE_UNKNOWN = 0x00, SOURCE_DEVICE_DIGITAL_STB = 0x01, SOURCE_DEVICE_DVD_PLAYER = 0x02, SOURCE_DEVICE_D_VHS = 0x03, SOURCE_DEVICE_HDD_VIDEORECORDER = 0x04, SOURCE_DEVICE_DVC = 0x05, SOURCE_DEVICE_DSC = 0x06, SOURCE_DEVICE_VIDEO_CD = 0x07, SOURCE_DEVICE_GAME = 0x08, SOURCE_DEVICE_PC_GENERAL = 0x09, SOURCE_DEVICE_BLU_RAY_DISK = 0x0a, SOURCE_DEVICE_SUPER_AUDIO_CD = 0x0b, SOURCE_DEVICE_RESERVED = 0x0c } SourceDevice; #define HDMI_PORT_ENABLE (1 << 31) #define HDMI_PORT_TRANSCODER_GEN4 (1 << 30) #define HDMI_PORT_TRANSCODER_IBX (1 << 30) #define HDMI_PORT_TRANSCODER_CPT (3 << 29) #define HDMI_PORT_ENCODING (3 << 10) #define HDMI_PORT_MODE (1 << 9) #define HDMI_PORT_AUDIO (1 << 6) #define HDMI_PORT_DETECTED (1 << 2) #define DIP_CTL_ENABLE (1 << 31) #define DIP_CTL_GCP_ENABLE (1 << 25) #define DIP_CTL_SPD_ENABLE (1 << 24) #define DIP_CTL_GAMUT_ENABLE (1 << 23) #define DIP_CTL_VENDOR_ENABLE (1 << 22) #define DIP_CTL_AVI_ENABLE (1 << 21) #define DIP_CTL_BUFFER_INDEX (3 << 19) #define DIP_CTL_BUFFER_AVI (0 << 19) #define DIP_CTL_BUFFER_VENDOR (1 << 19) #define DIP_CTL_BUFFER_GAMUT (2 << 19) #define DIP_CTL_BUFFER_SPD (3 << 19) #define DIP_CTL_FREQUENCY (3 << 16) #define DIP_CTL_FREQ_ONCE (0 << 16) #define DIP_CTL_FREQ_EVERY (1 << 16) #define DIP_CTL_FREQ_EVERY_OTHER (2 << 16) #define DIP_CTL_BUFFER_SIZE (15 << 8) #define DIP_CTL_ACCESS_ADDR (15 << 0) #define DIP_CTL_PORT_SEL_MASK_GEN4 (3 << 29) #define DIP_CTL_PORT_SEL_B_GEN4 (1 << 29) #define DIP_CTL_PORT_SEL_C_GEN4 (2 << 29) #define DIP_CTL_BUFFER_TRANS_ACTIVE_GEN4 (1 << 28) #define AVI_INFOFRAME_TYPE 0x82 #define AVI_INFOFRAME_VERSION 0x02 #define AVI_INFOFRAME_LENGTH 0x0d #define SPD_INFOFRAME_TYPE 0x83 #define SPD_INFOFRAME_VERSION 0x01 #define SPD_INFOFRAME_LENGTH 0x19 #define VENDOR_ID_HDMI 0x000c03 typedef struct { uint8_t type; uint8_t version; uint8_t length; uint8_t ecc; } DipInfoFrameHeader; typedef union { struct { DipInfoFrameHeader header; uint8_t checksum; uint8_t S :2; uint8_t B :2; uint8_t A :1; uint8_t Y :2; uint8_t Rsvd0 :1; uint8_t R :4; uint8_t M :2; uint8_t C :2; uint8_t SC :2; uint8_t Q :2; uint8_t EC :3; uint8_t ITC :1; uint8_t VIC :7; uint8_t Rsvd1 :1; uint8_t PR :4; uint8_t Rsvd2 :4; uint16_t top; uint16_t bottom; uint16_t left; uint16_t right; uint16_t Rsvd3; uint32_t Rsvd4[3]; } avi; struct { DipInfoFrameHeader header; uint8_t checksum; uint8_t vendor[8]; uint8_t description[16]; uint8_t source; } __attribute__((packed)) spd; struct { DipInfoFrameHeader header; uint8_t checksum; uint8_t id[3]; uint8_t Rsvd0 :5; uint8_t video_format :3; union { uint8_t vic; struct { uint8_t Rsvd1 :4; uint8_t s3d_structure :4; } s3d; } pb5; uint8_t Rsvd2 :4; uint8_t s3d_ext_data :4; } __attribute__((packed)) vendor; struct { DipInfoFrameHeader header; uint8_t body[27]; } generic; uint8_t data8[128]; uint32_t data32[16]; } DipInfoFrame; Register vlv_hdmi_ports[] = { REG_HDMIB_VLV, REG_HDMIC_VLV, }; Register vlv_dip_ctl_regs[] = { REG_DIP_CTL_A_VLV, REG_DIP_CTL_B_VLV, }; Register vlv_dip_data_regs[] = { REG_DIP_DATA_A_VLV, REG_DIP_DATA_B_VLV, }; Register gen4_hdmi_ports[] = { REG_HDMIB_GEN4, REG_HDMIC_GEN4, }; Register pch_hdmi_ports[] = { REG_HDMIB_PCH, REG_HDMIC_PCH, REG_HDMID_PCH }; Register pch_dip_ctl_regs[] = { REG_DIP_CTL_A, REG_DIP_CTL_B, REG_DIP_CTL_C }; Register pch_dip_data_regs[] = { REG_DIP_DATA_A, REG_DIP_DATA_B, REG_DIP_DATA_C }; const char *hdmi_port_names[] = { "HDMIB", "HDMIC", "HDMID" }; const char *transcoder_names[] = { "A", "B", "C" }; const char *dip_frequency_names[] = { "once", "every vsync", "every other vsync", "reserved (invalid)" }; struct pci_device *pci_dev; int gen = 0; static const char *spd_source_to_string(SourceDevice source) { switch (source) { case SOURCE_DEVICE_UNKNOWN: return "unknown"; case SOURCE_DEVICE_DIGITAL_STB: return "digital stb"; case SOURCE_DEVICE_DVD_PLAYER: return "dvd player"; case SOURCE_DEVICE_D_VHS: return "d vhs"; case SOURCE_DEVICE_HDD_VIDEORECORDER: return "hdd videorecorder"; case SOURCE_DEVICE_DVC: return "dvc"; case SOURCE_DEVICE_DSC: return "dsc"; case SOURCE_DEVICE_VIDEO_CD: return "video cd"; case SOURCE_DEVICE_GAME: return "game"; case SOURCE_DEVICE_PC_GENERAL: return "pc general"; case SOURCE_DEVICE_BLU_RAY_DISK: return "blu-ray disk"; case SOURCE_DEVICE_SUPER_AUDIO_CD: return "super audio cd"; default: return "reserved"; } } static Register get_dip_ctl_reg(Transcoder transcoder) { if (IS_VALLEYVIEW(pci_dev->device_id)) return vlv_dip_ctl_regs[transcoder]; else if (gen == 4) return REG_DIP_CTL_GEN4; else return pch_dip_ctl_regs[transcoder]; } static Register get_dip_data_reg(Transcoder transcoder) { if (IS_VALLEYVIEW(pci_dev->device_id)) return vlv_dip_data_regs[transcoder]; else if (gen == 4) return REG_DIP_DATA_GEN4; else return pch_dip_data_regs[transcoder]; } static Register get_hdmi_port(int hdmi_port_index) { if (IS_VALLEYVIEW(pci_dev->device_id)) return vlv_hdmi_ports[hdmi_port_index]; else if (gen == 4) { assert(hdmi_port_index < 2); return gen4_hdmi_ports[hdmi_port_index]; } else { return pch_hdmi_ports[hdmi_port_index]; } } static void load_infoframe(Transcoder transcoder, DipInfoFrame *frame, DipType type) { Register ctl_reg = get_dip_ctl_reg(transcoder); Register data_reg = get_dip_data_reg(transcoder); uint32_t ctl_val; uint32_t i; ctl_val = INREG(ctl_reg); ctl_val &= ~DIP_CTL_BUFFER_INDEX; ctl_val |= type << 19; OUTREG(ctl_reg, ctl_val); ctl_val = INREG(ctl_reg); ctl_val &= ~DIP_CTL_ACCESS_ADDR; OUTREG(ctl_reg, ctl_val); for (i = 0; i < 16; i++) { ctl_val = INREG(ctl_reg); assert((ctl_val & DIP_CTL_ACCESS_ADDR) == i); frame->data32[i] = INREG(data_reg); } } static int infoframe_valid_checksum(DipInfoFrame *frame) { int i; int length = frame->generic.header.length; uint8_t csum; csum = frame->generic.header.type + frame->generic.header.version + frame->generic.header.length; /* no ecc */ for (i = 0; i < length + 1; i++) csum += frame->generic.body[i]; return (csum == 0); } static void infoframe_fix_checksum(DipInfoFrame *frame) { int i; int length = frame->generic.header.length; uint8_t csum; csum = frame->generic.header.type + frame->generic.header.version + frame->generic.header.length; /* no ecc */ /* Length does not include the header field nor the checksum */ for (i = 1; i < length + 1; i++) csum += frame->generic.body[i]; frame->generic.body[0] = 0x100 - csum; } static void dump_port_info(int hdmi_port_index) { Register port = get_hdmi_port(hdmi_port_index); uint32_t val = INREG(port); Transcoder transcoder; printf("\nPort %s:\n", hdmi_port_names[hdmi_port_index]); printf("- %sdetected\n", val & HDMI_PORT_DETECTED ? "" : "not "); printf("- %s\n", val & HDMI_PORT_ENABLE ? "enabled" : "disabled"); if (!(val & HDMI_PORT_ENABLE)) return; if (gen == 4 || IS_VALLEYVIEW(pci_dev->device_id)) transcoder = (val & HDMI_PORT_TRANSCODER_GEN4) >> 30; else if (intel_pch >= PCH_CPT) transcoder = (val & HDMI_PORT_TRANSCODER_CPT) >> 29; else transcoder = (val & HDMI_PORT_TRANSCODER_IBX) >> 30; printf("- transcoder: %s\n", transcoder_names[transcoder]); switch ((val & HDMI_PORT_ENCODING) >> 10) { case 0: printf("- mode: SDVO\n"); break; case 2: printf("- mode: TMDS\n"); break; default: printf("- mode: INVALID!\n"); } printf("- mode: %s\n", val & HDMI_PORT_MODE ? "HDMI" : "DVI"); printf("- audio: %s\n", val & HDMI_PORT_AUDIO ? "enabled" : "disabled"); } static void dump_raw_infoframe(DipInfoFrame *frame) { unsigned int i; printf("- raw:"); for (i = 0; i < 16; i++) { if (i % 4 == 0) printf("\n "); printf(" %08x", frame->data32[i]); } printf("\n"); } static void dump_avi_info(Transcoder transcoder) { Register reg = get_dip_ctl_reg(transcoder); uint32_t val; DipFrequency freq; DipInfoFrame frame; load_infoframe(transcoder, &frame, DIP_AVI); val = INREG(reg); printf("AVI InfoFrame:\n"); if (gen == 4) { printf("- %sbeing transmitted\n", val & DIP_CTL_BUFFER_TRANS_ACTIVE_GEN4 ? "" : "not "); } freq = (val & DIP_CTL_FREQUENCY) >> 16; printf("- frequency: %s\n", dip_frequency_names[freq]); dump_raw_infoframe(&frame); printf("- type: %x, version: %x, length: %x, ecc: %x, checksum: %x\n", frame.avi.header.type, frame.avi.header.version, frame.avi.header.length, frame.avi.header.ecc, frame.avi.checksum); printf("- S: %x, B: %x, A: %x, Y: %x, Rsvd0: %x\n", frame.avi.S, frame.avi.B, frame.avi.A, frame.avi.Y, frame.avi.Rsvd0); printf("- R: %x, M: %x, C: %x\n", frame.avi.R, frame.avi.M, frame.avi.C); printf("- SC: %x, Q: %x, EC: %x, ITC: %x\n", frame.avi.SC, frame.avi.Q, frame.avi.EC, frame.avi.ITC); printf("- VIC: %d, Rsvd1: %x\n", frame.avi.VIC, frame.avi.Rsvd1); printf("- PR: %x, Rsvd2: %x\n", frame.avi.PR, frame.avi.Rsvd2); printf("- top: %x, bottom: %x, left: %x, right: %x\n", frame.avi.top, frame.avi.bottom, frame.avi.left, frame.avi.right); printf("- Rsvd3: %x, Rsvd4[0]: %x, Rsvd4[1]: %x, Rsvd4[2]: %x\n", frame.avi.Rsvd3, frame.avi.Rsvd4[0], frame.avi.Rsvd4[1], frame.avi.Rsvd4[2]); if (!infoframe_valid_checksum(&frame)) printf("Invalid InfoFrame checksum!\n"); } static const char *vendor_id_to_string(uint32_t id) { switch (id) { case VENDOR_ID_HDMI: return "HDMI"; default: return "Unknown"; } } static const char *s3d_structure_to_string(int format) { switch (format) { case 0: return "Frame Packing"; case 6: return "Top Bottom"; case 8: return "Side By Side (half)"; default: return "Reserved"; } } static void dump_vendor_hdmi(DipInfoFrame *frame) { int vic_present = frame->vendor.video_format & 0x1; int s3d_present = frame->vendor.video_format & 0x2; printf("- video format: 0x%03x %s\n", frame->vendor.video_format, s3d_present ? "(3D)" : ""); if (vic_present && s3d_present) { printf("Error: HDMI VIC and S3D bits set. Only one of those " " at a time is valid\n"); return; } if (vic_present) printf("- HDMI VIC: %d\n", frame->vendor.pb5.vic); else if (s3d_present) { int s3d_structure = frame->vendor.pb5.s3d.s3d_structure; printf("- 3D Format: %s\n", s3d_structure_to_string(s3d_structure)); /* Side-by-side (half) */ if (s3d_structure >= 8) printf("- 3D Ext Data 0x%x\n", frame->vendor.s3d_ext_data); } } static void dump_vendor_info(Transcoder transcoder) { Register reg = get_dip_ctl_reg(transcoder); uint32_t val, vendor_id; DipFrequency freq; DipInfoFrame frame; load_infoframe(transcoder, &frame, DIP_VENDOR); val = INREG(reg); printf("Vendor InfoFrame:\n"); if (gen == 4) { printf("- %sbeing transmitted\n", val & DIP_CTL_BUFFER_TRANS_ACTIVE_GEN4 ? "" : "not "); } freq = (val & DIP_CTL_FREQUENCY) >> 16; printf("- frequency: %s\n", dip_frequency_names[freq]); dump_raw_infoframe(&frame); vendor_id = frame.vendor.id[2] << 16 | frame.vendor.id[1] << 8 | frame.vendor.id[0]; printf("- vendor Id: 0x%06x (%s)\n", vendor_id, vendor_id_to_string(vendor_id)); if (vendor_id == VENDOR_ID_HDMI) dump_vendor_hdmi(&frame); if (!infoframe_valid_checksum(&frame)) printf("Invalid InfoFrame checksum!\n"); } static void dump_gamut_info(Transcoder transcoder) { Register reg = get_dip_ctl_reg(transcoder); uint32_t val; DipFrequency freq; DipInfoFrame frame; load_infoframe(transcoder, &frame, DIP_GAMUT); val = INREG(reg); printf("Gamut InfoFrame:\n"); if (gen == 4) { printf("- %sbeing transmitted\n", val & DIP_CTL_BUFFER_TRANS_ACTIVE_GEN4 ? "" : "not "); } freq = (val & DIP_CTL_FREQUENCY) >> 16; printf("- frequency: %s\n", dip_frequency_names[freq]); dump_raw_infoframe(&frame); if (!infoframe_valid_checksum(&frame)) printf("Invalid InfoFrame checksum!\n"); } static void dump_spd_info(Transcoder transcoder) { Register reg = get_dip_ctl_reg(transcoder); uint32_t val; DipFrequency freq; DipInfoFrame frame; char vendor[9]; char description[17]; load_infoframe(transcoder, &frame, DIP_SPD); val = INREG(reg); printf("SPD InfoFrame:\n"); if (gen == 4) { printf("- %sbeing transmitted\n", val & DIP_CTL_BUFFER_TRANS_ACTIVE_GEN4 ? "" : "not "); } freq = (val & DIP_CTL_FREQUENCY) >> 16; printf("- frequency: %s\n", dip_frequency_names[freq]); dump_raw_infoframe(&frame); printf("- type: %x, version: %x, length: %x, ecc: %x, checksum: %x\n", frame.spd.header.type, frame.spd.header.version, frame.spd.header.length, frame.spd.header.ecc, frame.spd.checksum); memcpy(vendor, frame.spd.vendor, 8); vendor[8] = '\0'; memcpy(description, frame.spd.description, 16); description[16] = '\0'; printf("- vendor: %s\n", vendor); printf("- description: %s\n", description); printf("- source: %s\n", spd_source_to_string(frame.spd.source)); if (!infoframe_valid_checksum(&frame)) printf("Invalid InfoFrame checksum!\n"); } static void dump_transcoder_info(Transcoder transcoder) { Register reg = get_dip_ctl_reg(transcoder); uint32_t val = INREG(reg); if (gen == 4) { printf("\nDIP information:\n"); switch (val & DIP_CTL_PORT_SEL_MASK_GEN4) { case DIP_CTL_PORT_SEL_B_GEN4: printf("- port B\n"); break; case DIP_CTL_PORT_SEL_C_GEN4: printf("- port C\n"); break; default: printf("- INVALID port!\n"); } } else { printf("\nTranscoder %s:\n", transcoder_names[transcoder]); } printf("- %s\n", val & DIP_CTL_ENABLE ? "enabled" : "disabled"); if (!(val & DIP_CTL_ENABLE)) return; printf("- GCP: %s\n", val & DIP_CTL_GCP_ENABLE ? "enabled" : "disabled"); if (val & DIP_CTL_AVI_ENABLE) dump_avi_info(transcoder); if (val & DIP_CTL_VENDOR_ENABLE) dump_vendor_info(transcoder); if (val & DIP_CTL_GAMUT_ENABLE) dump_gamut_info(transcoder); if (val & DIP_CTL_SPD_ENABLE) dump_spd_info(transcoder); } static void dump_all_info(void) { unsigned int i; if (IS_VALLEYVIEW(pci_dev->device_id)) { for (i = 0; i < ARRAY_SIZE(vlv_hdmi_ports); i++) dump_port_info(i); for (i = 0; i < ARRAY_SIZE(vlv_dip_ctl_regs); i++) dump_transcoder_info(i); } else if (gen == 4) { for (i = 0; i < ARRAY_SIZE(gen4_hdmi_ports); i++) dump_port_info(i); dump_transcoder_info(0); } else { for (i = 0; i < ARRAY_SIZE(pch_hdmi_ports); i++) dump_port_info(i); for (i = 0; i < ARRAY_SIZE(pch_dip_ctl_regs); i++) dump_transcoder_info(i); } } static void write_infoframe(Transcoder transcoder, DipType type, DipInfoFrame *frame) { Register ctl_reg = get_dip_ctl_reg(transcoder); Register data_reg = get_dip_data_reg(transcoder); uint32_t ctl_val; unsigned int i; ctl_val = INREG(ctl_reg); ctl_val &= ~DIP_CTL_BUFFER_INDEX; ctl_val |= (type << 19); ctl_val &= ~DIP_CTL_ACCESS_ADDR; OUTREG(ctl_reg, ctl_val); for (i = 0; i < 8; i++) { ctl_val = INREG(ctl_reg); assert((ctl_val & DIP_CTL_ACCESS_ADDR) == i); OUTREG(data_reg, frame->data32[i]); } } static void disable_infoframe(Transcoder transcoder, DipType type) { Register reg = get_dip_ctl_reg(transcoder); uint32_t val = INREG(reg); if (gen != 4 && type == DIP_AVI) val &= ~DIP_CTL_ENABLE; val &= ~(1 << (21 + type)); OUTREG(reg, val); } static void enable_infoframe(Transcoder transcoder, DipType type) { Register reg = get_dip_ctl_reg(transcoder); uint32_t val = INREG(reg); if (gen != 4 && type == DIP_AVI) val |= DIP_CTL_ENABLE; val |= (1 << (21 + type)); OUTREG(reg, val); } static int parse_infoframe_option_u(const char *name, const char *s, uint32_t min, uint32_t max, uint32_t *value, char **commands) { int read, rc; if (!strcmp(name, s)) { rc = sscanf(*commands, "%x%n", value, &read); *commands = &(*commands)[read]; if (rc != 1) { printf("Invalid value.\n"); return 0; } if (*value < min || *value > max) { printf("Value outside allowed range.\n"); return 0; } return 1; } return 0; } static int parse_infoframe_option_s(const char *name, const char *s, int min_size, int max_size, char *value, char **commands) { int size, read, rc; if (!strcmp(name, s)) { rc = sscanf(*commands, "%31s%n", value, &read); *commands = &(*commands)[read]; if (rc != 1) { printf("Invalid value.\n"); return 0; } size = strlen(value); if (size < min_size || size > max_size) { printf("String either too big or too small.\n"); return 0; } return 1; } return 0; } static void change_avi_infoframe(Transcoder transcoder, char *commands) { Register reg = get_dip_ctl_reg(transcoder); uint32_t val; DipInfoFrame frame; char option[32]; uint32_t option_val; int rc, read; char *current = commands; load_infoframe(transcoder, &frame, DIP_AVI); val = INREG(reg); while (1) { rc = sscanf(current, "%31s%n", option, &read); current = ¤t[read]; if (rc == EOF) { break; } else if (rc != 1) { printf("Invalid option: %s\n", option); continue; } if (parse_infoframe_option_u("S", option, 0, 2, &option_val, ¤t)) frame.avi.S = option_val; else if (parse_infoframe_option_u("B", option, 0, 3, &option_val, ¤t)) frame.avi.B = option_val; else if (parse_infoframe_option_u("A", option, 0, 1, &option_val, ¤t)) frame.avi.A = option_val; else if (parse_infoframe_option_u("Y", option, 0, 2, &option_val, ¤t)) frame.avi.Y = option_val; else if (parse_infoframe_option_u("R", option, 0, 15, &option_val, ¤t)) frame.avi.R = option_val; else if (parse_infoframe_option_u("M", option, 0, 2, &option_val, ¤t)) frame.avi.M = option_val; else if (parse_infoframe_option_u("C", option, 0, 3, &option_val, ¤t)) frame.avi.C = option_val; else if (parse_infoframe_option_u("SC", option, 0, 3, &option_val, ¤t)) frame.avi.SC = option_val; else if (parse_infoframe_option_u("Q", option, 0, 2, &option_val, ¤t)) frame.avi.Q = option_val; else if (parse_infoframe_option_u("EC", option, 0, 1, &option_val,¤t)) frame.avi.EC = option_val; else if (parse_infoframe_option_u("ITC", option, 0, 1, &option_val, ¤t)) frame.avi.ITC = option_val; else if (parse_infoframe_option_u("VIC", option, 0, 127, &option_val, ¤t)) frame.avi.VIC = option_val; else if (parse_infoframe_option_u("PR", option, 0, 15, &option_val, ¤t)) frame.avi.PR = option_val; else if (parse_infoframe_option_u("top", option, 0, 65535, &option_val, ¤t)) frame.avi.top = option_val; else if (parse_infoframe_option_u("bottom", option, 0, 65535, &option_val, ¤t)) frame.avi.bottom = option_val; else if (parse_infoframe_option_u("left", option, 0, 65535, &option_val, ¤t)) frame.avi.left = option_val; else if (parse_infoframe_option_u("right", option, 0, 65535, &option_val, ¤t)) frame.avi.right = option_val; else printf("Unrecognized option: %s\n", option); } val &= ~DIP_CTL_FREQUENCY; val |= DIP_CTL_FREQ_EVERY; OUTREG(reg, val); frame.avi.header.type = AVI_INFOFRAME_TYPE; frame.avi.header.version = AVI_INFOFRAME_VERSION; frame.avi.header.length = AVI_INFOFRAME_LENGTH; frame.avi.Rsvd0 = 0; frame.avi.Rsvd1 = 0; frame.avi.Rsvd2 = 0; frame.avi.Rsvd3 = 0; frame.avi.Rsvd4[0] = 0; frame.avi.Rsvd4[1] = 0; frame.avi.Rsvd4[2] = 0; infoframe_fix_checksum(&frame); disable_infoframe(transcoder, DIP_AVI); write_infoframe(transcoder, DIP_AVI, &frame); enable_infoframe(transcoder, DIP_AVI); } static void change_spd_infoframe(Transcoder transcoder, char *commands) { Register reg = get_dip_ctl_reg(transcoder); uint32_t val; DipInfoFrame frame; char option[16]; char option_val_s[32]; uint32_t option_val_i; int rc, read; char *current = commands; load_infoframe(transcoder, &frame, DIP_SPD); val = INREG(reg); while (1) { rc = sscanf(current, "%15s%n", option, &read); current = ¤t[read]; if (rc == EOF) { break; } else if (rc != 1) { printf("Invalid option: %s\n", option); continue; } memset(option_val_s, 0, 32); if (parse_infoframe_option_s("vendor", option, 0, 8, option_val_s, ¤t)) memcpy(frame.spd.vendor, option_val_s, 8); else if (parse_infoframe_option_s("description", option, 0, 16, option_val_s, ¤t)) memcpy(frame.spd.description, option_val_s, 16); else if (parse_infoframe_option_u("source", option, 0, 0x0c, &option_val_i, ¤t)) frame.spd.source = option_val_i; else printf("Unrecognized option: %s\n", option); } val &= ~DIP_CTL_FREQUENCY; val |= DIP_CTL_FREQ_EVERY_OTHER; OUTREG(reg, val); frame.spd.header.type = SPD_INFOFRAME_TYPE; frame.spd.header.version = SPD_INFOFRAME_VERSION; frame.spd.header.length = SPD_INFOFRAME_LENGTH; infoframe_fix_checksum(&frame); disable_infoframe(transcoder, DIP_SPD); write_infoframe(transcoder, DIP_SPD, &frame); enable_infoframe(transcoder, DIP_SPD); } static void change_infoframe_checksum(Transcoder transcoder, DipType type, uint32_t selected_csum) { DipInfoFrame frame; load_infoframe(transcoder, &frame, type); frame.generic.body[0] = selected_csum; disable_infoframe(transcoder, type); write_infoframe(transcoder, type, &frame); enable_infoframe(transcoder, type); } static void change_infoframe_frequency(Transcoder transcoder, DipType type, DipFrequency frequency) { Register reg = get_dip_ctl_reg(transcoder); uint32_t val = INREG(reg); if (type == DIP_AVI && frequency != DIP_FREQ_EVERY_VSYNC) { printf("Error: AVI infoframe must be sent every VSync!\n"); frequency = DIP_FREQ_EVERY_VSYNC; } val &= ~DIP_CTL_FREQUENCY; val |= (frequency << 16); OUTREG(reg, val); } static void disable_dip(Transcoder transcoder) { Register reg = get_dip_ctl_reg(transcoder); uint32_t val = INREG(reg); val &= ~DIP_CTL_ENABLE; OUTREG(reg, val); } static void enable_dip(Transcoder transcoder) { Register reg = get_dip_ctl_reg(transcoder); uint32_t val = INREG(reg); val |= DIP_CTL_ENABLE; OUTREG(reg, val); } static void disable_hdmi_port(Register reg) { uint32_t val = INREG(reg); val &= ~HDMI_PORT_ENABLE; OUTREG(reg, val); } static void enable_hdmi_port(Register reg) { uint32_t val = INREG(reg); val |= HDMI_PORT_ENABLE; OUTREG(reg, val); } static void print_usage(void) { printf("Options:\n" " -d, --dump\n" " dump information about all transcoders\n" " -c, --change-fields [fields]\n" " change infoframe fields from selected transcoder\n" " -k, --change-checksum [checksum]\n" " change infoframe checksum (value in hex)\n" " -q, --change-frequency [frequency]\n" " change infoframe frequency (once, everyvsync or everyothervsync)\n" " -n, --disable\n" " disable the selected infoframe from the selected transcoder\n" " -N, --enable\n" " enable the selected infoframe from the selected transcoder\n" " -x, --disable-infoframes\n" " disable all infoframes from selected transcoder\n" " -X, --enable-infoframes\n" " enable sending infoframes on the selected transcoder\n" " -p, --disable-hdmi-port [port]\n" " disable hdmi port on the selected transcoder (B, C or D)\n" " -P, --enable-hdmi-port [port]\n" " enable hdmi port on the selected transcoder (B, C or D)\n" " -t, --transcoder\n" " select transcoder (A, B or C)\n" " -f, --infoframe\n" " select infoframe (AVI, Vendor, Gamut or SPD)\n" " -h, --help\n" " prints this message\n" "\n" "Examples:\n" "\n" " Dump information:\n" " intel_infoframes\n" "\n" " Disable overscan and set ITC on transcoder B:\n" " intel_infoframes -t B -f AVI -c 'S 2 ITC 1'\n" "\n" " Many actions on the same command:\n" " - enable overscan on transcoder A\n" " - enable overscan and change description on transcoder B\n" " - disable all infoframes on transcoder C\n" " - dump the resulting state:\n" " intel_infoframes -t A -f AVI -c 'S 1' \\\n" " -t B -f AVI -c 'S 2' \\\n" " -f SPD -c 'description Linux' \\\n" " -t C --disable-infoframes \\\n" " -d\n" "\n" " Even more:\n" " - print the help message\n" " - completely disable all infoframes on all transcoders\n" " - dump the state" " - enable sending infoframes on transcoder B, but disable all infoframes\n" " - enable AVI infoframes transcoder B, use underscan and declare ITC\n" " - also enable SPD infoframes on the same transcoder, change frequency to\n" " every vsync and change vendor, description and source\n" " - dump the state again\n" " intel_infoframes -h \\\n" " -t A -x -t B -x -t C -x \\\n" " -d \\\n" " -t A -X -f AVI -n -f Vendor -n \\\n" " -f Gamut -n -f SPD -n \\\n" " -f AVI -N -c 'S 2 ITC 1'\\\n" " -f SPD -q everyvsync \\\n" " -c 'vendor me description mine source 0x09' \\\n" " -d\n" "\n" "Infoframe fields used by the --change-fields option:\n" " - AVI infoframe fields:\n" " S B A Y R M C SC Q EC ITC VIC PR top bottom left right\n" " - SPD infoframe fields:\n" " vendor description source\n" " - Other infoframe fields are not implemented yet.\n"); } #define CHECK_TRANSCODER(transcoder) \ if (transcoder == TRANSC_INVALID) { \ printf("Transcoder not selected.\n"); \ ret = 1; \ goto out; \ } #define CHECK_DIP(dip) \ if (dip == DIP_INVALID) { \ printf("Infoframe not selected.\n"); \ ret = 1; \ goto out; \ } int main(int argc, char *argv[]) { int opt; int ret = 0; Transcoder transcoder = TRANSC_INVALID; DipType dip = DIP_INVALID; Register hdmi_port; char short_opts[] = "dc:k:q:nNxXp:P:t:f:h"; struct option long_opts[] = { { "dump", no_argument, NULL, 'd' }, { "change-fields", required_argument, NULL, 'c' }, { "change-checksum", required_argument, NULL, 'k' }, { "change-frequency", required_argument, NULL, 'q' }, { "disable", no_argument, NULL, 'n' }, { "enable", no_argument, NULL, 'N' }, { "disable-infoframes", no_argument, NULL, 'x' }, { "enable-infoframes", no_argument, NULL, 'X' }, { "disable-hdmi-port", required_argument, NULL, 'p' }, { "enable-hdmi-port", required_argument, NULL, 'P' }, { "transcoder" , required_argument, NULL, 't' }, { "infoframe", required_argument, NULL, 'f' }, { "help", no_argument, NULL, 'h' }, { 0 } }; printf("WARNING: This is just a debugging tool! Don't expect it to work" " perfectly: the Kernel might undo our changes.\n"); pci_dev = intel_get_pci_device(); intel_register_access_init(pci_dev, 0, -1); intel_check_pch(); if (IS_GEN4(pci_dev->device_id)) gen = 4; else if (IS_GEN5(pci_dev->device_id)) gen = 5; else if (IS_GEN6(pci_dev->device_id)) gen = 6; else if (IS_GEN7(pci_dev->device_id)) gen = 7; else { printf("This program does not support your hardware yet.\n"); ret = 1; goto out; } while (1) { opt = getopt_long(argc, argv, short_opts, long_opts, NULL); if (opt == -1) break; switch (opt) { case 'd': dump_all_info(); break; case 'c': if (transcoder == TRANSC_INVALID) { printf("Transcoder not selected.\n"); ret = 1; goto out; } switch (dip) { case DIP_AVI: change_avi_infoframe(transcoder, optarg); break; case DIP_VENDOR: case DIP_GAMUT: printf("Option not implemented yet.\n"); ret = 1; goto out; case DIP_SPD: change_spd_infoframe(transcoder, optarg); break; case DIP_INVALID: printf("Infoframe not selected.\n"); ret = 1; goto out; } break; case 'k': CHECK_TRANSCODER(transcoder); CHECK_DIP(dip); change_infoframe_checksum(transcoder, dip, atoi(optarg)); break; case 'q': CHECK_TRANSCODER(transcoder); CHECK_DIP(dip); if (!strcmp(optarg, "once")) change_infoframe_frequency(transcoder, dip, DIP_FREQ_ONCE); else if (!strcmp(optarg, "everyvsync")) change_infoframe_frequency(transcoder, dip, DIP_FREQ_EVERY_VSYNC); else if (!strcmp(optarg, "everyothervsync")) change_infoframe_frequency(transcoder, dip, DIP_FREQ_EVERY_OTHER_VSYNC); else { printf("Invalid frequency.\n"); ret = 1; goto out; } break; case 'n': CHECK_TRANSCODER(transcoder); CHECK_DIP(dip); disable_infoframe(transcoder, dip); break; case 'N': CHECK_TRANSCODER(transcoder); CHECK_DIP(dip); enable_infoframe(transcoder, dip); break; case 'x': CHECK_TRANSCODER(transcoder); disable_dip(transcoder); break; case 'X': CHECK_TRANSCODER(transcoder); enable_dip(transcoder); break; case 'p': case 'P': if (!strcmp(optarg, "B")) hdmi_port = get_hdmi_port(0); else if (!strcmp(optarg, "C")) hdmi_port = get_hdmi_port(1); else if (!strcmp(optarg, "D")) hdmi_port = get_hdmi_port(2); else { printf("Invalid HDMI port.\n"); ret = 1; goto out; } if (opt == 'p') disable_hdmi_port(hdmi_port); else enable_hdmi_port(hdmi_port); break; case 't': if (!strcmp(optarg, "A")) transcoder = TRANSC_A; else if (!strcmp(optarg, "B")) transcoder = TRANSC_B; else if (intel_pch >= PCH_CPT && !strcmp(optarg, "C")) { transcoder = TRANSC_C; } else { printf("Invalid transcoder.\n"); ret = 1; goto out; } break; case 'f': if (!strcmp(optarg, "AVI")) dip = DIP_AVI; else if (!strcmp(optarg, "Vendor")) dip = DIP_VENDOR; else if (!strcmp(optarg, "Gamut")) dip = DIP_GAMUT; else if (!strcmp(optarg, "SPD")) dip = DIP_SPD; else { printf("Invalid infoframe.\n"); ret = 1; goto out; } break; case 'h': print_usage(); break; default: print_usage(); ret = 1; goto out; } } out: intel_register_access_fini(); return ret; }