On Wed, 2011-02-09 at 15:01 +0000, Chris Wilson wrote: > 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. 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. > > 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@xxxxxxx> > Signed-off-by: Chris Wilson <chris@xxxxxxxxxxxxxxxxxx> > Cc: Dave Airlie <airlied@xxxxxxxxxx> > --- > 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 { _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel