summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/Makefile.am1
-rw-r--r--man/intel_infoframes.man26
-rw-r--r--tools/.gitignore1
-rw-r--r--tools/Makefile.am1
-rw-r--r--tools/intel_infoframes.c1050
5 files changed, 1079 insertions, 0 deletions
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 <paulo.r.zanoni@intel.com>
+ *
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.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");
+}
+
+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 = &current[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,
+ &current))
+ frame.avi.S = option_val;
+ else if (parse_infoframe_option_u("B", option, 0, 3,
+ &option_val, &current))
+ frame.avi.B = option_val;
+ else if (parse_infoframe_option_u("A", option, 0, 1,
+ &option_val, &current))
+ frame.avi.A = option_val;
+ else if (parse_infoframe_option_u("Y", option, 0, 2,
+ &option_val, &current))
+ frame.avi.Y = option_val;
+ else if (parse_infoframe_option_u("R", option, 0, 15,
+ &option_val, &current))
+ frame.avi.R = option_val;
+ else if (parse_infoframe_option_u("M", option, 0, 2,
+ &option_val, &current))
+ frame.avi.M = option_val;
+ else if (parse_infoframe_option_u("C", option, 0, 3,
+ &option_val, &current))
+ frame.avi.C = option_val;
+ else if (parse_infoframe_option_u("SC", option, 0, 3,
+ &option_val, &current))
+ frame.avi.SC = option_val;
+ else if (parse_infoframe_option_u("Q", option, 0, 2,
+ &option_val, &current))
+ frame.avi.Q = option_val;
+ else if (parse_infoframe_option_u("EC", option, 0, 1,
+ &option_val,&current))
+ frame.avi.EC = option_val;
+ else if (parse_infoframe_option_u("ITC", option, 0, 1,
+ &option_val, &current))
+ frame.avi.ITC = option_val;
+ else if (parse_infoframe_option_u("VIC", option, 0, 127,
+ &option_val, &current))
+ frame.avi.VIC = option_val;
+ else if (parse_infoframe_option_u("PR", option, 0, 15,
+ &option_val, &current))
+ frame.avi.PR = option_val;
+ else if (parse_infoframe_option_u("top", option, 0, 65535,
+ &option_val, &current))
+ frame.avi.top = option_val;
+ else if (parse_infoframe_option_u("bottom", option, 0, 65535,
+ &option_val, &current))
+ frame.avi.bottom = option_val;
+ else if (parse_infoframe_option_u("left", option, 0, 65535,
+ &option_val, &current))
+ frame.avi.left = option_val;
+ else if (parse_infoframe_option_u("right", option, 0, 65535,
+ &option_val, &current))
+ 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 = &current[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, &current))
+ memcpy(frame.spd.vendor, option_val_s, 8);
+ else if (parse_infoframe_option_s("description", option, 0, 16,
+ option_val_s, &current))
+ memcpy(frame.spd.description, option_val_s, 16);
+ else if (parse_infoframe_option_u("source", option, 0, 0x0c,
+ &option_val_i, &current))
+ 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;
+}