Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 14464b8c authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nvd0/disp: move remaining interrupt handling to core



Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 206c38a9
Loading
Loading
Loading
Loading
+265 −1
Original line number Original line Diff line number Diff line
@@ -33,6 +33,13 @@
#include <subdev/timer.h>
#include <subdev/timer.h>
#include <subdev/fb.h>
#include <subdev/fb.h>
#include <subdev/bar.h>
#include <subdev/bar.h>
#include <subdev/clock.h>

#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
#include <subdev/bios/disp.h>
#include <subdev/bios/init.h>
#include <subdev/bios/pll.h>


#include "nv50.h"
#include "nv50.h"


@@ -563,6 +570,206 @@ nvd0_disp_sclass[] = {
 * Display engine implementation
 * Display engine implementation
 ******************************************************************************/
 ******************************************************************************/


static u16
exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
	    struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
	    struct nvbios_outp *info)
{
	struct nouveau_bios *bios = nouveau_bios(priv);
	u16 data, idx = 0;
	u16 mask, type;

	if (outp < 4) {
		type = DCB_OUTPUT_ANALOG;
		mask = 0;
	} else {
		outp -= 4;
		switch (ctrl & 0x00000f00) {
		case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
		case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
		case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break;
		case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break;
		case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break;
		case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break;
		default:
			nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl);
			return 0x0000;
		}
		dcb->sorconf.link = mask;
	}

	mask  = 0x00c0 & (mask << 6);
	mask |= 0x0001 << outp;
	mask |= 0x0100 << head;

	/* this is a tad special, but for the moment its needed to get
	 * all the dcb data required by the vbios scripts.. will be cleaned
	 * up later as more bits are moved to the core..
	 */
	while ((data = dcb_outp(bios, idx++, ver, hdr))) {
		u32 conn = nv_ro32(bios, data + 0);
		u32 conf = nv_ro32(bios, data + 4);
		if ((conn & 0x00300000) ||
		    (conn & 0x0000000f) != type ||
		    (conn & 0x0f000000) != (0x01000000 << outp))
			continue;

		if ( (mask & 0x00c0) && (mask & 0x00c0) !=
		    ((mask & 0x00c0) & ((conf & 0x00000030) << 2)))
			continue;

		dcb->type = type;
		dcb->or = 1 << outp;
		dcb->connector = (conn & 0x0000f000) >> 12;

		return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
	}

	return 0x0000;
}

static bool
exec_script(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, int id)
{
	struct nouveau_bios *bios = nouveau_bios(priv);
	struct nvbios_outp info;
	struct dcb_output dcb;
	u8  ver, hdr, cnt, len;
	u16 data;

	data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info);
	if (data) {
		struct nvbios_init init = {
			.subdev = nv_subdev(priv),
			.bios = bios,
			.offset = info.script[id],
			.outp = &dcb,
			.crtc = head,
			.execute = 1,
		};

		return nvbios_exec(&init) == 0;
	}

	return false;
}

static bool
exec_clkcmp(struct nv50_disp_priv *priv, int head, int outp,
	    u32 ctrl, u32 conf, int id, u32 pclk)
{
	struct nouveau_bios *bios = nouveau_bios(priv);
	struct nvbios_outp info1;
	struct nvbios_ocfg info2;
	struct dcb_output dcb;
	u8  ver, hdr, cnt, len;
	u16 data;

	data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info1);
	if (data) {
		data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
		if (data) {
			data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
			if (data) {
				struct nvbios_init init = {
					.subdev = nv_subdev(priv),
					.bios = bios,
					.offset = data,
					.outp = &dcb,
					.crtc = head,
					.execute = 1,
				};

				return nvbios_exec(&init) == 0;
			}
		}
	}

	return false;
}

static void
nvd0_display_unk1_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
{
	int i;

	for (i = 0; mask && i < 8; i++) {
		u32 mcc = nv_rd32(priv, 0x640180 + (i * 0x20));
		if (mcc & (1 << head))
			exec_script(priv, head, i, mcc, 1);
	}

	nv_wr32(priv, 0x6101d4, 0x00000000);
	nv_wr32(priv, 0x6109d4, 0x00000000);
	nv_wr32(priv, 0x6101d0, 0x80000000);
}

