Commit c4e6812c authored by Ben Skeggs's avatar Ben Skeggs

drm/nouveau/kms/nv50: separate out viewport commit

This commit separates the calculation of EVO state from the commit, in
order to make the same code useful for atomic modesetting.

The legacy interfaces have been wrapped on top of them.
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 6bbab3b6
...@@ -124,6 +124,7 @@ struct nouveau_conn_atom { ...@@ -124,6 +124,7 @@ struct nouveau_conn_atom {
u32 hborder; u32 hborder;
u32 vborder; u32 vborder;
} underscan; } underscan;
bool full;
} scaler; } scaler;
struct { struct {
......
...@@ -71,6 +71,13 @@ ...@@ -71,6 +71,13 @@
struct nv50_head_atom { struct nv50_head_atom {
struct drm_crtc_state state; struct drm_crtc_state state;
struct {
u16 iW;
u16 iH;
u16 oW;
u16 oH;
} view;
struct nv50_head_mode { struct nv50_head_mode {
bool interlace; bool interlace;
u32 clock; u32 clock;
...@@ -1065,6 +1072,34 @@ nv50_head_mode(struct nv50_head *head, struct nv50_head_atom *asyh) ...@@ -1065,6 +1072,34 @@ nv50_head_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
} }
} }
static void
nv50_head_view(struct nv50_head *head, struct nv50_head_atom *asyh)
{
struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->mast.base;
u32 *push;
if ((push = evo_wait(core, 10))) {
if (core->base.user.oclass < GF110_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x08a4 + (head->base.index * 0x400), 1);
evo_data(push, 0x00000000);
evo_mthd(push, 0x08c8 + (head->base.index * 0x400), 1);
evo_data(push, (asyh->view.iH << 16) | asyh->view.iW);
evo_mthd(push, 0x08d8 + (head->base.index * 0x400), 2);
evo_data(push, (asyh->view.oH << 16) | asyh->view.oW);
evo_data(push, (asyh->view.oH << 16) | asyh->view.oW);
} else {
evo_mthd(push, 0x0494 + (head->base.index * 0x300), 1);
evo_data(push, 0x00000000);
evo_mthd(push, 0x04b8 + (head->base.index * 0x300), 1);
evo_data(push, (asyh->view.iH << 16) | asyh->view.iW);
evo_mthd(push, 0x04c0 + (head->base.index * 0x300), 3);
evo_data(push, (asyh->view.oH << 16) | asyh->view.oW);
evo_data(push, (asyh->view.oH << 16) | asyh->view.oW);
evo_data(push, (asyh->view.oH << 16) | asyh->view.oW);
}
evo_kick(push, core);
}
}
static void static void
nv50_head_flush_clr(struct nv50_head *head, struct nv50_head_atom *asyh, bool y) nv50_head_flush_clr(struct nv50_head *head, struct nv50_head_atom *asyh, bool y)
{ {
...@@ -1079,6 +1114,7 @@ nv50_head_flush_clr(struct nv50_head *head, struct nv50_head_atom *asyh, bool y) ...@@ -1079,6 +1114,7 @@ nv50_head_flush_clr(struct nv50_head *head, struct nv50_head_atom *asyh, bool y)
static void static void
nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh) nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh)
{ {
if (asyh->set.view ) nv50_head_view (head, asyh);
if (asyh->set.mode ) nv50_head_mode (head, asyh); if (asyh->set.mode ) nv50_head_mode (head, asyh);
if (asyh->set.core ) nv50_head_lut_set (head, asyh); if (asyh->set.core ) nv50_head_lut_set (head, asyh);
if (asyh->set.core ) nv50_head_core_set(head, asyh); if (asyh->set.core ) nv50_head_core_set(head, asyh);
...@@ -1087,6 +1123,83 @@ nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh) ...@@ -1087,6 +1123,83 @@ nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh)
if (asyh->set.ovly ) nv50_head_ovly (head, asyh); if (asyh->set.ovly ) nv50_head_ovly (head, asyh);
} }
static void
nv50_head_atomic_check_view(struct nv50_head_atom *armh,
struct nv50_head_atom *asyh,
struct nouveau_conn_atom *asyc)
{
struct drm_connector *connector = asyc->state.connector;
struct drm_display_mode *omode = &asyh->state.adjusted_mode;
struct drm_display_mode *umode = &asyh->state.mode;
int mode = asyc->scaler.mode;
struct edid *edid;
if (connector->edid_blob_ptr)
edid = (struct edid *)connector->edid_blob_ptr->data;
else
edid = NULL;
if (!asyc->scaler.full) {
if (mode == DRM_MODE_SCALE_NONE)
omode = umode;
} else {
/* Non-EDID LVDS/eDP mode. */
mode = DRM_MODE_SCALE_FULLSCREEN;
}
asyh->view.iW = umode->hdisplay;
asyh->view.iH = umode->vdisplay;
asyh->view.oW = omode->hdisplay;
asyh->view.oH = omode->vdisplay;
if (omode->flags & DRM_MODE_FLAG_DBLSCAN)
asyh->view.oH *= 2;
/* 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 ((asyc->scaler.underscan.mode == UNDERSCAN_ON ||
(asyc->scaler.underscan.mode == UNDERSCAN_AUTO &&
drm_detect_hdmi_monitor(edid)))) {
u32 bX = asyc->scaler.underscan.hborder;
u32 bY = asyc->scaler.underscan.vborder;
u32 r = (asyh->view.oH << 19) / asyh->view.oW;
if (bX) {
asyh->view.oW -= (bX * 2);
if (bY) asyh->view.oH -= (bY * 2);
else asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19;
} else {
asyh->view.oW -= (asyh->view.oW >> 4) + 32;
if (bY) asyh->view.oH -= (bY * 2);
else asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19;
}
}
/* Handle CENTER/ASPECT scaling, taking into account the areas
* removed already for overscan compensation.
*/
switch (mode) {
case DRM_MODE_SCALE_CENTER:
asyh->view.oW = min((u16)umode->hdisplay, asyh->view.oW);
asyh->view.oH = min((u16)umode->vdisplay, asyh->view.oH);
/* fall-through */
case DRM_MODE_SCALE_ASPECT:
if (asyh->view.oH < asyh->view.oW) {
u32 r = (asyh->view.iW << 19) / asyh->view.iH;
asyh->view.oW = ((asyh->view.oH * r) + (r / 2)) >> 19;
} else {
u32 r = (asyh->view.iH << 19) / asyh->view.iW;
asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19;
}
break;
default:
break;
}
asyh->set.view = true;
}
static void static void
nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
{ {
...@@ -1264,105 +1377,27 @@ nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) ...@@ -1264,105 +1377,27 @@ nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update)
static int static int
nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update)
{ {
struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); struct nv50_head *head = nv50_head(&nv_crtc->base);
struct drm_display_mode *omode, *umode = &nv_crtc->base.mode; struct nv50_head_atom *asyh = &head->asy;
struct drm_crtc *crtc = &nv_crtc->base; struct drm_crtc *crtc = &nv_crtc->base;
struct nouveau_connector *nv_connector; struct nouveau_connector *nv_connector;
int mode = DRM_MODE_SCALE_NONE; struct nouveau_conn_atom asyc;
u32 oX, oY, *push;
/* start off at the resolution we programmed the crtc for, this
* effectively handles NONE/FULL scaling
*/
nv_connector = nouveau_crtc_connector_get(nv_crtc); nv_connector = nouveau_crtc_connector_get(nv_crtc);
if (nv_connector && nv_connector->native_mode) {
mode = nv_connector->scaling_mode;
if (nv_connector->scaling_full) /* non-EDID LVDS/eDP mode */
mode = DRM_MODE_SCALE_FULLSCREEN;
}
if (mode != DRM_MODE_SCALE_NONE)
omode = nv_connector->native_mode;
else
omode = umode;
oX = omode->hdisplay;
oY = omode->vdisplay;
if (omode->flags & DRM_MODE_FLAG_DBLSCAN)
oY *= 2;
/* 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 &&
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 {
oX -= (oX >> 4) + 32;
if (bY) oY -= (bY * 2);
else oY = ((oX * aspect) + (aspect / 2)) >> 19;
}
}
/* handle CENTER/ASPECT scaling, taking into account the areas asyc.state.connector = &nv_connector->base;
* removed already for overscan compensation asyc.scaler.mode = nv_connector->scaling_mode;
*/ asyc.scaler.full = nv_connector->scaling_full;
switch (mode) { asyc.scaler.underscan.mode = nv_connector->underscan;
case DRM_MODE_SCALE_CENTER: asyc.scaler.underscan.hborder = nv_connector->underscan_hborder;
oX = min((u32)umode->hdisplay, oX); asyc.scaler.underscan.vborder = nv_connector->underscan_vborder;
oY = min((u32)umode->vdisplay, oY); nv50_head_atomic_check(&head->base.base, &asyh->state);
/* fall-through */ nv50_head_atomic_check_view(&head->arm, asyh, &asyc);
case DRM_MODE_SCALE_ASPECT: nv50_head_flush_set(head, asyh);
if (oY < oX) {
u32 aspect = (umode->hdisplay << 19) / umode->vdisplay;
oX = ((oY * aspect) + (aspect / 2)) >> 19;
} else {
u32 aspect = (umode->vdisplay << 19) / umode->hdisplay;
oY = ((oX * aspect) + (aspect / 2)) >> 19;
}
break;
default:
break;
}
push = evo_wait(mast, 8);
if (push) {
if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
/*XXX: SCALE_CTRL_ACTIVE??? */
evo_mthd(push, 0x08d8 + (nv_crtc->index * 0x400), 2);
evo_data(push, (oY << 16) | oX);
evo_data(push, (oY << 16) | oX);
evo_mthd(push, 0x08a4 + (nv_crtc->index * 0x400), 1);
evo_data(push, 0x00000000);
evo_mthd(push, 0x08c8 + (nv_crtc->index * 0x400), 1);
evo_data(push, umode->vdisplay << 16 | umode->hdisplay);
} else {
evo_mthd(push, 0x04c0 + (nv_crtc->index * 0x300), 3);
evo_data(push, (oY << 16) | oX);
evo_data(push, (oY << 16) | oX);
evo_data(push, (oY << 16) | oX);
evo_mthd(push, 0x0494 + (nv_crtc->index * 0x300), 1);
evo_data(push, 0x00000000);
evo_mthd(push, 0x04b8 + (nv_crtc->index * 0x300), 1);
evo_data(push, umode->vdisplay << 16 | umode->hdisplay);
}
evo_kick(push, mast);
if (update) { if (update) {
nv50_display_flip_stop(crtc); nv50_display_flip_stop(crtc);
nv50_display_flip_next(crtc, crtc->primary->fb, nv50_display_flip_next(crtc, crtc->primary->fb, NULL, 1);
NULL, 1);
}
} }
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment