| // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <linux/i2c.h> |
| #include <linux/i2c-dev.h> |
| #include <sys/ioctl.h> |
| #include <sys/time.h> |
| #include <X11/Xlib.h> |
| #include <X11/Xlibint.h> |
| #include <X11/Xproto.h> |
| #include <X11/Xatom.h> |
| #include <X11/extensions/Xrandr.h> |
| #include <X11/extensions/dpms.h> |
| |
| #if RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR >= 2) |
| #define HAS_RANDR_1_2 1 |
| #else |
| #error Only tested with xrandr 1.2 |
| #endif |
| |
| #include "audio_utils.h" |
| #include "ch7036.h" |
| #include "edid_utils.h" |
| #include "xrr_utils.h" |
| |
| #define MAX_EDID_EXT 4 |
| |
| #define DEF_DEV "/dev/i2c-2" |
| #define DEF_FW "/lib/firmware/chrontel/fw7036.bin" |
| #define EDID_LOG "/var/log/hdmi_edid.log" |
| |
| /* Test flags used for experiments */ |
| int test_flags = 0; |
| |
| #define CHTEST_FORCEDETECT 0x01 |
| |
| /* These are defined for convenience during development */ |
| /* to allow experiments to be patched in and tested across monitors */ |
| /* They should not be used in final code pushed to mainline tree */ |
| #define CHTEST_EXPERIMENT1 0x100 |
| #define CHTEST_EXPERIMENT2 0x200 |
| |
| |
| /* Internal error codes */ |
| #define ERR_FAILED -1 |
| #define ERR_NEED_RELOAD -2 |
| #define ERR_NEWFW_RETRY -3 |
| #define ERR_READ_FAILED -4 |
| #define ERR_EXTRD_FAILED -5 |
| |
| /* Width fixups for supported displays */ |
| int width_fix_1366x768[4] = {1366, 1024, 960, 1366}; |
| int width_fix_1280x800[4] = {1280, 1066, 1000, 1280}; |
| |
| /* Known timings (from tbl7036.c) */ |
| static struct timing_ch7036 known_modes[] = { |
| { |
| /* Put this first in table and use as default */ |
| 25200, |
| MK_PIXEL_HDMI(0, 1, 1), |
| 0, 0, 1, |
| 800, 640, 16, 96, 525, 480, 10, 2 , |
| MK_SCALE(0, 0), |
| }, |
| { |
| 27027, |
| MK_PIXEL_HDMI(0, 2, 1), |
| 0, 0, 1, |
| 858, 720, 16, 62, 525, 480, 9, 6 , |
| MK_SCALE(0, 0), |
| }, |
| { |
| 74250, |
| MK_PIXEL_HDMI(0, 4, 2), |
| 1, 1, 1, |
| 1650, 1280, 110, 40, 750, 720, 5, 5 , |
| MK_SCALE(0, 0), |
| }, |
| { |
| 148500, |
| MK_PIXEL_HDMI(0, 16, 2), // dvi, fmt, aspect |
| 1, 1, 1, // hpo, vpo, depo |
| 2200, 1920, 88, 44, 1125, 1080, 4, 5, |
| MK_SCALE(0, 0), |
| }, |
| { |
| // Interlaced, put second so it won't be picked on res alone |
| 74250, |
| MK_PIXEL_HDMI(0, 5, 2), // dvi, fmt, aspect |
| 1, 1, 1, // hpo, vpo, depo |
| 2200, 1920, 88, 44, 1125, 1080, 4, 5 , |
| MK_SCALE(0, 0), |
| }, |
| { |
| 27000, |
| MK_PIXEL_HDMI(0, 17, 1), |
| 0, 0, 1, |
| 864, 720, 12, 64, 625, 576, 5, 5, |
| MK_SCALE(0, 0), |
| }, |
| { |
| 74250, |
| MK_PIXEL_HDMI(0, 19, 2), |
| 1, 1, 1, |
| 1980, 1280, 440, 40, 750, 720, 5, 5, |
| MK_SCALE(0, 0), |
| }, |
| { |
| 148500, |
| MK_PIXEL_HDMI(0, 31, 2), // dvi, fmt, aspect |
| 1, 1, 1, // hpo, vpo, depo |
| 2640, 1920, 528, 44, 1125, 1080, 4, 5, |
| MK_SCALE(0, 0), |
| }, |
| { |
| // Interlaced, put second |
| 74250, |
| MK_PIXEL_HDMI(0, 20, 2), // dvi, fmt, aspect |
| 1, 1, 1, // hpo, vpo, depo |
| 2640, 1920, 528, 44, 1125, 1080, 4, 5, |
| MK_SCALE(0, 0), |
| }, |
| |
| ////// DVI modes |
| { |
| 25200, |
| MK_PIXEL_HDMI(1, 0, 0), |
| 1, 1, 1, |
| 800, 640, 16, 96, 525, 480, 10, 2 , |
| MK_SCALE(0, 0), |
| }, |
| { |
| 27027, |
| MK_PIXEL_HDMI(1, 0, 0), |
| 1, 1, 1, |
| 858, 720, 16, 62, 525, 480, 9, 6 , |
| MK_SCALE(0, 0), |
| }, |
| { |
| 27000, |
| MK_PIXEL_HDMI(1, 0, 0), |
| 1, 1, 1, |
| 864, 720, 12, 64, 625, 576, 5, 5, |
| MK_SCALE(0, 0), |
| }, |
| { |
| 40000, |
| MK_PIXEL_HDMI(1, 0, 0), |
| 1, 1, 1, |
| 1056, 800, 40, 128, 628, 600, 1, 4, |
| MK_SCALE(0, 0), |
| }, |
| { |
| 65000, |
| MK_PIXEL_HDMI(1, 0, 0), |
| 1, 1, 1, |
| 1344, 1024, 24, 136, 806, 768, 3, 6, |
| MK_SCALE(0, 0), |
| }, |
| { |
| 74250, |
| MK_PIXEL_HDMI(1, 0, 0), |
| 1, 1, 1, |
| 1650, 1280, 110, 40, 750, 720, 5, 5 , |
| MK_SCALE(0, 0), |
| }, |
| { |
| 108000, |
| MK_PIXEL_HDMI(1, 0, 0), |
| 1, 1, 1, |
| 1688, 1280, 48, 112, 1066, 1024, 1, 3, |
| MK_SCALE(0, 0), |
| }, |
| { |
| 88750, |
| MK_PIXEL_HDMI(1, 0, 0), |
| 1, 1, 1, |
| 1600, 1440, 48, 32, 926, 900, 3, 6, |
| MK_SCALE(0, 0), |
| }, |
| { |
| 119000, |
| MK_PIXEL_HDMI(1, 0, 0), |
| 1, 1, 1, |
| 1840, 1680, 48, 32, 1080, 1050, 3, 6, |
| MK_SCALE(0, 0), |
| }, |
| { |
| 148500, |
| MK_PIXEL_HDMI(1, 0, 0), // dvi, fmt, aspect |
| 1, 1, 1, // hpo, vpo, depo |
| 2200, 1920, 88, 44, 1125, 1080, 4, 5, |
| MK_SCALE(0, 0), |
| }, |
| { |
| 162000, |
| MK_PIXEL_HDMI(1, 0, 0), |
| 1, 1, 1, |
| 2160, 1600, 64, 192, 1250, 1200, 1, 3, |
| MK_SCALE(0, 0), |
| }, |
| //===== |
| { |
| 130250, |
| MK_PIXEL_HDMI(1, 0, 0), |
| 1, 1, 1, |
| 1760, 1600, 48, 32, 1235,1200, 3, 4 , |
| MK_SCALE(0, 0), |
| }, |
| { |
| 161000, |
| MK_PIXEL_HDMI(1, 0, 0), |
| 1, 1, 1, |
| 2160, 1600,112, 168, 1245,1200, 3, 4 , |
| MK_SCALE(0, 0), |
| }, |
| |
| |
| }; |
| #define N_KNOWN_MODES (sizeof(known_modes)/sizeof(struct timing_ch7036)) |
| |
| |
| struct timeval mkwin_tout; |
| |
| |
| Window x_start_blank(Display *dpy, int screen, Window root, int lw, int lh) |
| { |
| Window black_window; |
| |
| XSetWindowAttributes attr; |
| XEvent expev; |
| struct timeval tv; |
| const struct timeval tout = {10, 500000}; |
| const struct timeval animate_delay = {0, 300000}; |
| |
| int seen_expose; |
| attr.event_mask = ExposureMask; |
| attr.background_pixel = BlackPixel(dpy, screen); |
| attr.border_pixel = BlackPixel(dpy, screen); |
| |
| black_window = XCreateWindow(dpy, root, |
| 0, 0, lw, lh, /* Full screen size */ |
| 0, 0, /* No border */ |
| InputOutput, (Visual *)CopyFromParent, |
| (CWBackPixel | CWBorderPixel | CWEventMask), |
| &attr); |
| XMapWindow(dpy, black_window); |
| |
| /* Need for the window to be created before we resize the root! */ |
| /* This is more tricky than it should be */ |
| /* The initial Expose event is for the whole window */ |
| /* even though it is still being animated on to the actual screen */ |
| /* So Im going to wait animate_delay seconds after */ |
| /* Same timer mechanism for 10 sec total timeout */ |
| gettimeofday(&tv, NULL); |
| timeradd(&tv, &tout, &mkwin_tout); |
| do { |
| seen_expose = XCheckWindowEvent(dpy, black_window, |
| ExposureMask, |
| (XEvent *)&expev); |
| gettimeofday(&tv, NULL); |
| if (seen_expose) { |
| timeradd(&tv, &animate_delay, &mkwin_tout); |
| break; |
| } |
| } while (timercmp(&tv, &mkwin_tout, <)); |
| return black_window; |
| } |
| |
| void set_x_size(Display *dpy, Window root, int w, int h, |
| int use_black, Window black_window) |
| { |
| XConfigureEvent ev; |
| XEvent expev; |
| struct timeval tv; |
| |
| /* Wait round if the timeout didn't expire */ |
| if (use_black) do { |
| /* Do the check to let X flush things */ |
| XCheckWindowEvent(dpy, black_window, ExposureMask, &expev); |
| gettimeofday(&tv, NULL); |
| } while (timercmp(&tv, &mkwin_tout, <)); |
| |
| ev.type = ConfigureNotify; |
| ev.display = dpy; |
| ev.serial = 0; |
| ev.send_event = True; |
| ev.event = root; |
| ev.window = root; |
| ev.x = 0; |
| ev.y = 0; |
| ev.width = w; |
| ev.height = h; |
| ev.border_width = 0; |
| ev.above = None; |
| ev.override_redirect = 0; |
| XSendEvent(dpy, root, False, |
| StructureNotifyMask|SubstructureNotifyMask, (XEvent *)&ev); |
| XFlush(dpy); |
| if (use_black) { |
| XDestroyWindow(dpy, black_window); |
| XFlush(dpy); |
| } |
| } |
| |
| |
| void usage(char *prog) |
| { |
| fprintf(stderr, |
| "Usage: %s [options]\n", prog); |
| fprintf(stderr, "-d<filename>: set i2c device to use (default %s)\n", |
| DEF_DEV); |
| fprintf(stderr, "-g<filename>: enable and set gpio device for HDMI detect\n"); |
| fprintf(stderr, "-f<filename>: set firmware to use (default %s)\n", DEF_FW); |
| fprintf(stderr, "-n: Use dummy i2c device\n"); |
| fprintf(stderr, "-v: Verbose, print every I2C access\n"); |
| fprintf(stderr, "-p: Probe: check I2C access and Chrontel ID and exit.\n"); |
| fprintf(stderr, "-W<width>,<aspect>: Force aspet and max width\n"); |
| fprintf(stderr, "-E<number>: Force use of test EDID number\n"); |
| fprintf(stderr, "-T<number>: Enable test flags based on number\n"); |
| fprintf(stderr, " 0x%03x - Force detection of monitor\n", |
| CHTEST_FORCEDETECT); |
| fprintf(stderr, " 0x%03x - Use experiment 1 (if there is one)\n", |
| CHTEST_EXPERIMENT1); |
| fprintf(stderr, " 0x%03x - Use experiment 2 (if there is one)\n", |
| CHTEST_EXPERIMENT2); |
| fprintf(stderr, "-m: list known modes\n"); |
| fprintf(stderr, "-M<number>: Force use of mode number\n"); |
| fprintf(stderr, "-x: Fixup X server width\n"); |
| fprintf(stderr, "-X: Fixup X server width and exit\n"); |
| |
| exit(1); |
| } |
| |
| |
| |
| int get_native(unsigned char *data, struct timing_ch7036* prefer, int* aspect, |
| int forcen_a, int verbose) |
| { |
| int seen_pref = 0; |
| int interlace = 0; |
| int i; |
| int n_dtds = EDID_N_DTDS; |
| int has_hdmi = edid_has_hdmi_info(data, data[EDID_EXT_FLAG] ? 1:0); |
| |
| /* In HDMI case know start of extra DTDs and max available space */ |
| /* This gives a max, some could be in the 00 padding area */ |
| if (has_hdmi) |
| n_dtds += (CEA_LAST_PAD - data[EDID_SIZE+CEA_DTD_OFFSET])/DTD_SIZE; |
| |
| for(i = 0; i < n_dtds; i++) { |
| int pelclk; |
| unsigned char* base; |
| |
| /* First DTDs are in the main EDID, any more are in the extension */ |
| if (i < EDID_N_DTDS) |
| base = data + EDID_DTD_BASE + DTD_SIZE*i; |
| else |
| base = (data + EDID_SIZE + data[EDID_SIZE+CEA_DTD_OFFSET] + |
| DTD_SIZE*(i-EDID_N_DTDS)); |
| |
| /* In extension block this will be 0 when we hit padding */ |
| /* in main block 0 indicates something other than DTD */ |
| pelclk = base[DTD_PCLK_LO] + (base[DTD_PCLK_HI]<<8); |
| |
| if (pelclk != 0) { |
| int hres = base[DTD_HA_LO] + ((base[DTD_HABL_HI] & 0xf0)<<4); |
| int hbl = base[DTD_HBL_LO] + ((base[DTD_HABL_HI] & 0x0f)<<8); |
| int vres = base[DTD_VA_LO] + ((base[DTD_VABL_HI] & 0xf0)<<4); |
| int vbl = base[DTD_VBL_LO] + ((base[DTD_VABL_HI] & 0x0f)<<8); |
| int hso = base[DTD_HSO_LO] + ((base[DTD_HVSX_HI] & 0xc0)<<2); |
| int hsw = base[DTD_HSW_LO] + ((base[DTD_HVSX_HI] & 0x30)<<4); |
| int vso = (base[DTD_VSX_LO] >> 4) + ((base[DTD_HVSX_HI] & 0x0c)<<2); |
| int vsw = (base[DTD_VSX_LO] & 0xf) + ((base[DTD_HVSX_HI] & 0x03)<<4); |
| int hsiz = base[DTD_HSIZE_LO] + ((base[DTD_HVSIZE_HI] & 0xf0)<<4); |
| int vsiz = base[DTD_VSIZE_LO] + ((base[DTD_HVSIZE_HI] & 0x0f)<<8); |
| //int hbdr = base[DTD_HBORDER]; |
| //int vbdr = base[DTD_VBORDER]; |
| int mdflg = base[DTD_FLAGS]; |
| |
| //int refr = (pelclk * 10000)/((hres+hbl)*(vres+vbl)); |
| //int refm = (pelclk * 10000)%((hres+hbl)*(vres+vbl)); |
| //int refd = (refm*100)/((hres+hbl)*(vres+vbl)); |
| |
| if (hres > CH_MAX_HRES) { |
| if (verbose) printf("Ignore %dx%d mode since too wide for scaler\n", |
| hres, vres); |
| continue; |
| } |
| /* First encountered is the preferred and native */ |
| /* BUT: Seen some devices that list interlaced mode first */ |
| /* If they also have non-interlaced then use that */ |
| /* HDMI monitors don't seem to obey this, there may be a better */ |
| /* mode in the extension block, look for something wider */ |
| if ((!seen_pref) || |
| (seen_pref && interlace && ((mdflg & 0x80) == 0)) || |
| (seen_pref && has_hdmi && |
| ((mdflg & 0x80) == 0) && (hres > prefer->ha))) { |
| |
| prefer->clk_khz = pelclk * 10; |
| prefer->pixel_fmt = (has_hdmi) ? 0 : PIXEL_HDMI_ENCODE_DVI; |
| if (mdflg & 0x80) prefer->pixel_fmt |= PIXEL_HDMI_ENCODE_INTERLACE; |
| prefer->hs_pol = (mdflg & 2) ? POL_HIGH : POL_LOW; |
| prefer->vs_pol = (mdflg & 4) ? POL_HIGH : POL_LOW; |
| prefer->de_pol = POL_HIGH; |
| |
| prefer->ht = hres + hbl; |
| prefer->ha = hres; |
| prefer->ho = hso; |
| prefer->hw = hsw; |
| prefer->vt = vres + vbl; |
| prefer->va = vres; |
| prefer->vo = vso; |
| prefer->vw = vsw; |
| prefer->scale = MK_SCALE(0, 0); |
| |
| *aspect = find_aspect(hres, vres); |
| interlace = (mdflg & 0x80); |
| |
| if (*aspect < 0) { |
| if (verbose) |
| printf("Didn't find aspect from %dx%d, trying size %dx%d\n", |
| hres, vres, hsiz, vsiz); |
| *aspect = find_aspect(hsiz, vsiz); |
| if ((*aspect < 0) && interlace) { |
| /* This is really about 1920x540i, seen in the wild */ |
| if (verbose) printf("No aspect for interlaced, try double v\n"); |
| *aspect = find_aspect(hres, vres*2); |
| } |
| if (*aspect < 0) { |
| if (verbose) printf("Still couldn't find aspect, use 4x3\n"); |
| *aspect = ASPECT_4_3; |
| } else |
| if (verbose) printf("Found aspect %s\n", aspect_to_str[*aspect]); |
| } |
| if ((forcen_a < 0) || (*aspect == forcen_a)) seen_pref = 1; |
| } |
| } |
| } |
| return seen_pref; |
| } |
| |
| /* Using 'fake' standard timing entries to cover some established timings */ |
| #define EST_TIME_640x480x60 (EDID_N_STDTIME+0) |
| #define EST_TIME_800x600x60 (EDID_N_STDTIME+1) |
| #define EST_TIME_1024x768x60 (EDID_N_STDTIME+2) |
| #define KNOWN_EST_TIMES 3 |
| |
| int get_standard(unsigned char *data, |
| struct timing_ch7036 **mon, int *found_aspect, |
| int force_maxw, int force_aspect, int verbose) |
| { |
| int curbest_aspect = -1; |
| struct timing_ch7036 *curbest = NULL; |
| int i, md; |
| int found_md; |
| int native_aspect; |
| |
| if (force_maxw > 0) |
| native_aspect = force_aspect; |
| else |
| native_aspect = find_aspect_fromisize(data); |
| |
| for(i=0; i < (EDID_N_STDTIME + KNOWN_EST_TIMES); i++) { |
| int hinfo = 0x01; |
| int vinfo = 0x01; |
| int hres, vres; |
| |
| if (i < EDID_N_STDTIME) { |
| hinfo = data[EDID_STDTIMEH + STDTIME_SIZE*i]; |
| vinfo = data[EDID_STDTIMEV + STDTIME_SIZE*i]; |
| } |
| /* 8-10 check established timing 60Hz entries */ |
| if ((i == EST_TIME_640x480x60) && (data[EDID_ESTTIME1] & 0x20)) { |
| hinfo = (640-STDTIME_HBASE)/STDTIME_HMULT; |
| vinfo = ASPECT_4_3 << STDTIME_VASPECT_SHIFT; |
| } |
| if ((i == EST_TIME_800x600x60) && (data[EDID_ESTTIME1] & 0x01)) { |
| hinfo = (800-STDTIME_HBASE)/STDTIME_HMULT; |
| vinfo = ASPECT_4_3 << STDTIME_VASPECT_SHIFT; |
| } |
| if ((i == EST_TIME_1024x768x60) && (data[EDID_ESTTIME2] & 0x08)) { |
| hinfo = (1024-STDTIME_HBASE)/STDTIME_HMULT; |
| vinfo = ASPECT_4_3 << STDTIME_VASPECT_SHIFT; |
| } |
| |
| /* 01 01 is pad by spec, but 00 00 and 20 20 are see in wild */ |
| if (((hinfo == 0x01) && (vinfo == 0x01)) || |
| ((hinfo == 0x00) && (vinfo == 0x00)) || |
| ((hinfo == 0x20) && (vinfo == 0x20))) |
| continue; |
| /* Not doing above 60Hz */ |
| if ((vinfo & STDTIME_VREFMINUS60_MASK) != 0) continue; |
| |
| hres = (hinfo * STDTIME_HMULT) + STDTIME_HBASE; |
| if (verbose) printf("Found %d wide %s\n", hres, |
| aspect_to_str[vinfo>>STDTIME_VASPECT_SHIFT]); |
| if ((force_maxw > 0) && (hres > force_maxw)) continue; |
| |
| if (verbose) printf("Try %d wide %s\n", hres, |
| aspect_to_str[vinfo>>STDTIME_VASPECT_SHIFT]); |
| |
| switch (vinfo >> STDTIME_VASPECT_SHIFT) { |
| case ASPECT_16_10: |
| vres = (hres * 10)/16; |
| break; |
| case ASPECT_4_3: |
| vres = (hres * 3)/4; |
| break; |
| case ASPECT_5_4: |
| vres = (hres * 4)/5; |
| break; |
| case ASPECT_16_9: |
| vres = (hres * 9)/16; |
| break; |
| } |
| found_md = -1; |
| for(md = 0; md < N_KNOWN_MODES; md++) { |
| if ((known_modes[md].ha == hres) && (known_modes[md].va == vres)) { |
| // Preference is for a DVI mode, so done when we find the first |
| if (known_modes[md].pixel_fmt & PIXEL_HDMI_ENCODE_DVI) { |
| found_md = md; |
| break; |
| } |
| // If no DVI modes then use the first matching entry |
| if (found_md < 0) found_md = md; |
| } |
| } |
| if (found_md >= 0) { |
| /* Found a mode that we know and monitor can do */ |
| if ((curbest == NULL) || |
| (((vinfo >> STDTIME_VASPECT_SHIFT) == native_aspect) && |
| ((curbest_aspect != native_aspect) || (hres > curbest->ha)))) { |
| /* We had nothing, or we found first or bigger native aspect */ |
| if (verbose) printf("Better mode width %d, aspect %s\n", |
| hres, aspect_to_str[vinfo>>STDTIME_VASPECT_SHIFT]); |
| curbest = known_modes + found_md; |
| curbest_aspect = (vinfo >> STDTIME_VASPECT_SHIFT); |
| } else if (curbest_aspect != native_aspect) { |
| if ((native_aspect == ASPECT_16_10) && |
| ((vinfo >> STDTIME_VASPECT_SHIFT) == ASPECT_16_9) && |
| ((curbest_aspect != ASPECT_16_9) || (hres > curbest->ha))) { |
| /* Looking for 16:10, found first 16:9 or bigger 16:9 */ |
| if (verbose) printf("Want 16:10 use 16:9 width %d, aspect %s\n", |
| hres, |
| aspect_to_str[vinfo>>STDTIME_VASPECT_SHIFT]); |
| curbest = known_modes + found_md; |
| curbest_aspect = (vinfo >> STDTIME_VASPECT_SHIFT); |
| } else if (hres > curbest->ha) { |
| if (verbose) printf("Better based on size: width %d, aspect %s\n", |
| hres, |
| aspect_to_str[vinfo>>STDTIME_VASPECT_SHIFT]); |
| curbest = known_modes + found_md; |
| curbest_aspect = (vinfo >> STDTIME_VASPECT_SHIFT); |
| } |
| } |
| } |
| } |
| if (curbest != NULL) { |
| *mon = curbest; |
| *found_aspect = curbest_aspect; |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* Function added by Chrontel */ |
| /* FIXME: Should this move to ch7036_access.c */ |
| void set_ch7036_config(int i2cdev) |
| { |
| struct config_ch7036 mycfg; |
| uint8 page; |
| |
| mycfg.size = sizeof(struct config_ch7036); |
| //assuming page 1, the read function will return right page |
| page = ch_read_reg(i2cdev, 0x1, 0x03); |
| ch_write_reg(i2cdev, page, 0x03, 0x04); |
| mycfg.deviceid = ch_read_reg(i2cdev,0x4,0x50); |
| |
| mycfg.revisionid = 0x0F & ch_read_reg(i2cdev,0x4,0x51); |
| ch_write_reg(i2cdev,0x4, 0x03, page); // restore orignal page |
| |
| mycfg.comv_off = CFG7036_COMV_OFF_YES; // only work for certain LVDS panel!!! |
| mycfg.comv_off = CFG7036_COMV_OFF_DEFAULT; |
| |
| mycfg.drv_strength = CFG7036_DRV_STRENGTH_DEFAULT; |
| mycfg.fbdly = CFG7036_FBDLY_DEFAULT; |
| mycfg.refdly = CFG7036_REFDLY_DEFAULT; |
| mycfg.lvds_inout = CFG7036_LVDS_INOUT_DEFAULT; |
| |
| SetGlobalConfigCH7036( &mycfg); |
| return; |
| } |
| |
| void show_ch_timing(struct timing_ch7036* timing, char* what) |
| { |
| printf("GenTableCH7036 %s timing set to:\n", what); |
| printf(" %d,\n", timing->clk_khz); |
| printf(" 0x%05x,\n", timing->pixel_fmt); |
| printf(" %d, %d, %d,\n", |
| timing->hs_pol, timing->vs_pol, timing->de_pol); |
| printf(" %d, %d, %d, %d, %d, %d, %d, %d,\n", |
| timing->ht, timing->ha, timing->ho, timing->hw, |
| timing->vt, timing->va, timing->vo, timing->vw); |
| printf(" 0x%04x,\n", timing->scale); |
| } |
| |
| int reloadfw(int i2cdev, char *fwfile, char *progname, |
| int gpiodev, int need_reset, int verbose) |
| { |
| int mcutry = 0; |
| /* This also intializes the changed vector */ |
| if (need_reset) ch_reset_todefault(i2cdev); |
| |
| if (ch_load_firmware(i2cdev, fwfile) < 0) { |
| fprintf(stderr, "%s: Could not load firmware %s\n", progname, fwfile); |
| return ERR_FAILED; |
| } |
| do { |
| if (ch_mcu_version(i2cdev, 1, verbose) > 0) break; |
| sleep(YIELD_FIRMWARE_START_SECS); |
| mcutry++; |
| if (mcutry >= MCU_ATTEMPTS_AFTER_FWLOAD) return ERR_NEED_RELOAD; |
| } while (mcutry < MCU_ATTEMPTS_AFTER_FWLOAD); |
| /* HDMI detection seems a bit bouncy after firmware load */ |
| /* If there was a sleep added here it would delay detection */ |
| /* at startup time, so don't do that */ |
| /* Using the longer ch_mcu_version timeout makes it less liekly */ |
| /* Turning the monitor on here seems to make the problem go away */ |
| /* but it causes LCD flicker so avoid when using gpio detection */ |
| if (gpiodev) { |
| if (verbose) printf("GPIO detection, don't bounce the display\n"); |
| } else { |
| /* Don't use verbose: hdmi setting is don't care for workaround */ |
| ch_monitor_on(i2cdev, 0, 0); |
| ch_monitor_off_keep_ddc(i2cdev); |
| } |
| return 0; |
| } |
| |
| int wait_monitor_attach(int i2cdev, int gpiodev, int verbose) |
| { |
| int needReload = 0; |
| |
| if (gpiodev) { |
| int chkfw = 0; |
| /* Force detect acts as if HPD is always set, so no need for loop */ |
| if (!(test_flags & CHTEST_FORCEDETECT)) { |
| /* Don't pass verbose flag here as 2nd arg, it is too verbose */ |
| while (!needReload && (ch_hdmi_gpio_detected(gpiodev, 0) == 0)) { |
| if (++chkfw > CHECK_FW_INTERVAL_IN_GPIOLOOPS) { |
| chkfw = 0; |
| if (ch_mcu_version(i2cdev, 0, 0) < 0) { |
| if (verbose) printf("Lost Chrontel firmware waiting for gpio\n"); |
| needReload = 1; |
| continue; |
| } |
| } |
| sleep(YIELD_GPIO_DETECT_SECS); |
| } |
| } |
| /* Need to enable DDC to allow EDID read */ |
| ch_write_reg(i2cdev, CH7036_MAGIC2_0E, 0x13); // MAGIC |
| if (!needReload && (ch_mcu_version(i2cdev, 0, 0) < 0)) { |
| if (verbose) printf("GPIO detected but lost the Chrontel firmware\n"); |
| needReload = 1; |
| } |
| } else |
| while (!needReload && |
| !(test_flags & CHTEST_FORCEDETECT) && !ch_hdmi_detected(i2cdev)) { |
| sleep(YIELD_CHRONTEL_DETECT_SECS); |
| /* Check the Chrontel firmware is still alive */ |
| /* Originally only did this when DPMS said screen came on */ |
| /* But that misses the case where lid close takes system to s3 */ |
| if (ch_mcu_version(i2cdev, 0, 0) < 0) { |
| if (verbose) printf("Lost the Chrontel firmware\n"); |
| needReload = 1; |
| } |
| } |
| return needReload; |
| } |
| |
| int get_edid(int i2cdev, unsigned char *edid, int newFirmware, int verbose) |
| { |
| int err = ch_get_edid(i2cdev, 0, &edid[0]); |
| |
| if (err < 0) { |
| if (ch_mcu_version(i2cdev, 0, verbose) < 0) { |
| if (verbose) printf("EDID error because Chrontel firmware lost\n"); |
| return ERR_NEED_RELOAD; |
| } |
| /* EDID timed out, but things seem alive */ |
| if (newFirmware) { |
| if (verbose) |
| printf("Ignore HPD because EDID failed just after firmware load\n"); |
| sleep(YIELD_CHRONTEL_DETECT_SECS); |
| /* Loop back to recheck HPD after sleep */ |
| return ERR_NEWFW_RETRY; |
| } |
| return ERR_READ_FAILED; |
| } else if (!edid_valid(&edid[0])) { |
| if (newFirmware) { |
| if (verbose) |
| printf("Ignore HPD since EDID not valid just after firmware load\n"); |
| sleep(YIELD_CHRONTEL_DETECT_SECS); |
| /* The EDID was read, so next time it is not new fw */ |
| /* Loop back to recheck HPD after sleep */ |
| return ERR_NEWFW_RETRY; |
| } |
| /* Returns ok because EDID was read. It won't be useful... */ |
| return 0; |
| } else if (edid[EDID_EXT_FLAG] > 0) |
| if (ch_get_edid(i2cdev, 1, edid+EDID_SIZE) < 0) return ERR_EXTRD_FAILED; |
| return 0; |
| } |
| |
| |
| int main(int argc, char *argv[]) |
| { |
| int i2cdev; |
| char *i2cfile = DEF_DEV; |
| int gpiodev; |
| char *gpiofile = NULL; |
| char *fwfile = DEF_FW; |
| int arg; |
| int err; |
| Display *dpy; |
| Window root; |
| int screen; |
| Window bwin; |
| XRRModeInfo *xmode; |
| int forcen_x = 0; |
| int forcen_a = -1; |
| int force_edid = 0; |
| int probe_only = 0; |
| int dummy_i2c = 0; |
| int verbose = 0; |
| int *width_fix; |
| unsigned char edid[256]; |
| unsigned char newedid[256]; |
| int use_new_edid = 0; |
| int aspect; |
| int hdmiOn; |
| int mondetect; |
| int expectres; |
| int needReload = 1; /* Force firmware to load on the first loop */ |
| int newFirmware = 1; |
| int force_mode = -1; |
| int fixupx = 0; |
| int only_fixupx = 0; |
| |
| struct reg_ch7036 *reglist; |
| |
| struct timing_ch7036 lcd_timing; |
| struct timing_ch7036 prefer; |
| struct timing_ch7036 *mon_timing; |
| /* First entry in known modes table is 640x480 @ 60Hz */ |
| struct timing_ch7036 *mode_640x480x60 = known_modes; |
| |
| /* Set stdout for easy logs during debug */ |
| setlinebuf(stdout); |
| printf("%s: starts\n", argv[0]); |
| for(arg=1; arg < argc; arg++) { |
| if ((argv[arg][0]) != '-') break; |
| switch (argv[arg][1]) { |
| |
| default: |
| fprintf(stderr, "Unknown option: %s\n", argv[arg]); |
| usage(argv[0]); |
| |
| case 'p': |
| probe_only = 1; |
| break; |
| |
| case 'n': |
| dummy_i2c = 1; |
| break; |
| |
| case 'X': |
| only_fixupx = 1; |
| /* Fall through */ |
| case 'x': |
| fixupx = 1; |
| break; |
| |
| case 'V': |
| ch7036_debugi2c = 1; |
| /* Fall through */ |
| case 'v': |
| verbose = 1; |
| break; |
| |
| case 'd': |
| if (argv[arg][2] != 0) |
| i2cfile = argv[arg] + 2; |
| else { |
| if (++arg < argc) |
| i2cfile = argv[arg]; |
| else { |
| fprintf(stderr, "-d needs filename: %s\n", argv[arg]); |
| usage(argv[0]); |
| } |
| } |
| break; |
| |
| case 'g': |
| if (argv[arg][2] != 0) |
| gpiofile = argv[arg] + 2; |
| else { |
| if (++arg < argc) |
| gpiofile = argv[arg]; |
| else { |
| fprintf(stderr, "-g needs filename: %s\n", argv[arg]); |
| usage(argv[0]); |
| } |
| } |
| break; |
| |
| case 'f': |
| if (argv[arg][2] != 0) |
| fwfile = argv[arg] + 2; |
| else { |
| if (++arg < argc) |
| fwfile = argv[arg]; |
| else { |
| fprintf(stderr, "-f needs filename: %s\n", argv[arg]); |
| usage(argv[0]); |
| } |
| } |
| break; |
| |
| case 'E': |
| force_edid = strtol(argv[arg] + 2, NULL, 10); |
| if ((force_edid < 1) || (force_edid > N_TEST_EDIDS)) { |
| fprintf(stderr, "-E%d out of range, must be 1-%d\n", |
| force_edid, N_TEST_EDIDS); |
| usage(argv[0]); |
| } |
| break; |
| |
| case 'T': |
| test_flags = strtol(argv[arg] + 2, NULL, 0); |
| if (test_flags) |
| fprintf(stderr, "%s: WARNING enabling tests with flags 0x%x\n", |
| argv[0], test_flags); |
| break; |
| |
| case 'W': |
| { |
| char *endn; |
| |
| forcen_x = strtol(argv[arg] + 2, &endn, 10); |
| if (endn[0] != ',') { |
| fprintf(stderr, "Expecting comma (-W<max_width>,<aspect>) in %s\n", |
| argv[arg]); |
| usage(argv[0]); |
| forcen_x = 0; |
| } else |
| forcen_a = strtol(endn + 1, &endn, 10); |
| if (endn != argv[arg]) argv[arg] = endn - 1; |
| } |
| break; |
| |
| case 'm': |
| { |
| int md; |
| |
| for(md = 0; |
| md < sizeof(known_modes)/sizeof(struct timing_ch7036); md++) |
| printf("%2d - %s %dx%d\n", md, |
| ((known_modes[md].pixel_fmt & PIXEL_HDMI_ENCODE_DVI) ? |
| "DVI" : "HDMI"), |
| known_modes[md].ha, known_modes[md].va); |
| exit(0); |
| } |
| case 'M': |
| { |
| char *endn; |
| |
| force_mode = strtol(argv[arg] + 2, &endn, 10); |
| if (endn != argv[arg]) argv[arg] = endn - 1; |
| } |
| } |
| } |
| |
| if (dummy_i2c) |
| i2cdev = -1; |
| else { |
| int tryi2c = probe_only ? 20 : 1; |
| |
| /* udev runs fairly late. */ |
| /* To allow early probe tollerate device not being there for a while */ |
| |
| while (tryi2c > 0) { |
| i2cdev = open(i2cfile, O_RDWR); |
| if (i2cdev > 0) break; |
| |
| if (verbose || !probe_only) |
| fprintf(stderr, "%s: Could not open %s: %s\n", |
| argv[0], i2cfile, strerror(errno)); |
| tryi2c--; |
| |
| if (tryi2c < 1) |
| exit(1); |
| sleep(1); |
| } |
| } |
| /* CH7036 only has one slave address option */ |
| if (dummy_i2c) |
| err = 0; |
| else |
| err = ioctl(i2cdev, I2C_SLAVE, CH7036_I2C_ADDR); |
| |
| if (err < 0) { |
| if (verbose || !probe_only) |
| fprintf(stderr, "%s: Could not set slave address 0x%x: %s\n", |
| argv[0], CH7036_I2C_ADDR, strerror(errno)); |
| exit(1); |
| } |
| |
| /* Read the information */ |
| err = ch_read_reg(i2cdev, CH7036_DEVID); |
| |
| if (verbose) |
| fprintf(stderr, "Found device ID 0x%02x\n", err); |
| |
| if (err != EXPECTED_CH7036_DEVID) { |
| if (verbose || !probe_only) |
| fprintf(stderr, |
| "%s: Fatal: Device ID 0x%02x not the expected 0x%02x\n", |
| argv[0], err, EXPECTED_CH7036_DEVID); |
| exit(1); |
| } |
| |
| if (probe_only) |
| exit(0); |
| |
| if (verbose) |
| fprintf(stderr, "Found revision ID 0x%02x\n", |
| ch_read_reg(i2cdev, CH7036_REVID)); |
| |
| if (gpiofile != NULL) { |
| gpiodev = open(gpiofile, O_RDONLY); |
| if (gpiodev < 0) { |
| fprintf(stderr, "Could not open %s: %s\n", gpiofile, strerror(errno)); |
| exit(1); |
| } |
| } else |
| gpiodev = 0; |
| |
| dpy = XOpenDisplay(":0.0"); |
| if (dpy == NULL) { |
| fprintf(stderr, "%s: Can't open display :0.0\n", argv[0]); |
| exit(1); |
| } |
| screen = DefaultScreen (dpy); |
| root = RootWindow (dpy, screen); |
| xmode = get_x_mode_info(dpy, root); |
| if (verbose) showmode(xmode); |
| |
| if (fixupx) { |
| set_x_size(dpy, root, xmode->width, xmode->height, 0, 0); |
| if (only_fixupx) exit(0); |
| } |
| |
| if ((xmode->width == 1366) && (xmode->height == 768)) { |
| width_fix = width_fix_1366x768; |
| } else if ((xmode->width == 1280) && (xmode->height == 800)) { |
| width_fix = width_fix_1280x800; |
| } else |
| width_fix = NULL; |
| |
| lcd_timing.clk_khz = xmode->dotClock/1000; |
| lcd_timing.pixel_fmt = PIXEL_FMT_18BIT; |
| lcd_timing.hs_pol = (xmode->modeFlags & RR_HSyncPositive) ? 1 : 0; |
| lcd_timing.vs_pol = (xmode->modeFlags & RR_VSyncPositive) ? 1 : 0; |
| lcd_timing.de_pol = 1; |
| |
| lcd_timing.ht = xmode->hTotal; |
| lcd_timing.ha = xmode->width; |
| lcd_timing.ho = xmode->hSyncStart - xmode->width; |
| lcd_timing.hw = xmode->hSyncEnd - xmode->hSyncStart; |
| lcd_timing.vt = xmode->vTotal; |
| lcd_timing.va = xmode->height; |
| lcd_timing.vo = xmode->vSyncStart - xmode->height; |
| lcd_timing.vw = xmode->vSyncEnd - xmode->vSyncStart; |
| lcd_timing.scale = MK_SCALE(0, 0); |
| |
| audio_init(verbose); |
| |
| while (1) { |
| int new_width; |
| int new_height; |
| FILE* edidlog; |
| char *why_res = "nothing"; |
| char *why_aud; |
| int enable_audio = 0; |
| int output_hdmi = 0; |
| int use_black_window; |
| int ares; |
| if ((edidlog = fopen(EDID_LOG, "w")) != NULL) { |
| fprintf(edidlog, "No HDMI device detected\n"); |
| fclose(edidlog); |
| } |
| |
| if (needReload) |
| needReload = reloadfw(i2cdev, fwfile, argv[0], gpiodev, 1, verbose); |
| if (needReload == ERR_FAILED) exit(1); |
| if (needReload) continue; |
| |
| needReload = wait_monitor_attach(i2cdev, gpiodev, verbose); |
| /* Recycle the outer while loop to hit the fw reload code */ |
| if (needReload) continue; |
| |
| if (force_edid) { |
| /* Should not fail because force_edid was checked above */ |
| err = get_test_edid(force_edid, &edid[0]); |
| } else if (use_new_edid) { |
| int i; |
| for(i=0; i < (EDID_SIZE + EEXT_SIZE); i++) edid[i] = newedid[i]; |
| err = 0; |
| use_new_edid = 0; |
| } else { |
| err = get_edid(i2cdev, &edid[0], newFirmware, verbose); |
| newFirmware = 0; |
| /* If the error happened with new firmware we try again */ |
| if (err == ERR_NEWFW_RETRY) continue; |
| } |
| |
| mon_timing = NULL; |
| enable_audio = 0; |
| why_aud = "LPCM Audio support not found in EDID"; |
| |
| if (force_mode >= 0) { |
| /* Mode forced on command line: ignore EDID */ |
| mon_timing = &known_modes[force_mode]; |
| aspect = find_aspect(mon_timing->ha, mon_timing->va); |
| // Select audio if it is an HDMI mode |
| if (mon_timing->pixel_fmt & PIXEL_HDMI_ENCODE_DVI) { |
| enable_audio = 0; |
| why_res = "DVI mode forced on command line"; |
| why_aud = "audio forced off"; |
| } else { |
| enable_audio = 1; |
| why_res = "HDMI mode forced on command line"; |
| why_aud = "audio forced on"; |
| } |
| if (verbose) printf("Force mode %d with aspect %d\n", |
| force_mode, aspect); |
| } else if (err) { |
| /* Error reading the EDID */ |
| fprintf(stderr, "%s: Get EDID Failed (%s block) using 640x480@60\n", |
| argv[0], (err == ERR_READ_FAILED) ? "initial" : "extension"); |
| why_res = "default resolution because EDID could not be read"; |
| mon_timing = mode_640x480x60; |
| aspect = ASPECT_4_3; |
| } else if (!edid_valid(&edid[0])) { |
| /* EDID is not valid */ |
| fprintf(stderr, "%s: EDID is not valid, using 640x480@60\n", |
| argv[0]); |
| why_res = "default resolution because EDID is not valid"; |
| mon_timing = mode_640x480x60; |
| aspect = ASPECT_4_3; |
| } else { |
| /* Got an EDID, use it to figure out the timing */ |
| if (verbose) { |
| show_edid_data(stdout, &edid[0], EDID_SIZE, 0); |
| if (edid[EDID_EXT_FLAG] > 0) |
| show_edid_data(stdout, &edid[EDID_SIZE], EEXT_SIZE, EDID_SIZE); |
| show_edid(stdout, &edid[0], (edid[EDID_EXT_FLAG] > 0) ? 1:0); |
| } |
| if ((forcen_x == 0) && |
| get_native(&edid[0], &prefer, &aspect, forcen_a, verbose)){ |
| mon_timing = &prefer; |
| why_res = "preferred/native resolution requested in EDID"; |
| |
| /* HDMI TVs tend to assume overscan (i.e. junk round the real frame)*/ |
| /* even when we tell them otherwise with the AVI INFO frame */ |
| /* even if they set the 'underscan' flag that says they should */ |
| /* If the EDID has an HDMI extension then assume this badness */ |
| /* unless it is not 16:9 because most monitors are well behaved */ |
| if (edid_has_hdmi_info(&edid[0], (edid[EDID_EXT_FLAG] > 0) ? 1:0)) { |
| if (aspect == ASPECT_16_9) { |
| prefer.scale = MK_SCALE(6, 5); |
| why_res = "preferred resolution in EDID scaled for overscan"; |
| } |
| if (edid_lpcm_support(&edid[0], (edid[EDID_EXT_FLAG] > 0) ? 1:0)) { |
| enable_audio = 1; |
| why_aud = "EDID indicates LPCM audio support"; |
| } |
| } |
| } else { |
| get_standard(&edid[0], &mon_timing, &aspect, forcen_x, forcen_a, |
| verbose); |
| why_res = "best standard resolution supported in EDID"; |
| } |
| } |
| |
| /* Could loop here 3 times: preferred, standard and default */ |
| do { |
| /* Catch error cases either from the code above or second pass of loop */ |
| if (mon_timing == NULL) { |
| if (verbose) |
| printf("%s: Fall back to default output 640x480@60\n", argv[0]); |
| why_res = "default resolution because nothing better was identified"; |
| mon_timing = mode_640x480x60; |
| aspect = ASPECT_4_3; |
| } |
| if (verbose) { |
| show_ch_timing(&lcd_timing, "input (laptop lcd)"); |
| show_ch_timing(mon_timing, "output (hdmi port)"); |
| } |
| if ((width_fix != NULL) && (aspect >= 0)) |
| new_width = width_fix[aspect]; |
| else |
| new_width = xmode->width; |
| new_height = xmode->height; |
| |
| /* Special case for 1280 width to avoid shrinking by a small amount */ |
| /* (the 5:4 aspect 1280x1024 will have reduced new_width above) */ |
| if ((new_width == 1366) && (mon_timing->ha == 1280)) { |
| new_width = 1280; |
| /* Mostly for 1280x720 which is popular for TVs, avoid y scale too */ |
| if (mon_timing->va < xmode->height) new_height = mon_timing->va; |
| } |
| |
| lcd_timing.ha = new_width; |
| lcd_timing.ho = xmode->hSyncStart - new_width; |
| lcd_timing.va = new_height; |
| lcd_timing.vo = xmode->vSyncStart - new_height; |
| |
| set_ch7036_config(i2cdev); |
| err = GenTableCH7036(&lcd_timing, mon_timing, ®list); |
| |
| if (err != 0) { |
| fprintf(stderr, "%s: GenTableCH7036 gave error %d\n", |
| argv[0], err); |
| if (mon_timing != &prefer) { |
| /* Force the default to be used */ |
| mon_timing = NULL; |
| } else { |
| /* Prefered timing didn't help us, try standard with no force */ |
| mon_timing = NULL; |
| get_standard(&edid[0], &mon_timing, &aspect, 0, 0, verbose); |
| } |
| } |
| } while (err != 0); |
| |
| use_black_window = 0; |
| |
| /* The use flag bogus_screen_resizes gives this define to both */ |
| /* this driver and the chromeos window manager. When set the */ |
| /* wm will clear the unused screen areas, so we don't have to */ |
| /* This allows the login/unlock screen to clear correctly */ |
| #ifndef BOGUS_SCREEN_RESIZES |
| if (new_width != xmode->width) { |
| bwin = x_start_blank(dpy, screen, root, xmode->width, xmode->height); |
| use_black_window = 1; |
| } |
| #endif |
| |
| if (verbose) printf("Restore defaults:\n"); |
| ch_restore_notin_seq(i2cdev, reglist, verbose); |
| |
| if (mon_timing->scale != MK_SCALE(0,0)) { |
| int res = ch_change_reg(reglist, CH7036_AVIINFO3, 0xFC, 0x1); |
| if (verbose) printf("Change AVIINFO3 from 0x%2x to 0x%2x\n", |
| res, (res & 0xFC)|0x1); |
| } |
| /* Note: SDTIME1 code that was here has moved to ch_monitor_on() */ |
| |
| /* Sequence provided has 2 resets each 2 writes which cause flicker */ |
| /* Only seem to need the reset that is in ch_monitor_on below */ |
| ares = ch_remove_last_reg(reglist, CH7036_RESET); |
| if (verbose) printf("Removed CH7036_RESET write of 0x%2x\n", ares); |
| ares = ch_remove_last_reg(reglist, CH7036_RESET); |
| if (verbose) printf("Removed CH7036_RESET write of 0x%2x\n", ares); |
| ares = ch_remove_last_reg(reglist, CH7036_RESET); |
| if (verbose) printf("Removed CH7036_RESET write of 0x%2x\n", ares); |
| ares = ch_remove_last_reg(reglist, CH7036_RESET); |
| if (verbose) printf("Removed CH7036_RESET write of 0x%2x\n", ares); |
| /* Select HDMI output if mode is not DVI or audio is enabled */ |
| output_hdmi = (((mon_timing->pixel_fmt & PIXEL_HDMI_ENCODE_DVI) == 0) || |
| enable_audio); |
| if (verbose) printf("Program registers:\n"); |
| ch_write_reg_sequence(i2cdev, reglist); |
| ch_calculate_incs(i2cdev); |
| ch_monitor_on(i2cdev, output_hdmi, verbose); |
| hdmiOn = 1; |
| |
| if (new_width != xmode->width) { |
| if (verbose) printf("Change X active to %dx%d\n", new_width, new_height); |
| set_x_size(dpy, root, new_width, new_height, use_black_window, bwin); |
| } |
| audio_to_hdmi(enable_audio); |
| if (verbose) printf("Monitor should be on!\n"); |
| if ((edidlog = fopen(EDID_LOG, "w")) != NULL) { |
| fprintf(edidlog, "%sI output %dx%d%s. Use %dx%d on local LCD.\n", |
| (mon_timing->pixel_fmt & PIXEL_HDMI_ENCODE_DVI) ? "DV" : "HDM", |
| mon_timing->ha, mon_timing->va, |
| (mon_timing->scale != MK_SCALE(0,0)) ? "(scaled)":"", |
| new_width, new_height); |
| fprintf(edidlog, "Use %s\n", why_res); |
| fprintf(edidlog, "%s audio pass through because %s\n\n", |
| enable_audio ? "Enable" : "No", why_aud); |
| fprintf(edidlog, "EDID Data read from HDMI:\n"); |
| show_edid_data(edidlog, &edid[0], EDID_SIZE, 0); |
| if (edid[EDID_EXT_FLAG] > 0) |
| show_edid_data(edidlog, &edid[EDID_SIZE], EEXT_SIZE, EDID_SIZE); |
| show_edid(edidlog, &edid[0], (edid[EDID_EXT_FLAG] > 0) ? 1:0); |
| fclose(edidlog); |
| } |
| /* Suspend the firmware because it has been seen to cause screen flicker */ |
| /* Can't do this if relying on firmware to report disconnect event */ |
| if (gpiodev) |
| expectres = ch_suspend_firmware(i2cdev, verbose); |
| else |
| expectres = 0x2f; |
| |
| mondetect = 1; |
| do { |
| CARD16 state; |
| BOOL onoff; |
| int resreg; |
| |
| /* Check for unexpected change in Chrontel */ |
| /* most likely happens because the machine suspended */ |
| if ((resreg = ch_read_reg(i2cdev, CH7036_RESET)) != expectres) { |
| int i; |
| if (verbose) |
| printf("Unexpected change in CH7036, RESET 0x%x expecting 0x%x\n", |
| resreg, expectres); |
| /* Reload registers assuming part got to default state */ |
| ch_write_reg_sequence(i2cdev, reglist); |
| ch_calculate_incs(i2cdev); |
| ch_monitor_on(i2cdev, output_hdmi, verbose); |
| audio_to_hdmi(enable_audio); |
| hdmiOn = 1; |
| /* Machine was suspended, so the firmware was lost */ |
| needReload = reloadfw(i2cdev, fwfile, argv[0], gpiodev, 0, verbose); |
| if (needReload) continue; |
| /* Need to enable DDC to allow EDID read */ |
| if (gpiodev) ch_write_reg(i2cdev, CH7036_MAGIC2_0E, 0x13); // MAGIC |
| err = get_edid(i2cdev, &newedid[0], 1, verbose); |
| if (err) { |
| if (verbose) printf("Could not get new EDID\n"); |
| needReload = 1; |
| continue; |
| } |
| for(i=0; i<EDID_SIZE; i++) if (edid[i] != newedid[i]) break; |
| if (i < EDID_SIZE) { |
| if (verbose) printf("New EDID is different, monitor changed!\n"); |
| /* Clear hdmiOn to prevent the display being disabled below */ |
| hdmiOn = 0; |
| use_new_edid = 1; |
| break; |
| } |
| if (verbose) printf("New EDID is the same, using existing settings\n"); |
| if (gpiodev) ch_suspend_firmware(i2cdev, verbose); |
| } else { |
| DPMSInfo(dpy, &state, &onoff); |
| if ((state != DPMSModeOn) && hdmiOn) { |
| if (verbose) printf("DPMS is not on, turn off hdmi\n"); |
| ch_monitor_off_keep_ddc(i2cdev); |
| hdmiOn = 0; |
| } else if ((state == DPMSModeOn) && !hdmiOn) { |
| if (verbose) printf("DPMS is on, turn on hdmi\n"); |
| ch_monitor_on(i2cdev, output_hdmi, verbose); |
| if (verbose) printf("Monitor back on with DPMS On\n"); |
| hdmiOn = 1; |
| } |
| } |
| audio_check_jack(enable_audio); |
| sleep(gpiodev ? YIELD_GPIO_DETECT_SECS : YIELD_CHRONTEL_DETECT_SECS); |
| if (test_flags & CHTEST_FORCEDETECT) |
| mondetect = 1; |
| else if (gpiodev) |
| mondetect = ch_hdmi_gpio_detected(gpiodev, 0); |
| else |
| mondetect = ch_hdmi_detected(i2cdev); |
| } while (!needReload && mondetect); |
| if (verbose) printf("Exit displaying needReload = %d, hdmiOn = %d\n", |
| needReload, hdmiOn); |
| |
| if (!needReload && gpiodev && hdmiOn) |
| ch_release_firmware(i2cdev, verbose); |
| |
| if (new_width != xmode->width) { |
| if (verbose) printf("Change X active to %dx%d\n", |
| xmode->width, xmode->height); |
| set_x_size(dpy, root, xmode->width, xmode->height, 0, bwin); |
| } |
| if (hdmiOn && !needReload) { |
| ch_monitor_off_keep_ddc(i2cdev); |
| audio_to_hdmi(0); |
| hdmiOn = 0; |
| if (verbose) printf("Monitor should be off!\n"); |
| sleep(YIELD_CHRONTEL_TURN_OFF_SECS); |
| } |
| } |
| close(i2cdev); |
| return 0; |
| } |