static void
nvd0_display_unk2_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
{
	u32 pclk;
	int i;

	for (i = 0; mask && i < 8; i++) {
		u32 mcc = nv_rd32(priv, 0x640180 + (i * 0x20));
		if (mcc & (1 << head))
			exec_script(priv, head, i, mcc, 2);
	}

	pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
	nv_debug(priv, "head %d pclk %d mask 0x%08x\n", head, pclk, mask);
	if (pclk && (mask & 0x00010000)) {
		struct nouveau_clock *clk = nouveau_clock(priv);
		clk->pll_set(clk, PLL_VPLL0 + head, pclk);
	}

	nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000);

	for (i = 0; mask && i < 8; i++) {
		u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20));
		u32 cfg = nv_rd32(priv, 0x660184 + (i * 0x20));
		if (mcp & (1 << head)) {
			if (exec_clkcmp(priv, head, i, mcp, cfg, 0, pclk)) {
				u32 addr, mask, data = 0x00000000;
				if (i < 4) {
					addr = 0x612280 + ((i - 0) * 0x800);
					mask = 0xffffffff;
				} else {
					addr = 0x612300 + ((i - 4) * 0x800);
					mask = 0x00000707;
					if (cfg & 0x00000100)
						data = 0x00000101;
				}
				nv_mask(priv, addr, mask, data);
			}
			break;
		}
	}

	nv_wr32(priv, 0x6101d4, 0x00000000);
	nv_wr32(priv, 0x6109d4, 0x00000000);
	nv_wr32(priv, 0x6101d0, 0x80000000);
}

static void
nvd0_display_unk4_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
{
	int pclk, i;

	pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;

	for (i = 0; mask && i < 8; i++) {
		u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20));
		u32 cfg = nv_rd32(priv, 0x660184 + (i * 0x20));
		if (mcp & (1 << head))
			exec_clkcmp(priv, head, i, mcp, cfg, 1, pclk);
	}

	nv_wr32(priv, 0x6101d4, 0x00000000);
	nv_wr32(priv, 0x6109d4, 0x00000000);
	nv_wr32(priv, 0x6101d0, 0x80000000);
}

static void
static void
nvd0_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
nvd0_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
{
{
@@ -599,7 +806,64 @@ nvd0_disp_intr(struct nouveau_subdev *subdev)
	u32 intr = nv_rd32(priv, 0x610088);
	u32 intr = nv_rd32(priv, 0x610088);
	int i;
	int i;


	for (i = 0; i < 4; i++) {
	if (intr & 0x00000001) {
		u32 stat = nv_rd32(priv, 0x61008c);
		nv_wr32(priv, 0x61008c, stat);
		intr &= ~0x00000001;
	}

	if (intr & 0x00000002) {
		u32 stat = nv_rd32(priv, 0x61009c);
		int chid = ffs(stat) - 1;
		if (chid >= 0) {
			u32 mthd = nv_rd32(priv, 0x6101f0 + (chid * 12));
			u32 data = nv_rd32(priv, 0x6101f4 + (chid * 12));
			u32 unkn = nv_rd32(priv, 0x6101f8 + (chid * 12));

			nv_error(priv, "chid %d mthd 0x%04x data 0x%08x "
				       "0x%08x 0x%08x\n",
				 chid, (mthd & 0x0000ffc), data, mthd, unkn);
			nv_wr32(priv, 0x61009c, (1 << chid));
			nv_wr32(priv, 0x6101f0 + (chid * 12), 0x90000000);
		}

		intr &= ~0x00000002;
	}

	if (intr & 0x00100000) {
		u32 stat = nv_rd32(priv, 0x6100ac);
		u32 mask = 0, crtc = ~0;

		while (!mask && ++crtc < priv->head.nr)
			mask = nv_rd32(priv, 0x6101d4 + (crtc * 0x800));

		if (stat & 0x00000001) {
			nv_wr32(priv, 0x6100ac, 0x00000001);
			nvd0_display_unk1_handler(priv, crtc, mask);
			stat &= ~0x00000001;
		}

		if (stat & 0x00000002) {
			nv_wr32(priv, 0x6100ac, 0x00000002);
			nvd0_display_unk2_handler(priv, crtc, mask);
			stat &= ~0x00000002;
		}

		if (stat & 0x00000004) {
			nv_wr32(priv, 0x6100ac, 0x00000004);
			nvd0_display_unk4_handler(priv, crtc, mask);
			stat &= ~0x00000004;
		}

		if (stat) {
			nv_info(priv, "unknown intr24 0x%08x\n", stat);
			nv_wr32(priv, 0x6100ac, stat);
		}

		intr &= ~0x00100000;
	}

	for (i = 0; i < priv->head.nr; i++) {
		u32 mask = 0x01000000 << i;
		u32 mask = 0x01000000 << i;
		if (mask & intr) {
		if (mask & intr) {
			u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800));
			u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800));
+5 −9
Original line number Original line Diff line number Diff line
@@ -61,16 +61,12 @@ nouveau_irq_handler(DRM_IRQ_ARGS)


	nv_subdev(pmc)->intr(nv_subdev(pmc));
	nv_subdev(pmc)->intr(nv_subdev(pmc));


	if (dev->mode_config.num_crtc) {
	if (dev->mode_config.num_crtc &&
		if (device->card_type >= NV_D0) {
	    device->card_type <= NV_C0 &&
			if (nv_rd32(device, 0x000100) & 0x04000000)
	    device->card_type >= NV_50) {
				nvd0_display_intr(dev);
		} else
		if (device->card_type >= NV_50) {
		if (nv_rd32(device, 0x000100) & 0x04000000)
		if (nv_rd32(device, 0x000100) & 0x04000000)
			nv50_display_intr(dev);
			nv50_display_intr(dev);
	}
	}
	}


	return IRQ_HANDLED;
	return IRQ_HANDLED;
}
}
+0 −1
Original line number Original line Diff line number Diff line
@@ -94,7 +94,6 @@ int nvd0_display_create(struct drm_device *);
void nvd0_display_destroy(struct drm_device *);
void nvd0_display_destroy(struct drm_device *);
int  nvd0_display_init(struct drm_device *);
int  nvd0_display_init(struct drm_device *);
void nvd0_display_fini(struct drm_device *);
void nvd0_display_fini(struct drm_device *);
void nvd0_display_intr(struct drm_device *);


