summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlibdlo <libdlo@displaylink.com>2009-06-12 10:42:50 -0700
committerlibdlo <libdlo@displaylink.com>2009-06-12 10:42:50 -0700
commitfa04eed0f8d6f86ba4569396ebd7d0eec6def4d2 (patch)
tree787da5daa76f7670f9741d6f4498a77991ccc8f2
parentfcaefab225fe5577b4be4400e06103fee805dd11 (diff)
Add path which uses direct video register sets (rather than tables) for preferred EDID mode
Useful also just as documentation of video registers. Warning: Not yet working in all cases, expect a few more git commits to refine. Verified to work on Mimo UM 710 and more recent DL chips. See failure on ASUS VW 223B.
-rw-r--r--src/dlo_mode.c555
-rw-r--r--src/dlo_mode.h18
-rw-r--r--src/dlo_structs.h134
-rw-r--r--src/libdlo.c22
-rw-r--r--src/libdlo.h9
5 files changed, 441 insertions, 297 deletions
diff --git a/src/dlo_mode.c b/src/dlo_mode.c
index c5ade6e..c0eb1be 100644
--- a/src/dlo_mode.c
+++ b/src/dlo_mode.c
@@ -1,4 +1,4 @@
-/** @file dlo_mode.c
+/*
*
* @brief Implementation of the screen mode-related functions.
*
@@ -144,149 +144,6 @@ typedef struct dlo_mode_data_s
} dlo_mode_data_t; /**< A struct @a dlo_mode_data_s. */
-/** Established timing information, derived from EDID.
- */
-typedef struct est_timing_s
-{
- uint16_t width; /**< Width of mode (pixels). */
- uint16_t height; /**< Height of mode (pixels). */
- uint8_t refresh; /**< Mode refresh rate (Hz). */
-} est_timing_t; /**< A struct @a est_timings_s. */
-
-
-/** Vendor/product information.
- */
-typedef struct edid_prod_id_s
-{
- uint16_t manuf_name; /**< ID manufacturer code. */
- uint16_t prod_code; /**< ID product code. */
- uint32_t serial_num; /**< ID serial number. */
- uint8_t manuf_wk; /**< Week of manufacture. */
- uint8_t manuf_yr; /**< Year of manufacture. */
-} edid_prod_id_t; /**< A struct @a edid_prod_id_s. */
-
-
-/** EDID structure information.
- */
-typedef struct edid_struct_vsn_s
-{
- uint8_t number; /**< Version number. */
- uint8_t revision; /**< Revision number. */
-} edid_struct_vsn_t; /**< A struct @a edid_struct_vsn_s. */
-
-
-/** Basic dislpay parameters/features.
- */
-typedef struct edid_basic_params_s
-{
- uint8_t input_def; /**< Video input definition. */
- uint8_t max_horiz_sz; /**< Maximum horizontal image size (cm). */
- uint8_t max_vert_sz; /**< Maximum vertical image size (cm). */
- float gamma; /**< Display transfer characteristic (gamma). */
- uint8_t features; /**< Feature support. */
-} edid_basic_params_t; /**< A struct @a edid_basic_params_s. */
-
-
-/** Colour characteristics.
- */
-typedef struct edid_colours_s
-{
- uint16_t red_grn_low; /**< Red/green low bits. */
- uint16_t blu_wht_low; /**< Blue/white low bits. */
- uint16_t red_x; /**< Red-x (bits 9-2). */
- uint16_t red_y; /**< Red-y (bits 9-2). */
- uint16_t grn_x; /**< Green-x (bits 9-2). */
- uint16_t grn_y; /**< Green-y (bits 9-2). */
- uint16_t blu_x; /**< Blue-x (bits 9-2). */
- uint16_t blu_y; /**< Blue-y (bits 9-2). */
- uint16_t wht_x; /**< White-x (bits 9-2). */
- uint16_t wht_y; /**< White-y (bits 9-2). */
-} edid_colours_t; /**< A struct @a edid_colours_s. */
-
-
-/** Established timings.
- */
-typedef struct edid_est_timings_s
-{
- uint8_t timings[2]; /**< Bitfields of established timings. */
- uint8_t resvd; /**< Manufacturer's reserved timings. */
-} edid_est_timings_t; /**< A struct @a edid_est_timings_s. */
-
-
-/** Standard timing identification.
- */
-typedef struct edid_std_timing_s
-{
- uint16_t timing_id[8]; /**< Standard timing identification. */
-} edid_std_timing_t; /**< A struct @a edid_std_timing_s. */
-
-
-/** Detailed timing description.
- */
-typedef struct edid_detail_s
-{
- bool is_detail; /**< Flag is set to true - the structure is an @a edid_detail_t. */
- float pixclk; /**< Timing parameter. */
- uint32_t hactl; /**< Timing parameter. */
- uint32_t hblankl; /**< Timing parameter. */
- uint32_t hactblankh; /**< Timing parameter. */
- uint32_t vactl; /**< Timing parameter. */
- uint32_t vblankl; /**< Timing parameter. */
- uint32_t vactblankh; /**< Timing parameter. */
- uint32_t hsyncoffl; /**< Timing parameter. */
- uint32_t hsyncwl; /**< Timing parameter. */
- uint32_t vsyncoffvsyncwl; /**< Timing parameter. */
- uint32_t synch; /**< Timing parameter. */
- uint32_t hsizel; /**< Timing parameter. */
- uint32_t vsizel; /**< Timing parameter. */
- uint32_t hvsizeh; /**< Timing parameter. */
- uint8_t hbord; /**< Timing parameter. */
- uint8_t vbord; /**< Timing parameter. */
- uint8_t flags; /**< Timing parameter. */
-} edid_detail_t; /**< A struct @a edid_detail_s. */
-
-
-/** Monitor descriptor.
- */
-typedef struct edid_monitor_desc_s
-{
- bool is_detail; /**< Flag is set to false - the structure is not an @a edid_detail_t. */
- uint8_t unknown[18]; /**< Contents of block are unknown. */
-} edid_monitor_desc_t; /**< A struct @a edid_monitor_desc_s. */
-
-
-/** A timing description block may be either a @a edid_detail_t or a @a edid_monitor_desc_t.
- */
-typedef union edid_timing_desc_u
-{
- edid_detail_t detail; /**< Either a detailed timing description. */
- edid_monitor_desc_t desc; /**< Or a monitor descriptor. */
-} edid_timing_desc_t; /**< A struct @a edid_timing_desc_s. */
-
-
-/** An EDID extension block.
- */
-typedef struct edid_ext_block_s
-{
- uint8_t unknown[128]; /**< Contents of block are unknown. */
-} edid_ext_block_t; /**< A struct @a edid_ext_block_s. */
-
-
-/** An EDID structure.
- */
-typedef struct edid_format_s
-{
- edid_prod_id_t product; /**< Vendor/product information. */
- edid_struct_vsn_t version; /**< EDID structure information. */
- edid_basic_params_t basic; /**< Basic dislpay parameters/features. */
- edid_colours_t colours; /**< Colour characteristics. */
- edid_est_timings_t est_timings; /**< Established timings. */
- edid_std_timing_t std_timings; /**< Standard timing identification. */
- edid_timing_desc_t timings[4]; /**< Timing descriptions. */
- uint8_t ext_blocks; /**< Number of extension blocks. */
-} edid_format_t; /**< A struct @a edid_format_s. */
-
-
/* External scope variables ------------------------------------------------------------*/
@@ -332,6 +189,17 @@ const est_timing_t est_timings[24] =
/* File-scope function declarations ----------------------------------------------------*/
+/** Calcualte refresh rate in hertz
+ *
+ * @param did A parsed, unpacked EDID detailed timing structure
+ *
+ * @return refresh rate in hertz
+ */
+inline uint8_t refresh_hz_from_edid(edid_detail_unpacked_t const * edid) {
+ return (uint8_t)(edid->pixelClock10KHz*10000/
+ ((edid->hBlanking + edid->hActive)*(edid->vBlanking + edid->vActive)))+0.5;
+}
+
/** Append a video register setting command onto the specified char block.
*
* @param dev Pointer to @a dlo_device_t structure.
@@ -366,10 +234,17 @@ static bool bad_edid_checksum(const uint8_t * const ptr, const size_t size);
/** Parse an EDID detailed timing descriptor/mode descriptor.
*
- * @param desc Pointer to descriptor structure to initialise.
- * @param ptr Pointer to base of descriptor in EDID data to parse.
+ * @param monitor Pointer to descriptor structure to initialise.
+ * @param ptr Pointer to base of descriptor in EDID data to parse.
*/
-static void parse_edid_detail_desc(edid_timing_desc_t * const desc, const uint8_t * const ptr);
+static dlo_retcode_t unpack_edid_detailed_timing(edid_detail_unpacked_t * const desc, const uint8_t * const ptr);
+
+/** Set mode based on preferred/default form EDID detailed timing descriptor/mode descriptor.
+ *
+ * @param dev Pointer to @a dlo_device_t structure.
+ * @param timing Pointer to @a edid_detail_unpacked_t structure.
+ */
+static dlo_retcode_t mode_set_from_edid(dlo_device_t * const dev, edid_detail_unpacked_t *edid, uint32_t base);
/** Parse EDID colour characteristics.
@@ -423,7 +298,7 @@ static uint16_t add_supported(dlo_device_t * const dev, uint16_t idx, const uint
*
* @return Return code, zero for no error.
*/
-static dlo_retcode_t lookup_edid_modes(dlo_device_t * const dev, const edid_format_t * const edid);
+static dlo_retcode_t lookup_edid_modes(dlo_device_t * const dev, const dlo_edid_t * const edid);
/** Program the base addresses of the video display in the device.
@@ -526,10 +401,17 @@ dlo_retcode_t dlo_mode_change(dlo_device_t * const dev, const dlo_mode_t * const
return mode_select(dev, desc, mode);
}
+dlo_retcode_t dlo_mode_set_default(dlo_device_t * const dev, uint32_t base)
+{
+ /* The first timing block is the preferred mode of the monitor */
+ DPRINTF("mode: dlo_mode_set_default (setting monitor preferred mode)\n");
+ return mode_set_from_edid(dev, &dev->edid.timings[0], base);
+}
+
dlo_retcode_t dlo_mode_parse_edid(dlo_device_t * const dev, const uint8_t * const ptr, const size_t size)
{
- static edid_format_t edid;
+ dlo_edid_t *edid = &dev->edid;
uint32_t i;
/* Copy the header bytes straight into our structure (assumes the structure is correct size!) */
@@ -552,47 +434,47 @@ dlo_retcode_t dlo_mode_parse_edid(dlo_device_t * const dev, const uint8_t * cons
}
#endif
- edid.product.manuf_name = LETOHS(RD_S(ptr + 0x08));
- edid.product.prod_code = LETOHS(RD_S(ptr + 0x0A));
- edid.product.serial_num = LETOHL(RD_L(ptr + 0x0C));
- edid.product.manuf_wk = RD_B(ptr + 0x10);
- edid.product.manuf_yr = RD_B(ptr + 0x11);
+ edid->product.manuf_name = LETOHS(RD_S(ptr + 0x08));
+ edid->product.prod_code = LETOHS(RD_S(ptr + 0x0A));
+ edid->product.serial_num = LETOHL(RD_L(ptr + 0x0C));
+ edid->product.manuf_wk = RD_B(ptr + 0x10);
+ edid->product.manuf_yr = RD_B(ptr + 0x11);
//DPRINTF("mode: edid: manuf &%X prod &%X serial &%X wk %u yr %u\n",
- // edid.product.manuf_name, edid.product.prod_code, edid.product.serial_num,
- // edid.product.manuf_wk, edid.product.manuf_yr + 1990);
+ // edid->product.manuf_name, edid->product.prod_code, edid->product.serial_num,
+ // edid->product.manuf_wk, edid->product.manuf_yr + 1990);
/* Parse the EDID structure information */
- edid.version.number = RD_B(ptr + 0x12);
- edid.version.revision = RD_B(ptr + 0x13);
- //DPRINTF("edid: version &%02X revision &%02X\n", edid.version.number, edid.version.revision);
+ edid->version.number = RD_B(ptr + 0x12);
+ edid->version.revision = RD_B(ptr + 0x13);
+ //DPRINTF("edid: version &%02X revision &%02X\n", edid->version.number, edid->version.revision);
/* Parse the basic dislpay parameters/features */
- edid.basic.input_def = RD_B(ptr + 0x14);
- edid.basic.max_horiz_sz = RD_B(ptr + 0x15);
- edid.basic.max_vert_sz = RD_B(ptr + 0x16);
- edid.basic.gamma = (100.0 + RD_B(ptr + 0x17)) / 100.0;
- edid.basic.features = RD_B(ptr + 0x18);
+ edid->basic.input_def = RD_B(ptr + 0x14);
+ edid->basic.max_horiz_sz = RD_B(ptr + 0x15);
+ edid->basic.max_vert_sz = RD_B(ptr + 0x16);
+ edid->basic.gamma = (100.0 + RD_B(ptr + 0x17)) / 100.0;
+ edid->basic.features = RD_B(ptr + 0x18);
/* Parse the colour characteristics */
- parse_edid_colours(&(edid.colours), ptr + 0x19);
+ parse_edid_colours(&(edid->colours), ptr + 0x19);
/* Parse the established timings */
- edid.est_timings.timings[0] = RD_B(ptr + 0x23);
- edid.est_timings.timings[1] = RD_B(ptr + 0x24);
- edid.est_timings.resvd = RD_B(ptr + 0x25);
+ edid->est_timings.timings[0] = RD_B(ptr + 0x23);
+ edid->est_timings.timings[1] = RD_B(ptr + 0x24);
+ edid->est_timings.resvd = RD_B(ptr + 0x25);
/* Parse the bits at the end of the EDID structure */
- edid.ext_blocks = RD_B(ptr + 0x7E);
+ edid->ext_blocks = RD_B(ptr + 0x7E);
/* Parse all of the standard timing identification */
- for (i = 0; i < sizeof(edid.std_timings.timing_id); i++)
- edid.std_timings.timing_id[i] = RD_B(ptr + 0x26 + i);
-
- /* Parse all of the detailed timing descriptions (or monitor descriptors) */
- for (i = 0; i < 4; i++)
- parse_edid_detail_desc(&(edid.timings[i]), ptr + 0x36 + (i * 0x12));
+ for (i = 0; i < sizeof(edid->std_timings.timing_id); i++)
+ edid->std_timings.timing_id[i] = RD_B(ptr + 0x26 + i);
- return lookup_edid_modes(dev, &edid);
+ /* Parse all of the detailed timing descriptions (monitor descriptors not supported) */
+ for (i = 0; i < 4; i++) {
+ unpack_edid_detailed_timing(&edid->timings[i], ptr + 0x36 + (i * 0x12));
+ }
+ return lookup_edid_modes(dev, edid);
}
@@ -649,9 +531,9 @@ static dlo_modenum_t get_mode_number(dlo_device_t * const dev, const uint16_t wi
{
/* Read the mode number from the device's supported modes array */
num = dev->supported[idx];
- if (num == DLO_INVALID_MODE)
+ if (num == DLO_INVALID_MODE) {
break;
-
+ }
/* This sequence of if statements looks odd, take care if you decide to 'optimise' it! */
if (dlo_mode_data[num].width != width)
continue;
@@ -670,6 +552,165 @@ static dlo_modenum_t get_mode_number(dlo_device_t * const dev, const uint16_t wi
return DLO_INVALID_MODE;
}
+/** Perform lfsr16 encoding of the argument.
+ *
+ * @param v Value to transform.
+ *
+ * @return Encoded value.
+ *
+ * Inefficient computation of a value as encoded by an 16 bit linear feedback
+ * shift register. Better implementations might use look-up tables or the like
+ * (which would require around 128KB of RAM).
+ *
+ * LFSR isn't actually in the mode registers for encryption purposes
+ * (which is why it doesn't apply to all registers). It's actually
+ * for convenience on the hardware side, as the registers are used as counters
+ */
+static uint32_t lfsr16(uint32_t v)
+{
+ static uint32_t prev = 0;
+ static uint32_t _v = 0xFFFF;
+
+ if (v != prev)
+ {
+ _v = 0xFFFF;
+ while (v--)
+ _v = ((_v << 1) | (((_v >> 15) ^ (_v >> 4) ^ (_v >> 2) ^ (_v >> 1)) & 1)) & 0xFFFF;
+ prev = v;
+ }
+ return _v;
+}
+
+inline dlo_retcode_t vreg_big_endian(dlo_device_t * const dev, uint8_t reg, uint16_t val){
+
+ if (IS_BIGENDIAN()) {
+ val = swap_endian_s(val);
+ }
+
+ ERR(vreg(dev, reg, val >> 8));
+ ERR(vreg(dev, reg+1, val & 0xFF));
+
+ return dlo_ok;
+}
+
+inline dlo_retcode_t vreg_little_endian(dlo_device_t * const dev, uint8_t reg, uint16_t val){
+
+
+ if (IS_BIGENDIAN()) {
+ val = swap_endian_s(val);
+ }
+
+ ERR(vreg(dev, reg, val & 0xFF));
+ ERR(vreg(dev, reg+1, val >> 8));
+
+ return dlo_ok;
+}
+
+inline dlo_retcode_t vreg_lfsr16(dlo_device_t * const dev, uint8_t reg, uint16_t val){
+
+ uint16_t lsfr_shifted = lfsr16(val);
+
+ return vreg_big_endian(dev, reg, lsfr_shifted);
+}
+
+static dlo_retcode_t edid_to_vreg_commands(dlo_device_t * const dev, edid_detail_unpacked_t *edid, uint8_t color_depth)
+{
+
+ uint16_t xDisplayStart = edid->hBlanking - edid->hSyncOffset;
+ uint16_t xDisplayEnd = xDisplayStart + edid->hActive;
+ uint16_t yDisplayStart = edid->vBlanking - edid->vSyncOffset;
+ uint16_t yDisplayEnd = yDisplayStart + edid->vActive;
+ uint16_t xEndCount = edid->hActive + edid->hBlanking - 1;
+ uint16_t hSyncStart = 1;
+ uint16_t hSyncEnd = edid->hSyncPulseWidth + 1;
+ uint16_t hPixels = edid->hActive;
+ uint16_t yEndCount = edid->vActive + edid->vBlanking;
+ uint16_t vSyncStart = 0;
+ uint16_t vSyncEnd = edid->vSyncPulseWidth;
+ uint16_t vPixels = edid->vActive;
+ uint16_t pixelClock5KHz = edid->pixelClock10KHz*2;
+
+ if (!(edid->bHSyncPositive)) { SWAP(hSyncStart, hSyncEnd); }
+ if (!(edid->bVSyncPositive)) { SWAP(vSyncStart, vSyncEnd); }
+
+ DPRINTF("dl_vreg: xDisplayStart: %u\n", xDisplayStart);
+ DPRINTF("dl_vreg: xDisplayEnd: %u\n", xDisplayEnd);
+ DPRINTF("dl_vreg: yDisplayStart: %u\n", yDisplayStart);
+ DPRINTF("dl_vreg: yDisplayEnd: %u\n", yDisplayEnd);
+ DPRINTF("dl_vreg: xEndCount: %u\n", xEndCount);
+ DPRINTF("dl_vreg: hSyncStart: %u\n", hSyncStart);
+ DPRINTF("dl_vreg: hSyncEnd: %u\n", hSyncEnd);
+ DPRINTF("dl_vreg: hPixels: %u\n", hPixels);
+ DPRINTF("dl_vreg: yEndCount: %u\n", yEndCount);
+ DPRINTF("dl_vreg: vSyncStart: %u\n", vSyncStart);
+ DPRINTF("dl_vreg: vSyncEnd: %u\n", vSyncEnd);
+ DPRINTF("dl_vreg: vPixels: %u\n", vPixels);
+ DPRINTF("dl_vreg: Pixel Clock: %u KHz\n", pixelClock5KHz * 5);
+
+ /* Begin writing to video registers */
+ //ERR(vreg(dev, 0xFF, 0));
+
+ ERR(vreg(dev, 0x00, (color_depth == 16) ? 0 : 1));
+ ERR(vreg_lfsr16(dev, 0x01, xDisplayStart));
+ ERR(vreg_lfsr16(dev, 0x03, xDisplayEnd));
+ ERR(vreg_lfsr16(dev, 0x05, yDisplayStart));
+ ERR(vreg_lfsr16(dev, 0x07, yDisplayEnd));
+ ERR(vreg_lfsr16(dev, 0x09, xEndCount));
+ ERR(vreg_lfsr16(dev, 0x0B, hSyncStart));
+ ERR(vreg_lfsr16(dev, 0x0D, hSyncEnd));
+ ERR(vreg_big_endian(dev, 0x0F, hPixels));
+ ERR(vreg_lfsr16(dev, 0x11, yEndCount));
+ ERR(vreg_lfsr16(dev, 0x13, vSyncStart));
+ ERR(vreg_lfsr16(dev, 0x15, vSyncEnd));
+ ERR(vreg_big_endian(dev, 0x17, vPixels));
+ ERR(vreg_little_endian(dev, 0x1B, pixelClock5KHz));
+
+ /* wake monitor: enable hsync, vsync, and video */
+ ERR(vreg(dev, 0x1F, 0));
+
+ /* flush register changes (write to reg 0xFF) and final null comand */
+ ERR(vbuf(dev, WRITE_VIDREG_UNLOCK, DSIZEOF(WRITE_VIDREG_UNLOCK)));
+
+ return dlo_ok;
+}
+
+static dlo_retcode_t mode_set_from_edid(dlo_device_t * const dev, edid_detail_unpacked_t *edid, uint32_t base) {
+
+ /* EDID standard for detecting if detailed block is populated */
+ if (edid->pixelClock10KHz == 0)
+ return DLO_INVALID_MODE;
+
+ /* Flush the command buffer */
+ if (dlo_ok != dlo_usb_write(dev))
+ return DLO_INVALID_MODE;
+
+ /* Base address must be aligned to a two byte boundary */
+ if (base & 1)
+ return dlo_err_bad_mode;
+
+ dev->mode.view.base = base;
+ dev->base8 = base + (BYTES_PER_16BPP * edid->hActive * edid->vActive);
+ ERR(set_base(dev, dev->mode.view.base, dev->base8));
+
+ //ERR(dlo_usb_std_chan(dev));
+ ERR(edid_to_vreg_commands(dev, edid, 24));
+
+ /* Flush the command buffer */
+ ERR(dlo_usb_write(dev));
+
+ /* Update the device with the new mode details */
+ dev->mode.view.width = edid->hActive;
+ dev->mode.view.height = edid->vActive;
+ dev->mode.view.bpp = 24;
+ dev->mode.refresh = refresh_hz_from_edid(edid);
+
+ DPRINTF("mode: mode_set_from_edid %ux%u @ %u Hz %u bpp (base &%X base8 &%X)\n",
+ dev->mode.view.width, dev->mode.view.height, dev->mode.refresh, dev->mode.view.bpp,
+ dev->mode.view.base, dev->base8);
+
+ return dlo_ok;
+
+}
static dlo_retcode_t mode_select(dlo_device_t * const dev, const dlo_mode_t * const desc, const dlo_modenum_t mode)
{
@@ -729,20 +770,17 @@ static uint16_t add_supported(dlo_device_t * const dev, uint16_t idx, const uint
if (num != DLO_INVALID_MODE)
dev->supported[idx++] = num;
-// DPRINTF("mode: add_supt: %ux%u @ %u Hz, %u bpp = num %u\n", width, height, refresh, 24, (int)num);
+ //DPRINTF("mode: add_supt: %ux%u @ %u Hz, %u bpp = num %u\n", width, height, refresh, 24, (int)num);
return idx;
}
-static dlo_retcode_t lookup_edid_modes(dlo_device_t * const dev, const edid_format_t * const edid)
+static dlo_retcode_t lookup_edid_modes(dlo_device_t * const dev, const dlo_edid_t * const edid)
{
uint32_t timings, bit;
uint32_t idx = 0;
- /* Clear the native mode information for the device */
- (void) dlo_memset(&dev->native, 0, sizeof(dlo_mode_t));
-
/* Add mode numbers for any supported established timing modes */
timings = edid->est_timings.timings[0] | (edid->est_timings.timings[1] << 8) | (edid->est_timings.resvd << 16);
for (bit = 0; bit < 24; bit++)
@@ -751,45 +789,6 @@ static dlo_retcode_t lookup_edid_modes(dlo_device_t * const dev, const edid_form
idx = add_supported(dev, idx, est_timings[bit].width, est_timings[bit].height, est_timings[bit].refresh);
}
- /* Add further support from the detailed timing descriptions */
- for (timings = 0; timings < 4; timings++)
- {
- const edid_detail_t * const detail = &(edid->timings[timings].detail);
-
- if (detail->is_detail)
- {
- uint8_t hz;
-
- for (hz = 50; hz < 100; hz++)
- {
- uint32_t prev = idx;
- uint32_t width = detail->hactl + ((detail->hactblankh & 0xF0) << 4);
- uint32_t height = detail->vactl + ((detail->vactblankh & 0xF0) << 4);
-
- idx = add_supported(dev, idx, width, height, hz);
-
- /* Have we added a mode definition for the native resolution reported by the display? */
- if (idx != prev)
- {
- dlo_modenum_t num = dev->supported[prev];
-
- dev->native.view.base = 0;
- dev->native.view.width = dlo_mode_data[num].width;
- dev->native.view.height = dlo_mode_data[num].height;
- dev->native.view.bpp = dlo_mode_data[num].bpp;
- dev->native.refresh = dlo_mode_data[num].refresh;
- DPRINTF("mode: lookup: native mode %u (%ux%u @ %u bpp, %uHz base &%X)\n",
- num,
- dev->native.view.width,
- dev->native.view.height,
- dev->native.view.bpp,
- dev->native.refresh,
- (int)dev->native.view.base);
- }
- }
- }
- }
-
/* Fill any remaining array entries with the invalid mode constant */
while (idx < DLO_MODE_DATA_NUM - 1)
dev->supported[idx++] = DLO_INVALID_MODE;
@@ -810,6 +809,7 @@ static dlo_retcode_t set_base(dlo_device_t * const dev, const dlo_ptr_t base, co
ERR(vreg(dev, 0x28, base8));
ERR(vbuf(dev, WRITE_VIDREG_UNLOCK, DSIZEOF(WRITE_VIDREG_UNLOCK)));
ERR(dlo_usb_write(dev));
+ //DPRINTF("mode: set_base complete\n");
return dlo_ok;
}
@@ -826,75 +826,48 @@ static bool bad_edid_checksum(const uint8_t * const ptr, const size_t size)
return 0 != csum;
}
-
-static void parse_edid_detail_desc(edid_timing_desc_t * const desc, const uint8_t * const ptr)
-{
- if (RD_B(ptr) || RD_B(ptr + 1) || RD_B(ptr + 2))
- {
- //DPRINTF("edid: found timing detail (&%04X)\n", LETOHS(RD_S(ptr)));
- desc->detail.is_detail = true;
- desc->detail.pixclk = (float)LETOHS(RD_S(ptr)) / 100.0;
- desc->detail.hactl = RD_B(ptr + 0x02);
- desc->detail.hblankl = RD_B(ptr + 0x03);
- desc->detail.hactblankh = RD_B(ptr + 0x04);
- desc->detail.vactl = RD_B(ptr + 0x05);
- desc->detail.vblankl = RD_B(ptr + 0x06);
- desc->detail.vactblankh = RD_B(ptr + 0x07);
- desc->detail.hsyncoffl = RD_B(ptr + 0x08);
- desc->detail.hsyncwl = RD_B(ptr + 0x09);
- desc->detail.vsyncoffvsyncwl = RD_B(ptr + 0x0A);
- desc->detail.synch = RD_B(ptr + 0x0B);
- desc->detail.hsizel = RD_B(ptr + 0x0C);
- desc->detail.vsizel = RD_B(ptr + 0x0D);
- desc->detail.hvsizeh = RD_B(ptr + 0x0E);
- desc->detail.hbord = RD_B(ptr + 0x0F);
- desc->detail.vbord = RD_B(ptr + 0x10);
- desc->detail.flags = RD_B(ptr + 0x11);
-
- //DPRINTF("edid: Pixel Clock: %f MHz\n", (float)desc->detail.pixclk);
- //DPRINTF("edid: H Active pixels: %d\n", desc->detail.hactl + ((desc->detail.hactblankh & 0xF0) << 4));
- //DPRINTF("edid: H Blank pixels: %d\n", desc->detail.hblankl + ((desc->detail.hactblankh & 0x0F) << 8));
- //DPRINTF("edid: V Active pixels: %d\n", desc->detail.vactl + ((desc->detail.vactblankh & 0xF0) << 4));
- //DPRINTF("edid: V Blank pixels: %d\n", desc->detail.vblankl + ((desc->detail.vactblankh & 0x0F) << 8));
- //DPRINTF("edid: H Sync Off: %d\n", desc->detail.hsyncoffl + ((desc->detail.synch & 0xC0) << 2));
- //DPRINTF("edid: H Sync Width: %d\n", desc->detail.hsyncwl + ((desc->detail.synch & 0x30) << 4));
- //DPRINTF("edid: V Sync Off: %d\n", (desc->detail.vsyncoffvsyncwl >> 4) + ((desc->detail.synch & 0x0C) << 6));
- //DPRINTF("edid: V Sync Width: %d\n", (desc->detail.vsyncoffvsyncwl & 0xF) + ((desc->detail.synch & 0x03) << 8));
- //DPRINTF("edid: H size: %d mm\n", desc->detail.hsizel + ((desc->detail.hvsizeh & 0xF0) << 4));
- //DPRINTF("edid: V size: %d mm\n", desc->detail.vsizel + ((desc->detail.hvsizeh & 0x0F) << 8));
- }
- else
- {
- /* It's a mode descriptor - ignore for now */
- //DPRINTF("edid: found a mode descriptor type &%02X\n", RD_B(ptr+3));
- desc->desc.is_detail = false;
- switch (RD_B(ptr+3))
- {
- case 0xFC:
- case 0xFE:
- case 0xFF:
- {
- char buf[14];
- char *chr;
-
- snprintf(buf, 13, "%s", ptr + 5);
- buf[13] = '\0';
- chr = strchr(buf, '\n');
- if (chr)
- *chr = '\0';
- //DPRINTF("edid: monitor string '%s'\n", buf);
- break;
- }
- default:
- {
- //uint32_t i;
- //DPRINTF("edid: ");
- //for (i = 0; i < 0x12; i++)
- // DPRINTF("%02X ", RD_B(ptr+i));
- //DPRINTF("\n");
- }
- }
- }
+static dlo_retcode_t unpack_edid_detailed_timing(edid_detail_unpacked_t * const monitor, const uint8_t * const ptr)
+{
+ monitor->pixelClock10KHz = LETOHS(RD_S(ptr));
+ if (monitor->pixelClock10KHz == 0)
+ return dlo_warn_no_edid_detailed_timing;
+
+ monitor->hActive = ptr[0x02] + ((ptr[0x04] & 0xF0) << 4);
+ monitor->hBlanking = ptr[0x03] + ((ptr[0x04] & 0x0F) << 8);
+ monitor->vActive = ptr[0x05] + ((ptr[0x07] & 0xF0) << 4);
+ monitor->vBlanking = ptr[0x06] + ((ptr[0x07] & 0x0F) << 8);
+ monitor->hSyncOffset = ptr[0x08] + ((ptr[0x0B] & 0xC0) << 2);
+ monitor->hSyncPulseWidth = ptr[0x09] + ((ptr[0x0B] & 0x30) << 4);
+ monitor->vSyncOffset = (ptr[0x0A] >> 4) + ((ptr[0x0B] & 0x0C) << 6);
+ monitor->vSyncPulseWidth = (ptr[0x0A] & 0x0F) + ((ptr[0x0B] & 0x03) << 8);
+ monitor->hImageSizeMm = ptr[0x0C] + ((ptr[0x0E] & 0xF0) << 4);
+ monitor->vImageSizeMm = ptr[0x0D] + ((ptr[0x0E] & 0x0F) << 8);
+ monitor->hBorder = ptr[0x0F];
+ monitor->vBorder = ptr[0x10];
+ monitor->bInterlaced = (ptr[0x11] & 0x80) ? 1 : 0;
+ monitor->bStereo = (ptr[0x11] & 0x60) ? 1 : 0;
+ monitor->bSeparateSync = (ptr[0x11] & 0x18) ? 1 : 0;
+ monitor->bVSyncPositive = (ptr[0x11] & 0x04) ? 1 : 0;
+ monitor->bHSyncPositive = (ptr[0x11] & 0x02) ? 1 : 0;
+ monitor->bStereoMode = (ptr[0x11] & 0x01) ? 1 : 0;
+
+ DPRINTF("edid: Pixel Clock: %u KHz\n", monitor->pixelClock10KHz * 10);
+ DPRINTF("edid: H Active pixels: %u\n", monitor->hActive);
+ DPRINTF("edid: H Blank pixels: %u\n", monitor->hBlanking);
+ DPRINTF("edid: V Active pixels: %u\n", monitor->vActive);
+ DPRINTF("edid: V Blank pixels: %u\n", monitor->vBlanking);
+ DPRINTF("edid: H Sync Offset: %u\n", monitor->hSyncOffset);
+ DPRINTF("edid: H Sync Width: %u\n", monitor->hSyncPulseWidth);
+ DPRINTF("edid: V Sync Offset: %u\n", monitor->vSyncOffset);
+ DPRINTF("edid: V Sync Width: %u\n", monitor->vSyncPulseWidth);
+ DPRINTF("edid: H size: %u mm\n", monitor->hImageSizeMm);
+ DPRINTF("edid: V size: %u mm\n", monitor->vImageSizeMm);
+ DPRINTF("edid: H Border %u\n", monitor->hBorder);
+ DPRINTF("edid: V Border %u\n", monitor->vBorder);
+ DPRINTF("edid: interlaced %u stereo %u separateSync %u\n", monitor->bInterlaced, monitor->bStereo, monitor->bSeparateSync);
+ DPRINTF("edid: bVSyncPositive %u bHSyncPositive %u bStereoMode %u\n", monitor->bVSyncPositive, monitor->bHSyncPositive, monitor->bStereoMode);
+
+ return dlo_ok;
}
static void parse_edid_colours(edid_colours_t * const cols, const uint8_t * const ptr)
diff --git a/src/dlo_mode.h b/src/dlo_mode.h
index 9932139..134398e 100644
--- a/src/dlo_mode.h
+++ b/src/dlo_mode.h
@@ -101,6 +101,24 @@ extern dlo_modenum_t dlo_mode_lookup(dlo_device_t * const dev, const uint16_t wi
*/
extern dlo_retcode_t dlo_mode_change(dlo_device_t * const dev, const dlo_mode_t * const desc, dlo_modenum_t mode);
+/** Set the mode of a DisplayLink chip
+ * to the monitor's preferred mode,
+ * using the EDID detailed timing descriptor
+ * Returns error if EDID detailed timing is not available
+ *
+ * @param dev Pointer to @a dlo_device_t structure.
+ * @param base Base address of view in framebuffer. Can be zero.
+ *
+ * @return Return code, zero for no error.
+ *
+ * Since this data originates from EDID, it is assumed that the
+ * monitor will support the mode. So the only checks will be if
+ * the DisplayLink chip supports it, too.
+ *
+ * Note: Chaging mode does not imply clearing the screen.
+ * Note: this call will cause any buffered commands to be sent to the device.
+ */
+extern dlo_retcode_t dlo_mode_set_default(dlo_device_t * const dev, uint32_t base);
/** Parse the EDID structure read from a display device and build a list of supported modes.
*
diff --git a/src/dlo_structs.h b/src/dlo_structs.h
index 35e600d..b892f81 100644
--- a/src/dlo_structs.h
+++ b/src/dlo_structs.h
@@ -31,6 +31,139 @@
*/
typedef struct dlo_device_s dlo_device_t;
+/** Established timing information, derived from EDID.
+ */
+typedef struct est_timing_s
+{
+ uint16_t width; /**< Width of mode (pixels). */
+ uint16_t height; /**< Height of mode (pixels). */
+ uint8_t refresh; /**< Mode refresh rate (Hz). */
+} est_timing_t; /**< A struct @a est_timings_s. */
+
+
+/** Vendor/product information.
+ */
+typedef struct edid_prod_id_s
+{
+ uint16_t manuf_name; /**< ID manufacturer code. */
+ uint16_t prod_code; /**< ID product code. */
+ uint32_t serial_num; /**< ID serial number. */
+ uint8_t manuf_wk; /**< Week of manufacture. */
+ uint8_t manuf_yr; /**< Year of manufacture. */
+} edid_prod_id_t; /**< A struct @a edid_prod_id_s. */
+
+
+/** EDID structure information.
+ */
+typedef struct edid_struct_vsn_s
+{
+ uint8_t number; /**< Version number. */
+ uint8_t revision; /**< Revision number. */
+} edid_struct_vsn_t; /**< A struct @a edid_struct_vsn_s. */
+
+
+/** Basic dislpay parameters/features.
+ */
+typedef struct edid_basic_params_s
+{
+ uint8_t input_def; /**< Video input definition. */
+ uint8_t max_horiz_sz; /**< Maximum horizontal image size (cm). */
+ uint8_t max_vert_sz; /**< Maximum vertical image size (cm). */
+ float gamma; /**< Display transfer characteristic (gamma). */
+ uint8_t features; /**< Feature support. */
+} edid_basic_params_t; /**< A struct @a edid_basic_params_s. */
+
+
+/** Colour characteristics.
+ */
+typedef struct edid_colours_s
+{
+ uint16_t red_grn_low; /**< Red/green low bits. */
+ uint16_t blu_wht_low; /**< Blue/white low bits. */
+ uint16_t red_x; /**< Red-x (bits 9-2). */
+ uint16_t red_y; /**< Red-y (bits 9-2). */
+ uint16_t grn_x; /**< Green-x (bits 9-2). */
+ uint16_t grn_y; /**< Green-y (bits 9-2). */
+ uint16_t blu_x; /**< Blue-x (bits 9-2). */
+ uint16_t blu_y; /**< Blue-y (bits 9-2). */
+ uint16_t wht_x; /**< White-x (bits 9-2). */
+ uint16_t wht_y; /**< White-y (bits 9-2). */
+} edid_colours_t; /**< A struct @a edid_colours_s. */
+
+
+/** Established timings.
+ */
+typedef struct edid_est_timings_s
+{
+ uint8_t timings[2]; /**< Bitfields of established timings. */
+ uint8_t resvd; /**< Manufacturer's reserved timings. */
+} edid_est_timings_t; /**< A struct @a edid_est_timings_s. */
+
+
+/** Standard timing identification.
+ */
+typedef struct edid_std_timing_s
+{
+ uint16_t timing_id[8]; /**< Standard timing identification. */
+} edid_std_timing_t; /**< A struct @a edid_std_timing_s. */
+
+
+/** Standard EDID Detailed Timing Block, in unpacked form.
+ ** There are 4 of them (18 bytes in packed form)
+ ** in each 128 byte EDID returned from the monitor.
+ ** The first of those 4 is the preferred mode for the monitor.
+ **
+ ** Only the packed form is called out in the standard. This
+ ** unpacked form is just for convenience, since the packed
+ ** form scatters low and higher order bits all over.
+ **
+ ** Units for all items are pixels (horizontal),
+ ** or lines (vertical), unless otherwise called out
+ */
+typedef struct edid_detail_unpacked_s
+{
+ uint16_t pixelClock10KHz; /**< in 10kHz units */
+ uint16_t hActive;
+ uint16_t hBlanking;
+ uint16_t vActive;
+ uint16_t vBlanking;
+ uint16_t hSyncOffset;
+ uint16_t hSyncPulseWidth;
+ uint16_t vSyncOffset;
+ uint16_t vSyncPulseWidth;
+ uint16_t hImageSizeMm; /**< in millimeters */
+ uint16_t vImageSizeMm; /**< in millimeters */
+ uint8_t hBorder;
+ uint8_t vBorder;
+ uint8_t bInterlaced;
+ uint8_t bStereo;
+ uint8_t bSeparateSync;
+ uint8_t bVSyncPositive;
+ uint8_t bHSyncPositive;
+ uint8_t bStereoMode;
+} edid_detail_unpacked_t;
+
+/** An EDID extension block.
+ */
+typedef struct edid_ext_block_s
+{
+ uint8_t unknown[128]; /**< Contents of block are unknown. */
+} edid_ext_block_t; /**< A struct @a edid_ext_block_s. */
+
+/** Struture holding parsed EDID information
+ */
+typedef struct dlo_edid_s
+{
+ edid_prod_id_t product; /**< Vendor/product information. */
+ edid_struct_vsn_t version; /**< EDID structure information. */
+ edid_basic_params_t basic; /**< Basic dislpay parameters/features. */
+ edid_colours_t colours; /**< Colour characteristics. */
+ edid_est_timings_t est_timings; /**< Established timings. */
+ edid_std_timing_t std_timings; /**< Standard timing identification. */
+ edid_detail_unpacked_t timings[4];/**< Detailed timing descriptions. */
+ uint8_t ext_blocks; /**< Number of extension blocks. */
+} dlo_edid_t;
+
/** A mode number used to index a specific mode from the list defined in dlo_mode_data.c.
*/
@@ -73,6 +206,7 @@ struct dlo_device_s
dlo_device_t *next; /**< Pointer to next node on device list. */
dlo_devtype_t type; /**< Type of DisplayLink device. */
char *serial; /**< Pointer to device serial number string. */
+ dlo_edid_t edid; /**< Parsed EDID information from device */
bool claimed; /**< Has the device been claimed by someone? */
bool check; /**< Flag is toggled for each enumeration to spot dead nodes in device list. */
uint32_t timeout; /**< Timeout for bulk communications (milliseconds). */
diff --git a/src/libdlo.c b/src/libdlo.c
index a202e00..6e72ef7 100644
--- a/src/libdlo.c
+++ b/src/libdlo.c
@@ -455,8 +455,7 @@ dlo_dev_t dlo_claim_device(const dlo_dev_t uid, const dlo_claim_t flags, const u
ERR_GOTO(dlo_usb_std_chan(dev));
/* Attempt to change mode into the native resolution of the display (if we have one) */
- if (dev->native.view.width)
- ERR_GOTO(dlo_mode_change(dev, &dev->native, DLO_INVALID_MODE));
+ dlo_mode_set_default(dev, 0);
return uid;
@@ -598,6 +597,25 @@ dlo_retcode_t dlo_set_mode(const dlo_dev_t uid, const dlo_mode_t * const desc)
if (!dev)
return dlo_err_bad_device;
+ /* TODO: clean up the cases we use the direct-from-EDID path, when we
+ * generally clean up (or is that clean out?) the standard mode tables
+ */
+ if ((desc == NULL) || (desc->view.width == 0) ||
+ /* or if width and hight the same as monitor's preferred mode */
+ ((dev->edid.timings[0].pixelClock10KHz) && (desc->view.width == dev->edid.timings[0].hActive) && (desc->view.height == dev->edid.timings[0].vActive))) {
+
+ uint32_t base = 0;
+
+ if (desc) {
+ base = desc->view.base;
+ }
+
+ /* Then do a modeset directly from the preferred mode in the EDID */
+ return dlo_mode_set_default(dev, base);
+ }
+
+ DPRINTF("dlo: set_mode: asking for width %u height %u refresh %u bpp %u\n", desc->view.width, desc->view.height, desc->refresh, desc->view.bpp);
+
/* See if we can provide a mode which matches the required parameters */
num = dlo_mode_lookup(dev, desc->view.width, desc->view.height, desc->refresh, desc->view.bpp);
DPRINTF("dlo: set_mode: lookup %d\n", (int32_t)num);
diff --git a/src/libdlo.h b/src/libdlo.h
index 525e383..3990e09 100644
--- a/src/libdlo.h
+++ b/src/libdlo.h
@@ -246,6 +246,7 @@ typedef enum
dlo_err_usb, /**< A USB-related error: call @c dlo_usb_strerror() for further info. */
/* Warnings... */
dlo_warn_dl160_mode = 0x10000000u, /**< This screen mode may not display correctly on DL120 devices. */
+ dlo_warn_no_edid_detailed_timing, /**< EDID descriptor not detailed timing */
/* User return codes... */
dlo_user_example = 0x80000000 /**< Return codes 0x80000000 to 0xFFFFFFFF are free for user allocation. */
} dlo_retcode_t; /**< Return codes. Used to indicate the success or otherwise of a call to the library. */
@@ -693,13 +694,13 @@ extern dlo_devinfo_t *dlo_device_info(const dlo_dev_t uid);
* then the supported mode list will be derived from the EDID information).
*
* The @a mode parameter describes the desired mode parameters, some of which
- * are optional and may be left as zero/NULL:
+ * are optional and may be left as zero/NULL. The mode itself may be NULL.
*
- * @li width in pixels (always required)
- * @li height in pixels (or zero to use first available of specified width)
+ * @li width in pixels (or zero to use the best match against EDID)
+ * @li height in pixels (or zero to use the best match against EDID)
* @li colour depth in bits per pixel (currently, only 24 is supported)
* @li base address in the device memory (of the origin of the mode's viewport)
- * @li refresh rate, in Hz (or zero to select the first available)
+ * @li refresh rate, in Hz (or zero to select the best match against EDID)
*
* This call will not cause the screen area to be cleared to a 'background'
* colour.