|
|
@@ -35,12 +35,34 @@
|
|
|
#include "nouveau_fb.h"
|
|
|
#include "nv50_display.h"
|
|
|
|
|
|
+#define EVO_DMA_NR 9
|
|
|
+
|
|
|
+#define EVO_MASTER (0x00)
|
|
|
+#define EVO_FLIP(c) (0x01 + (c))
|
|
|
+#define EVO_OVLY(c) (0x05 + (c))
|
|
|
+#define EVO_OIMM(c) (0x09 + (c))
|
|
|
+#define EVO_CURS(c) (0x0d + (c))
|
|
|
+
|
|
|
+/* offsets in shared sync bo of various structures */
|
|
|
+#define EVO_SYNC(c, o) ((c) * 0x0100 + (o))
|
|
|
+#define EVO_MAST_NTFY EVO_SYNC( 0, 0x00)
|
|
|
+#define EVO_FLIP_SEM0(c) EVO_SYNC((c), 0x00)
|
|
|
+#define EVO_FLIP_SEM1(c) EVO_SYNC((c), 0x10)
|
|
|
+
|
|
|
+struct evo {
|
|
|
+ int idx;
|
|
|
+ dma_addr_t handle;
|
|
|
+ u32 *ptr;
|
|
|
+ struct {
|
|
|
+ u32 offset;
|
|
|
+ u16 value;
|
|
|
+ } sem;
|
|
|
+};
|
|
|
+
|
|
|
struct nvd0_display {
|
|
|
struct nouveau_gpuobj *mem;
|
|
|
- struct {
|
|
|
- dma_addr_t handle;
|
|
|
- u32 *ptr;
|
|
|
- } evo[1];
|
|
|
+ struct nouveau_bo *sync;
|
|
|
+ struct evo evo[9];
|
|
|
|
|
|
struct tasklet_struct tasklet;
|
|
|
u32 modeset;
|
|
|
@@ -53,6 +75,15 @@ nvd0_display(struct drm_device *dev)
|
|
|
return dev_priv->engine.display.priv;
|
|
|
}
|
|
|
|
|
|
+static struct drm_crtc *
|
|
|
+nvd0_display_crtc_get(struct drm_encoder *encoder)
|
|
|
+{
|
|
|
+ return nouveau_encoder(encoder)->crtc;
|
|
|
+}
|
|
|
+
|
|
|
+/******************************************************************************
|
|
|
+ * EVO channel helpers
|
|
|
+ *****************************************************************************/
|
|
|
static inline int
|
|
|
evo_icmd(struct drm_device *dev, int id, u32 mthd, u32 data)
|
|
|
{
|
|
|
@@ -84,6 +115,9 @@ evo_wait(struct drm_device *dev, int id, int nr)
|
|
|
put = 0;
|
|
|
}
|
|
|
|
|
|
+ if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
|
|
|
+ NV_INFO(dev, "Evo%d: %p START\n", id, disp->evo[id].ptr + put);
|
|
|
+
|
|
|
return disp->evo[id].ptr + put;
|
|
|
}
|
|
|
|
|
|
@@ -91,40 +125,264 @@ static void
|
|
|
evo_kick(u32 *push, struct drm_device *dev, int id)
|
|
|
{
|
|
|
struct nvd0_display *disp = nvd0_display(dev);
|
|
|
+
|
|
|
+ if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) {
|
|
|
+ u32 curp = nv_rd32(dev, 0x640000 + (id * 0x1000)) >> 2;
|
|
|
+ u32 *cur = disp->evo[id].ptr + curp;
|
|
|
+
|
|
|
+ while (cur < push)
|
|
|
+ NV_INFO(dev, "Evo%d: 0x%08x\n", id, *cur++);
|
|
|
+ NV_INFO(dev, "Evo%d: %p KICK!\n", id, push);
|
|
|
+ }
|
|
|
+
|
|
|
nv_wr32(dev, 0x640000 + (id * 0x1000), (push - disp->evo[id].ptr) << 2);
|
|
|
}
|
|
|
|
|
|
#define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m))
|
|
|
#define evo_data(p,d) *((p)++) = (d)
|
|
|
|
|
|
-static struct drm_crtc *
|
|
|
-nvd0_display_crtc_get(struct drm_encoder *encoder)
|
|
|
+static int
|
|
|
+evo_init_dma(struct drm_device *dev, int ch)
|
|
|
{
|
|
|
- return nouveau_encoder(encoder)->crtc;
|
|
|
+ struct nvd0_display *disp = nvd0_display(dev);
|
|
|
+ u32 flags;
|
|
|
+
|
|
|
+ flags = 0x00000000;
|
|
|
+ if (ch == EVO_MASTER)
|
|
|
+ flags |= 0x01000000;
|
|
|
+
|
|
|
+ nv_wr32(dev, 0x610494 + (ch * 0x0010), (disp->evo[ch].handle >> 8) | 3);
|
|
|
+ nv_wr32(dev, 0x610498 + (ch * 0x0010), 0x00010000);
|
|
|
+ nv_wr32(dev, 0x61049c + (ch * 0x0010), 0x00000001);
|
|
|
+ nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000010, 0x00000010);
|
|
|
+ nv_wr32(dev, 0x640000 + (ch * 0x1000), 0x00000000);
|
|
|
+ nv_wr32(dev, 0x610490 + (ch * 0x0010), 0x00000013 | flags);
|
|
|
+ if (!nv_wait(dev, 0x610490 + (ch * 0x0010), 0x80000000, 0x00000000)) {
|
|
|
+ NV_ERROR(dev, "PDISP: ch%d 0x%08x\n", ch,
|
|
|
+ nv_rd32(dev, 0x610490 + (ch * 0x0010)));
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+
|
|
|
+ nv_mask(dev, 0x610090, (1 << ch), (1 << ch));
|
|
|
+ nv_mask(dev, 0x6100a0, (1 << ch), (1 << ch));
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+evo_fini_dma(struct drm_device *dev, int ch)
|
|
|
+{
|
|
|
+ if (!(nv_rd32(dev, 0x610490 + (ch * 0x0010)) & 0x00000010))
|
|
|
+ return;
|
|
|
+
|
|
|
+ nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000010, 0x00000000);
|
|
|
+ nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000003, 0x00000000);
|
|
|
+ nv_wait(dev, 0x610490 + (ch * 0x0010), 0x80000000, 0x00000000);
|
|
|
+ nv_mask(dev, 0x610090, (1 << ch), 0x00000000);
|
|
|
+ nv_mask(dev, 0x6100a0, (1 << ch), 0x00000000);
|
|
|
+}
|
|
|
+
|
|
|
+static inline void
|
|
|
+evo_piow(struct drm_device *dev, int ch, u16 mthd, u32 data)
|
|
|
+{
|
|
|
+ nv_wr32(dev, 0x640000 + (ch * 0x1000) + mthd, data);
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+evo_init_pio(struct drm_device *dev, int ch)
|
|
|
+{
|
|
|
+ nv_wr32(dev, 0x610490 + (ch * 0x0010), 0x00000001);
|
|
|
+ if (!nv_wait(dev, 0x610490 + (ch * 0x0010), 0x00010000, 0x00010000)) {
|
|
|
+ NV_ERROR(dev, "PDISP: ch%d 0x%08x\n", ch,
|
|
|
+ nv_rd32(dev, 0x610490 + (ch * 0x0010)));
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+
|
|
|
+ nv_mask(dev, 0x610090, (1 << ch), (1 << ch));
|
|
|
+ nv_mask(dev, 0x6100a0, (1 << ch), (1 << ch));
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+evo_fini_pio(struct drm_device *dev, int ch)
|
|
|
+{
|
|
|
+ if (!(nv_rd32(dev, 0x610490 + (ch * 0x0010)) & 0x00000001))
|
|
|
+ return;
|
|
|
+
|
|
|
+ nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000010, 0x00000010);
|
|
|
+ nv_mask(dev, 0x610490 + (ch * 0x0010), 0x00000001, 0x00000000);
|
|
|
+ nv_wait(dev, 0x610490 + (ch * 0x0010), 0x00010000, 0x00000000);
|
|
|
+ nv_mask(dev, 0x610090, (1 << ch), 0x00000000);
|
|
|
+ nv_mask(dev, 0x6100a0, (1 << ch), 0x00000000);
|
|
|
+}
|
|
|
+
|
|
|
+static bool
|
|
|
+evo_sync_wait(void *data)
|
|
|
+{
|
|
|
+ return nouveau_bo_rd32(data, EVO_MAST_NTFY) != 0x00000000;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+evo_sync(struct drm_device *dev, int ch)
|
|
|
+{
|
|
|
+ struct nvd0_display *disp = nvd0_display(dev);
|
|
|
+ u32 *push = evo_wait(dev, ch, 8);
|
|
|
+ if (push) {
|
|
|
+ nouveau_bo_wr32(disp->sync, EVO_MAST_NTFY, 0x00000000);
|
|
|
+ evo_mthd(push, 0x0084, 1);
|
|
|
+ evo_data(push, 0x80000000 | EVO_MAST_NTFY);
|
|
|
+ evo_mthd(push, 0x0080, 2);
|
|
|
+ evo_data(push, 0x00000000);
|
|
|
+ evo_data(push, 0x00000000);
|
|
|
+ evo_kick(push, dev, ch);
|
|
|
+ if (nv_wait_cb(dev, evo_sync_wait, disp->sync))
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return -EBUSY;
|
|
|
+}
|
|
|
+
|
|
|
+/******************************************************************************
|
|
|
+ * Page flipping channel
|
|
|
+ *****************************************************************************/
|
|
|
+struct nouveau_bo *
|
|
|
+nvd0_display_crtc_sema(struct drm_device *dev, int crtc)
|
|
|
+{
|
|
|
+ return nvd0_display(dev)->sync;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+nvd0_display_flip_stop(struct drm_crtc *crtc)
|
|
|
+{
|
|
|
+ struct nvd0_display *disp = nvd0_display(crtc->dev);
|
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
|
+ struct evo *evo = &disp->evo[EVO_FLIP(nv_crtc->index)];
|
|
|
+ u32 *push;
|
|
|
+
|
|
|
+ push = evo_wait(crtc->dev, evo->idx, 8);
|
|
|
+ if (push) {
|
|
|
+ evo_mthd(push, 0x0084, 1);
|
|
|
+ evo_data(push, 0x00000000);
|
|
|
+ evo_mthd(push, 0x0094, 1);
|
|
|
+ evo_data(push, 0x00000000);
|
|
|
+ evo_mthd(push, 0x00c0, 1);
|
|
|
+ evo_data(push, 0x00000000);
|
|
|
+ evo_mthd(push, 0x0080, 1);
|
|
|
+ evo_data(push, 0x00000000);
|
|
|
+ evo_kick(push, crtc->dev, evo->idx);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+nvd0_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
|
|
+ struct nouveau_channel *chan, u32 swap_interval)
|
|
|
+{
|
|
|
+ struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb);
|
|
|
+ struct nvd0_display *disp = nvd0_display(crtc->dev);
|
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
|
+ struct evo *evo = &disp->evo[EVO_FLIP(nv_crtc->index)];
|
|
|
+ u64 offset;
|
|
|
+ u32 *push;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ swap_interval <<= 4;
|
|
|
+ if (swap_interval == 0)
|
|
|
+ swap_interval |= 0x100;
|
|
|
+
|
|
|
+ push = evo_wait(crtc->dev, evo->idx, 128);
|
|
|
+ if (unlikely(push == NULL))
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ /* synchronise with the rendering channel, if necessary */
|
|
|
+ if (likely(chan)) {
|
|
|
+ ret = RING_SPACE(chan, 10);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ offset = chan->dispc_vma[nv_crtc->index].offset;
|
|
|
+ offset += evo->sem.offset;
|
|
|
+
|
|
|
+ BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
|
|
|
+ OUT_RING (chan, upper_32_bits(offset));
|
|
|
+ OUT_RING (chan, lower_32_bits(offset));
|
|
|
+ OUT_RING (chan, 0xf00d0000 | evo->sem.value);
|
|
|
+ OUT_RING (chan, 0x1002);
|
|
|
+ BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
|
|
|
+ OUT_RING (chan, upper_32_bits(offset));
|
|
|
+ OUT_RING (chan, lower_32_bits(offset ^ 0x10));
|
|
|
+ OUT_RING (chan, 0x74b1e000);
|
|
|
+ OUT_RING (chan, 0x1001);
|
|
|
+ FIRE_RING (chan);
|
|
|
+ } else {
|
|
|
+ nouveau_bo_wr32(disp->sync, evo->sem.offset / 4,
|
|
|
+ 0xf00d0000 | evo->sem.value);
|
|
|
+ evo_sync(crtc->dev, EVO_MASTER);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* queue the flip */
|
|
|
+ evo_mthd(push, 0x0100, 1);
|
|
|
+ evo_data(push, 0xfffe0000);
|
|
|
+ evo_mthd(push, 0x0084, 1);
|
|
|
+ evo_data(push, swap_interval);
|
|
|
+ if (!(swap_interval & 0x00000100)) {
|
|
|
+ evo_mthd(push, 0x00e0, 1);
|
|
|
+ evo_data(push, 0x40000000);
|
|
|
+ }
|
|
|
+ evo_mthd(push, 0x0088, 4);
|
|
|
+ evo_data(push, evo->sem.offset);
|
|
|
+ evo_data(push, 0xf00d0000 | evo->sem.value);
|
|
|
+ evo_data(push, 0x74b1e000);
|
|
|
+ evo_data(push, NvEvoSync);
|
|
|
+ evo_mthd(push, 0x00a0, 2);
|
|
|
+ evo_data(push, 0x00000000);
|
|
|
+ evo_data(push, 0x00000000);
|
|
|
+ evo_mthd(push, 0x00c0, 1);
|
|
|
+ evo_data(push, nv_fb->r_dma);
|
|
|
+ evo_mthd(push, 0x0110, 2);
|
|
|
+ evo_data(push, 0x00000000);
|
|
|
+ evo_data(push, 0x00000000);
|
|
|
+ evo_mthd(push, 0x0400, 5);
|
|
|
+ evo_data(push, nv_fb->nvbo->bo.offset >> 8);
|
|
|
+ evo_data(push, 0);
|
|
|
+ evo_data(push, (fb->height << 16) | fb->width);
|
|
|
+ evo_data(push, nv_fb->r_pitch);
|
|
|
+ evo_data(push, nv_fb->r_format);
|
|
|
+ evo_mthd(push, 0x0080, 1);
|
|
|
+ evo_data(push, 0x00000000);
|
|
|
+ evo_kick(push, crtc->dev, evo->idx);
|
|
|
+
|
|
|
+ evo->sem.offset ^= 0x10;
|
|
|
+ evo->sem.value++;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
* CRTC
|
|
|
*****************************************************************************/
|
|
|
static int
|
|
|
-nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update)
|
|
|
+nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update)
|
|
|
{
|
|
|
struct drm_device *dev = nv_crtc->base.dev;
|
|
|
- u32 *push, mode;
|
|
|
+ struct nouveau_connector *nv_connector;
|
|
|
+ struct drm_connector *connector;
|
|
|
+ u32 *push, mode = 0x00;
|
|
|
|
|
|
- mode = 0x00000000;
|
|
|
- if (on) {
|
|
|
- /* 0x11: 6bpc dynamic 2x2
|
|
|
- * 0x13: 8bpc dynamic 2x2
|
|
|
- * 0x19: 6bpc static 2x2
|
|
|
- * 0x1b: 8bpc static 2x2
|
|
|
- * 0x21: 6bpc temporal
|
|
|
- * 0x23: 8bpc temporal
|
|
|
- */
|
|
|
- mode = 0x00000011;
|
|
|
+ nv_connector = nouveau_crtc_connector_get(nv_crtc);
|
|
|
+ connector = &nv_connector->base;
|
|
|
+ if (nv_connector->dithering_mode == DITHERING_MODE_AUTO) {
|
|
|
+ if (nv_crtc->base.fb->depth > connector->display_info.bpc * 3)
|
|
|
+ mode = DITHERING_MODE_DYNAMIC2X2;
|
|
|
+ } else {
|
|
|
+ mode = nv_connector->dithering_mode;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (nv_connector->dithering_depth == DITHERING_DEPTH_AUTO) {
|
|
|
+ if (connector->display_info.bpc >= 8)
|
|
|
+ mode |= DITHERING_DEPTH_8BPC;
|
|
|
+ } else {
|
|
|
+ mode |= nv_connector->dithering_depth;
|
|
|
}
|
|
|
|
|
|
- push = evo_wait(dev, 0, 4);
|
|
|
+ push = evo_wait(dev, EVO_MASTER, 4);
|
|
|
if (push) {
|
|
|
evo_mthd(push, 0x0490 + (nv_crtc->index * 0x300), 1);
|
|
|
evo_data(push, mode);
|
|
|
@@ -132,63 +390,98 @@ nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update)
|
|
|
evo_mthd(push, 0x0080, 1);
|
|
|
evo_data(push, 0x00000000);
|
|
|
}
|
|
|
- evo_kick(push, dev, 0);
|
|
|
+ evo_kick(push, dev, EVO_MASTER);
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
-nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, int type, bool update)
|
|
|
+nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update)
|
|
|
{
|
|
|
- struct drm_display_mode *mode = &nv_crtc->base.mode;
|
|
|
+ struct drm_display_mode *omode, *umode = &nv_crtc->base.mode;
|
|
|
struct drm_device *dev = nv_crtc->base.dev;
|
|
|
+ struct drm_crtc *crtc = &nv_crtc->base;
|
|
|
struct nouveau_connector *nv_connector;
|
|
|
- u32 *push, outX, outY;
|
|
|
-
|
|
|
- outX = mode->hdisplay;
|
|
|
- outY = mode->vdisplay;
|
|
|
+ int mode = DRM_MODE_SCALE_NONE;
|
|
|
+ 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);
|
|
|
- if (nv_connector && nv_connector->native_mode) {
|
|
|
- struct drm_display_mode *native = nv_connector->native_mode;
|
|
|
- u32 xratio = (native->hdisplay << 19) / mode->hdisplay;
|
|
|
- u32 yratio = (native->vdisplay << 19) / mode->vdisplay;
|
|
|
-
|
|
|
- switch (type) {
|
|
|
- case DRM_MODE_SCALE_ASPECT:
|
|
|
- if (xratio > yratio) {
|
|
|
- outX = (mode->hdisplay * yratio) >> 19;
|
|
|
- outY = (mode->vdisplay * yratio) >> 19;
|
|
|
- } else {
|
|
|
- outX = (mode->hdisplay * xratio) >> 19;
|
|
|
- outY = (mode->vdisplay * xratio) >> 19;
|
|
|
- }
|
|
|
- break;
|
|
|
- case DRM_MODE_SCALE_FULLSCREEN:
|
|
|
- outX = native->hdisplay;
|
|
|
- outY = native->vdisplay;
|
|
|
- break;
|
|
|
- default:
|
|
|
- break;
|
|
|
+ if (nv_connector && nv_connector->native_mode)
|
|
|
+ mode = nv_connector->scaling_mode;
|
|
|
+
|
|
|
+ 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 &&
|
|
|
+ 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 {
|
|
|
+ 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
|
|
|
+ * removed already for overscan compensation
|
|
|
+ */
|
|
|
+ switch (mode) {
|
|
|
+ case DRM_MODE_SCALE_CENTER:
|
|
|
+ oX = min((u32)umode->hdisplay, oX);
|
|
|
+ oY = min((u32)umode->vdisplay, oY);
|
|
|
+ /* fall-through */
|
|
|
+ case DRM_MODE_SCALE_ASPECT:
|
|
|
+ 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(dev, 0, 16);
|
|
|
+ push = evo_wait(dev, EVO_MASTER, 8);
|
|
|
if (push) {
|
|
|
evo_mthd(push, 0x04c0 + (nv_crtc->index * 0x300), 3);
|
|
|
- evo_data(push, (outY << 16) | outX);
|
|
|
- evo_data(push, (outY << 16) | outX);
|
|
|
- evo_data(push, (outY << 16) | outX);
|
|
|
+ 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, (mode->vdisplay << 16) | mode->hdisplay);
|
|
|
+ evo_data(push, (umode->vdisplay << 16) | umode->hdisplay);
|
|
|
+ evo_kick(push, dev, EVO_MASTER);
|
|
|
if (update) {
|
|
|
- evo_mthd(push, 0x0080, 1);
|
|
|
- evo_data(push, 0x00000000);
|
|
|
+ nvd0_display_flip_stop(crtc);
|
|
|
+ nvd0_display_flip_next(crtc, crtc->fb, NULL, 1);
|
|
|
}
|
|
|
- evo_kick(push, dev, 0);
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
@@ -201,7 +494,7 @@ nvd0_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb,
|
|
|
struct nouveau_framebuffer *nvfb = nouveau_framebuffer(fb);
|
|
|
u32 *push;
|
|
|
|
|
|
- push = evo_wait(fb->dev, 0, 16);
|
|
|
+ push = evo_wait(fb->dev, EVO_MASTER, 16);
|
|
|
if (push) {
|
|
|
evo_mthd(push, 0x0460 + (nv_crtc->index * 0x300), 1);
|
|
|
evo_data(push, nvfb->nvbo->bo.offset >> 8);
|
|
|
@@ -216,7 +509,7 @@ nvd0_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb,
|
|
|
evo_mthd(push, 0x0080, 1);
|
|
|
evo_data(push, 0x00000000);
|
|
|
}
|
|
|
- evo_kick(push, fb->dev, 0);
|
|
|
+ evo_kick(push, fb->dev, EVO_MASTER);
|
|
|
}
|
|
|
|
|
|
nv_crtc->fb.tile_flags = nvfb->r_dma;
|
|
|
@@ -227,7 +520,7 @@ static void
|
|
|
nvd0_crtc_cursor_show(struct nouveau_crtc *nv_crtc, bool show, bool update)
|
|
|
{
|
|
|
struct drm_device *dev = nv_crtc->base.dev;
|
|
|
- u32 *push = evo_wait(dev, 0, 16);
|
|
|
+ u32 *push = evo_wait(dev, EVO_MASTER, 16);
|
|
|
if (push) {
|
|
|
if (show) {
|
|
|
evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 2);
|
|
|
@@ -247,7 +540,7 @@ nvd0_crtc_cursor_show(struct nouveau_crtc *nv_crtc, bool show, bool update)
|
|
|
evo_data(push, 0x00000000);
|
|
|
}
|
|
|
|
|
|
- evo_kick(push, dev, 0);
|
|
|
+ evo_kick(push, dev, EVO_MASTER);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -262,7 +555,9 @@ nvd0_crtc_prepare(struct drm_crtc *crtc)
|
|
|
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
|
u32 *push;
|
|
|
|
|
|
- push = evo_wait(crtc->dev, 0, 2);
|
|
|
+ nvd0_display_flip_stop(crtc);
|
|
|
+
|
|
|
+ push = evo_wait(crtc->dev, EVO_MASTER, 2);
|
|
|
if (push) {
|
|
|
evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1);
|
|
|
evo_data(push, 0x00000000);
|
|
|
@@ -270,7 +565,7 @@ nvd0_crtc_prepare(struct drm_crtc *crtc)
|
|
|
evo_data(push, 0x03000000);
|
|
|
evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1);
|
|
|
evo_data(push, 0x00000000);
|
|
|
- evo_kick(push, crtc->dev, 0);
|
|
|
+ evo_kick(push, crtc->dev, EVO_MASTER);
|
|
|
}
|
|
|
|
|
|
nvd0_crtc_cursor_show(nv_crtc, false, false);
|
|
|
@@ -282,7 +577,7 @@ nvd0_crtc_commit(struct drm_crtc *crtc)
|
|
|
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
|
u32 *push;
|
|
|
|
|
|
- push = evo_wait(crtc->dev, 0, 32);
|
|
|
+ push = evo_wait(crtc->dev, EVO_MASTER, 32);
|
|
|
if (push) {
|
|
|
evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1);
|
|
|
evo_data(push, nv_crtc->fb.tile_flags);
|
|
|
@@ -295,10 +590,11 @@ nvd0_crtc_commit(struct drm_crtc *crtc)
|
|
|
evo_data(push, NvEvoVRAM);
|
|
|
evo_mthd(push, 0x0430 + (nv_crtc->index * 0x300), 1);
|
|
|
evo_data(push, 0xffffff00);
|
|
|
- evo_kick(push, crtc->dev, 0);
|
|
|
+ evo_kick(push, crtc->dev, EVO_MASTER);
|
|
|
}
|
|
|
|
|
|
- nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, true);
|
|
|
+ nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, false);
|
|
|
+ nvd0_display_flip_next(crtc, crtc->fb, NULL, 1);
|
|
|
}
|
|
|
|
|
|
static bool
|
|
|
@@ -333,21 +629,35 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
|
|
|
{
|
|
|
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
|
struct nouveau_connector *nv_connector;
|
|
|
- u32 htotal = mode->htotal;
|
|
|
- u32 vtotal = mode->vtotal;
|
|
|
- u32 hsyncw = mode->hsync_end - mode->hsync_start - 1;
|
|
|
- u32 vsyncw = mode->vsync_end - mode->vsync_start - 1;
|
|
|
- u32 hfrntp = mode->hsync_start - mode->hdisplay;
|
|
|
- u32 vfrntp = mode->vsync_start - mode->vdisplay;
|
|
|
- u32 hbackp = mode->htotal - mode->hsync_end;
|
|
|
- u32 vbackp = mode->vtotal - mode->vsync_end;
|
|
|
- u32 hss2be = hsyncw + hbackp;
|
|
|
- u32 vss2be = vsyncw + vbackp;
|
|
|
- u32 hss2de = htotal - hfrntp;
|
|
|
- u32 vss2de = vtotal - vfrntp;
|
|
|
+ u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1;
|
|
|
+ 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 magic = 0x31ec6000;
|
|
|
u32 syncs, *push;
|
|
|
int ret;
|
|
|
|
|
|
+ hactive = mode->htotal;
|
|
|
+ hsynce = mode->hsync_end - mode->hsync_start - 1;
|
|
|
+ hbackp = mode->htotal - mode->hsync_end;
|
|
|
+ hblanke = hsynce + hbackp;
|
|
|
+ hfrontp = mode->hsync_start - mode->hdisplay;
|
|
|
+ hblanks = mode->htotal - hfrontp - 1;
|
|
|
+
|
|
|
+ vactive = mode->vtotal * vscan / ilace;
|
|
|
+ vsynce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1;
|
|
|
+ vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace;
|
|
|
+ vblanke = vsynce + vbackp;
|
|
|
+ vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace;
|
|
|
+ vblanks = vactive - vfrontp - 1;
|
|
|
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
|
|
|
+ vblan2e = vactive + vsynce + vbackp;
|
|
|
+ vblan2s = vblan2e + (mode->vdisplay * vscan / ilace);
|
|
|
+ vactive = (vactive * 2) + 1;
|
|
|
+ magic |= 0x00000001;
|
|
|
+ }
|
|
|
+
|
|
|
syncs = 0x00000001;
|
|
|
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
|
|
syncs |= 0x00000008;
|
|
|
@@ -358,28 +668,33 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- push = evo_wait(crtc->dev, 0, 64);
|
|
|
+ push = evo_wait(crtc->dev, EVO_MASTER, 64);
|
|
|
if (push) {
|
|
|
- evo_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 5);
|
|
|
+ evo_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 6);
|
|
|
evo_data(push, 0x00000000);
|
|
|
- evo_data(push, (vtotal << 16) | htotal);
|
|
|
- evo_data(push, (vsyncw << 16) | hsyncw);
|
|
|
- evo_data(push, (vss2be << 16) | hss2be);
|
|
|
- evo_data(push, (vss2de << 16) | hss2de);
|
|
|
+ evo_data(push, (vactive << 16) | hactive);
|
|
|
+ evo_data(push, ( vsynce << 16) | hsynce);
|
|
|
+ evo_data(push, (vblanke << 16) | hblanke);
|
|
|
+ evo_data(push, (vblanks << 16) | hblanks);
|
|
|
+ evo_data(push, (vblan2e << 16) | vblan2s);
|
|
|
evo_mthd(push, 0x042c + (nv_crtc->index * 0x300), 1);
|
|
|
evo_data(push, 0x00000000); /* ??? */
|
|
|
evo_mthd(push, 0x0450 + (nv_crtc->index * 0x300), 3);
|
|
|
evo_data(push, mode->clock * 1000);
|
|
|
evo_data(push, 0x00200000); /* ??? */
|
|
|
evo_data(push, mode->clock * 1000);
|
|
|
- evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 1);
|
|
|
+ evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2);
|
|
|
evo_data(push, syncs);
|
|
|
- evo_kick(push, crtc->dev, 0);
|
|
|
+ evo_data(push, magic);
|
|
|
+ evo_mthd(push, 0x04d0 + (nv_crtc->index * 0x300), 2);
|
|
|
+ evo_data(push, 0x00000311);
|
|
|
+ evo_data(push, 0x00000100);
|
|
|
+ evo_kick(push, crtc->dev, EVO_MASTER);
|
|
|
}
|
|
|
|
|
|
nv_connector = nouveau_crtc_connector_get(nv_crtc);
|
|
|
- nvd0_crtc_set_dither(nv_crtc, nv_connector->use_dithering, false);
|
|
|
- nvd0_crtc_set_scale(nv_crtc, nv_connector->scaling_mode, false);
|
|
|
+ nvd0_crtc_set_dither(nv_crtc, false);
|
|
|
+ nvd0_crtc_set_scale(nv_crtc, false);
|
|
|
nvd0_crtc_set_image(nv_crtc, crtc->fb, x, y, false);
|
|
|
return 0;
|
|
|
}
|
|
|
@@ -400,7 +715,9 @@ nvd0_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
+ nvd0_display_flip_stop(crtc);
|
|
|
nvd0_crtc_set_image(nv_crtc, crtc->fb, x, y, true);
|
|
|
+ nvd0_display_flip_next(crtc, crtc->fb, NULL, 1);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
@@ -410,6 +727,7 @@ nvd0_crtc_mode_set_base_atomic(struct drm_crtc *crtc,
|
|
|
enum mode_set_atomic state)
|
|
|
{
|
|
|
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
|
+ nvd0_display_flip_stop(crtc);
|
|
|
nvd0_crtc_set_image(nv_crtc, fb, x, y, true);
|
|
|
return 0;
|
|
|
}
|
|
|
@@ -472,10 +790,10 @@ static int
|
|
|
nvd0_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
|
|
|
{
|
|
|
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
|
|
- const u32 data = (y << 16) | x;
|
|
|
+ int ch = EVO_CURS(nv_crtc->index);
|
|
|
|
|
|
- nv_wr32(crtc->dev, 0x64d084 + (nv_crtc->index * 0x1000), data);
|
|
|
- nv_wr32(crtc->dev, 0x64d080 + (nv_crtc->index * 0x1000), 0x00000000);
|
|
|
+ evo_piow(crtc->dev, ch, 0x0084, (y << 16) | x);
|
|
|
+ evo_piow(crtc->dev, ch, 0x0080, 0x00000000);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
@@ -525,6 +843,7 @@ static const struct drm_crtc_funcs nvd0_crtc_func = {
|
|
|
.gamma_set = nvd0_crtc_gamma_set,
|
|
|
.set_config = drm_crtc_helper_set_config,
|
|
|
.destroy = nvd0_crtc_destroy,
|
|
|
+ .page_flip = nouveau_crtc_page_flip,
|
|
|
};
|
|
|
|
|
|
static void
|
|
|
@@ -659,12 +978,12 @@ nvd0_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
|
|
|
|
|
nvd0_dac_dpms(encoder, DRM_MODE_DPMS_ON);
|
|
|
|
|
|
- push = evo_wait(encoder->dev, 0, 4);
|
|
|
+ push = evo_wait(encoder->dev, EVO_MASTER, 4);
|
|
|
if (push) {
|
|
|
evo_mthd(push, 0x0180 + (nv_encoder->or * 0x20), 2);
|
|
|
evo_data(push, 1 << nv_crtc->index);
|
|
|
evo_data(push, 0x00ff);
|
|
|
- evo_kick(push, encoder->dev, 0);
|
|
|
+ evo_kick(push, encoder->dev, EVO_MASTER);
|
|
|
}
|
|
|
|
|
|
nv_encoder->crtc = encoder->crtc;
|
|
|
@@ -680,13 +999,13 @@ nvd0_dac_disconnect(struct drm_encoder *encoder)
|
|
|
if (nv_encoder->crtc) {
|
|
|
nvd0_crtc_prepare(nv_encoder->crtc);
|
|
|
|
|
|
- push = evo_wait(dev, 0, 4);
|
|
|
+ push = evo_wait(dev, EVO_MASTER, 4);
|
|
|
if (push) {
|
|
|
evo_mthd(push, 0x0180 + (nv_encoder->or * 0x20), 1);
|
|
|
evo_data(push, 0x00000000);
|
|
|
evo_mthd(push, 0x0080, 1);
|
|
|
evo_data(push, 0x00000000);
|
|
|
- evo_kick(push, dev, 0);
|
|
|
+ evo_kick(push, dev, EVO_MASTER);
|
|
|
}
|
|
|
|
|
|
nv_encoder->crtc = NULL;
|
|
|
@@ -759,6 +1078,108 @@ nvd0_dac_create(struct drm_connector *connector, struct dcb_entry *dcbe)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/******************************************************************************
|
|
|
+ * Audio
|
|
|
+ *****************************************************************************/
|
|
|
+static void
|
|
|
+nvd0_audio_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
|
|
+{
|
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
|
+ struct nouveau_connector *nv_connector;
|
|
|
+ struct drm_device *dev = encoder->dev;
|
|
|
+ int i, or = nv_encoder->or * 0x30;
|
|
|
+
|
|
|
+ nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
|
|
+ if (!drm_detect_monitor_audio(nv_connector->edid))
|
|
|
+ return;
|
|
|
+
|
|
|
+ nv_mask(dev, 0x10ec10 + or, 0x80000003, 0x80000001);
|
|
|
+
|
|
|
+ drm_edid_to_eld(&nv_connector->base, nv_connector->edid);
|
|
|
+ if (nv_connector->base.eld[0]) {
|
|
|
+ u8 *eld = nv_connector->base.eld;
|
|
|
+
|
|
|
+ for (i = 0; i < eld[2] * 4; i++)
|
|
|
+ nv_wr32(dev, 0x10ec00 + or, (i << 8) | eld[i]);
|
|
|
+ for (i = eld[2] * 4; i < 0x60; i++)
|
|
|
+ nv_wr32(dev, 0x10ec00 + or, (i << 8) | 0x00);
|
|
|
+
|
|
|
+ nv_mask(dev, 0x10ec10 + or, 0x80000002, 0x80000002);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+nvd0_audio_disconnect(struct drm_encoder *encoder)
|
|
|
+{
|
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
|
+ struct drm_device *dev = encoder->dev;
|
|
|
+ int or = nv_encoder->or * 0x30;
|
|
|
+
|
|
|
+ nv_mask(dev, 0x10ec10 + or, 0x80000003, 0x80000000);
|
|
|
+}
|
|
|
+
|
|
|
+/******************************************************************************
|
|
|
+ * HDMI
|
|
|
+ *****************************************************************************/
|
|
|
+static void
|
|
|
+nvd0_hdmi_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 drm_device *dev = encoder->dev;
|
|
|
+ int head = nv_crtc->index * 0x800;
|
|
|
+ u32 rekey = 56; /* binary driver, and tegra constant */
|
|
|
+ u32 max_ac_packet;
|
|
|
+
|
|
|
+ nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
|
|
+ if (!drm_detect_hdmi_monitor(nv_connector->edid))
|
|
|
+ return;
|
|
|
+
|
|
|
+ max_ac_packet = mode->htotal - mode->hdisplay;
|
|
|
+ max_ac_packet -= rekey;
|
|
|
+ max_ac_packet -= 18; /* constant from tegra */
|
|
|
+ max_ac_packet /= 32;
|
|
|
+
|
|
|
+ /* AVI InfoFrame */
|
|
|
+ nv_mask(dev, 0x616714 + head, 0x00000001, 0x00000000);
|
|
|
+ nv_wr32(dev, 0x61671c + head, 0x000d0282);
|
|
|
+ nv_wr32(dev, 0x616720 + head, 0x0000006f);
|
|
|
+ nv_wr32(dev, 0x616724 + head, 0x00000000);
|
|
|
+ nv_wr32(dev, 0x616728 + head, 0x00000000);
|
|
|
+ nv_wr32(dev, 0x61672c + head, 0x00000000);
|
|
|
+ nv_mask(dev, 0x616714 + head, 0x00000001, 0x00000001);
|
|
|
+
|
|
|
+ /* ??? InfoFrame? */
|
|
|
+ nv_mask(dev, 0x6167a4 + head, 0x00000001, 0x00000000);
|
|
|
+ nv_wr32(dev, 0x6167ac + head, 0x00000010);
|
|
|
+ nv_mask(dev, 0x6167a4 + head, 0x00000001, 0x00000001);
|
|
|
+
|
|
|
+ /* HDMI_CTRL */
|
|
|
+ nv_mask(dev, 0x616798 + head, 0x401f007f, 0x40000000 | rekey |
|
|
|
+ max_ac_packet << 16);
|
|
|
+
|
|
|
+ /* NFI, audio doesn't work without it though.. */
|
|
|
+ nv_mask(dev, 0x616548 + head, 0x00000070, 0x00000000);
|
|
|
+
|
|
|
+ nvd0_audio_mode_set(encoder, mode);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+nvd0_hdmi_disconnect(struct drm_encoder *encoder)
|
|
|
+{
|
|
|
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
|
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc);
|
|
|
+ struct drm_device *dev = encoder->dev;
|
|
|
+ int head = nv_crtc->index * 0x800;
|
|
|
+
|
|
|
+ nvd0_audio_disconnect(encoder);
|
|
|
+
|
|
|
+ nv_mask(dev, 0x616798 + head, 0x40000000, 0x00000000);
|
|
|
+ nv_mask(dev, 0x6167a4 + head, 0x00000001, 0x00000000);
|
|
|
+ nv_mask(dev, 0x616714 + head, 0x00000001, 0x00000000);
|
|
|
+}
|
|
|
+
|
|
|
/******************************************************************************
|
|
|
* SOR
|
|
|
*****************************************************************************/
|
|
|
@@ -829,7 +1250,8 @@ static void
|
|
|
nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
|
|
|
struct drm_display_mode *mode)
|
|
|
{
|
|
|
- struct drm_nouveau_private *dev_priv = encoder->dev->dev_private;
|
|
|
+ struct drm_device *dev = encoder->dev;
|
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
|
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
|
|
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
|
|
|
struct nouveau_connector *nv_connector;
|
|
|
@@ -852,6 +1274,8 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
|
|
|
or_config = (mode_ctrl & 0x00000f00) >> 8;
|
|
|
if (mode->clock >= 165000)
|
|
|
or_config |= 0x0100;
|
|
|
+
|
|
|
+ nvd0_hdmi_mode_set(encoder, mode);
|
|
|
break;
|
|
|
case OUTPUT_LVDS:
|
|
|
or_config = (mode_ctrl & 0x00000f00) >> 8;
|
|
|
@@ -861,7 +1285,7 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
|
|
|
if (bios->fp.if_is_24bit)
|
|
|
or_config |= 0x0200;
|
|
|
} else {
|
|
|
- if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) {
|
|
|
+ if (nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
|
|
|
if (((u8 *)nv_connector->edid)[121] == 2)
|
|
|
or_config |= 0x0100;
|
|
|
} else
|
|
|
@@ -889,12 +1313,12 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
|
|
|
|
|
|
nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON);
|
|
|
|
|
|
- push = evo_wait(encoder->dev, 0, 4);
|
|
|
+ push = evo_wait(dev, EVO_MASTER, 4);
|
|
|
if (push) {
|
|
|
evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 2);
|
|
|
evo_data(push, mode_ctrl);
|
|
|
evo_data(push, or_config);
|
|
|
- evo_kick(push, encoder->dev, 0);
|
|
|
+ evo_kick(push, dev, EVO_MASTER);
|
|
|
}
|
|
|
|
|
|
nv_encoder->crtc = encoder->crtc;
|
|
|
@@ -910,15 +1334,17 @@ nvd0_sor_disconnect(struct drm_encoder *encoder)
|
|
|
if (nv_encoder->crtc) {
|
|
|
nvd0_crtc_prepare(nv_encoder->crtc);
|
|
|
|
|
|
- push = evo_wait(dev, 0, 4);
|
|
|
+ push = evo_wait(dev, EVO_MASTER, 4);
|
|
|
if (push) {
|
|
|
evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1);
|
|
|
evo_data(push, 0x00000000);
|
|
|
evo_mthd(push, 0x0080, 1);
|
|
|
evo_data(push, 0x00000000);
|
|
|
- evo_kick(push, dev, 0);
|
|
|
+ evo_kick(push, dev, EVO_MASTER);
|
|
|
}
|
|
|
|
|
|
+ nvd0_hdmi_disconnect(encoder);
|
|
|
+
|
|
|
nv_encoder->crtc = NULL;
|
|
|
nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
|
|
|
}
|
|
|
@@ -1159,6 +1585,12 @@ nvd0_display_intr(struct drm_device *dev)
|
|
|
struct nvd0_display *disp = nvd0_display(dev);
|
|
|
u32 intr = nv_rd32(dev, 0x610088);
|
|
|
|
|
|
+ if (intr & 0x00000001) {
|
|
|
+ u32 stat = nv_rd32(dev, 0x61008c);
|
|
|
+ nv_wr32(dev, 0x61008c, stat);
|
|
|
+ intr &= ~0x00000001;
|
|
|
+ }
|
|
|
+
|
|
|
if (intr & 0x00000002) {
|
|
|
u32 stat = nv_rd32(dev, 0x61009c);
|
|
|
int chid = ffs(stat) - 1;
|
|
|
@@ -1215,38 +1647,29 @@ nvd0_display_intr(struct drm_device *dev)
|
|
|
/******************************************************************************
|
|
|
* Init
|
|
|
*****************************************************************************/
|
|
|
-static void
|
|
|
+void
|
|
|
nvd0_display_fini(struct drm_device *dev)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
- /* fini cursors */
|
|
|
- for (i = 14; i >= 13; i--) {
|
|
|
- if (!(nv_rd32(dev, 0x610490 + (i * 0x10)) & 0x00000001))
|
|
|
- continue;
|
|
|
-
|
|
|
- nv_mask(dev, 0x610490 + (i * 0x10), 0x00000001, 0x00000000);
|
|
|
- nv_wait(dev, 0x610490 + (i * 0x10), 0x00010000, 0x00000000);
|
|
|
- nv_mask(dev, 0x610090, 1 << i, 0x00000000);
|
|
|
- nv_mask(dev, 0x6100a0, 1 << i, 0x00000000);
|
|
|
+ /* fini cursors + overlays + flips */
|
|
|
+ for (i = 1; i >= 0; i--) {
|
|
|
+ evo_fini_pio(dev, EVO_CURS(i));
|
|
|
+ evo_fini_pio(dev, EVO_OIMM(i));
|
|
|
+ evo_fini_dma(dev, EVO_OVLY(i));
|
|
|
+ evo_fini_dma(dev, EVO_FLIP(i));
|
|
|
}
|
|
|
|
|
|
/* fini master */
|
|
|
- if (nv_rd32(dev, 0x610490) & 0x00000010) {
|
|
|
- nv_mask(dev, 0x610490, 0x00000010, 0x00000000);
|
|
|
- nv_mask(dev, 0x610490, 0x00000003, 0x00000000);
|
|
|
- nv_wait(dev, 0x610490, 0x80000000, 0x00000000);
|
|
|
- nv_mask(dev, 0x610090, 0x00000001, 0x00000000);
|
|
|
- nv_mask(dev, 0x6100a0, 0x00000001, 0x00000000);
|
|
|
- }
|
|
|
+ evo_fini_dma(dev, EVO_MASTER);
|
|
|
}
|
|
|
|
|
|
int
|
|
|
nvd0_display_init(struct drm_device *dev)
|
|
|
{
|
|
|
struct nvd0_display *disp = nvd0_display(dev);
|
|
|
+ int ret, i;
|
|
|
u32 *push;
|
|
|
- int i;
|
|
|
|
|
|
if (nv_rd32(dev, 0x6100ac) & 0x00000100) {
|
|
|
nv_wr32(dev, 0x6100ac, 0x00000100);
|
|
|
@@ -1271,7 +1694,7 @@ nvd0_display_init(struct drm_device *dev)
|
|
|
nv_wr32(dev, 0x6301c4 + (i * 0x800), sor);
|
|
|
}
|
|
|
|
|
|
- for (i = 0; i < 2; i++) {
|
|
|
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
|
|
|
u32 crtc0 = nv_rd32(dev, 0x616104 + (i * 0x800));
|
|
|
u32 crtc1 = nv_rd32(dev, 0x616108 + (i * 0x800));
|
|
|
u32 crtc2 = nv_rd32(dev, 0x61610c + (i * 0x800));
|
|
|
@@ -1285,36 +1708,24 @@ nvd0_display_init(struct drm_device *dev)
|
|
|
nv_mask(dev, 0x6100b0, 0x00000307, 0x00000307);
|
|
|
|
|
|
/* init master */
|
|
|
- nv_wr32(dev, 0x610494, (disp->evo[0].handle >> 8) | 3);
|
|
|
- nv_wr32(dev, 0x610498, 0x00010000);
|
|
|
- nv_wr32(dev, 0x61049c, 0x00000001);
|
|
|
- nv_mask(dev, 0x610490, 0x00000010, 0x00000010);
|
|
|
- nv_wr32(dev, 0x640000, 0x00000000);
|
|
|
- nv_wr32(dev, 0x610490, 0x01000013);
|
|
|
- if (!nv_wait(dev, 0x610490, 0x80000000, 0x00000000)) {
|
|
|
- NV_ERROR(dev, "PDISP: master 0x%08x\n",
|
|
|
- nv_rd32(dev, 0x610490));
|
|
|
- return -EBUSY;
|
|
|
+ ret = evo_init_dma(dev, EVO_MASTER);
|
|
|
+ if (ret)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ /* init flips + overlays + cursors */
|
|
|
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
|
|
|
+ if ((ret = evo_init_dma(dev, EVO_FLIP(i))) ||
|
|
|
+ (ret = evo_init_dma(dev, EVO_OVLY(i))) ||
|
|
|
+ (ret = evo_init_pio(dev, EVO_OIMM(i))) ||
|
|
|
+ (ret = evo_init_pio(dev, EVO_CURS(i))))
|
|
|
+ goto error;
|
|
|
}
|
|
|
- nv_mask(dev, 0x610090, 0x00000001, 0x00000001);
|
|
|
- nv_mask(dev, 0x6100a0, 0x00000001, 0x00000001);
|
|
|
|
|
|
- /* init cursors */
|
|
|
- for (i = 13; i <= 14; i++) {
|
|
|
- nv_wr32(dev, 0x610490 + (i * 0x10), 0x00000001);
|
|
|
- if (!nv_wait(dev, 0x610490 + (i * 0x10), 0x00010000, 0x00010000)) {
|
|
|
- NV_ERROR(dev, "PDISP: curs%d 0x%08x\n", i,
|
|
|
- nv_rd32(dev, 0x610490 + (i * 0x10)));
|
|
|
- return -EBUSY;
|
|
|
- }
|
|
|
-
|
|
|
- nv_mask(dev, 0x610090, 1 << i, 1 << i);
|
|
|
- nv_mask(dev, 0x6100a0, 1 << i, 1 << i);
|
|
|
+ push = evo_wait(dev, EVO_MASTER, 32);
|
|
|
+ if (!push) {
|
|
|
+ ret = -EBUSY;
|
|
|
+ goto error;
|
|
|
}
|
|
|
-
|
|
|
- push = evo_wait(dev, 0, 32);
|
|
|
- if (!push)
|
|
|
- return -EBUSY;
|
|
|
evo_mthd(push, 0x0088, 1);
|
|
|
evo_data(push, NvEvoSync);
|
|
|
evo_mthd(push, 0x0084, 1);
|
|
|
@@ -1323,9 +1734,12 @@ nvd0_display_init(struct drm_device *dev)
|
|
|
evo_data(push, 0x80000000);
|
|
|
evo_mthd(push, 0x008c, 1);
|
|
|
evo_data(push, 0x00000000);
|
|
|
- evo_kick(push, dev, 0);
|
|
|
+ evo_kick(push, dev, EVO_MASTER);
|
|
|
|
|
|
- return 0;
|
|
|
+error:
|
|
|
+ if (ret)
|
|
|
+ nvd0_display_fini(dev);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
void
|
|
|
@@ -1334,11 +1748,16 @@ nvd0_display_destroy(struct drm_device *dev)
|
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
|
struct nvd0_display *disp = nvd0_display(dev);
|
|
|
struct pci_dev *pdev = dev->pdev;
|
|
|
+ int i;
|
|
|
|
|
|
- nvd0_display_fini(dev);
|
|
|
+ for (i = 0; i < EVO_DMA_NR; i++) {
|
|
|
+ struct evo *evo = &disp->evo[i];
|
|
|
+ pci_free_consistent(pdev, PAGE_SIZE, evo->ptr, evo->handle);
|
|
|
+ }
|
|
|
|
|
|
- pci_free_consistent(pdev, PAGE_SIZE, disp->evo[0].ptr, disp->evo[0].handle);
|
|
|
nouveau_gpuobj_ref(NULL, &disp->mem);
|
|
|
+ nouveau_bo_unmap(disp->sync);
|
|
|
+ nouveau_bo_ref(NULL, &disp->sync);
|
|
|
nouveau_irq_unregister(dev, 26);
|
|
|
|
|
|
dev_priv->engine.display.priv = NULL;
|
|
|
@@ -1410,61 +1829,83 @@ nvd0_display_create(struct drm_device *dev)
|
|
|
tasklet_init(&disp->tasklet, nvd0_display_bh, (unsigned long)dev);
|
|
|
nouveau_irq_register(dev, 26, nvd0_display_intr);
|
|
|
|
|
|
+ /* small shared memory area we use for notifiers and semaphores */
|
|
|
+ ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
|
|
|
+ 0, 0x0000, &disp->sync);
|
|
|
+ if (!ret) {
|
|
|
+ ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM);
|
|
|
+ if (!ret)
|
|
|
+ ret = nouveau_bo_map(disp->sync);
|
|
|
+ if (ret)
|
|
|
+ nouveau_bo_ref(NULL, &disp->sync);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ret)
|
|
|
+ goto out;
|
|
|
+
|
|
|
/* hash table and dma objects for the memory areas we care about */
|
|
|
ret = nouveau_gpuobj_new(dev, NULL, 0x4000, 0x10000,
|
|
|
NVOBJ_FLAG_ZERO_ALLOC, &disp->mem);
|
|
|
if (ret)
|
|
|
goto out;
|
|
|
|
|
|
- nv_wo32(disp->mem, 0x1000, 0x00000049);
|
|
|
- nv_wo32(disp->mem, 0x1004, (disp->mem->vinst + 0x2000) >> 8);
|
|
|
- nv_wo32(disp->mem, 0x1008, (disp->mem->vinst + 0x2fff) >> 8);
|
|
|
- nv_wo32(disp->mem, 0x100c, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x1010, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x1014, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x0000, NvEvoSync);
|
|
|
- nv_wo32(disp->mem, 0x0004, (0x1000 << 9) | 0x00000001);
|
|
|
-
|
|
|
- nv_wo32(disp->mem, 0x1020, 0x00000049);
|
|
|
- nv_wo32(disp->mem, 0x1024, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x1028, (dev_priv->vram_size - 1) >> 8);
|
|
|
- nv_wo32(disp->mem, 0x102c, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x1030, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x1034, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x0008, NvEvoVRAM);
|
|
|
- nv_wo32(disp->mem, 0x000c, (0x1020 << 9) | 0x00000001);
|
|
|
-
|
|
|
- nv_wo32(disp->mem, 0x1040, 0x00000009);
|
|
|
- nv_wo32(disp->mem, 0x1044, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x1048, (dev_priv->vram_size - 1) >> 8);
|
|
|
- nv_wo32(disp->mem, 0x104c, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x1050, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x1054, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x0010, NvEvoVRAM_LP);
|
|
|
- nv_wo32(disp->mem, 0x0014, (0x1040 << 9) | 0x00000001);
|
|
|
-
|
|
|
- nv_wo32(disp->mem, 0x1060, 0x0fe00009);
|
|
|
- nv_wo32(disp->mem, 0x1064, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x1068, (dev_priv->vram_size - 1) >> 8);
|
|
|
- nv_wo32(disp->mem, 0x106c, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x1070, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x1074, 0x00000000);
|
|
|
- nv_wo32(disp->mem, 0x0018, NvEvoFB32);
|
|
|
- nv_wo32(disp->mem, 0x001c, (0x1060 << 9) | 0x00000001);
|
|
|
-
|
|
|
- pinstmem->flush(dev);
|
|
|
+ /* create evo dma channels */
|
|
|
+ for (i = 0; i < EVO_DMA_NR; i++) {
|
|
|
+ struct evo *evo = &disp->evo[i];
|
|
|
+ u64 offset = disp->sync->bo.offset;
|
|
|
+ u32 dmao = 0x1000 + (i * 0x100);
|
|
|
+ u32 hash = 0x0000 + (i * 0x040);
|
|
|
+
|
|
|
+ evo->idx = i;
|
|
|
+ evo->sem.offset = EVO_SYNC(evo->idx, 0x00);
|
|
|
+ evo->ptr = pci_alloc_consistent(pdev, PAGE_SIZE, &evo->handle);
|
|
|
+ if (!evo->ptr) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
|
|
|
- /* push buffers for evo channels */
|
|
|
- disp->evo[0].ptr =
|
|
|
- pci_alloc_consistent(pdev, PAGE_SIZE, &disp->evo[0].handle);
|
|
|
- if (!disp->evo[0].ptr) {
|
|
|
- ret = -ENOMEM;
|
|
|
- goto out;
|
|
|
+ nv_wo32(disp->mem, dmao + 0x00, 0x00000049);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x04, (offset + 0x0000) >> 8);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x08, (offset + 0x0fff) >> 8);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x0c, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x10, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x14, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, hash + 0x00, NvEvoSync);
|
|
|
+ nv_wo32(disp->mem, hash + 0x04, 0x00000001 | (i << 27) |
|
|
|
+ ((dmao + 0x00) << 9));
|
|
|
+
|
|
|
+ nv_wo32(disp->mem, dmao + 0x20, 0x00000049);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x24, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x28, (dev_priv->vram_size - 1) >> 8);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x2c, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x30, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x34, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, hash + 0x08, NvEvoVRAM);
|
|
|
+ nv_wo32(disp->mem, hash + 0x0c, 0x00000001 | (i << 27) |
|
|
|
+ ((dmao + 0x20) << 9));
|
|
|
+
|
|
|
+ nv_wo32(disp->mem, dmao + 0x40, 0x00000009);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x44, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x48, (dev_priv->vram_size - 1) >> 8);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x4c, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x50, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x54, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, hash + 0x10, NvEvoVRAM_LP);
|
|
|
+ nv_wo32(disp->mem, hash + 0x14, 0x00000001 | (i << 27) |
|
|
|
+ ((dmao + 0x40) << 9));
|
|
|
+
|
|
|
+ nv_wo32(disp->mem, dmao + 0x60, 0x0fe00009);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x64, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x68, (dev_priv->vram_size - 1) >> 8);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x6c, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x70, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, dmao + 0x74, 0x00000000);
|
|
|
+ nv_wo32(disp->mem, hash + 0x18, NvEvoFB32);
|
|
|
+ nv_wo32(disp->mem, hash + 0x1c, 0x00000001 | (i << 27) |
|
|
|
+ ((dmao + 0x60) << 9));
|
|
|
}
|
|
|
|
|
|
- ret = nvd0_display_init(dev);
|
|
|
- if (ret)
|
|
|
- goto out;
|
|
|
+ pinstmem->flush(dev);
|
|
|
|
|
|
out:
|
|
|
if (ret)
|