From fd585207961f9916e1603852a703d6c656604656 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 13 Apr 2012 10:27:01 -0300 Subject: Add tools/intel_infoframes This is a command-line tool that allows us to display and modify the InfoFrames we send. v2: use argv instead of stdin Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- man/Makefile.am | 1 + man/intel_infoframes.man | 26 ++ tools/.gitignore | 1 + tools/Makefile.am | 1 + tools/intel_infoframes.c | 1050 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1079 insertions(+) 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 0f197a9f..0d04f934 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_panel_fitter.man \ intel_reg_dumper.man \ diff --git a/man/intel_infoframes.man b/man/intel_infoframes.man new file mode 100644 index 00000000..c20d4b49 --- /dev/null +++ b/man/intel_infoframes.man @@ -0,0 +1,26 @@ +.\" 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. + +Use the +.B -h +or +.B --help +options to learn how to use the command +.SH LIMITATIONS +Not all HDMI monitors respect the InfoFrames sent to them. Only Iron Lake +or newer hardware is supported yet. +.SH SEE ALSO +HDMI specification, CEA-861 specification. diff --git a/tools/.gitignore b/tools/.gitignore index 44959fbe..4d1b37b5 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -11,6 +11,7 @@ intel_gpu_dump intel_gpu_time intel_gpu_top intel_gtt +intel_infoframes intel_lid intel_panel_fitter intel_reg_checker diff --git a/tools/Makefile.am b/tools/Makefile.am index a368130b..058835c2 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -18,6 +18,7 @@ bin_PROGRAMS = \ noinst_PROGRAMS = \ intel_dump_decode \ + intel_infoframes \ intel_lid \ intel_panel_fitter diff --git a/tools/intel_infoframes.c b/tools/intel_infoframes.c new file mode 100644 index 00000000..bd8d66e1 --- /dev/null +++ b/tools/intel_infoframes.c @@ -0,0 +1,1050 @@ +/* + * 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_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"); +} + +static void dump_vendor_info(Transcoder transcoder) +{ + Register reg = dip_ctl_regs[transcoder]; + uint32_t val = INREG(reg); + DipFrequency freq; + DipInfoFrame frame; + + load_infoframe(transcoder, &frame, DIP_VENDOR); + val = INREG(reg); + + printf("Vendor InfoFrame:\n"); + + 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_gamut_info(Transcoder transcoder) +{ + Register reg = dip_ctl_regs[transcoder]; + uint32_t val = INREG(reg); + DipFrequency freq; + DipInfoFrame frame; + + load_infoframe(transcoder, &frame, DIP_GAMUT); + val = INREG(reg); + + printf("Gamut InfoFrame:\n"); + + 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 = 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 (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; + + 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 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, 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 = dip_ctl_regs[transcoder]; + uint32_t val = INREG(reg); + 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 = dip_ctl_regs[transcoder]; + uint32_t val = INREG(reg); + 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, "%31s%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 = 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 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; + struct pci_device *pci_dev; + Transcoder transcoder = TRANSC_INVALID; + DipType dip = DIP_INVALID; + Register hdmi_port; + + char short_opts[] = "dc:k:q:nNxXpPt: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", no_argument, NULL, 'p' }, + { "enable-hdmi-port", no_argument, NULL, 'P' }, + { "transcoder" , required_argument, NULL, 't' }, + { "infoframe", required_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + }; + + 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); + intel_check_pch(); + + if (!HAS_PCH_SPLIT(pci_dev->device_id)) { + printf("This program still only supports ILK or newer.\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 = REG_HDMIB; + else if (!strcmp(optarg, "C")) + hdmi_port = REG_HDMIC; + else if (!strcmp(optarg, "D")) + hdmi_port = REG_HDMID; + 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 (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; +} -- cgit v1.2.3