From: Paulo Zanoni <paulo.r.zanoni at intel.com> This is a command-line tool that allows us to display and modify the InfoFrames we send. Signed-off-by: Paulo Zanoni <paulo.r.zanoni at intel.com> --- Hi Before we add Kernel interfaces to deal with the infoframes, I decided to write this tool. It allows us to quickly test everything without requiring a patched Kernel, so is very convenient for people who want to change their InfoFrames quickly. The coolest feature behind this tool is the ability to change between overscan and underscan, so now you can connect your computer to your TV and, maybe, be able to see your whole desktop (even the corners!). I tested this with 3 TVs, but only one of them respected the overscan/underscan settings. I still need to investigate more the reasons why the settings were ignored on two TVs (I didn't look at the EDID stuff). So, to change between overscan and underscan: $ sudo ./intel_infoframes You'll see the main menu. Type "1" and then press enter to see what's happening on your machine. You'll see which transcoder is associated to which HDMI port. If any of the transcoders is enabled, you'll also see the InfoFrames they're sending. Note that to really understand each InfoFrame field you'll have to look at the HDMI or CEA-861 specifications. Then, select option 2 to change an InfoFrame. You'll need to select a transcoder: scroll up and read the output of the previous command. Then, select the AVI infoframe (option 1). On the next menu, select option 1 again. The option that deals with overscan/uderscan is S. Value "0" is default, value "1" is overscan and value "2" is underscan. Write "S 2" and then press enter. Then, write "done" and press enter. If your TV repects the underscan settings of the AVI infoframes, you'll now have underscan. Next features: - Support for Iron Lake (I only tested with Sandy Bridge) - Other InfoFrames (I only added support for AVI and SPD) - Code that tries to check which InfoFrames your TV is supposed to respect. Feedback/testing would be appreciated. Cheers, Paulo man/Makefile.am | 1 + man/intel_infoframes.man | 20 + tools/.gitignore | 1 + tools/Makefile.am | 1 + tools/intel_infoframes.c | 993 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1016 insertions(+), 0 deletions(-) create mode 100644 man/intel_infoframes.man create mode 100644 tools/intel_infoframes.c diff --git a/man/Makefile.am b/man/Makefile.am index 2b54195..7c14a81 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -6,6 +6,7 @@ appman_PRE = \ intel_error_decode.man \ intel_gpu_top.man \ intel_gtt.man \ + intel_infoframes.man \ intel_lid.man \ intel_reg_dumper.man \ intel_reg_read.man \ diff --git a/man/intel_infoframes.man b/man/intel_infoframes.man new file mode 100644 index 0000000..1caf91b --- /dev/null +++ b/man/intel_infoframes.man @@ -0,0 +1,20 @@ +.\" shorthand for double quote that works everywhere. +.ds q \N'34' +.TH intel_infoframes __appmansuffix__ __xorgversion__ +.SH NAME +intel_infoframes \- View and change HDMI InfoFrames +.SH SYNOPSIS +.B intel_infoframes +.SH DESCRIPTION +.B intel_infoframes +is a tool to view and change the HDMI InfoFrames sent by the GPU. Its main +purpose is to be used as a debugging tool. In some cases (e.g., when +changing modes) the Kernel will undo the changes made by this tool. + +Descriptions of the InfoFrame fields can be found on the HDMI and CEA-861 +specifications. +.SH LIMITATIONS +Not all HDMI monitors respect the InfoFrames sent to them. Only Sandy Bridge +or newer hardware is supported yet. +.SH SEE ALSO +HDMI specification, CEA-861 specification. diff --git a/tools/.gitignore b/tools/.gitignore index 0bbdde1..ce70e30 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -10,6 +10,7 @@ intel_gpu_dump intel_gpu_time intel_gpu_top intel_gtt +intel_infoframes intel_lid intel_reg_checker intel_reg_dumper diff --git a/tools/Makefile.am b/tools/Makefile.am index 4969a11..1287220 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -8,6 +8,7 @@ bin_PROGRAMS = \ intel_gpu_top \ intel_gpu_time \ intel_gtt \ + intel_infoframes \ intel_stepping \ intel_reg_checker \ intel_reg_dumper \ diff --git a/tools/intel_infoframes.c b/tools/intel_infoframes.c new file mode 100644 index 0000000..d4bfd67 --- /dev/null +++ b/tools/intel_infoframes.c @@ -0,0 +1,993 @@ +/* + * 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 <paulo.r.zanoni at intel.com> + * + */ + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include "intel_gpu_tools.h" + +typedef enum { + TRANSC_A = 0, + TRANSC_B = 1, + TRANSC_C = 2, + TRANSC_INVALID +} Transcoder; + +typedef enum { + REG_HDMIB = 0xe1140, + REG_HDMIC = 0xe1150, + REG_HDMID = 0xe1160, + REG_DIP_CTL_A = 0xe0200, + REG_DIP_CTL_B = 0xe1200, + REG_DIP_CTL_C = 0xe2200, + 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_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 AVI_INFOFRAME_TYPE 0x82 +#define AVI_INFOFRAME_VERSION 0x01 +#define AVI_INFOFRAME_LENGTH 0x0d +#define SPD_INFOFRAME_TYPE 0x83 +#define SPD_INFOFRAME_VERSION 0x01 +#define SPD_INFOFRAME_LENGTH 0x19 + +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 body[27]; + } generic; + uint8_t data8[128]; + uint32_t data32[16]; +} DipInfoFrame; + +Register hdmi_ports[] = { + REG_HDMIB, + REG_HDMIC, + REG_HDMID +}; +Register dip_ctl_regs[] = { + REG_DIP_CTL_A, + REG_DIP_CTL_B, + REG_DIP_CTL_C +}; +Register 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)" +}; + +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 void load_infoframe(Transcoder transcoder, DipInfoFrame *frame, + DipType type) +{ + Register ctl_reg = dip_ctl_regs[transcoder]; + Register data_reg = dip_data_regs[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 = hdmi_ports[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 (pch >= PCH_CPT) + transcoder = (val & HDMI_PORT_TRANSCODER_CPT) >> 29; + else + transcoder = (val & HDMI_PORT_TRANSCODER_CPT) >> 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 = dip_ctl_regs[transcoder]; + uint32_t val = INREG(reg); + DipFrequency freq; + DipInfoFrame frame; + + load_infoframe(transcoder, &frame, DIP_AVI); + val = INREG(reg); + + printf("AVI InfoFrame:\n"); + + 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: %x, 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"); +} + +#if 0 +static void dump_vendor_info(Transcoder transcoder) +{ + printf("Not implemented yet!\n"); +} + +static void dump_gamut_info(Transcoder transcoder) +{ + printf("Not implemented yet!\n"); +} +#endif + +static void dump_spd_info(Transcoder transcoder) +{ + Register reg = dip_ctl_regs[transcoder]; + uint32_t val = INREG(reg); + DipFrequency freq; + DipInfoFrame frame; + char vendor[9]; + char description[17]; + + load_infoframe(transcoder, &frame, DIP_SPD); + val = INREG(reg); + + printf("SPD InfoFrame:\n"); + + 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 = dip_ctl_regs[transcoder]; + uint32_t val = INREG(reg); + + 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 0 + if (val & DIP_CTL_VENDOR_ENABLE) + dump_vendor_info(transcoder); + if (val & DIP_CTL_GAMUT_ENABLE) + dump_gamut_info(transcoder); +#endif + if (val & DIP_CTL_SPD_ENABLE) + dump_spd_info(transcoder); +} + +static void dump_all_info(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(hdmi_ports); i++) + dump_port_info(i); + for (i = 0; i < ARRAY_SIZE(dip_ctl_regs); i++) + dump_transcoder_info(i); +} + +static Transcoder select_transcoder_menu(void) +{ + int option; + printf("Select transcoder:\n"); + printf("1 - A\n"); + printf("2 - B\n"); + if (pch >= PCH_CPT) + printf("3 - C\n"); + if (scanf("%d", &option) != 1) { + printf("Invalid number.\n"); + return TRANSC_INVALID; + } + switch (option) { + case 1: + return TRANSC_A; + case 2: + return TRANSC_B; + case 3: + if (pch >= PCH_CPT) { + return TRANSC_C; + } else { + printf("Invalid option.\n"); + return TRANSC_INVALID; + } + default: + printf("Invalid option.\n"); + return TRANSC_INVALID; + } +} + +static void write_infoframe(Transcoder transcoder, DipType type, + DipInfoFrame *frame) +{ + Register ctl_reg = dip_ctl_regs[transcoder]; + Register data_reg = dip_data_regs[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 = dip_ctl_regs[transcoder]; + uint32_t val = INREG(reg); + if (type == DIP_AVI) + val &= ~DIP_CTL_ENABLE; + val &= ~(1 << (21 + type)); + OUTREG(reg, val); +} + +static void enable_infoframe(Transcoder transcoder, DipType type) +{ + Register reg = dip_ctl_regs[transcoder]; + uint32_t val = INREG(reg); + if (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) +{ + if (!strcmp(name, s)) { + if (scanf("%x", value) != 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) +{ + int size; + if (!strcmp(name, s)) { + if (scanf("%31s", value) != 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) +{ + Register reg = dip_ctl_regs[transcoder]; + uint32_t val = INREG(reg); + DipInfoFrame frame; + char option[16]; + uint32_t option_val; + + load_infoframe(transcoder, &frame, DIP_AVI); + val = INREG(reg); + + while (1) { + printf("Current infoframe:\n"); + printf("- S=%x B=%x A=%x Y=%x R=%x M=%x C=%x SC=%x Q=%x\n" + "- EC=%x ITC=%x VIC=%x PR=%x top=%x bottom=%x left=%x " + "right=%x\n", frame.avi.S, frame.avi.B, frame.avi.A, + frame.avi.Y, frame.avi.R, frame.avi.M, frame.avi.C, + frame.avi.SC, frame.avi.Q, frame.avi.EC, frame.avi.ITC, + frame.avi.VIC, frame.avi.PR, frame.avi.top, + frame.avi.bottom, frame.avi.left, frame.avi.right); + printf("Type the name of the field you want to change, a\n" + "space and the value in *hex*.\n" + "When you're done, type 'done'.\n"); + if (scanf("%15s", option) != 1) { + printf("Invalid option.\n"); + continue; + } + + if (!strcmp(option, "done")) + break; + if (parse_infoframe_option_u("S", option, 0, 2, &option_val)) + frame.avi.S = option_val; + if (parse_infoframe_option_u("B", option, 0, 3, &option_val)) + frame.avi.B = option_val; + if (parse_infoframe_option_u("A", option, 0, 1, &option_val)) + frame.avi.A = option_val; + if (parse_infoframe_option_u("Y", option, 0, 2, &option_val)) + frame.avi.Y = option_val; + if (parse_infoframe_option_u("R", option, 0, 15, &option_val)) + frame.avi.R = option_val; + if (parse_infoframe_option_u("M", option, 0, 2, &option_val)) + frame.avi.M = option_val; + if (parse_infoframe_option_u("C", option, 0, 3, &option_val)) + frame.avi.C = option_val; + if (parse_infoframe_option_u("SC", option, 0, 3, &option_val)) + frame.avi.SC = option_val; + if (parse_infoframe_option_u("Q", option, 0, 2, &option_val)) + frame.avi.Q = option_val; + if (parse_infoframe_option_u("EC", option, 0, 1, &option_val)) + frame.avi.EC = option_val; + if (parse_infoframe_option_u("ITC", option, 0, 1, &option_val)) + frame.avi.ITC = option_val; + if (parse_infoframe_option_u("VIC", option, 0, 127, + &option_val)) + frame.avi.VIC = option_val; + if (parse_infoframe_option_u("PR", option, 0, 15, &option_val)) + frame.avi.PR = option_val; + if (parse_infoframe_option_u("top", option, 0, 65535, + &option_val)) + frame.avi.top = option_val; + if (parse_infoframe_option_u("bottom", option, 0, 65535, + &option_val)) + frame.avi.bottom = option_val; + if (parse_infoframe_option_u("left", option, 0, 65535, + &option_val)) + frame.avi.left = option_val; + if (parse_infoframe_option_u("right", option, 0, 65535, + &option_val)) + frame.avi.right = option_val; + } + + 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) +{ + Register reg = dip_ctl_regs[transcoder]; + uint32_t val = INREG(reg); + DipInfoFrame frame; + char option[16]; + char option_val_s[32]; + uint32_t option_val_i; + + load_infoframe(transcoder, &frame, DIP_SPD); + val = INREG(reg); + + while (1) { + printf("Current infoframe:\n"); + printf("- vendor=\"%s\" description=\"%s\", source=%s\n", + frame.spd.vendor, frame.spd.description, + spd_source_to_string(frame.spd.source)); + printf("Type the name of the field you want to change, a\n" + "space and the value in *hex*.\n" + "When you're done, type 'done'.\n"); + if (scanf("%16s", option) != 1) { + printf("Invalid option.\n"); + continue; + } + memset(option_val_s, 0, 32); + + if (!strcmp(option, "done")) + break; + if (parse_infoframe_option_s("vendor", option, 0, 8, + option_val_s)) + memcpy(frame.spd.vendor, option_val_s, 8); + if (parse_infoframe_option_s("description", option, 0, 16, + option_val_s)) + memcpy(frame.spd.description, option_val_s, 16); + if (parse_infoframe_option_u("source", option, 0, 0x0c, + &option_val_i)) + frame.spd.source = option_val_i; + } + + 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) +{ + DipInfoFrame frame; + uint32_t selected_csum; + + load_infoframe(transcoder, &frame, type); + + printf("Type the checksum value (in hex):\n"); + if (scanf("%x", &selected_csum) != 1) { + printf("Invalid number.\n"); + return; + } + + 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 = dip_ctl_regs[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 = dip_ctl_regs[transcoder]; + uint32_t val = INREG(reg); + val &= ~DIP_CTL_ENABLE; + OUTREG(reg, val); +} + +static void enable_dip(Transcoder transcoder) +{ + Register reg = dip_ctl_regs[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 int select_frequency_menu(DipFrequency *frequency) +{ + int option; + printf("Select frequency:\n"); + printf("1 - Once\n"); + printf("2 - Every VSync\n"); + printf("3 - Every other VSync\n"); + if (scanf("%d", &option) != 1) { + printf("Invalid number.\n"); + return 0; + } + + switch (option) { + case 1: + *frequency = DIP_FREQ_ONCE; + return 1; + case 2: + *frequency = DIP_FREQ_EVERY_VSYNC; + return 1; + case 3: + *frequency = DIP_FREQ_EVERY_OTHER_VSYNC; + return 1; + default: + printf("Invalid option.\n"); + return 0; + } +} + +static void change_infoframe_menu(Transcoder transcoder, DipType type) +{ + int option; + DipFrequency frequency; + + printf("Select option:\n"); + printf("1 - Change infoframe fields\n"); + printf("2 - Change infoframe checksum\n"); + printf("3 - Change infoframe frequency\n"); + printf("4 - Disable infoframe\n"); + printf("5 - Enable infoframe\n"); + if (scanf("%d", &option) != 1) { + printf("Invalid number.\n"); + return; + } + + switch (option) { + case 1: + switch (type) { + case DIP_AVI: + change_avi_infoframe(transcoder); + break; + case DIP_VENDOR: + printf("Not implemented yet!\n"); + break; + case DIP_GAMUT: + printf("Not implemented yet!\n"); + break; + case DIP_SPD: + change_spd_infoframe(transcoder); + break; + default: + assert(0); + } + break; + case 2: + change_infoframe_checksum(transcoder, type); + break; + case 3: + if (select_frequency_menu(&frequency)) + change_infoframe_frequency(transcoder, type, frequency); + break; + case 4: + disable_infoframe(transcoder, type); + break; + case 5: + enable_infoframe(transcoder, type); + break; + default: + printf("Invalid option.\n"); + return; + } +} + +static DipType select_infoframe_menu(void) +{ + int option; + printf("Select infoframe:\n"); + printf("1 - AVI\n"); + printf("2 - Vendor\n"); + printf("3 - Gamut\n"); + printf("4 - SPD\n"); + if (scanf("%d", &option) != 1) { + printf("Invalid number.\n"); + return DIP_INVALID; + } + switch (option) { + case 1: + return DIP_AVI; + case 2: + return DIP_VENDOR; + case 3: + return DIP_GAMUT; + case 4: + return DIP_SPD; + default: + return DIP_INVALID; + } +} + +static Register select_hdmi_port_menu(void) +{ + int option; + printf("Select HDMI port:\n"); + printf("1 - HDMIB\n"); + printf("2 - HDMIC\n"); + printf("3 - HDMID\n"); + if (scanf("%d", &option) != 1) { + printf("Invalid number.\n"); + return 0; + } + switch (option) { + case 1: + return REG_HDMIB; + case 2: + return REG_HDMIC; + case 3: + return REG_HDMID; + default: + printf("Invalid option.\n"); + return 0; + } +} + +static void main_menu(void) +{ + int option; + Transcoder transcoder; + DipType infoframe; + Register port; + + while (1) { + printf("\nMain menu:\n"); + printf("1 - Dump current infoframe information\n"); + printf("2 - Change infoframe from selected transcoder\n"); + printf("3 - Disable DIP from selected transcoder\n"); + printf("4 - Enable DIP from selected transcoder\n"); + printf("5 - Disable HDMI port\n"); + printf("6 - Enable HDMI port\n"); + printf("7 - Quit\n"); + if (scanf("%d", &option) != 1) { + printf("Invalid number.\n"); + continue; + } + switch (option) { + case 1: + dump_all_info(); + break; + case 2: + transcoder = select_transcoder_menu(); + if (transcoder == TRANSC_INVALID) + break; + infoframe = select_infoframe_menu(); + if (infoframe == DIP_INVALID) + break; + change_infoframe_menu(transcoder, infoframe); + break; + case 3: + transcoder = select_transcoder_menu(); + if (transcoder == TRANSC_INVALID) + break; + disable_dip(transcoder); + break; + case 4: + transcoder = select_transcoder_menu(); + if (transcoder == TRANSC_INVALID) + break; + enable_dip(transcoder); + break; + case 5: + port = select_hdmi_port_menu(); + if (port) + disable_hdmi_port(port); + break; + case 6: + port = select_hdmi_port_menu(); + if (port) + enable_hdmi_port(port); + break; + case 7: + return; + default: + printf("Invalid option.\n"); + } + } +} + +int main(void) +{ + intel_get_mmio(intel_get_pci_device()); + intel_check_pch(); + + if (pch < PCH_CPT) { + printf("This program still only supports SNB or newer.\n"); + return 1; + } + + printf("WARNING: This is just a debugging tool! Don't expect it to work" + " perfectly: the Kernel might undo our changes.\n\n"); + printf("The commands you should input are just the numbers.\n"); + + main_menu(); + return 0; +} -- 1.7.7.3