This is an automatic generated email to let you know that the following patch were queued:
Subject: edid-decode: add support to parse SCDC data from the DDC line Author: Hans Verkuil <hverkuil-ci...@xs4all.nl> Date: Tue Aug 20 09:49:33 2024 +0200 Add options to read and parse the SCDC data from the DDC line. Signed-off-by: Hans Verkuil <hverkuil-ci...@xs4all.nl> ddc.cpp | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ edid-decode.cpp | 15 +++- edid-decode.h | 2 + 3 files changed, 223 insertions(+), 2 deletions(-) --- diff --git a/ddc.cpp b/ddc.cpp index 939e968f9b2c..b9640d8c71f2 100644 --- a/ddc.cpp +++ b/ddc.cpp @@ -344,3 +344,211 @@ int read_hdcp_ri(int adapter_fd, double ri_time) } return 0; } + +static int read_scdc_registers(int adapter_fd, __u8 *scdc, bool update_only) +{ + struct i2c_rdwr_ioctl_data data; + struct i2c_msg write_message; + struct i2c_msg read_message; + __u8 offset = 0; + int err; + + write_message = { + .addr = SCDC_ADDR, + .len = 1, + .buf = &offset + }; + read_message = { + .addr = SCDC_ADDR, + .flags = I2C_M_RD, + .len = (__u16)(update_only ? 2 : 256), + .buf = scdc + (update_only ? 0x10 : 0) + }; + + struct i2c_msg msgs[2] = { write_message, read_message }; + + data.msgs = msgs + update_only; + data.nmsgs = ARRAY_SIZE(msgs) - update_only; + err = ioctl(adapter_fd, I2C_RDWR, &data); + + if (err < 0) { + fprintf(stderr, "Unable to read SCDC: %s\n", strerror(errno)); + return -1; + } + return 0; +} + +static void print_scdc_update(const __u8 *scdc) +{ + __u8 flags = scdc[0x10]; + + printf("Update Flags: 0x%02x 0x%02x\n", scdc[0x10], scdc[0x11]); + if (flags & 0x01) + printf("\tStatus_Update\n"); + if (flags & 0x02) + printf("\tCED_Update\n"); + if (flags & 0x04) + printf("\tRR_Test\n"); + if (flags & 0x08) + printf("\tSource_Test_Update\n"); + if (flags & 0x10) + printf("\tFRL_start\n"); + if (flags & 0x20) + printf("\tFLT_update\n"); + if (flags & 0x40) + printf("\tRSED_Update\n"); +} + +int read_scdc(int adapter_fd, bool update_only) +{ + static const char *frl_rates[] = { + "Disable FRL", + "3 Lanes at 3 Gbps per Lane", + "3 Lanes at 6 Gbps per Lane", + "4 Lanes at 6 Gbps per Lane", + "4 Lanes at 8 Gbps per Lane", + "4 Lanes at 10 Gbps per Lane", + "4 Lanes at 12 Gbps per Lane", + }; + __u8 scdc[256]; + + if (read_scdc_registers(adapter_fd, scdc, update_only)) + return -1; + if (update_only) { + print_scdc_update(scdc); + } else { + printf("SCDC Hex Dump:\n\n"); + hex_block("", scdc, 128, false); + printf("\n"); + hex_block("", scdc + 128, 128, false); + printf("\n"); + + printf("Sink Version: %u Source Version: %u\n", scdc[0x01], scdc[0x02]); + + print_scdc_update(scdc); + + __u8 v = scdc[0x20]; + printf("TMDS Configuration: 0x%02x\n", v); + if (v & 0x01) + printf("\tScrambling_Enable\n"); + printf("\tTMDS_Bit_Clock_Ratio: 1/%u\n", (v & 0x02) ? 40 : 10); + + v = scdc[0x21]; + printf("TMDS Scrambler Status: 0x%02x\n", v); + if (v & 0x01) + printf("\tTMDS_Scrambling_Status\n"); + + v = scdc[0x30]; + printf("Sink Configuration: 0x%02x 0x%02x\n", v, scdc[0x31]); + if (v & 0x01) + printf("\tRR_Enable\n"); + if (v & 0x02) + printf("\tFLT_no_retrain\n"); + if (v & 0x04) + printf("\tDAISY_ERR\n"); + if (v & 0x08) + printf("\tMONO_DIR_ON\n"); + if (v & 0x10) + printf("\tMONO_DIR_ERR\n"); + if (v & 0x20) + printf("\tCA_PWR_ERR\n"); + v = scdc[0x31]; + if ((v & 0xf) >= ARRAY_SIZE(frl_rates)) + printf("\tFRL_Rate: %u\n", v & 0xf); + else + printf("\tFRL_Rate: %s\n", frl_rates[v & 0xf]); + printf("\tFFE_Levels: %u\n", v >> 4); + + v = scdc[0x35]; + printf("Source Test Configuration: 0x%02x\n", v); + if (v & 0x02) + printf("\tTxFFE_Pre_Shoot_Only\n"); + if (v & 0x04) + printf("\tTxFFE_De_Emphasis_Only\n"); + if (v & 0x08) + printf("\tTxFFE_No_FFE\n"); + if (v & 0x20) + printf("\tFLT_no_timeout\n"); + if (v & 0x40) + printf("\tDSC_FRL_Max\n"); + if (v & 0x80) + printf("\tFRL_Max\n"); + + v = scdc[0x40]; + printf("Status Flags: 0x%02x 0x%02x 0x%02x\n", v, scdc[0x41], scdc[0x42]); + if (v & 0x01) + printf("\tClock_Detected\n"); + if (v & 0x02) + printf("\tCh0_Ln0_Locked\n"); + if (v & 0x04) + printf("\tCh1_Ln1_Locked\n"); + if (v & 0x08) + printf("\tCh2_Ln2_Locked\n"); + if (v & 0x10) + printf("\tLane3_Locked\n"); + if (v & 0x40) + printf("\tFLT_Ready\n"); + if (v & 0x80) + printf("\tDSC_DecodeFail\n"); + if (scdc[0x41] || scdc[0x42]) { + v = scdc[0x41]; + printf("\tLn0_LTP_req: %u\n", v & 0xf); + printf("\tLn1_LTP_req: %u\n", v >> 4); + v = scdc[0x42]; + printf("\tLn2_LTP_req: %u\n", v & 0xf); + printf("\tLn3_LTP_req: %u\n", v >> 4); + } + + if ((scdc[0x51] & 0x80) || (scdc[0x53] & 0x80) || (scdc[0x55] & 0x80) || + (scdc[0x58] & 0x80) || (scdc[0x5a] & 0x80)) { + printf("Character Error Detection:\n"); + __u16 cnt = scdc[0x51] << 8 | scdc[0x50]; + if (cnt & 0x8000) + printf("\tChannel 0 Error Count: %u\n", cnt & 0x7fff); + cnt = scdc[0x53] << 8 | scdc[0x52]; + if (cnt & 0x8000) + printf("\tChannel 1 Error Count: %u\n", cnt & 0x7fff); + cnt = scdc[0x55] << 8 | scdc[0x54]; + if (cnt & 0x8000) + printf("\tChannel 2 Error Count: %u\n", cnt & 0x7fff); + __u8 sum = 0; + for (unsigned i = 0x50; i <= 0x55; i++) + sum += scdc[i]; + sum = 0xff - sum; + sum++; + if (sum != scdc[0x56]) + printf("\tInvalid Checksum: expected 0x%02x, got 0x%02x\n", + sum, scdc[0x56]); + cnt = scdc[0x58] << 8 | scdc[0x57]; + if (cnt & 0x8000) + printf("\tLane 3 Error Count: %u\n", cnt & 0x7fff); + cnt = scdc[0x5a] << 8 | scdc[0x59]; + if (cnt & 0x8000) + printf("\tReed-Solomon Corrections Counter: %u\n", cnt & 0x7fff); + } else { + printf("Character Error Detection: no errors detected\n"); + } + + v = scdc[0xc0]; + if (v) { + printf("Test Read Request: 0x%02x\n", v); + if (v & 0x80) + printf("\tTestReadRequest\n"); + printf("\tTestReadRequestDelay: %ums\n", v & 0x7f); + } + + if (scdc[0xd0] || scdc[0xd1] || scdc[0xd2]) { + printf("Manufacturer Specific:\n"); + printf("\tOUI: %02x-%02x-%02x\n", scdc[0xd2], scdc[0xd1], scdc[0xd0]); + char s[9] = {}; + memcpy(s, scdc + 0xd3, 8); + printf("\tDevice_ID_String: '%s'\n", s); + printf("\tHW Revision: %u.%u SW Revision: %u.%u\n", + scdc[0xdb] >> 4, scdc[0xdb] & 0xf, + scdc[0xdc], scdc[0xdd]); + } else { + printf("Manufacturer Specific: none\n"); + } + } + return 0; +} diff --git a/edid-decode.cpp b/edid-decode.cpp index d565da06146f..dc019387801d 100644 --- a/edid-decode.cpp +++ b/edid-decode.cpp @@ -64,6 +64,8 @@ enum Option { OptVersion, OptDiag, OptI2CEDID, + OptI2CSCDC, + OptI2CSCDCUpdate, OptI2CHDCP, OptI2CHDCPRi, OptSTD, @@ -110,6 +112,8 @@ static struct option long_options[] = { #ifdef __HAS_I2C_DEV__ { "i2c-adapter", required_argument, 0, OptI2CAdapter }, { "i2c-edid", no_argument, 0, OptI2CEDID }, + { "i2c-scdc-update", no_argument, 0, OptI2CSCDCUpdate }, + { "i2c-scdc", no_argument, 0, OptI2CSCDC }, { "i2c-hdcp", no_argument, 0, OptI2CHDCP }, { "i2c-hdcp-ri", required_argument, 0, OptI2CHDCPRi }, #endif @@ -169,6 +173,8 @@ static void usage(void) #ifdef __HAS_I2C_DEV__ " -a, --i2c-adapter <num> Use /dev/i2c-<num> to access the DDC lines.\n" " --i2c-edid Read the EDID from the DDC lines.\n" + " --i2c-scdc Read the SCDC from the DDC lines.\n" + " --i2c-scdc-update Read the SCDC Update information (bytes 0x10-0x11) from the DDC lines.\n" " --i2c-hdcp Read the HDCP from the DDC lines.\n" " --i2c-hdcp-ri=<t> Read and print the HDCP Ri information every <t> seconds.\n" #endif @@ -2449,6 +2455,7 @@ int main(int argc, char **argv) } if (optind == argc) { + ret = 0; if (adapter_fd >= 0 && options[OptI2CEDID]) { ret = read_edid(adapter_fd, edid); if (ret > 0) { @@ -2457,9 +2464,13 @@ int main(int argc, char **argv) ret = 0; } } else if (adapter_fd >= 0) { - if (options[OptI2CHDCP]) + if (options[OptI2CSCDC]) + ret = read_scdc(adapter_fd, false); + if (!ret && options[OptI2CSCDCUpdate]) + ret = read_scdc(adapter_fd, true); + if (!ret && options[OptI2CHDCP]) ret = read_hdcp(adapter_fd); - if (options[OptI2CHDCPRi]) + if (!ret && options[OptI2CHDCPRi]) ret = read_hdcp_ri(adapter_fd, hdcp_ri_sleep); } else if (options[OptInfoFrame] && !options[OptGTF]) { ret = 0; diff --git a/edid-decode.h b/edid-decode.h index 926fa3305562..279f77186fa9 100644 --- a/edid-decode.h +++ b/edid-decode.h @@ -621,12 +621,14 @@ int request_i2c_adapter(unsigned adapnr); int read_edid(int adapter_fd, unsigned char *edid); int read_hdcp(int adapter_fd); int read_hdcp_ri(int adapter_fd, double ri_time); +int read_scdc(int adapter_fd, bool update_only); #else static inline int read_edid(int adapter_fd, unsigned char *edid) { return -ENODEV; } static inline int read_hdcp(int adapter_fd) { return -ENODEV; } static inline int read_hdcp_ri(int adapter_fd, double ri_time) { return -ENODEV; } +static inline int read_scdc(int adapter_fd, bool update_only) { return -ENODEV; } #endif