void nvd0_display_flip_stop(struct drm_crtc *);
void nvd0_display_flip_stop(struct drm_crtc *);
int  nvd0_display_flip_next(struct drm_crtc *, struct drm_framebuffer *,
int  nvd0_display_flip_next(struct drm_crtc *, struct drm_framebuffer *,
+0 −252
Original line number Original line Diff line number Diff line
@@ -271,7 +271,6 @@ struct nvd0_disp {
	struct nouveau_object *core;
	struct nouveau_object *core;
	struct nvd0_mast mast;
	struct nvd0_mast mast;


	struct tasklet_struct tasklet;
	u32 modeset;
	u32 modeset;


	struct nouveau_bo *sync;
	struct nouveau_bo *sync;
@@ -1762,254 +1761,6 @@ nvd0_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
	return 0;
	return 0;
}
}


/******************************************************************************
 * IRQ
 *****************************************************************************/
static struct dcb_output *
lookup_dcb(struct drm_device *dev, int id, u32 mc)
{
	struct nouveau_drm *drm = nouveau_drm(dev);
	int type, or, i, link = -1;

	if (id < 4) {
		type = DCB_OUTPUT_ANALOG;
		or   = id;
	} else {
		switch (mc & 0x00000f00) {
		case 0x00000000: link = 0; type = DCB_OUTPUT_LVDS; break;
		case 0x00000100: link = 0; type = DCB_OUTPUT_TMDS; break;
		case 0x00000200: link = 1; type = DCB_OUTPUT_TMDS; break;
		case 0x00000500: link = 0; type = DCB_OUTPUT_TMDS; break;
		case 0x00000800: link = 0; type = DCB_OUTPUT_DP; break;
		case 0x00000900: link = 1; type = DCB_OUTPUT_DP; break;
		default:
			NV_ERROR(drm, "PDISP: unknown SOR mc 0x%08x\n", mc);
			return NULL;
		}

		or = id - 4;
	}

	for (i = 0; i < drm->vbios.dcb.entries; i++) {
		struct dcb_output *dcb = &drm->vbios.dcb.entry[i];
		if (dcb->type == type && (dcb->or & (1 << or)) &&
		    (link < 0 || link == !(dcb->sorconf.link & 1)))
			return dcb;
	}

	NV_ERROR(drm, "PDISP: DCB for %d/0x%08x not found\n", id, mc);
	return NULL;
}

static void
nvd0_display_unk1_handler(struct drm_device *dev, u32 crtc, u32 mask)
{
	struct nouveau_device *device = nouveau_dev(dev);
	struct dcb_output *dcb;
	int i;

	for (i = 0; mask && i < 8; i++) {
		u32 mcc = nv_rd32(device, 0x640180 + (i * 0x20));
		if (!(mcc & (1 << crtc)))
			continue;

		dcb = lookup_dcb(dev, i, mcc);
		if (!dcb)
			continue;

		nouveau_bios_run_display_table(dev, 0x0000, -1, dcb, crtc);
	}

	nv_wr32(device, 0x6101d4, 0x00000000);
	nv_wr32(device, 0x6109d4, 0x00000000);
	nv_wr32(device, 0x6101d0, 0x80000000);
}

static void
nvd0_display_unk2_handler(struct drm_device *dev, u32 crtc, u32 mask)
{
	struct nouveau_device *device = nouveau_dev(dev);
	struct nouveau_drm *drm = nouveau_drm(dev);
	struct dcb_output *dcb;
	u32 or, tmp, pclk;
	int i;

	for (i = 0; mask && i < 8; i++) {
		u32 mcc = nv_rd32(device, 0x640180 + (i * 0x20));
		if (!(mcc & (1 << crtc)))
			continue;

		dcb = lookup_dcb(dev, i, mcc);
		if (!dcb)
			continue;

		nouveau_bios_run_display_table(dev, 0x0000, -2, dcb, crtc);
	}

	pclk = nv_rd32(device, 0x660450 + (crtc * 0x300)) / 1000;
	NV_DEBUG(drm, "PDISP: crtc %d pclk %d mask 0x%08x\n",
			  crtc, pclk, mask);
	if (pclk && (mask & 0x00010000)) {
		nv50_crtc_set_clock(dev, crtc, pclk);
	}

	for (i = 0; mask && i < 8; i++) {
		u32 mcp = nv_rd32(device, 0x660180 + (i * 0x20));
		u32 cfg = nv_rd32(device, 0x660184 + (i * 0x20));
		if (!(mcp & (1 << crtc)))
			continue;

		dcb = lookup_dcb(dev, i, mcp);
		if (!dcb)
			continue;
		or = ffs(dcb->or) - 1;

		nouveau_bios_run_display_table(dev, cfg, pclk, dcb, crtc);

		nv_wr32(device, 0x612200 + (crtc * 0x800), 0x00000000);
		switch (dcb->type) {
		case DCB_OUTPUT_ANALOG:
			nv_wr32(device, 0x612280 + (or * 0x800), 0x00000000);
			break;
		case DCB_OUTPUT_TMDS:
		case DCB_OUTPUT_LVDS:
		case DCB_OUTPUT_DP:
			if (cfg & 0x00000100)
				tmp = 0x00000101;
			else
				tmp = 0x00000000;

			nv_mask(device, 0x612300 + (or * 0x800), 0x00000707, tmp);
			break;
		default:
			break;
		}

		break;
	}

	nv_wr32(device, 0x6101d4, 0x00000000);
	nv_wr32(device, 0x6109d4, 0x00000000);
	nv_wr32(device, 0x6101d0, 0x80000000);
}

static void
nvd0_display_unk4_handler(struct drm_device *dev, u32 crtc, u32 mask)
{
	struct nouveau_device *device = nouveau_dev(dev);
	struct dcb_output *dcb;
	int pclk, i;

	pclk = nv_rd32(device, 0x660450 + (crtc * 0x300)) / 1000;

	for (i = 0; mask && i < 8; i++) {
		u32 mcp = nv_rd32(device, 0x660180 + (i * 0x20));
		u32 cfg = nv_rd32(device, 0x660184 + (i * 0x20));
		if (!(mcp & (1 << crtc)))
			continue;

		dcb = lookup_dcb(dev, i, mcp);
		if (!dcb)
			continue;

		nouveau_bios_run_display_table(dev, cfg, -pclk, dcb, crtc);
	}

	nv_wr32(device, 0x6101d4, 0x00000000);
	nv_wr32(device, 0x6109d4, 0x00000000);
	nv_wr32(device, 0x6101d0, 0x80000000);
}

static void
nvd0_display_bh(unsigned long data)
{
	struct drm_device *dev = (struct drm_device *)data;
	struct nouveau_device *device = nouveau_dev(dev);
	struct nouveau_drm *drm = nouveau_drm(dev);
	struct nvd0_disp *disp = nvd0_disp(dev);
	u32 mask = 0, crtc = ~0;
	int i;

	if (drm_debug & (DRM_UT_DRIVER | DRM_UT_KMS)) {
		NV_INFO(drm, "PDISP: modeset req %d\n", disp->modeset);
		NV_INFO(drm, " STAT: 0x%08x 0x%08x 0x%08x\n",
			 nv_rd32(device, 0x6101d0),
			 nv_rd32(device, 0x6101d4), nv_rd32(device, 0x6109d4));
		for (i = 0; i < 8; i++) {
			NV_INFO(drm, " %s%d: 0x%08x 0x%08x\n",
				i < 4 ? "DAC" : "SOR", i,
				nv_rd32(device, 0x640180 + (i * 0x20)),
				nv_rd32(device, 0x660180 + (i * 0x20)));
		}
	}

	while (!mask && ++crtc < dev->mode_config.num_crtc)
		mask = nv_rd32(device, 0x6101d4 + (crtc * 0x800));

	if (disp->modeset & 0x00000001)
		nvd0_display_unk1_handler(dev, crtc, mask);
	if (disp->modeset & 0x00000002)
		nvd0_display_unk2_handler(dev, crtc, mask);
	if (disp->modeset & 0x00000004)
		nvd0_display_unk4_handler(dev, crtc, mask);
}

void
nvd0_display_intr(struct drm_device *dev)
{
	struct nvd0_disp *disp = nvd0_disp(dev);
	struct nouveau_device *device = nouveau_dev(dev);
	struct nouveau_drm *drm = nouveau_drm(dev);
	u32 intr = nv_rd32(device, 0x610088);

	if (intr & 0x00000001) {
		u32 stat = nv_rd32(device, 0x61008c);
		nv_wr32(device, 0x61008c, stat);
		intr &= ~0x00000001;
	}

	if (intr & 0x00000002) {
		u32 stat = nv_rd32(device, 0x61009c);
		int chid = ffs(stat) - 1;
		if (chid >= 0) {
			u32 mthd = nv_rd32(device, 0x6101f0 + (chid * 12));
			u32 data = nv_rd32(device, 0x6101f4 + (chid * 12));
			u32 unkn = nv_rd32(device, 0x6101f8 + (chid * 12));

			NV_INFO(drm, "EvoCh: chid %d mthd 0x%04x data 0x%08x "
				     "0x%08x 0x%08x\n",
				chid, (mthd & 0x0000ffc), data, mthd, unkn);
			nv_wr32(device, 0x61009c, (1 << chid));
			nv_wr32(device, 0x6101f0 + (chid * 12), 0x90000000);
		}

		intr &= ~0x00000002;
	}

	if (intr & 0x00100000) {
		u32 stat = nv_rd32(device, 0x6100ac);

		if (stat & 0x00000007) {
			disp->modeset = stat;
			tasklet_schedule(&disp->tasklet);

			nv_wr32(device, 0x6100ac, (stat & 0x00000007));
			stat &= ~0x00000007;
		}

		if (stat) {
			NV_INFO(drm, "PDISP: unknown intr24 0x%08x\n", stat);
			nv_wr32(device, 0x6100ac, stat);
		}

		intr &= ~0x00100000;
	}

	intr &= ~0x0f000000; /* vblank, handled in core */
	if (intr)
		NV_INFO(drm, "PDISP: unknown intr 0x%08x\n", intr);
}

/******************************************************************************
/******************************************************************************
 * Init
 * Init
 *****************************************************************************/
 *****************************************************************************/
@@ -2156,9 +1907,6 @@ nvd0_display_create(struct drm_device *dev)
		connector->funcs->destroy(connector);
		connector->funcs->destroy(connector);
	}
	}


	/* setup interrupt handling */
	tasklet_init(&disp->tasklet, nvd0_display_bh, (unsigned long)dev);

out:
out:
	if (ret)
	if (ret)
		nvd0_display_destroy(dev);
		nvd0_display_destroy(dev);