diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv50_display.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_display.c | 251 |
1 files changed, 141 insertions, 110 deletions
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 06e564a9ccb2..e3132a2ce34d 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -23,6 +23,7 @@ */ #include <linux/dma-mapping.h> +#include <linux/hdmi.h> #include <drm/drmP.h> #include <drm/drm_atomic.h> @@ -31,6 +32,7 @@ #include <drm/drm_dp_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_plane_helper.h> +#include <drm/drm_edid.h> #include <nvif/class.h> #include <nvif/cl0002.h> @@ -1033,7 +1035,7 @@ nv50_wndw_reset(struct drm_plane *plane) plane->funcs->atomic_destroy_state(plane, plane->state); plane->state = &asyw->state; plane->state->plane = plane; - plane->state->rotation = DRM_ROTATE_0; + plane->state->rotation = DRM_MODE_ROTATE_0; } static void @@ -1965,6 +1967,7 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, struct drm_display_mode *umode = &asyh->state.mode; int mode = asyc->scaler.mode; struct edid *edid; + int umode_vdisplay, omode_hdisplay, omode_vdisplay; if (connector->edid_blob_ptr) edid = (struct edid *)connector->edid_blob_ptr->data; @@ -1979,12 +1982,18 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, mode = DRM_MODE_SCALE_FULLSCREEN; } + /* For the user-specified mode, we must ignore doublescan and + * the like, but honor frame packing. + */ + umode_vdisplay = umode->vdisplay; + if ((umode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + umode_vdisplay += umode->vtotal; 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; + asyh->view.iH = umode_vdisplay; + /* For the output mode, we can just use the stock helper. */ + drm_mode_get_hv_timing(omode, &omode_hdisplay, &omode_vdisplay); + asyh->view.oW = omode_hdisplay; + asyh->view.oH = omode_vdisplay; /* Add overscan compensation if necessary, will keep the aspect * ratio the same as the backend mode unless overridden by the @@ -2014,7 +2023,7 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, 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); + asyh->view.oH = min((u16)umode_vdisplay, asyh->view.oH); /* fall-through */ case DRM_MODE_SCALE_ASPECT: if (asyh->view.oH < asyh->view.oW) { @@ -2036,34 +2045,37 @@ static void nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) { struct drm_display_mode *mode = &asyh->state.adjusted_mode; - u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1; - u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; - u32 hbackp = mode->htotal - mode->hsync_end; - u32 vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace; - u32 hfrontp = mode->hsync_start - mode->hdisplay; - u32 vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; - u32 blankus; struct nv50_head_mode *m = &asyh->mode; + u32 blankus; - m->h.active = mode->htotal; - m->h.synce = mode->hsync_end - mode->hsync_start - 1; - m->h.blanke = m->h.synce + hbackp; - m->h.blanks = mode->htotal - hfrontp - 1; + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE); - m->v.active = mode->vtotal * vscan / ilace; - m->v.synce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1; - m->v.blanke = m->v.synce + vbackp; - m->v.blanks = m->v.active - vfrontp - 1; + /* + * DRM modes are defined in terms of a repeating interval + * starting with the active display area. The hardware modes + * are defined in terms of a repeating interval starting one + * unit (pixel or line) into the sync pulse. So, add bias. + */ + + m->h.active = mode->crtc_htotal; + m->h.synce = mode->crtc_hsync_end - mode->crtc_hsync_start - 1; + m->h.blanke = mode->crtc_hblank_end - mode->crtc_hsync_start - 1; + m->h.blanks = m->h.blanke + mode->crtc_hdisplay; + + m->v.active = mode->crtc_vtotal; + m->v.synce = mode->crtc_vsync_end - mode->crtc_vsync_start - 1; + m->v.blanke = mode->crtc_vblank_end - mode->crtc_vsync_start - 1; + m->v.blanks = m->v.blanke + mode->crtc_vdisplay; /*XXX: Safe underestimate, even "0" works */ - blankus = (m->v.active - mode->vdisplay - 2) * m->h.active; + blankus = (m->v.active - mode->crtc_vdisplay - 2) * m->h.active; blankus *= 1000; - blankus /= mode->clock; + blankus /= mode->crtc_clock; m->v.blankus = blankus; if (mode->flags & DRM_MODE_FLAG_INTERLACE) { - m->v.blank2e = m->v.active + m->v.synce + vbackp; - m->v.blank2s = m->v.blank2e + (mode->vdisplay * vscan / ilace); + m->v.blank2e = m->v.active + m->v.blanke; + m->v.blank2s = m->v.blank2e + mode->crtc_vdisplay; m->v.active = (m->v.active * 2) + 1; m->interlace = true; } else { @@ -2071,9 +2083,8 @@ nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) m->v.blank2s = 1; m->interlace = false; } - m->clock = mode->clock; + m->clock = mode->crtc_clock; - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); asyh->set.mode = true; } @@ -2392,6 +2403,51 @@ out: /****************************************************************************** * Output path helpers *****************************************************************************/ +static void +nv50_outp_release(struct nouveau_encoder *nv_encoder) +{ + struct nv50_disp *disp = nv50_disp(nv_encoder->base.base.dev); + struct { + struct nv50_disp_mthd_v1 base; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_RELEASE, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = nv_encoder->dcb->hashm, + }; + + nvif_mthd(disp->disp, 0, &args, sizeof(args)); + nv_encoder->or = -1; + nv_encoder->link = 0; +} + +static int +nv50_outp_acquire(struct nouveau_encoder *nv_encoder) +{ + struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); + struct nv50_disp *disp = nv50_disp(drm->dev); + struct { + struct nv50_disp_mthd_v1 base; + struct nv50_disp_acquire_v0 info; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_ACQUIRE, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = nv_encoder->dcb->hashm, + }; + int ret; + + ret = nvif_mthd(disp->disp, 0, &args, sizeof(args)); + if (ret) { + NV_ERROR(drm, "error acquiring output path: %d\n", ret); + return ret; + } + + nv_encoder->or = args.info.or; + nv_encoder->link = args.info.link; + return 0; +} + static int nv50_outp_atomic_check_view(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, @@ -2449,30 +2505,6 @@ nv50_outp_atomic_check(struct drm_encoder *encoder, * DAC *****************************************************************************/ static void -nv50_dac_dpms(struct drm_encoder *encoder, int mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nv50_disp *disp = nv50_disp(encoder->dev); - struct { - struct nv50_disp_mthd_v1 base; - struct nv50_disp_dac_pwr_v0 pwr; - } args = { - .base.version = 1, - .base.method = NV50_DISP_MTHD_V1_DAC_PWR, - .base.hasht = nv_encoder->dcb->hasht, - .base.hashm = nv_encoder->dcb->hashm, - .pwr.state = 1, - .pwr.data = 1, - .pwr.vsync = (mode != DRM_MODE_DPMS_SUSPEND && - mode != DRM_MODE_DPMS_OFF), - .pwr.hsync = (mode != DRM_MODE_DPMS_STANDBY && - mode != DRM_MODE_DPMS_OFF), - }; - - nvif_mthd(disp->disp, 0, &args, sizeof(args)); -} - -static void nv50_dac_disable(struct drm_encoder *encoder) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); @@ -2495,6 +2527,7 @@ nv50_dac_disable(struct drm_encoder *encoder) } nv_encoder->crtc = NULL; + nv50_outp_release(nv_encoder); } static void @@ -2506,6 +2539,8 @@ nv50_dac_enable(struct drm_encoder *encoder) struct drm_display_mode *mode = &nv_crtc->base.state->adjusted_mode; u32 *push; + nv50_outp_acquire(nv_encoder); + push = evo_wait(mast, 8); if (push) { if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { @@ -2573,7 +2608,6 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) static const struct drm_encoder_helper_funcs nv50_dac_help = { - .dpms = nv50_dac_dpms, .atomic_check = nv50_outp_atomic_check, .enable = nv50_dac_enable, .disable = nv50_dac_disable, @@ -2606,7 +2640,6 @@ nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe) if (!nv_encoder) return -ENOMEM; nv_encoder->dcb = dcbe; - nv_encoder->or = ffs(dcbe->or) - 1; bus = nvkm_i2c_bus_find(i2c, dcbe->i2c_index); if (bus) @@ -2708,6 +2741,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) struct { struct nv50_disp_mthd_v1 base; struct nv50_disp_sor_hdmi_pwr_v0 pwr; + u8 infoframes[2 * 17]; /* two frames, up to 17 bytes each */ } args = { .base.version = 1, .base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR, @@ -2719,17 +2753,42 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) }; struct nouveau_connector *nv_connector; u32 max_ac_packet; + union hdmi_infoframe avi_frame; + union hdmi_infoframe vendor_frame; + int ret; + int size; nv_connector = nouveau_encoder_connector_get(nv_encoder); if (!drm_detect_hdmi_monitor(nv_connector->edid)) return; + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, mode); + if (!ret) { + /* We have an AVI InfoFrame, populate it to the display */ + args.pwr.avi_infoframe_length + = hdmi_infoframe_pack(&avi_frame, args.infoframes, 17); + } + + ret = drm_hdmi_vendor_infoframe_from_display_mode(&vendor_frame.vendor.hdmi, mode); + if (!ret) { + /* We have a Vendor InfoFrame, populate it to the display */ + args.pwr.vendor_infoframe_length + = hdmi_infoframe_pack(&vendor_frame, + args.infoframes + + args.pwr.avi_infoframe_length, + 17); + } + max_ac_packet = mode->htotal - mode->hdisplay; max_ac_packet -= args.pwr.rekey; max_ac_packet -= 18; /* constant from tegra */ args.pwr.max_ac_packet = max_ac_packet / 32; - nvif_mthd(disp->disp, 0, &args, sizeof(args)); + size = sizeof(args.base) + + sizeof(args.pwr) + + args.pwr.avi_infoframe_length + + args.pwr.vendor_infoframe_length; + nvif_mthd(disp->disp, 0, &args, size); nv50_audio_enable(encoder, mode); } @@ -2747,6 +2806,8 @@ struct nv50_mstm { struct nv50_msto *msto[4]; bool modified; + bool disabled; + int links; }; struct nv50_mstc { @@ -2873,17 +2934,20 @@ nv50_msto_enable(struct drm_encoder *encoder) struct nv50_mstc *mstc = NULL; struct nv50_mstm *mstm = NULL; struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; u8 proto, depth; int slots; bool r; - drm_for_each_connector(connector, encoder->dev) { + drm_connector_list_iter_begin(encoder->dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->state->best_encoder == &msto->encoder) { mstc = nv50_mstc(connector); mstm = mstc->mstm; break; } } + drm_connector_list_iter_end(&conn_iter); if (WARN_ON(!mstc)) return; @@ -2892,7 +2956,10 @@ nv50_msto_enable(struct drm_encoder *encoder) r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, mstc->pbn, slots); WARN_ON(!r); - if (mstm->outp->dcb->sorconf.link & 1) + if (!mstm->links++) + nv50_outp_acquire(mstm->outp); + + if (mstm->outp->link & 1) proto = 0x8; else proto = 0x9; @@ -2924,6 +2991,8 @@ nv50_msto_disable(struct drm_encoder *encoder) mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0); mstm->modified = true; + if (!--mstm->links) + mstm->disabled = true; msto->disabled = true; } @@ -3139,6 +3208,12 @@ nv50_mstm_prepare(struct nv50_mstm *mstm) nv50_msto_prepare(msto); } } + + if (mstm->disabled) { + if (!mstm->links) + nv50_outp_release(mstm->outp); + mstm->disabled = false; + } } static void @@ -3366,25 +3441,6 @@ nv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max, * SOR *****************************************************************************/ static void -nv50_sor_dpms(struct drm_encoder *encoder, int mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nv50_disp *disp = nv50_disp(encoder->dev); - struct { - struct nv50_disp_mthd_v1 base; - struct nv50_disp_sor_pwr_v0 pwr; - } args = { - .base.version = 1, - .base.method = NV50_DISP_MTHD_V1_SOR_PWR, - .base.hasht = nv_encoder->dcb->hasht, - .base.hashm = nv_encoder->dcb->hashm, - .pwr.state = mode == DRM_MODE_DPMS_ON, - }; - - nvif_mthd(disp->disp, 0, &args, sizeof(args)); -} - -static void nv50_sor_update(struct nouveau_encoder *nv_encoder, u8 head, struct drm_display_mode *mode, u8 proto, u8 depth) { @@ -3456,6 +3512,7 @@ nv50_sor_disable(struct drm_encoder *encoder) nv_encoder->update(nv_encoder, nv_crtc->index, NULL, 0, 0); nv50_audio_disable(encoder, nv_crtc); nv50_hdmi_disable(&nv_encoder->base.base, nv_crtc); + nv50_outp_release(nv_encoder); } } @@ -3484,10 +3541,11 @@ nv50_sor_enable(struct drm_encoder *encoder) nv_connector = nouveau_encoder_connector_get(nv_encoder); nv_encoder->crtc = encoder->crtc; + nv50_outp_acquire(nv_encoder); switch (nv_encoder->dcb->type) { case DCB_OUTPUT_TMDS: - if (nv_encoder->dcb->sorconf.link & 1) { + if (nv_encoder->link & 1) { proto = 0x1; /* Only enable dual-link if: * - Need to (i.e. rate > 165MHz) @@ -3545,7 +3603,7 @@ nv50_sor_enable(struct drm_encoder *encoder) else depth = 0x6; - if (nv_encoder->dcb->sorconf.link & 1) + if (nv_encoder->link & 1) proto = 0x8; else proto = 0x9; @@ -3562,7 +3620,6 @@ nv50_sor_enable(struct drm_encoder *encoder) static const struct drm_encoder_helper_funcs nv50_sor_help = { - .dpms = nv50_sor_dpms, .atomic_check = nv50_outp_atomic_check, .enable = nv50_sor_enable, .disable = nv50_sor_disable, @@ -3605,7 +3662,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) if (!nv_encoder) return -ENOMEM; nv_encoder->dcb = dcbe; - nv_encoder->or = ffs(dcbe->or) - 1; nv_encoder->update = nv50_sor_update; encoder = to_drm_encoder(nv_encoder); @@ -3646,26 +3702,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) /****************************************************************************** * PIOR *****************************************************************************/ -static void -nv50_pior_dpms(struct drm_encoder *encoder, int mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nv50_disp *disp = nv50_disp(encoder->dev); - struct { - struct nv50_disp_mthd_v1 base; - struct nv50_disp_pior_pwr_v0 pwr; - } args = { - .base.version = 1, - .base.method = NV50_DISP_MTHD_V1_PIOR_PWR, - .base.hasht = nv_encoder->dcb->hasht, - .base.hashm = nv_encoder->dcb->hashm, - .pwr.state = mode == DRM_MODE_DPMS_ON, - .pwr.type = nv_encoder->dcb->type, - }; - - nvif_mthd(disp->disp, 0, &args, sizeof(args)); -} - static int nv50_pior_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, @@ -3698,6 +3734,7 @@ nv50_pior_disable(struct drm_encoder *encoder) } nv_encoder->crtc = NULL; + nv50_outp_release(nv_encoder); } static void @@ -3712,6 +3749,8 @@ nv50_pior_enable(struct drm_encoder *encoder) u8 proto, depth; u32 *push; + nv50_outp_acquire(nv_encoder); + nv_connector = nouveau_encoder_connector_get(nv_encoder); switch (nv_connector->base.display_info.bpc) { case 10: depth = 0x6; break; @@ -3750,7 +3789,6 @@ nv50_pior_enable(struct drm_encoder *encoder) static const struct drm_encoder_helper_funcs nv50_pior_help = { - .dpms = nv50_pior_dpms, .atomic_check = nv50_pior_atomic_check, .enable = nv50_pior_enable, .disable = nv50_pior_disable, @@ -3800,7 +3838,6 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) if (!nv_encoder) return -ENOMEM; nv_encoder->dcb = dcbe; - nv_encoder->or = ffs(dcbe->or) - 1; nv_encoder->i2c = ddc; nv_encoder->aux = aux; @@ -4315,14 +4352,8 @@ nv50_display_init(struct drm_device *dev) list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { - const struct drm_encoder_helper_funcs *help; - struct nouveau_encoder *nv_encoder; - - nv_encoder = nouveau_encoder(encoder); - help = encoder->helper_private; - if (help && help->dpms) - help->dpms(encoder, DRM_MODE_DPMS_ON); - + struct nouveau_encoder *nv_encoder = + nouveau_encoder(encoder); nv50_mstm_init(nv_encoder->dp.mstm); } } |