| /* |
| * 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_GEN4 = 0x61140, |
| REG_HDMIC_GEN4 = 0x61160, |
| REG_HDMIB_PCH = 0xe1140, |
| REG_HDMIC_PCH = 0xe1150, |
| REG_HDMID_PCH = 0xe1160, |
| REG_DIP_CTL_GEN4 = 0x61170, |
| REG_DIP_CTL_A = 0xe0200, |
| REG_DIP_CTL_B = 0xe1200, |
| REG_DIP_CTL_C = 0xe2200, |
| REG_DIP_DATA_GEN4 = 0x61178, |
| 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; |
| |
| uint8_t Rsvd1 :4; |
| uint8_t s3d_structure :4; |
| |
| 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 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)" |
| }; |
| |
| 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 (gen == 4) |
| return REG_DIP_CTL_GEN4; |
| else |
| return pch_dip_ctl_regs[transcoder]; |
| } |
| |
| static Register get_dip_data_reg(Transcoder transcoder) |
| { |
| 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 (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) |
| transcoder = (val & HDMI_PORT_TRANSCODER_GEN4) >> 30; |
| else if (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: %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 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 s3d_present = frame->vendor.video_format & 0x2; |
| |
| printf("- video format: 0x%03x %s\n", frame->vendor.video_format, |
| s3d_present ? "(3D)" : ""); |
| if (s3d_present) |
| printf("- 3D Format: %s\n", |
| s3d_structure_to_string(frame->vendor.s3d_structure)); |
| } |
| |
| 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 (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; |
| struct pci_device *pci_dev; |
| 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' }, |
| }; |
| |
| 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 (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 (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; |
| } |