diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv50_display.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_display.c | 193 |
1 files changed, 109 insertions, 84 deletions
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 03949eaa629f..490b90866baf 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -26,6 +26,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> #include <drm/drm_dp_helper.h> #include <nvif/class.h> @@ -65,15 +66,29 @@ static int nv50_chan_create(struct nvif_object *disp, const u32 *oclass, u8 head, void *data, u32 size, struct nv50_chan *chan) { + const u32 handle = (oclass[0] << 16) | head; + u32 sclass[8]; + int ret, i; + + ret = nvif_object_sclass(disp, sclass, ARRAY_SIZE(sclass)); + WARN_ON(ret > ARRAY_SIZE(sclass)); + if (ret < 0) + return ret; + while (oclass[0]) { - int ret = nvif_object_init(disp, NULL, (oclass[0] << 16) | head, - oclass[0], data, size, - &chan->user); - if (oclass++, ret == 0) { - nvif_object_map(&chan->user); - return ret; + for (i = 0; i < ARRAY_SIZE(sclass); i++) { + if (sclass[i] == oclass[0]) { + ret = nvif_object_init(disp, NULL, handle, + oclass[0], data, size, + &chan->user); + if (ret == 0) + nvif_object_map(&chan->user); + return ret; + } } + oclass++; } + return -ENOSYS; } @@ -110,6 +125,7 @@ nv50_pioc_create(struct nvif_object *disp, const u32 *oclass, u8 head, struct nv50_curs { struct nv50_pioc base; + struct nouveau_bo *image; }; static int @@ -265,6 +281,7 @@ nv50_core_create(struct nvif_object *disp, u64 syncbuf, struct nv50_mast *core) .pushbuf = 0xb0007d00, }; static const u32 oclass[] = { + GM204_DISP_CORE_CHANNEL_DMA, GM107_DISP_CORE_CHANNEL_DMA, GK110_DISP_CORE_CHANNEL_DMA, GK104_DISP_CORE_CHANNEL_DMA, @@ -424,8 +441,21 @@ evo_kick(u32 *push, void *evoc) mutex_unlock(&dmac->lock); } +#if 1 #define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m)) #define evo_data(p,d) *((p)++) = (d) +#else +#define evo_mthd(p,m,s) do { \ + const u32 _m = (m), _s = (s); \ + printk(KERN_ERR "%04x %d %s\n", _m, _s, __func__); \ + *((p)++) = ((_s << 18) | _m); \ +} while(0) +#define evo_data(p,d) do { \ + const u32 _d = (d); \ + printk(KERN_ERR "\t%08x\n", _d); \ + *((p)++) = _d; \ +} while(0) +#endif static bool evo_sync_wait(void *data) @@ -791,6 +821,22 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) } static int +nv50_crtc_set_raster_vblank_dmi(struct nouveau_crtc *nv_crtc, u32 usec) +{ + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + u32 *push; + + push = evo_wait(mast, 8); + if (!push) + return -ENOMEM; + + evo_mthd(push, 0x0828 + (nv_crtc->index * 0x400), 1); + evo_data(push, usec); + evo_kick(push, mast); + return 0; +} + +static int nv50_crtc_set_color_vibrance(struct nouveau_crtc *nv_crtc, bool update) { struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); @@ -871,23 +917,24 @@ static void nv50_crtc_cursor_show(struct nouveau_crtc *nv_crtc) { struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + struct nv50_curs *curs = nv50_curs(&nv_crtc->base); u32 *push = evo_wait(mast, 16); if (push) { if (nv50_vers(mast) < G82_DISP_CORE_CHANNEL_DMA) { evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 2); evo_data(push, 0x85000000); - evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8); + evo_data(push, curs->image->bo.offset >> 8); } else if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 2); evo_data(push, 0x85000000); - evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8); + evo_data(push, curs->image->bo.offset >> 8); evo_mthd(push, 0x089c + (nv_crtc->index * 0x400), 1); evo_data(push, mast->base.vram.handle); } else { evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 2); evo_data(push, 0x85000000); - evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8); + evo_data(push, curs->image->bo.offset >> 8); evo_mthd(push, 0x048c + (nv_crtc->index * 0x300), 1); evo_data(push, mast->base.vram.handle); } @@ -924,8 +971,9 @@ static void nv50_crtc_cursor_show_hide(struct nouveau_crtc *nv_crtc, bool show, bool update) { struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + struct nv50_curs *curs = nv50_curs(&nv_crtc->base); - if (show) + if (show && curs->image) nv50_crtc_cursor_show(nv_crtc); else nv50_crtc_cursor_hide(nv_crtc); @@ -1025,7 +1073,7 @@ nv50_crtc_commit(struct drm_crtc *crtc) evo_kick(push, mast); } - nv50_crtc_cursor_show_hide(nv_crtc, nv_crtc->cursor.visible, true); + nv50_crtc_cursor_show_hide(nv_crtc, true, true); nv50_display_flip_next(crtc, crtc->primary->fb, NULL, 1); } @@ -1044,7 +1092,7 @@ nv50_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb) struct nv50_head *head = nv50_head(crtc); int ret; - ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM); + ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM, true); if (ret == 0) { if (head->image) nouveau_bo_unpin(head->image); @@ -1066,7 +1114,7 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks; u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks; - u32 vblan2e = 0, vblan2s = 1; + u32 vblan2e = 0, vblan2s = 1, vblankus = 0; u32 *push; int ret; @@ -1083,6 +1131,11 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, vblanke = vsynce + vbackp; vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; vblanks = vactive - vfrontp - 1; + /* XXX: Safe underestimate, even "0" works */ + vblankus = (vactive - mode->vdisplay - 2) * hactive; + vblankus *= 1000; + vblankus /= mode->clock; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { vblan2e = vactive + vsynce + vbackp; vblan2s = vblan2e + (mode->vdisplay * vscan / ilace); @@ -1136,6 +1189,11 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, nv_connector = nouveau_crtc_connector_get(nv_crtc); nv50_crtc_set_dither(nv_crtc, false); nv50_crtc_set_scale(nv_crtc, false); + + /* G94 only accepts this after setting scale */ + if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) + nv50_crtc_set_raster_vblank_dmi(nv_crtc, vblankus); + nv50_crtc_set_color_vibrance(nv_crtc, false); nv50_crtc_set_image(nv_crtc, crtc->primary->fb, x, y, false); return 0; @@ -1215,13 +1273,13 @@ nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, uint32_t handle, uint32_t width, uint32_t height) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv50_curs *curs = nv50_curs(crtc); struct drm_device *dev = crtc->dev; - struct drm_gem_object *gem; - struct nouveau_bo *nvbo; - bool visible = (handle != 0); - int i, ret = 0; + struct drm_gem_object *gem = NULL; + struct nouveau_bo *nvbo = NULL; + int ret = 0; - if (visible) { + if (handle) { if (width != 64 || height != 64) return -EINVAL; @@ -1230,23 +1288,17 @@ nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, return -ENOENT; nvbo = nouveau_gem_object(gem); - ret = nouveau_bo_map(nvbo); - if (ret == 0) { - for (i = 0; i < 64 * 64; i++) { - u32 v = nouveau_bo_rd32(nvbo, i); - nouveau_bo_wr32(nv_crtc->cursor.nvbo, i, v); - } - nouveau_bo_unmap(nvbo); - } - - drm_gem_object_unreference_unlocked(gem); + ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM, true); } - if (visible != nv_crtc->cursor.visible) { - nv50_crtc_cursor_show_hide(nv_crtc, visible, true); - nv_crtc->cursor.visible = visible; + if (ret == 0) { + if (curs->image) + nouveau_bo_unpin(curs->image); + nouveau_bo_ref(nvbo, &curs->image); } + drm_gem_object_unreference_unlocked(gem); + nv50_crtc_cursor_show_hide(nv_crtc, true, true); return ret; } @@ -1301,10 +1353,10 @@ nv50_crtc_destroy(struct drm_crtc *crtc) nouveau_bo_unpin(head->image); nouveau_bo_ref(NULL, &head->image); - nouveau_bo_unmap(nv_crtc->cursor.nvbo); - if (nv_crtc->cursor.nvbo) - nouveau_bo_unpin(nv_crtc->cursor.nvbo); - nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + /*XXX: ditto */ + if (head->curs.image) + nouveau_bo_unpin(head->curs.image); + nouveau_bo_ref(NULL, &head->curs.image); nouveau_bo_unmap(nv_crtc->lut.nvbo); if (nv_crtc->lut.nvbo) @@ -1336,16 +1388,6 @@ static const struct drm_crtc_funcs nv50_crtc_func = { .page_flip = nouveau_crtc_page_flip, }; -static void -nv50_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y) -{ -} - -static void -nv50_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset) -{ -} - static int nv50_crtc_create(struct drm_device *dev, int index) { @@ -1364,8 +1406,6 @@ nv50_crtc_create(struct drm_device *dev, int index) head->base.set_color_vibrance = nv50_crtc_set_color_vibrance; head->base.color_vibrance = 50; head->base.vibrant_hue = 0; - head->base.cursor.set_offset = nv50_cursor_set_offset; - head->base.cursor.set_pos = nv50_cursor_set_pos; for (i = 0; i < 256; i++) { head->base.lut.r[i] = i << 8; head->base.lut.g[i] = i << 8; @@ -1378,9 +1418,9 @@ nv50_crtc_create(struct drm_device *dev, int index) drm_mode_crtc_set_gamma_size(crtc, 256); ret = nouveau_bo_new(dev, 8192, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &head->base.lut.nvbo); + 0, 0x0000, NULL, NULL, &head->base.lut.nvbo); if (!ret) { - ret = nouveau_bo_pin(head->base.lut.nvbo, TTM_PL_FLAG_VRAM); + ret = nouveau_bo_pin(head->base.lut.nvbo, TTM_PL_FLAG_VRAM, true); if (!ret) { ret = nouveau_bo_map(head->base.lut.nvbo); if (ret) @@ -1400,22 +1440,6 @@ nv50_crtc_create(struct drm_device *dev, int index) if (ret) goto out; - ret = nouveau_bo_new(dev, 64 * 64 * 4, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &head->base.cursor.nvbo); - if (!ret) { - ret = nouveau_bo_pin(head->base.cursor.nvbo, TTM_PL_FLAG_VRAM); - if (!ret) { - ret = nouveau_bo_map(head->base.cursor.nvbo); - if (ret) - nouveau_bo_unpin(head->base.lut.nvbo); - } - if (ret) - nouveau_bo_ref(NULL, &head->base.cursor.nvbo); - } - - if (ret) - goto out; - /* allocate page flip / sync resources */ ret = nv50_base_create(disp->disp, index, disp->sync->bo.offset, &head->sync); @@ -1651,17 +1675,21 @@ static void nv50_audio_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); struct nouveau_connector *nv_connector; struct nv50_disp *disp = nv50_disp(encoder->dev); - struct { - struct nv50_disp_mthd_v1 base; - struct nv50_disp_sor_hda_eld_v0 eld; + struct __packed { + struct { + struct nv50_disp_mthd_v1 mthd; + struct nv50_disp_sor_hda_eld_v0 eld; + } base; u8 data[sizeof(nv_connector->base.eld)]; } args = { - .base.version = 1, - .base.method = NV50_DISP_MTHD_V1_SOR_HDA_ELD, - .base.hasht = nv_encoder->dcb->hasht, - .base.hashm = nv_encoder->dcb->hashm, + .base.mthd.version = 1, + .base.mthd.method = NV50_DISP_MTHD_V1_SOR_HDA_ELD, + .base.mthd.hasht = nv_encoder->dcb->hasht, + .base.mthd.hashm = (0xf0ff & nv_encoder->dcb->hashm) | + (0x0100 << nv_crtc->index), }; nv_connector = nouveau_encoder_connector_get(nv_encoder); @@ -1671,11 +1699,12 @@ nv50_audio_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) drm_edid_to_eld(&nv_connector->base, nv_connector->edid); memcpy(args.data, nv_connector->base.eld, sizeof(args.data)); - nvif_mthd(disp->disp, 0, &args, sizeof(args)); + nvif_mthd(disp->disp, 0, &args, + sizeof(args.base) + drm_eld_size(args.data)); } static void -nv50_audio_disconnect(struct drm_encoder *encoder) +nv50_audio_disconnect(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nv50_disp *disp = nv50_disp(encoder->dev); @@ -1686,7 +1715,8 @@ nv50_audio_disconnect(struct drm_encoder *encoder) .base.version = 1, .base.method = NV50_DISP_MTHD_V1_SOR_HDA_ELD, .base.hasht = nv_encoder->dcb->hasht, - .base.hashm = nv_encoder->dcb->hashm, + .base.hashm = (0xf0ff & nv_encoder->dcb->hashm) | + (0x0100 << nv_crtc->index), }; nvif_mthd(disp->disp, 0, &args, sizeof(args)); @@ -1745,8 +1775,6 @@ nv50_hdmi_disconnect(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) (0x0100 << nv_crtc->index), }; - nv50_audio_disconnect(encoder); - nvif_mthd(disp->disp, 0, &args, sizeof(args)); } @@ -1855,6 +1883,7 @@ nv50_sor_disconnect(struct drm_encoder *encoder) if (nv_crtc) { nv50_crtc_prepare(&nv_crtc->base); nv50_sor_ctrl(nv_encoder, 1 << nv_crtc->index, 0); + nv50_audio_disconnect(encoder, nv_crtc); nv50_hdmi_disconnect(&nv_encoder->base.base, nv_crtc); } } @@ -1954,6 +1983,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, proto = 0x8; else proto = 0x9; + nv50_audio_mode_set(encoder, mode); break; default: BUG_ON(1); @@ -2342,11 +2372,6 @@ nv50_fb_ctor(struct drm_framebuffer *fb) u8 kind = nouveau_bo_tile_layout(nvbo) >> 8; u8 tile = nvbo->tile_mode; - if (nvbo->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG) { - NV_ERROR(drm, "framebuffer requires contiguous bo\n"); - return -EINVAL; - } - if (drm->device.info.chipset >= 0xc0) tile >>= 4; /* yep.. */ @@ -2458,9 +2483,9 @@ nv50_display_create(struct drm_device *dev) /* small shared memory area we use for notifiers and semaphores */ ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &disp->sync); + 0, 0x0000, NULL, NULL, &disp->sync); if (!ret) { - ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM); + ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM, true); if (!ret) { ret = nouveau_bo_map(disp->sync); if (ret) |