From 2ef2ac4d1704bc7920de80147aafe6fb74c20e91 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Fri, 14 Mar 2014 11:17:17 +0100 Subject: [PATCH] v4l2-ctl: add EDID get and set options Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil --- utils/v4l2-ctl/Makefile.am | 3 +- utils/v4l2-ctl/v4l2-ctl-common.cpp | 1 + utils/v4l2-ctl/v4l2-ctl-edid.cpp | 405 +++++++++++++++++++++++++++++++++++++ utils/v4l2-ctl/v4l2-ctl.cpp | 10 + utils/v4l2-ctl/v4l2-ctl.h | 9 + 5 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 utils/v4l2-ctl/v4l2-ctl-edid.cpp diff --git a/utils/v4l2-ctl/Makefile.am b/utils/v4l2-ctl/Makefile.am index becaa15..489f92f 100644 --- a/utils/v4l2-ctl/Makefile.am +++ b/utils/v4l2-ctl/Makefile.am @@ -8,5 +8,6 @@ ivtv_ctl_LDFLAGS = -lm v4l2_ctl_SOURCES = v4l2-ctl.cpp v4l2-ctl.h v4l2-ctl-common.cpp v4l2-ctl-tuner.cpp \ v4l2-ctl-io.cpp v4l2-ctl-stds.cpp v4l2-ctl-vidcap.cpp v4l2-ctl-vidout.cpp \ v4l2-ctl-overlay.cpp v4l2-ctl-vbi.cpp v4l2-ctl-selection.cpp v4l2-ctl-misc.cpp \ - v4l2-ctl-streaming.cpp v4l2-ctl-test-patterns.cpp v4l2-ctl-sdr.cpp + v4l2-ctl-streaming.cpp v4l2-ctl-test-patterns.cpp v4l2-ctl-sdr.cpp \ + v4l2-ctl-edid.cpp v4l2_ctl_LDADD = ../../lib/libv4l2/libv4l2.la ../../lib/libv4lconvert/libv4lconvert.la diff --git a/utils/v4l2-ctl/v4l2-ctl-common.cpp b/utils/v4l2-ctl/v4l2-ctl-common.cpp index a68f1c6..8d7c7a2 100644 --- a/utils/v4l2-ctl/v4l2-ctl-common.cpp +++ b/utils/v4l2-ctl/v4l2-ctl-common.cpp @@ -75,6 +75,7 @@ void common_usage(void) " --help-vbi VBI format options\n" " --help-vidcap video capture format options\n" " --help-vidout vidout output format options\n" + " --help-edid edid handling options\n" " -k, --concise be more concise if possible.\n" " -l, --list-ctrls display all controls and their values [VIDIOC_QUERYCTRL]\n" " -L, --list-ctrls-menus\n" diff --git a/utils/v4l2-ctl/v4l2-ctl-edid.cpp b/utils/v4l2-ctl/v4l2-ctl-edid.cpp new file mode 100644 index 0000000..39a02ca --- /dev/null +++ b/utils/v4l2-ctl/v4l2-ctl-edid.cpp @@ -0,0 +1,405 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "v4l2-ctl.h" + +enum format { + HEX, + RAW, + CARRAY +}; + +void edid_usage(void) +{ + printf("\nEDID options:\n" + " --set-edid=pad=,[edid=|file=]\n" + " is the input or output index for which to set the EDID.\n" + " can be 'hdmi', 'dvid' or 'vga'. A predefined EDID suitable\n" + " for that connector type will be set. It has a 1920x1080p60 native resolution.\n" + " If is '-', then the data is read from stdin, otherwise it is\n" + " read from the given file. The file format must be in hex as in get-edid.\n" + " The 'edid' or 'file' arguments are mutually exclusive. One of the two\n" + " must be specified.\n" + " --get-edid=pad=,startblock=,blocks=,format=,file=\n" + " is the input or output index for which to get the EDID.\n" + " is the first block number you want to read. Default 0.\n" + " is the number of blocks you want to read. Default is\n" + " all blocks.\n" + " is one of:\n" + " hex: hex numbers in ascii text\n" + " raw: can be piped directly into the edid-decode tool\n" + " carray: c-program struct\n" + " If is '-' or not the 'file' argument is not supplied, then the data\n" + " is written to stdout.\n" + ); +} + +static void read_edid_file(FILE *f, struct v4l2_subdev_edid *e) +{ + char value[4] = { 0 }; + int blocks = 1; + int i = 0; + int c; + + fseek(f, SEEK_SET, 0); + e->edid = (unsigned char *)malloc(blocks * 128); + + while ((c = fgetc(f)) != EOF) { + if (isxdigit(c)) { + if (i & 0x01) { + value[1] = c; + e->edid[i/2] = strtoul(value, 0, 16); + } else { + value[0] = c; + } + i++; + if (i / 2 > blocks * 128) { + blocks++; + e->edid = (unsigned char*)realloc(e->edid, blocks * 128); + } + if (blocks > 256) { + fprintf(stderr, "edid file error: too long\n"); + free(e->edid); + e->edid = NULL; + exit(1); + } + } + } + e->blocks = blocks; +} + +static bool crc_ok(unsigned char *b) +{ + unsigned char sum = 0; + int i; + + for (i = 0; i < 128; i++) + sum += b[i]; + return sum == 0; +} + +static void hexdumpedid(FILE *f, struct v4l2_subdev_edid *e) +{ + for (unsigned b = 0; b < e->blocks; b++) { + unsigned char *buf = e->edid + 128 * b; + + for (unsigned i = 0; i < 128; i += 0x10) { + fprintf(f, "%02x", buf[i]); + for (unsigned j = 1; j < 0x10; j++) { + fprintf(f, " %02x", buf[i + j]); + } + fprintf(f, "\n"); + } + if (!crc_ok(buf)) + fprintf(f, "Block has a checksum error\n"); + } +} + +static void rawdumpedid(FILE *f, struct v4l2_subdev_edid *e) +{ + for (unsigned b = 0; b < e->blocks; b++) { + unsigned char *buf = e->edid + 128 * b; + + for (unsigned i = 0; i < 128; i++) + fprintf(f, "%c", buf[i]); + } +} + +static void carraydumpedid(FILE *f, struct v4l2_subdev_edid *e) +{ + fprintf(f, "unsigned char edid[] = {\n"); + for (unsigned b = 0; b < e->blocks; b++) { + unsigned char *buf = e->edid + 128 * b; + + if (b) + fprintf(f, "\n"); + for (unsigned i = 0; i < 128; i += 8) { + fprintf(f, "\t0x%02x,", buf[i]); + for (unsigned j = 1; j < 8; j++) { + fprintf(f, " 0x%02x,", buf[i + j]); + } + fprintf(f, "\n"); + } + if (!crc_ok(buf)) + fprintf(f, "\t/* Block has a checksum error */\n"); + } + fprintf(f, "};\n"); +} + +static void printedid(FILE *f, struct v4l2_subdev_edid *e, enum format gf) +{ + switch (gf) { + default: + case HEX: + hexdumpedid(f, e); + break; + case RAW: + rawdumpedid(f, e); + break; + case CARRAY: + carraydumpedid(f, e); + break; + } +} + +/****************** EDIDs *****************************/ +static uint8_t dvid_edid[128] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x63, 0x3a, 0xaa, 0x55, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x18, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78, + 0x0e, 0x00, 0xb2, 0xa0, 0x57, 0x49, 0x9b, 0x26, + 0x10, 0x48, 0x4f, 0x2f, 0xcf, 0x00, 0x31, 0x59, + 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, + 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x02, 0x3a, + 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x46, 0x00, 0x10, 0x09, 0x00, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, + 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 'v', + '4', 'l', '2', '-', 'd', 'v', 'i', 'd', + 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec +}; +static uint8_t vga_edid[128] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x63, 0x3a, 0xaa, 0x55, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x18, 0x01, 0x03, 0x08, 0x10, 0x09, 0x78, + 0x0a, 0x00, 0xb2, 0xa0, 0x57, 0x49, 0x9b, 0x26, + 0x10, 0x48, 0x4f, 0x2f, 0xcf, 0x00, 0x31, 0x59, + 0x45, 0x59, 0x61, 0x59, 0x81, 0x40, 0x81, 0x80, + 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x02, 0x3a, + 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x45, 0x00, 0x10, 0x09, 0x00, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfd, 0x00, 0x31, 0x55, 0x18, + 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 'v', + '4', 'l', '2', '-', 'v', 'g', 'a', 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5 +}; +static uint8_t hdmi_edid[256] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x63, 0x3a, 0xaa, 0x55, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x18, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78, + 0x0e, 0x00, 0xb2, 0xa0, 0x57, 0x49, 0x9b, 0x26, + 0x10, 0x48, 0x4f, 0x2f, 0xcf, 0x00, 0x31, 0x59, + 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, + 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x02, 0x3a, + 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x46, 0x00, 0x10, 0x09, 0x00, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, + 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 'v', + '4', 'l', '2', '-', 'h', 'd', 'm', 'i', + 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, + + 0x02, 0x03, 0x1a, 0xc0, 0x48, 0xa2, 0x10, 0x04, + 0x02, 0x01, 0x21, 0x14, 0x13, 0x23, 0x09, 0x07, + 0x07, 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0xe2, + 0x00, 0x2a, 0x01, 0x1d, 0x00, 0x80, 0x51, 0xd0, + 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x8a, + 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd7 +}; +/******************************************************/ + + +static struct v4l2_subdev_edid sedid; +static char *file_in; + +static struct v4l2_subdev_edid gedid; +static char *file_out; +static enum format gformat; + +void edid_cmd(int ch, char *optarg) +{ + char *value, *subs; + + switch (ch) { + case OptSetEdid: + memset(&sedid, 0, sizeof(sedid)); + file_in = NULL; + if (!optarg) + break; + subs = optarg; + while (*subs != '\0') { + static const char *const subopts[] = { + "pad", + "edid", + "file", + NULL + }; + switch (parse_subopt(&subs, subopts, &value)) { + case 0: + sedid.pad = strtoul(value, 0, 0); + break; + case 1: + if (!strcmp(value, "dvid")) { + sedid.edid = dvid_edid; + sedid.blocks = sizeof(dvid_edid) / 128; + } else if (!strcmp(value, "vga")) { + sedid.edid = vga_edid; + sedid.blocks = sizeof(vga_edid) / 128; + } else if (!strcmp(value, "hdmi")) { + sedid.edid = hdmi_edid; + sedid.blocks = sizeof(hdmi_edid) / 128; + } else { + edid_usage(); + exit(1); + } + if (file_in) { + fprintf(stderr, "The edid and file options can't be used together.\n"); + exit(1); + } + break; + case 2: + if (value) { + file_in = value; + if (sedid.edid) { + fprintf(stderr, "The edid and file options can't be used together.\n"); + exit(1); + } + } + break; + default: + edid_usage(); + exit(1); + } + } + break; + + case OptGetEdid: + memset(&gedid, 0, sizeof(gedid)); + gedid.blocks = 256; /* default all blocks */ + gformat = HEX; /* default hex output */ + file_out = NULL; + if (!optarg) + break; + subs = optarg; + while (*subs != '\0') { + static const char *const subopts[] = { + "pad", + "startblock", + "blocks", + "format", + "file", + NULL + }; + switch (parse_subopt(&subs, subopts, &value)) { + case 0: + gedid.pad = strtoul(value, 0, 0); + break; + case 1: + gedid.start_block = strtoul(value, 0, 0); + if (gedid.start_block > 255) { + fprintf(stderr, "startblock %d too large, max 255\n", gedid.start_block); + exit(1); + } + break; + case 2: + gedid.blocks = strtoul(value, 0, 0); + break; + case 3: + if (!strcmp(value, "hex")) { + gformat = HEX; + } else if (!strcmp(value, "raw")) { + gformat = RAW; + } else if (!strcmp(value, "carray")) { + gformat = CARRAY; + } else { + edid_usage(); + exit(1); + } + break; + case 4: + if (value) + file_out = value; + break; + default: + edid_usage(); + exit(1); + } + } + if (gedid.start_block + gedid.blocks > 256) + gedid.blocks = 256 - gedid.start_block; + } +} + +void edid_set(int fd) +{ + if (options[OptSetEdid]) { + FILE *fin = NULL; + + if (file_in) { + if (!strcmp(file_in, "-")) + fin = stdin; + else + fin = fopen(file_in, "r"); + if (!fin) { + fprintf(stderr, "Failed to open %s: %s\n", file_in, + strerror(errno)); + exit(1); + } + } + if (fin) + read_edid_file(fin, &sedid); + doioctl(fd, VIDIOC_SUBDEV_S_EDID, &sedid); + if (fin) { + if (sedid.edid) { + free(sedid.edid); + sedid.edid = NULL; + } + if (fin != stdin) + fclose(fin); + } + } +} + +void edid_get(int fd) +{ + if (options[OptGetEdid]) { + FILE *fout = stdout; + + if (file_out) { + if (!strcmp(file_out, "-")) + fout = stdout; + else + fout = fopen(file_out, "w+"); + if (!fout) { + fprintf(stderr, "Failed to open %s: %s\n", file_out, + strerror(errno)); + exit(1); + } + } + gedid.edid = (unsigned char *)malloc(gedid.blocks * 128); + if (doioctl(fd, VIDIOC_SUBDEV_G_EDID, &gedid) == 0) + printedid(fout, &gedid, gformat); + if (file_out && fout != stdout) + fclose(fout); + free(gedid.edid); + } +} diff --git a/utils/v4l2-ctl/v4l2-ctl.cpp b/utils/v4l2-ctl/v4l2-ctl.cpp index 6baae8e..ae22fed 100644 --- a/utils/v4l2-ctl/v4l2-ctl.cpp +++ b/utils/v4l2-ctl/v4l2-ctl.cpp @@ -88,6 +88,7 @@ static struct option long_options[] = { {"help-selection", no_argument, 0, OptHelpSelection}, {"help-misc", no_argument, 0, OptHelpMisc}, {"help-streaming", no_argument, 0, OptHelpStreaming}, + {"help-edid", no_argument, 0, OptHelpEdid}, {"help-all", no_argument, 0, OptHelpAll}, {"wrapper", no_argument, 0, OptUseWrapper}, {"concise", no_argument, 0, OptConcise}, @@ -191,6 +192,8 @@ static struct option long_options[] = { {"try-encoder-cmd", required_argument, 0, OptTryEncoderCmd}, {"decoder-cmd", required_argument, 0, OptDecoderCmd}, {"try-decoder-cmd", required_argument, 0, OptTryDecoderCmd}, + {"set-edid", optional_argument, 0, OptSetEdid}, + {"get-edid", optional_argument, 0, OptGetEdid}, {"tuner-index", required_argument, 0, OptTunerIndex}, {"list-buffers", no_argument, 0, OptListBuffers}, {"list-buffers-out", no_argument, 0, OptListBuffersOut}, @@ -229,6 +232,7 @@ static void usage_all(void) selection_usage(); misc_usage(); streaming_usage(); + edid_usage(); } static int test_open(const char *file, int oflag) @@ -915,6 +919,9 @@ int main(int argc, char **argv) case OptHelpStreaming: streaming_usage(); return 0; + case OptHelpEdid: + edid_usage(); + return 0; case OptHelpAll: usage_all(); return 0; @@ -972,6 +979,7 @@ int main(int argc, char **argv) selection_cmd(ch, optarg); misc_cmd(ch, optarg); streaming_cmd(ch, optarg); + edid_cmd(ch, optarg); break; } } @@ -1104,6 +1112,7 @@ int main(int argc, char **argv) selection_set(fd); streaming_set(fd, out_fd); misc_set(fd); + edid_set(fd); /* Get options */ @@ -1118,6 +1127,7 @@ int main(int argc, char **argv) sdr_get(fd); selection_get(fd); misc_get(fd); + edid_get(fd); /* List options */ diff --git a/utils/v4l2-ctl/v4l2-ctl.h b/utils/v4l2-ctl/v4l2-ctl.h index 1a21335..ef7b602 100644 --- a/utils/v4l2-ctl/v4l2-ctl.h +++ b/utils/v4l2-ctl/v4l2-ctl.h @@ -149,6 +149,8 @@ enum Option { OptStreamOutMmap, OptStreamOutUser, OptStreamOutDmaBuf, + OptSetEdid, + OptGetEdid, OptHelpTuner, OptHelpIO, OptHelpStds, @@ -160,6 +162,7 @@ enum Option { OptHelpSelection, OptHelpMisc, OptHelpStreaming, + OptHelpEdid, OptHelpAll, OptLast = 256 }; @@ -300,4 +303,10 @@ void streaming_list(int fd, int out_fd); void fill_buffer(void *buffer, struct v4l2_pix_format *pix); bool precalculate_bars(__u32 pixfmt, unsigned pattern); +// v4l2-ctl-edid.cpp +void edid_usage(void); +void edid_cmd(int ch, char *optarg); +void edid_set(int fd); +void edid_get(int fd); + #endif -- 2.7.4