diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv50_crtc.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_crtc.c | 99 |
1 files changed, 58 insertions, 41 deletions
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index 8eb108b..e426a9b 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -215,74 +215,91 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc) static int nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update) { - struct nouveau_connector *nv_connector = - nouveau_crtc_connector_get(nv_crtc); + struct nouveau_connector *nv_connector; struct drm_crtc *crtc = &nv_crtc->base; struct drm_device *dev = crtc->dev; struct nouveau_channel *evo = nv50_display(dev)->master; - struct drm_display_mode *native_mode = NULL; struct drm_display_mode *mode = &crtc->mode; - uint32_t outX, outY, horiz, vert; + u32 ctrl = 0, oX, oY; int ret; NV_DEBUG_KMS(dev, "\n"); - switch (scaling_mode) { - case DRM_MODE_SCALE_NONE: - break; - default: - if (!nv_connector || !nv_connector->native_mode) { - NV_ERROR(dev, "No native mode, forcing panel scaling\n"); - scaling_mode = DRM_MODE_SCALE_NONE; + nv_connector = nouveau_crtc_connector_get(nv_crtc); + if (!nv_connector || !nv_connector->native_mode) { + NV_ERROR(dev, "no native mode, forcing panel scaling\n"); + scaling_mode = DRM_MODE_SCALE_NONE; + } + + /* start off at the resolution we programmed the crtc for, this + * effectively handles NONE/FULL scaling + */ + if (scaling_mode != DRM_MODE_SCALE_NONE) { + oX = nv_connector->native_mode->hdisplay; + oY = nv_connector->native_mode->vdisplay; + } else { + oX = mode->hdisplay; + oY = mode->vdisplay; + } + + /* add overscan compensation if necessary, will keep the aspect + * ratio the same as the backend mode unless overridden by the + * user setting both hborder and vborder properties. + */ + if (nv_connector && ( nv_connector->underscan == UNDERSCAN_ON || + (nv_connector->underscan == UNDERSCAN_AUTO && + nv_connector->edid && + drm_detect_hdmi_monitor(nv_connector->edid)))) { + u32 bX = nv_connector->underscan_hborder; + u32 bY = nv_connector->underscan_vborder; + u32 aspect = (oY << 19) / oX; + + if (bX) { + oX -= (bX * 2); + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; } else { - native_mode = nv_connector->native_mode; + oX -= (oX >> 4) + 32; + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; } - break; } + /* handle CENTER/ASPECT scaling, taking into account the areas + * removed already for overscan compensation + */ switch (scaling_mode) { + case DRM_MODE_SCALE_CENTER: + oX = min((u32)mode->hdisplay, oX); + oY = min((u32)mode->vdisplay, oY); + /* fall-through */ case DRM_MODE_SCALE_ASPECT: - horiz = (native_mode->hdisplay << 19) / mode->hdisplay; - vert = (native_mode->vdisplay << 19) / mode->vdisplay; - - if (vert > horiz) { - outX = (mode->hdisplay * horiz) >> 19; - outY = (mode->vdisplay * horiz) >> 19; + if (oY < oX) { + u32 aspect = (mode->hdisplay << 19) / mode->vdisplay; + oX = ((oY * aspect) + (aspect / 2)) >> 19; } else { - outX = (mode->hdisplay * vert) >> 19; - outY = (mode->vdisplay * vert) >> 19; + u32 aspect = (mode->vdisplay << 19) / mode->hdisplay; + oY = ((oX * aspect) + (aspect / 2)) >> 19; } break; - case DRM_MODE_SCALE_FULLSCREEN: - outX = native_mode->hdisplay; - outY = native_mode->vdisplay; - break; - case DRM_MODE_SCALE_CENTER: - case DRM_MODE_SCALE_NONE: default: - outX = mode->hdisplay; - outY = mode->vdisplay; break; } + if (mode->hdisplay != oX || mode->vdisplay != oY || + mode->flags & DRM_MODE_FLAG_INTERLACE || + mode->flags & DRM_MODE_FLAG_DBLSCAN) + ctrl |= NV50_EVO_CRTC_SCALE_CTRL_ACTIVE; + ret = RING_SPACE(evo, 5); if (ret) return ret; - /* Got a better name for SCALER_ACTIVE? */ - /* One day i've got to really figure out why this is needed. */ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1); - if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) || - (mode->flags & DRM_MODE_FLAG_INTERLACE) || - mode->hdisplay != outX || mode->vdisplay != outY) { - OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_ACTIVE); - } else { - OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_INACTIVE); - } - + OUT_RING (evo, ctrl); BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2); - OUT_RING(evo, outY << 16 | outX); - OUT_RING(evo, outY << 16 | outX); + OUT_RING (evo, oY << 16 | oX); + OUT_RING (evo, oY << 16 | oX); if (update) { nv50_display_flip_stop(crtc); |