The LVDS code ignores any connector for which it cannot find a fixed mode (through an EDID, vBIOS tables or the current active mode). Some platforms may include an LVDS header on the board and this may then be partnered with a panel without an EDID. This results in us ignoring the connector and not lighting up the panel.
Under UMS, it was possible to override this by specifying the mode through the Xorg.conf. For KMS, one specifies the modeline through the video= parameter. So we need to include this user modeline when checking for panel fixed modes.
The machinery to parse the video= modes and generate the appropriate drm_mode is already built into drm_fb_herlper and so we can just extract, move it to the core and also use it from intel_lvds.c
Reported-by: Oliver Seitz info@vtnd.de Signed-off-by: Chris Wilson chris@chris-wilson.co.uk Cc: Dave Airlie airlied@redhat.com --- drivers/gpu/drm/drm_fb_helper.c | 207 +++++++------------------------------ drivers/gpu/drm/drm_modes.c | 154 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_lvds.c | 17 +++ include/drm/drmP.h | 25 +++++ include/drm/drm_fb_helper.h | 16 +--- 5 files changed, 233 insertions(+), 186 deletions(-)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 6977a1c..5a80412 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -70,174 +70,50 @@ fail: } EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
-/** - * drm_fb_helper_connector_parse_command_line - parse command line for connector - * @connector - connector to parse line for - * @mode_option - per connector mode option - * - * This parses the connector specific then generic command lines for - * modes and options to configure the connector. - * - * This uses the same parameters as the fb modedb.c, except for extra - * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd] - * - * enable/enable Digital/disable bit at the end - */ -static bool drm_fb_helper_connector_parse_command_line(struct drm_fb_helper_connector *fb_helper_conn, - const char *mode_option) -{ - const char *name; - unsigned int namelen; - int res_specified = 0, bpp_specified = 0, refresh_specified = 0; - unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0; - int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0; - int i; - enum drm_connector_force force = DRM_FORCE_UNSPECIFIED; - struct drm_fb_helper_cmdline_mode *cmdline_mode; - struct drm_connector *connector; - - if (!fb_helper_conn) - return false; - connector = fb_helper_conn->connector; - - cmdline_mode = &fb_helper_conn->cmdline_mode; - if (!mode_option) - mode_option = fb_mode_option; - - if (!mode_option) { - cmdline_mode->specified = false; - return false; - } - - name = mode_option; - namelen = strlen(name); - for (i = namelen-1; i >= 0; i--) { - switch (name[i]) { - case '@': - namelen = i; - if (!refresh_specified && !bpp_specified && - !yres_specified) { - refresh = simple_strtol(&name[i+1], NULL, 10); - refresh_specified = 1; - if (cvt || rb) - cvt = 0; - } else - goto done; - break; - case '-': - namelen = i; - if (!bpp_specified && !yres_specified) { - bpp = simple_strtol(&name[i+1], NULL, 10); - bpp_specified = 1; - if (cvt || rb) - cvt = 0; - } else - goto done; - break; - case 'x': - if (!yres_specified) { - yres = simple_strtol(&name[i+1], NULL, 10); - yres_specified = 1; - } else - goto done; - case '0' ... '9': - break; - case 'M': - if (!yres_specified) - cvt = 1; - break; - case 'R': - if (cvt) - rb = 1; - break; - case 'm': - if (!cvt) - margins = 1; - break; - case 'i': - if (!cvt) - interlace = 1; - break; - case 'e': - force = DRM_FORCE_ON; - break; - case 'D': - if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) && - (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB)) - force = DRM_FORCE_ON; - else - force = DRM_FORCE_ON_DIGITAL; - break; - case 'd': - force = DRM_FORCE_OFF; - break; - default: - goto done; - } - } - if (i < 0 && yres_specified) { - xres = simple_strtol(name, NULL, 10); - res_specified = 1; - } -done: - - DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", - drm_get_connector_name(connector), xres, yres, - (refresh) ? refresh : 60, (rb) ? " reduced blanking" : - "", (margins) ? " with margins" : "", (interlace) ? - " interlaced" : ""); - - if (force) { - const char *s; - switch (force) { - case DRM_FORCE_OFF: s = "OFF"; break; - case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break; - default: - case DRM_FORCE_ON: s = "ON"; break; - } - - DRM_INFO("forcing %s connector %s\n", - drm_get_connector_name(connector), s); - connector->force = force; - } - - if (res_specified) { - cmdline_mode->specified = true; - cmdline_mode->xres = xres; - cmdline_mode->yres = yres; - } - - if (refresh_specified) { - cmdline_mode->refresh_specified = true; - cmdline_mode->refresh = refresh; - } - - if (bpp_specified) { - cmdline_mode->bpp_specified = true; - cmdline_mode->bpp = bpp; - } - cmdline_mode->rb = rb ? true : false; - cmdline_mode->cvt = cvt ? true : false; - cmdline_mode->interlace = interlace ? true : false; - - return true; -} - static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper) { struct drm_fb_helper_connector *fb_helper_conn; int i;
for (i = 0; i < fb_helper->connector_count; i++) { + struct drm_cmdline_mode *mode; + struct drm_connector *connector; char *option = NULL;
fb_helper_conn = fb_helper->connector_info[i]; + connector = fb_helper_conn->connector; + mode = &fb_helper_conn->cmdline_mode;
/* do something on return - turn off connector maybe */ - if (fb_get_options(drm_get_connector_name(fb_helper_conn->connector), &option)) + if (fb_get_options(drm_get_connector_name(connector), &option)) continue;
- drm_fb_helper_connector_parse_command_line(fb_helper_conn, option); + if (drm_mode_parse_command_line_for_connector(option, + connector, + mode)) { + if (mode->force) { + const char *s; + switch (mode->force) { + case DRM_FORCE_OFF: s = "OFF"; break; + case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break; + default: + case DRM_FORCE_ON: s = "ON"; break; + } + + DRM_INFO("forcing %s connector %s\n", + drm_get_connector_name(connector), s); + connector->force = mode->force; + } + + DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", + drm_get_connector_name(connector), + mode->xres, mode->yres, + mode->refresh_specified ? mode->refresh : 60, + mode->rb ? " reduced blanking" : "", + mode->margins ? " with margins" : "", + mode->interlace ? " interlaced" : ""); + } + } return 0; } @@ -883,7 +759,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, /* first up get a count of crtcs now in use and new min/maxes width/heights */ for (i = 0; i < fb_helper->connector_count; i++) { struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; - struct drm_fb_helper_cmdline_mode *cmdline_mode; + struct drm_cmdline_mode *cmdline_mode;
cmdline_mode = &fb_helper_conn->cmdline_mode;
@@ -1105,7 +981,7 @@ static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_conn
static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) { - struct drm_fb_helper_cmdline_mode *cmdline_mode; + struct drm_cmdline_mode *cmdline_mode; cmdline_mode = &fb_connector->cmdline_mode; return cmdline_mode->specified; } @@ -1113,7 +989,7 @@ static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, int width, int height) { - struct drm_fb_helper_cmdline_mode *cmdline_mode; + struct drm_cmdline_mode *cmdline_mode; struct drm_display_mode *mode = NULL;
cmdline_mode = &fb_helper_conn->cmdline_mode; @@ -1145,19 +1021,8 @@ static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_conne }
create_mode: - if (cmdline_mode->cvt) - mode = drm_cvt_mode(fb_helper_conn->connector->dev, - cmdline_mode->xres, cmdline_mode->yres, - cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60, - cmdline_mode->rb, cmdline_mode->interlace, - cmdline_mode->margins); - else - mode = drm_gtf_mode(fb_helper_conn->connector->dev, - cmdline_mode->xres, cmdline_mode->yres, - cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60, - cmdline_mode->interlace, - cmdline_mode->margins); - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, + cmdline_mode); list_add(&mode->head, &fb_helper_conn->connector->modes); return mode; } diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 58e65f9..f9da47e 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -974,3 +974,157 @@ void drm_mode_connector_list_update(struct drm_connector *connector) } } EXPORT_SYMBOL(drm_mode_connector_list_update); + +/** + * drm_mode_parse_command_line_for_connector - parse command line for connector + * @mode_option - per connector mode option + * @connector - connector to parse line for + * + * This parses the connector specific then generic command lines for + * modes and options to configure the connector. + * + * This uses the same parameters as the fb modedb.c, except for extra + * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd] + * + * enable/enable Digital/disable bit at the end + */ +bool drm_mode_parse_command_line_for_connector(const char *mode_option, + struct drm_connector *connector, + struct drm_cmdline_mode *mode) +{ + const char *name; + unsigned int namelen; + int res_specified = 0, bpp_specified = 0, refresh_specified = 0; + unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0; + int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0; + int i; + enum drm_connector_force force = DRM_FORCE_UNSPECIFIED; + + if (!mode_option) + mode_option = fb_mode_option; + + if (!mode_option) { + mode->specified = false; + return false; + } + + name = mode_option; + namelen = strlen(name); + for (i = namelen-1; i >= 0; i--) { + switch (name[i]) { + case '@': + namelen = i; + if (!refresh_specified && !bpp_specified && + !yres_specified) { + refresh = simple_strtol(&name[i+1], NULL, 10); + refresh_specified = 1; + if (cvt || rb) + cvt = 0; + } else + goto done; + break; + case '-': + namelen = i; + if (!bpp_specified && !yres_specified) { + bpp = simple_strtol(&name[i+1], NULL, 10); + bpp_specified = 1; + if (cvt || rb) + cvt = 0; + } else + goto done; + break; + case 'x': + if (!yres_specified) { + yres = simple_strtol(&name[i+1], NULL, 10); + yres_specified = 1; + } else + goto done; + case '0' ... '9': + break; + case 'M': + if (!yres_specified) + cvt = 1; + break; + case 'R': + if (cvt) + rb = 1; + break; + case 'm': + if (!cvt) + margins = 1; + break; + case 'i': + if (!cvt) + interlace = 1; + break; + case 'e': + force = DRM_FORCE_ON; + break; + case 'D': + if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) && + (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB)) + force = DRM_FORCE_ON; + else + force = DRM_FORCE_ON_DIGITAL; + break; + case 'd': + force = DRM_FORCE_OFF; + break; + default: + goto done; + } + } + if (i < 0 && yres_specified) { + xres = simple_strtol(name, NULL, 10); + res_specified = 1; + } +done: + if (res_specified) { + mode->specified = true; + mode->xres = xres; + mode->yres = yres; + } + + if (refresh_specified) { + mode->refresh_specified = true; + mode->refresh = refresh; + } + + if (bpp_specified) { + mode->bpp_specified = true; + mode->bpp = bpp; + } + mode->rb = rb ? true : false; + mode->cvt = cvt ? true : false; + mode->interlace = interlace ? true : false; + mode->force = force; + + return true; +} +EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector); + +struct drm_display_mode * +drm_mode_create_from_cmdline_mode(struct drm_device *dev, + struct drm_cmdline_mode *cmd) +{ + struct drm_display_mode *mode; + + if (cmd->cvt) + mode = drm_cvt_mode(dev, + cmd->xres, cmd->yres, + cmd->refresh_specified ? cmd->refresh : 60, + cmd->rb, cmd->interlace, + cmd->margins); + else + mode = drm_gtf_mode(dev, + cmd->xres, cmd->yres, + cmd->refresh_specified ? cmd->refresh : 60, + cmd->interlace, + cmd->margins); + if (!mode) + return NULL; + + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + return mode; +} +EXPORT_SYMBOL(drm_mode_create_from_cmdline_mode); diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index cd08960..af4ef17 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -874,6 +874,8 @@ bool intel_lvds_init(struct drm_device *dev) struct drm_encoder *encoder; struct drm_display_mode *scan; /* *modes, *bios_mode; */ struct drm_crtc *crtc; + char *cmdline_option = NULL; + struct drm_cmdline_mode cmdline_mode; u32 lvds; int pipe; u8 pin; @@ -951,6 +953,7 @@ bool intel_lvds_init(struct drm_device *dev) intel_lvds->fitting_mode = DRM_MODE_SCALE_ASPECT; /* * LVDS discovery: + * 0) user override * 1) check for EDID on DDC * 2) check for VBT data * 3) check to see if LVDS is already on @@ -959,6 +962,20 @@ bool intel_lvds_init(struct drm_device *dev) * if closed, act like it's not there for now */
+ if (fb_get_options(drm_get_connector_name(connector), + &cmdline_option) == 0 && + drm_mode_parse_command_line_for_connector(cmdline_option, + connector, + &cmdline_mode)) { + intel_lvds->fixed_mode = + drm_mode_create_from_cmdline_mode(dev, &cmdline_mode); + if (intel_lvds->fixed_mode) { + intel_lvds->fixed_mode->type |= + DRM_MODE_TYPE_PREFERRED; + goto out; + } + } + /* * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. diff --git a/include/drm/drmP.h b/include/drm/drmP.h index fe29aad..bf01108 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -968,6 +968,22 @@ struct drm_minor { struct drm_mode_group mode_group; };
+/* mode specified on the command line */ +struct drm_cmdline_mode { + bool specified; + bool refresh_specified; + bool bpp_specified; + int xres, yres; + int bpp; + int refresh; + bool rb; + bool interlace; + bool cvt; + bool margins; + enum drm_connector_force force; +}; + + struct drm_pending_vblank_event { struct drm_pending_event base; int pipe; @@ -1381,6 +1397,15 @@ extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, struct drm_crtc *refcrtc); extern void drm_calc_timestamping_constants(struct drm_crtc *crtc);
+extern bool +drm_mode_parse_command_line_for_connector(const char *mode_option, + struct drm_connector *connector, + struct drm_cmdline_mode *mode); + +extern struct drm_display_mode * +drm_mode_create_from_cmdline_mode(struct drm_device *dev, + struct drm_cmdline_mode *cmd); + /* Modesetting support */ extern void drm_vblank_pre_modeset(struct drm_device *dev, int crtc); extern void drm_vblank_post_modeset(struct drm_device *dev, int crtc); diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index f22e7fe..4e66488 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -40,20 +40,6 @@ struct drm_fb_helper_crtc { struct drm_display_mode *desired_mode; };
-/* mode specified on the command line */ -struct drm_fb_helper_cmdline_mode { - bool specified; - bool refresh_specified; - bool bpp_specified; - int xres, yres; - int bpp; - int refresh; - bool rb; - bool interlace; - bool cvt; - bool margins; -}; - struct drm_fb_helper_surface_size { u32 fb_width; u32 fb_height; @@ -74,8 +60,8 @@ struct drm_fb_helper_funcs { };
struct drm_fb_helper_connector { - struct drm_fb_helper_cmdline_mode cmdline_mode; struct drm_connector *connector; + struct drm_cmdline_mode cmdline_mode; };
struct drm_fb_helper {
On Wed, 2011-02-09 at 15:01 +0000, Chris Wilson wrote:
Yeah not like this.
you want to make the command line the *last* option we use, the final fallback. LVDS panels have EDID and VBT hardcoded modes for a good reason, they don't work with other modes that well. You always want to use a scaler on the LVDS panel to do modes not the native mode. So if I have a VBT or EDID and you set video= I should get a scaled mode, not garbage.
So what I suspect you really want is to leave video= alone or enhance it somehow, or maybe add i915.lvds_native_mode= parameter.
Dave.
Sorry, about the empty reply. ----- Original message -----
Hi Chris, have you updated this patch? I have an Intel D525 (Pineview) system with HDMI port connected through an LVDS converter. The "panel timings" are the HDMI output mode, and it gets set through a BIOS option making it impossible to use the native resolution on the HDTV display.
Ideally, it would be better if this hardware configuration could be quirked to represent the output as a HDMI connector, but programmed through the LVDS registers, but I'm not sure how feasible that is. At least being able to set custom timings for the fixed mode would be a great improvment.
On Mon, 28 Mar 2011 22:46:55 +0100, Steven Newbury steve@snewbury.org.uk wrote:
Hi Chris, have you updated this patch? I have an Intel D525 (Pineview) system with HDMI port connected through an LVDS converter. The "panel timings" are the HDMI output mode, and it gets set through a BIOS option making it impossible to use the native resolution on the HDTV display.
Can you please attach the EDID for the connection and let's see if there is any tell-tale? -Chris
On Tue, Mar 29, 2011 at 5:29 PM, Chris Wilson chris@chris-wilson.co.uk wrote:
can you guys ask someone internally about it also, there is a driver somewhere in Google also for driving the LVDS->HDMI adapter but I'm not sure what i2c bus its hanging off.
Dave.
On Tue, Mar 29, 2011 at 5:49 PM, Dave Airlie airlied@gmail.com wrote:
http://git.chromium.org/gitweb/?p=chrontel.git;a=tree
may or may not be the thing.
Dave.
----- Original message -----
Simply running the resulting executables didn't work, it fails to detect the chip, the code also references accesses through GPIO and seems it wants an nm10_gpio driver which isn't in my kernel tree. My board is an NM10 chipset system, as is the target "Cr48 Chrome Notebook" so it could well be the same hardware.
I cherry-picked the nm10_gpio driver from the ChromeOS kernel, but while it worked fine the chrontel driver still couldn't detect the chip:
XAUTHORITY=//home/mythtv/.Xauthority ./ch7036_monitor -v -p ./ch7036_monitor: starts Found device ID 0xff ./ch7036_monitor: Fatal: Device ID 0xff not the expected 0x56
So either it isn't a ch7036 or I'm still not doing everything necessary to expose it.
Absolutely no help from Zotac :-
Unfortunately we only provide support for Windows XP, Vista, and 7 operating systems.
I can suggest searching through different forums on the web for any support regarding you situation
We apologize for the inconvenience
Please let us know if you have any other questions
...
You wouldn't think it too much to ask for a hardware company to support it's hardware and leave Windows support to Microsoft. :-(
----- Original message -----
Unfortunately EDID doesn't seem to be making it through. /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-LVDS-1/edid is empty so I tried read-edid, but it reported "Monitor and video card combination does not support DDC1/2 transfers". EDID was present using VGA-DVI so the TV does support EDID. (Although it didn't actually support the modes it claimed to when it determined it was connected to a PC!)
I've attached dmidecode output in case it helps.
dri-devel@lists.freedesktop.org