[PATCH 11/11] drm/fbdevdrm: Detect and validate display modes

Daniel Vetter daniel at ffwll.ch
Tue Mar 26 18:20:38 UTC 2019


On Tue, Mar 26, 2019 at 06:47:41PM +0200, Ville Syrjälä wrote:
> On Tue, Mar 26, 2019 at 10:17:44AM +0100, Thomas Zimmermann wrote:
> > Mode detection currently reports the modes listed in fb_info::modelist.
> > The list is either build from EDID information or, more often, a list of
> > previously set modes. A later update to the mode detection could also
> > take into account the modes in fb_monspecs::modedb or test pre-defined
> > VESA modes.
> > 
> > Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de>
> > ---
> >  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 163 +++++++++++++++++++-
> >  1 file changed, 162 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
> > index 87f56ec76edf..e89eca4b58df 100644
> > --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
> > +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
> > @@ -21,9 +21,16 @@
> >  #include <drm/drm_print.h>
> >  #include <drm/drm_probe_helper.h>
> >  #include <linux/fb.h>
> > +#include "fbdevdrm_device.h"
> >  #include "fbdevdrm_modes.h"
> >  #include "fbdevdrm_primary.h"
> >  
> > +static struct fbdevdrm_modeset* fbdevdrm_modeset_of_connector(
> > +	struct drm_connector *connector)
> > +{
> > +	return container_of(connector, struct fbdevdrm_modeset, connector);
> > +}
> > +
> >  static struct fbdevdrm_modeset* fbdevdrm_modeset_of_crtc(
> >  	struct drm_crtc *crtc)
> >  {
> > @@ -353,11 +360,130 @@ static const struct drm_encoder_funcs fbdevdrm_encoder_funcs = {
> >   * Connector
> >   */
> >  
> > -static int connector_helper_get_modes(struct drm_connector *connector)
> > +static int update_display_info(struct drm_display_info *info, struct fb_info *fb_info)
> > +{
> > +	int num_pixel;
> > +
> > +	if (fb_info->fix.type != FB_TYPE_PACKED_PIXELS)
> > +		return -EINVAL; /* rule out text mode, etc. */
> > +
> > +	if (fb_info->fix.id[0]) {
> > +		strncpy(info->name, fb_info->fix.id, sizeof(info->name) - 1);
> > +		info->name[sizeof(info->name) - 1] = '\0';
> > +	} else {
> > +		memset(info->name, '\0', sizeof(info->name));
> > +	}
> > +
> > +	info->width_mm = fb_info->var.width;
> > +	info->height_mm = fb_info->var.height;
> > +	info->pixel_clock = PICOS2KHZ(fb_info->var.pixclock);
> > +
> > +	num_pixel = 0;
> > +	if (fb_info->var.red.length)
> > +		++num_pixel;
> > +	if (fb_info->var.green.length)
> > +		++num_pixel;
> > +	if (fb_info->var.blue.length)
> > +		++num_pixel;
> > +	if (fb_info->var.transp.length)
> > +		++num_pixel;
> > +
> > +	if (num_pixel)
> > +		info->bpc = fb_info->var.bits_per_pixel;
> > +	else
> > +		info->bpc = 0;
> > +
> > +	info->subpixel_order = SubPixelUnknown;
> > +	info->color_formats = DRM_COLOR_FORMAT_RGB444;
> > +	info->bus_formats = &info->color_formats;
> > +	info->num_bus_formats = 1;
> > +	info->bus_flags = 0;
> > +	info->max_tmds_clock = 0;
> > +	info->dvi_dual = false;
> > +	info->has_hdmi_infoframe = false;
> > +	info->edid_hdmi_dc_modes = 0;
> > +	info->cea_rev = 0;
> > +	memset(&info->hdmi, 0, sizeof(info->hdmi));
> > +	info->non_desktop = 0;
> 
> I think the only things here you may want to set are
> width_mm and height_mm. The rest should not matter.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +static int drm_mode_probed_add_from_fb_videomode(
> > +	struct drm_connector *connector, const struct fb_videomode *fb_mode,
> > +	struct fb_info *fb_info)
> >  {
> > +	struct drm_display_mode *mode;
> > +
> > +	mode = drm_mode_create_from_fb_videomode(connector->dev, fb_mode);
> > +	if (!mode)
> > +		return -ENOMEM;
> > +
> > +	mode->width_mm = fb_info->var.width;
> > +	mode->height_mm = fb_info->var.height;
> > +
> > +	drm_mode_probed_add(connector, mode);
> > +
> > +	/* update connector properties from display mode */
> > +	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
> > +		connector->interlace_allowed = true;
> > +	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
> > +		connector->doublescan_allowed = true;
> > +	if (mode->flags & DRM_MODE_FLAG_3D_MASK)
> > +		connector->stereo_allowed = true;
> > +
> >  	return 0;
> >  }
> >  
> > +static int connector_helper_get_modes(struct drm_connector *connector)
> > +{
> > +	struct fbdevdrm_modeset *modeset;
> > +	struct list_head *pos;
> > +	int ret, num_modes = 0;
> > +
> > +	modeset = fbdevdrm_modeset_of_connector(connector);
> > +
> > +	ret = update_display_info(&connector->display_info, modeset->fb_info);
> > +	if (ret)
> > +		return 0;
> > +
> > +	/* update connector properties from video modes */
> > +	connector->interlace_allowed = 0;
> > +	connector->doublescan_allowed = 0;
> > +	connector->stereo_allowed = 0;
> > +
> > +	if (!num_modes && modeset->fb_info->mode) {
> > +		ret = drm_mode_probed_add_from_fb_videomode(
> > +			connector, modeset->fb_info->mode, modeset->fb_info);
> > +		if (!ret)
> > +			++num_modes;
> > +	}
> > +
> > +	if (!num_modes) {
> > +
> > +		/* DRM backporting notes: we go through all modes in the
> > +		 * fb_info's mode list and convert each to a DRM modes. If
> > +		 * you convert an fbdev driver to DRM, replace this code
> > +		 * with an actual hardware query. This will usually involve
> > +		 * reading the monitor EDID via DDC.
> > +		 */
> > +
> > +		list_for_each(pos, &modeset->fb_info->modelist) {
> 
> fbdev has a modelist? I guess it does. But not exposed to
> userspace, which is probably the reason I never realized this.

It's exposed in sysfs.

Don't ask why I know this, it hurts.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


More information about the dri-devel mailing list