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
 

Reply via email to