diff options
author | Ricardo Salveti de Araujo <ricardo.salveti@canonical.com> | 2011-09-01 08:08:09 +0100 |
---|---|---|
committer | Andy Green <andy.green@linaro.org> | 2011-09-01 08:08:09 +0100 |
commit | b92253bd99ed8e56033808b7031fc0973dea709f (patch) | |
tree | c4a5c97fb91a04b26cce8f4a5a87c5a70e7a1fa8 | |
parent | 15d2a95ade13bbac1b86b4a0d0b646cae3d17141 (diff) |
drm/omap: add common scaled modes
The idea behind it is just to add more modes, and add them with reduced
blanking, so we have more choices while probing the EDID.
It tries to identify what would be the native mode for the monitor, and
add the scaler modes that would be compatible with it, but with reduced
blanking. With that it can get more compatible modes with OMAP pixel
clock.
Signed-off-by: Ricardo Salveti de Araujo <ricardo.salveti@canonical.com>
-rw-r--r-- | drivers/staging/omapdrm/omap_connector.c | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/drivers/staging/omapdrm/omap_connector.c b/drivers/staging/omapdrm/omap_connector.c index c1071c171bd..77f209e52b2 100644 --- a/drivers/staging/omapdrm/omap_connector.c +++ b/drivers/staging/omapdrm/omap_connector.c @@ -32,6 +32,7 @@ struct omap_connector { struct drm_connector base; struct omap_dss_device *dssdev; + struct drm_display_mode *native_mode; }; static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode, @@ -118,6 +119,104 @@ static void omap_connector_destroy(struct drm_connector *connector) omap_dss_put_device(dssdev); } +static struct drm_display_mode * omap_connector_native_mode( + struct drm_connector *connector) +{ + struct omap_connector *omap_connector = to_omap_connector(connector); + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode, *largest = NULL; + int high_w = 0, high_h = 0, high_v = 0; + + list_for_each_entry(mode, &omap_connector->base.probed_modes, head) { + mode->vrefresh = drm_mode_vrefresh(mode); + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + continue; + + /* Use preferred mode if there is one */ + if (mode->type & DRM_MODE_TYPE_PREFERRED) { + DBG("native mode from preferred: %dx%d@%d", + mode->hdisplay, mode->vdisplay, mode->vrefresh); + return drm_mode_duplicate(dev, mode); + } + + /* Otherwise, take the resolution with the largest width, then + * height, then vertical refresh + */ + if (mode->hdisplay < high_w) + continue; + + if (mode->hdisplay == high_w && mode->vdisplay < high_h) + continue; + + if (mode->hdisplay == high_w && mode->vdisplay == high_h && + mode->vrefresh < high_v) + continue; + + high_w = mode->hdisplay; + high_h = mode->vdisplay; + high_v = mode->vrefresh; + largest = mode; + } + + DBG("native mode from largest: %dx%d@%d", high_w, high_h, high_v); + return largest ? drm_mode_duplicate(dev, largest) : NULL; +} + +struct moderec { + int hdisplay; + int vdisplay; +}; + +static struct moderec scaler_modes[] = { + { 1920, 1200 }, + { 1920, 1080 }, + { 1680, 1050 }, + { 1600, 1200 }, + { 1400, 1050 }, + { 1400, 900 }, + { 1280, 1024 }, + { 1280, 960 }, + { 1280, 720 }, + { 1152, 768 }, + { 1024, 768 }, + { 800, 600 }, + { 720, 480 }, + { 640, 480 }, + {} +}; + +static int omap_connector_scaler_modes_add(struct drm_connector *connector) +{ + struct omap_connector *omap_connector = to_omap_connector(connector); + struct drm_display_mode *native = omap_connector->native_mode, *m; + struct drm_device *dev = connector->dev; + struct moderec *mode = &scaler_modes[0]; + int modes = 0; + + if (!native) + return 0; + + while (mode->hdisplay) { + if (mode->hdisplay <= native->hdisplay && + mode->vdisplay <= native->vdisplay) { + m = drm_cvt_mode(dev, mode->hdisplay, mode->vdisplay, + 60, true, false, false); + if (!m) + continue; + + m->type |= DRM_MODE_TYPE_DRIVER; + + DBG("adding scaler mode: %dx%d@%d", mode->hdisplay, + mode->vdisplay, drm_mode_vrefresh(m)); + drm_mode_probed_add(connector, m); + modes++; + } + mode++; + } + + return modes; +} + #define MAX_EDID 256 static int omap_connector_get_modes(struct drm_connector *connector) @@ -130,6 +229,11 @@ static int omap_connector_get_modes(struct drm_connector *connector) DBG("%s", omap_connector->dssdev->name); + if (omap_connector->native_mode) { + drm_mode_destroy(dev, omap_connector->native_mode); + omap_connector->native_mode = NULL; + } + /* if display exposes EDID, then we parse that in the normal way to * build table of supported modes.. otherwise (ie. fixed resolution * LCD panels) we just return a single mode corresponding to the @@ -142,6 +246,9 @@ static int omap_connector_get_modes(struct drm_connector *connector) drm_edid_is_valid(edid)) { drm_mode_connector_update_edid_property(connector, edid); n = drm_add_edid_modes(connector, edid); + omap_connector->native_mode = + omap_connector_native_mode(connector); + n += omap_connector_scaler_modes_add(connector); kfree(connector->display_info.raw_edid); connector->display_info.raw_edid = edid; } else { |