nv50.c 20.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright 2012 Red Hat Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors: Ben Skeggs
 */
24
#include "nv50.h"
25
#include "rootnv50.h"
26

27
#include <core/client.h>
28
#include <core/enum.h>
29
#include <core/gpuobj.h>
30 31 32 33
#include <subdev/bios.h>
#include <subdev/bios/disp.h>
#include <subdev/bios/init.h>
#include <subdev/bios/pll.h>
34
#include <subdev/devinit.h>
35

36 37 38
static void
nv50_disp_vblank_fini(struct nvkm_event *event, int type, int head)
{
39
	struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
40 41
	struct nvkm_device *device = disp->engine.subdev.device;
	nvkm_mask(device, 0x61002c, (4 << head), 0);
42 43 44 45 46
}

static void
nv50_disp_vblank_init(struct nvkm_event *event, int type, int head)
{
47
	struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
48 49
	struct nvkm_device *device = disp->engine.subdev.device;
	nvkm_mask(device, 0x61002c, (4 << head), (4 << head));
50 51 52 53
}

const struct nvkm_event_func
nv50_disp_vblank_func = {
54
	.ctor = nvkm_disp_vblank_ctor,
55 56 57 58
	.init = nv50_disp_vblank_init,
	.fini = nv50_disp_vblank_fini,
};

59
static const struct nvkm_enum
60 61 62 63 64 65 66
nv50_disp_intr_error_type[] = {
	{ 3, "ILLEGAL_MTHD" },
	{ 4, "INVALID_VALUE" },
	{ 5, "INVALID_STATE" },
	{ 7, "INVALID_HANDLE" },
	{}
};
67

68
static const struct nvkm_enum
69 70 71 72
nv50_disp_intr_error_code[] = {
	{ 0x00, "" },
	{}
};
73

74
static void
B
Ben Skeggs 已提交
75
nv50_disp_intr_error(struct nv50_disp *disp, int chid)
76
{
77 78
	struct nvkm_subdev *subdev = &disp->base.engine.subdev;
	struct nvkm_device *device = subdev->device;
79 80
	u32 data = nvkm_rd32(device, 0x610084 + (chid * 0x08));
	u32 addr = nvkm_rd32(device, 0x610080 + (chid * 0x08));
81 82 83
	u32 code = (addr & 0x00ff0000) >> 16;
	u32 type = (addr & 0x00007000) >> 12;
	u32 mthd = (addr & 0x00000ffc);
84
	const struct nvkm_enum *ec, *et;
85

86 87
	et = nvkm_enum_find(nv50_disp_intr_error_type, type);
	ec = nvkm_enum_find(nv50_disp_intr_error_code, code);
88

89 90 91 92
	nvkm_error(subdev,
		   "ERROR %d [%s] %02x [%s] chid %d mthd %04x data %08x\n",
		   type, et ? et->name : "", code, ec ? ec->name : "",
		   chid, mthd, data);
93

94
	if (chid < ARRAY_SIZE(disp->chan)) {
95 96
		switch (mthd) {
		case 0x0080:
97
			nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR);
98 99 100 101 102 103
			break;
		default:
			break;
		}
	}

104 105
	nvkm_wr32(device, 0x610020, 0x00010000 << chid);
	nvkm_wr32(device, 0x610080 + (chid * 0x08), 0x90000000);
106 107
}

108
static struct nvkm_output *
B
Ben Skeggs 已提交
109
exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl,
110
	    u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
111 112
	    struct nvbios_outp *info)
{
113 114
	struct nvkm_subdev *subdev = &disp->base.engine.subdev;
	struct nvkm_bios *bios = subdev->device->bios;
115 116
	struct nvkm_output *outp;
	u16 mask, type;
117

118
	if (or < 4) {
119 120
		type = DCB_OUTPUT_ANALOG;
		mask = 0;
121
	} else
122
	if (or < 8) {
123 124 125 126 127 128 129 130
		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:
131
			nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl);
132
			return NULL;
133
		}
134
		or  -= 4;
135
	} else {
136
		or   = or - 8;
137 138 139
		type = 0x0010;
		mask = 0;
		switch (ctrl & 0x00000f00) {
B
Ben Skeggs 已提交
140
		case 0x00000000: type |= disp->pior.type[or]; break;
141
		default:
142
			nvkm_error(subdev, "unknown PIOR mc %08x\n", ctrl);
143
			return NULL;
144
		}
145 146 147
	}

	mask  = 0x00c0 & (mask << 6);
148
	mask |= 0x0001 << or;
149 150
	mask |= 0x0100 << head;

B
Ben Skeggs 已提交
151
	list_for_each_entry(outp, &disp->base.outp, head) {
152 153 154 155 156 157 158 159 160 161
		if ((outp->info.hasht & 0xff) == type &&
		    (outp->info.hashm & mask) == mask) {
			*data = nvbios_outp_match(bios, outp->info.hasht,
							outp->info.hashm,
						  ver, hdr, cnt, len, info);
			if (!*data)
				return NULL;
			return outp;
		}
	}
162

163
	return NULL;
164 165
}

166
static struct nvkm_output *
B
Ben Skeggs 已提交
167
exec_script(struct nv50_disp *disp, int head, int id)
168
{
169 170
	struct nvkm_device *device = disp->base.engine.subdev.device;
	struct nvkm_bios *bios = device->bios;
171
	struct nvkm_output *outp;
172 173
	struct nvbios_outp info;
	u8  ver, hdr, cnt, len;
174
	u32 data, ctrl = 0;
175
	u32 reg;
176 177
	int i;

178
	/* DAC */
B
Ben Skeggs 已提交
179
	for (i = 0; !(ctrl & (1 << head)) && i < disp->dac.nr; i++)
180
		ctrl = nvkm_rd32(device, 0x610b5c + (i * 8));
181

182
	/* SOR */
183
	if (!(ctrl & (1 << head))) {
B
Ben Skeggs 已提交
184 185 186
		if (nv_device(disp)->chipset  < 0x90 ||
		    nv_device(disp)->chipset == 0x92 ||
		    nv_device(disp)->chipset == 0xa0) {
187
			reg = 0x610b74;
188
		} else {
189
			reg = 0x610798;
190
		}
B
Ben Skeggs 已提交
191
		for (i = 0; !(ctrl & (1 << head)) && i < disp->sor.nr; i++)
192
			ctrl = nvkm_rd32(device, reg + (i * 8));
193
		i += 4;
194 195
	}

196 197
	/* PIOR */
	if (!(ctrl & (1 << head))) {
B
Ben Skeggs 已提交
198
		for (i = 0; !(ctrl & (1 << head)) && i < disp->pior.nr; i++)
199
			ctrl = nvkm_rd32(device, 0x610b84 + (i * 8));
200 201 202
		i += 8;
	}

203
	if (!(ctrl & (1 << head)))
204
		return NULL;
205
	i--;
206

B
Ben Skeggs 已提交
207
	outp = exec_lookup(disp, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info);
208
	if (outp) {
209
		struct nvbios_init init = {
B
Ben Skeggs 已提交
210
			.subdev = nv_subdev(disp),
211 212
			.bios = bios,
			.offset = info.script[id],
213
			.outp = &outp->info,
214 215 216 217
			.crtc = head,
			.execute = 1,
		};

218
		nvbios_exec(&init);
219 220
	}

221
	return outp;
222 223
}

224
static struct nvkm_output *
B
Ben Skeggs 已提交
225
exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf)
226
{
227 228
	struct nvkm_device *device = disp->base.engine.subdev.device;
	struct nvkm_bios *bios = device->bios;
229
	struct nvkm_output *outp;
230 231 232
	struct nvbios_outp info1;
	struct nvbios_ocfg info2;
	u8  ver, hdr, cnt, len;
233
	u32 data, ctrl = 0;
234
	u32 reg;
235 236
	int i;

237
	/* DAC */
B
Ben Skeggs 已提交
238
	for (i = 0; !(ctrl & (1 << head)) && i < disp->dac.nr; i++)
239
		ctrl = nvkm_rd32(device, 0x610b58 + (i * 8));
240

241
	/* SOR */
242
	if (!(ctrl & (1 << head))) {
B
Ben Skeggs 已提交
243 244 245
		if (nv_device(disp)->chipset  < 0x90 ||
		    nv_device(disp)->chipset == 0x92 ||
		    nv_device(disp)->chipset == 0xa0) {
246
			reg = 0x610b70;
247
		} else {
248
			reg = 0x610794;
249
		}
B
Ben Skeggs 已提交
250
		for (i = 0; !(ctrl & (1 << head)) && i < disp->sor.nr; i++)
251
			ctrl = nvkm_rd32(device, reg + (i * 8));
252
		i += 4;
253 254
	}

255 256
	/* PIOR */
	if (!(ctrl & (1 << head))) {
B
Ben Skeggs 已提交
257
		for (i = 0; !(ctrl & (1 << head)) && i < disp->pior.nr; i++)
258
			ctrl = nvkm_rd32(device, 0x610b80 + (i * 8));
259 260 261
		i += 8;
	}

262
	if (!(ctrl & (1 << head)))
263
		return NULL;
264
	i--;
265

B
Ben Skeggs 已提交
266
	outp = exec_lookup(disp, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info1);
267
	if (!outp)
268
		return NULL;
269

270 271
	if (outp->info.location == 0) {
		switch (outp->info.type) {
272
		case DCB_OUTPUT_TMDS:
273
			*conf = (ctrl & 0x00000f00) >> 8;
274
			if (pclk >= 165000)
275
				*conf |= 0x0100;
276 277
			break;
		case DCB_OUTPUT_LVDS:
B
Ben Skeggs 已提交
278
			*conf = disp->sor.lvdsconf;
279 280
			break;
		case DCB_OUTPUT_DP:
281
			*conf = (ctrl & 0x00000f00) >> 8;
282 283 284
			break;
		case DCB_OUTPUT_ANALOG:
		default:
285
			*conf = 0x00ff;
286 287 288
			break;
		}
	} else {
289
		*conf = (ctrl & 0x00000f00) >> 8;
290
		pclk = pclk / 2;
291 292
	}

293
	data = nvbios_ocfg_match(bios, data, *conf, &ver, &hdr, &cnt, &len, &info2);
294
	if (data && id < 0xff) {
295 296 297
		data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
		if (data) {
			struct nvbios_init init = {
B
Ben Skeggs 已提交
298
				.subdev = nv_subdev(disp),
299 300
				.bios = bios,
				.offset = data,
301
				.outp = &outp->info,
302 303 304 305
				.crtc = head,
				.execute = 1,
			};

306
			nvbios_exec(&init);
307 308 309
		}
	}

310
	return outp;
311 312 313
}

static void
B
Ben Skeggs 已提交
314
nv50_disp_intr_unk10_0(struct nv50_disp *disp, int head)
315
{
B
Ben Skeggs 已提交
316
	exec_script(disp, head, 1);
317
}
318

319
static void
B
Ben Skeggs 已提交
320
nv50_disp_intr_unk20_0(struct nv50_disp *disp, int head)
321
{
B
Ben Skeggs 已提交
322
	struct nvkm_output *outp = exec_script(disp, head, 2);
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337

	/* the binary driver does this outside of the supervisor handling
	 * (after the third supervisor from a detach).  we (currently?)
	 * allow both detach/attach to happen in the same set of
	 * supervisor interrupts, so it would make sense to execute this
	 * (full power down?) script after all the detach phases of the
	 * supervisor handling.  like with training if needed from the
	 * second supervisor, nvidia doesn't do this, so who knows if it's
	 * entirely safe, but it does appear to work..
	 *
	 * without this script being run, on some configurations i've
	 * seen, switching from DP to TMDS on a DP connector may result
	 * in a blank screen (SOR_PWR off/on can restore it)
	 */
	if (outp && outp->info.type == DCB_OUTPUT_DP) {
338
		struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
339
		struct nvbios_init init = {
B
Ben Skeggs 已提交
340 341
			.subdev = nv_subdev(disp),
			.bios = nvkm_bios(disp),
342 343 344 345 346 347 348 349 350
			.outp = &outp->info,
			.crtc = head,
			.offset = outpdp->info.script[4],
			.execute = 1,
		};

		nvbios_exec(&init);
		atomic_set(&outpdp->lt.done, 0);
	}
351 352 353
}

static void
B
Ben Skeggs 已提交
354
nv50_disp_intr_unk20_1(struct nv50_disp *disp, int head)
355
{
356 357 358
	struct nvkm_device *device = disp->base.engine.subdev.device;
	struct nvkm_devinit *devinit = device->devinit;
	u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff;
359
	if (pclk)
360
		devinit->pll_set(devinit, PLL_VPLL0 + head, pclk);
361 362 363
}

static void
B
Ben Skeggs 已提交
364
nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head,
365
			  struct dcb_output *outp, u32 pclk)
366
{
367 368
	struct nvkm_subdev *subdev = &disp->base.engine.subdev;
	struct nvkm_device *device = subdev->device;
369 370 371 372
	const int link = !(outp->sorconf.link & 1);
	const int   or = ffs(outp->or) - 1;
	const u32 soff = (  or * 0x800);
	const u32 loff = (link * 0x080) + soff;
373
	const u32 ctrl = nvkm_rd32(device, 0x610794 + (or * 8));
374
	const u32 symbol = 100000;
375 376 377 378 379
	const s32 vactive = nvkm_rd32(device, 0x610af8 + (head * 0x540)) & 0xffff;
	const s32 vblanke = nvkm_rd32(device, 0x610ae8 + (head * 0x540)) & 0xffff;
	const s32 vblanks = nvkm_rd32(device, 0x610af0 + (head * 0x540)) & 0xffff;
	u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff);
	u32 clksor = nvkm_rd32(device, 0x614300 + soff);
380 381 382 383
	int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
	int TU, VTUi, VTUf, VTUa;
	u64 link_data_rate, link_ratio, unk;
	u32 best_diff = 64 * symbol;
B
Ben Skeggs 已提交
384
	u32 link_nr, link_bw, bits;
385 386 387 388 389 390 391 392 393 394
	u64 value;

	link_bw = (clksor & 0x000c0000) ? 270000 : 162000;
	link_nr = hweight32(dpctrl & 0x000f0000);

	/* symbols/hblank - algorithm taken from comments in tegra driver */
	value = vblanke + vactive - vblanks - 7;
	value = value * link_bw;
	do_div(value, pclk);
	value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr);
395
	nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, value);
396 397 398 399 400 401

	/* symbols/vblank - algorithm taken from comments in tegra driver */
	value = vblanks - vblanke - 25;
	value = value * link_bw;
	do_div(value, pclk);
	value = value - ((36 / link_nr) + 3) - 1;
402
	nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, value);
403 404

	/* watermark / activesym */
405 406 407 408
	if      ((ctrl & 0xf0000) == 0x60000) bits = 30;
	else if ((ctrl & 0xf0000) == 0x50000) bits = 24;
	else                                  bits = 18;

409 410 411 412
	link_data_rate = (pclk * bits / 8) / link_nr;

	/* calculate ratio of packed data rate to link symbol rate */
	link_ratio = link_data_rate * symbol;
B
Ben Skeggs 已提交
413
	do_div(link_ratio, link_bw);
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466

	for (TU = 64; TU >= 32; TU--) {
		/* calculate average number of valid symbols in each TU */
		u32 tu_valid = link_ratio * TU;
		u32 calc, diff;

		/* find a hw representation for the fraction.. */
		VTUi = tu_valid / symbol;
		calc = VTUi * symbol;
		diff = tu_valid - calc;
		if (diff) {
			if (diff >= (symbol / 2)) {
				VTUf = symbol / (symbol - diff);
				if (symbol - (VTUf * diff))
					VTUf++;

				if (VTUf <= 15) {
					VTUa  = 1;
					calc += symbol - (symbol / VTUf);
				} else {
					VTUa  = 0;
					VTUf  = 1;
					calc += symbol;
				}
			} else {
				VTUa  = 0;
				VTUf  = min((int)(symbol / diff), 15);
				calc += symbol / VTUf;
			}

			diff = calc - tu_valid;
		} else {
			/* no remainder, but the hw doesn't like the fractional
			 * part to be zero.  decrement the integer part and
			 * have the fraction add a whole symbol back
			 */
			VTUa = 0;
			VTUf = 1;
			VTUi--;
		}

		if (diff < best_diff) {
			best_diff = diff;
			bestTU = TU;
			bestVTUa = VTUa;
			bestVTUf = VTUf;
			bestVTUi = VTUi;
			if (diff == 0)
				break;
		}
	}

	if (!bestTU) {
467
		nvkm_error(subdev, "unable to find suitable dp config\n");
468 469 470 471 472 473
		return;
	}

	/* XXX close to vbios numbers, but not right */
	unk  = (symbol - link_ratio) * bestTU;
	unk *= link_ratio;
B
Ben Skeggs 已提交
474 475
	do_div(unk, symbol);
	do_div(unk, symbol);
476 477
	unk += 6;

478 479
	nvkm_mask(device, 0x61c10c + loff, 0x000001fc, bestTU << 2);
	nvkm_mask(device, 0x61c128 + loff, 0x010f7f3f, bestVTUa << 24 |
480 481 482 483 484
						   bestVTUf << 16 |
						   bestVTUi << 8 | unk);
}

static void
B
Ben Skeggs 已提交
485
nv50_disp_intr_unk20_2(struct nv50_disp *disp, int head)
486
{
487
	struct nvkm_device *device = disp->base.engine.subdev.device;
488
	struct nvkm_output *outp;
489
	u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff;
490 491
	u32 hval, hreg = 0x614200 + (head * 0x800);
	u32 oval, oreg;
492
	u32 mask, conf;
493

B
Ben Skeggs 已提交
494
	outp = exec_clkcmp(disp, head, 0xff, pclk, &conf);
495 496
	if (!outp)
		return;
497

498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
	/* we allow both encoder attach and detach operations to occur
	 * within a single supervisor (ie. modeset) sequence.  the
	 * encoder detach scripts quite often switch off power to the
	 * lanes, which requires the link to be re-trained.
	 *
	 * this is not generally an issue as the sink "must" (heh)
	 * signal an irq when it's lost sync so the driver can
	 * re-train.
	 *
	 * however, on some boards, if one does not configure at least
	 * the gpu side of the link *before* attaching, then various
	 * things can go horribly wrong (PDISP disappearing from mmio,
	 * third supervisor never happens, etc).
	 *
	 * the solution is simply to retrain here, if necessary.  last
	 * i checked, the binary driver userspace does not appear to
	 * trigger this situation (it forces an UPDATE between steps).
	 */
516
	if (outp->info.type == DCB_OUTPUT_DP) {
517
		u32 soff = (ffs(outp->info.or) - 1) * 0x08;
518 519 520
		u32 ctrl, datarate;

		if (outp->info.location == 0) {
521
			ctrl = nvkm_rd32(device, 0x610794 + soff);
522 523
			soff = 1;
		} else {
524
			ctrl = nvkm_rd32(device, 0x610b80 + soff);
525 526
			soff = 2;
		}
527 528

		switch ((ctrl & 0x000f0000) >> 16) {
529 530
		case 6: datarate = pclk * 30; break;
		case 5: datarate = pclk * 24; break;
531 532
		case 2:
		default:
533
			datarate = pclk * 18;
534
			break;
535 536
		}

537
		if (nvkm_output_dp_train(outp, datarate / soff, true))
538
			OUTP_ERR(outp, "link not trained before attach");
539 540
	}

B
Ben Skeggs 已提交
541
	exec_clkcmp(disp, head, 0, pclk, &conf);
542 543 544 545 546 547 548 549 550

	if (!outp->info.location && outp->info.type == DCB_OUTPUT_ANALOG) {
		oreg = 0x614280 + (ffs(outp->info.or) - 1) * 0x800;
		oval = 0x00000000;
		hval = 0x00000000;
		mask = 0xffffffff;
	} else
	if (!outp->info.location) {
		if (outp->info.type == DCB_OUTPUT_DP)
B
Ben Skeggs 已提交
551
			nv50_disp_intr_unk20_2_dp(disp, head, &outp->info, pclk);
552 553 554 555 556 557 558 559 560
		oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800;
		oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
		hval = 0x00000000;
		mask = 0x00000707;
	} else {
		oreg = 0x614380 + (ffs(outp->info.or) - 1) * 0x800;
		oval = 0x00000001;
		hval = 0x00000001;
		mask = 0x00000707;
561
	}
562

563 564
	nvkm_mask(device, hreg, 0x0000000f, hval);
	nvkm_mask(device, oreg, mask, oval);
565 566 567 568 569 570 571 572 573 574 575
}

/* If programming a TMDS output on a SOR that can also be configured for
 * DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off.
 *
 * It looks like the VBIOS TMDS scripts make an attempt at this, however,
 * the VBIOS scripts on at least one board I have only switch it off on
 * link 0, causing a blank display if the output has previously been
 * programmed for DisplayPort.
 */
static void
B
Ben Skeggs 已提交
576
nv50_disp_intr_unk40_0_tmds(struct nv50_disp *disp,
577
			    struct dcb_output *outp)
578
{
579 580
	struct nvkm_device *device = disp->base.engine.subdev.device;
	struct nvkm_bios *bios = device->bios;
581 582 583 584
	const int link = !(outp->sorconf.link & 1);
	const int   or = ffs(outp->or) - 1;
	const u32 loff = (or * 0x800) + (link * 0x80);
	const u16 mask = (outp->sorconf.link << 6) | outp->or;
585
	struct dcb_output match;
586 587
	u8  ver, hdr;

588
	if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, &match))
589
		nvkm_mask(device, 0x61c10c + loff, 0x00000001, 0x00000000);
590 591 592
}

static void
B
Ben Skeggs 已提交
593
nv50_disp_intr_unk40_0(struct nv50_disp *disp, int head)
594
{
595
	struct nvkm_device *device = disp->base.engine.subdev.device;
596
	struct nvkm_output *outp;
597
	u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff;
598
	u32 conf;
599

B
Ben Skeggs 已提交
600
	outp = exec_clkcmp(disp, head, 1, pclk, &conf);
601 602 603 604
	if (!outp)
		return;

	if (outp->info.location == 0 && outp->info.type == DCB_OUTPUT_TMDS)
B
Ben Skeggs 已提交
605
		nv50_disp_intr_unk40_0_tmds(disp, &outp->info);
606 607
}

608 609
void
nv50_disp_intr_supervisor(struct work_struct *work)
610
{
B
Ben Skeggs 已提交
611 612
	struct nv50_disp *disp =
		container_of(work, struct nv50_disp, supervisor);
613 614
	struct nvkm_subdev *subdev = &disp->base.engine.subdev;
	struct nvkm_device *device = subdev->device;
615
	u32 super = nvkm_rd32(device, 0x610030);
616
	int head;
617

618
	nvkm_debug(subdev, "supervisor %08x %08x\n", disp->super, super);
619

B
Ben Skeggs 已提交
620
	if (disp->super & 0x00000010) {
621
		nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG);
B
Ben Skeggs 已提交
622
		for (head = 0; head < disp->head.nr; head++) {
623 624 625 626
			if (!(super & (0x00000020 << head)))
				continue;
			if (!(super & (0x00000080 << head)))
				continue;
B
Ben Skeggs 已提交
627
			nv50_disp_intr_unk10_0(disp, head);
628 629
		}
	} else
B
Ben Skeggs 已提交
630 631
	if (disp->super & 0x00000020) {
		for (head = 0; head < disp->head.nr; head++) {
632 633
			if (!(super & (0x00000080 << head)))
				continue;
B
Ben Skeggs 已提交
634
			nv50_disp_intr_unk20_0(disp, head);
635
		}
B
Ben Skeggs 已提交
636
		for (head = 0; head < disp->head.nr; head++) {
637 638
			if (!(super & (0x00000200 << head)))
				continue;
B
Ben Skeggs 已提交
639
			nv50_disp_intr_unk20_1(disp, head);
640
		}
B
Ben Skeggs 已提交
641
		for (head = 0; head < disp->head.nr; head++) {
642 643
			if (!(super & (0x00000080 << head)))
				continue;
B
Ben Skeggs 已提交
644
			nv50_disp_intr_unk20_2(disp, head);
645 646
		}
	} else
B
Ben Skeggs 已提交
647 648
	if (disp->super & 0x00000040) {
		for (head = 0; head < disp->head.nr; head++) {
649 650
			if (!(super & (0x00000080 << head)))
				continue;
B
Ben Skeggs 已提交
651
			nv50_disp_intr_unk40_0(disp, head);
652 653 654
		}
	}

655
	nvkm_wr32(device, 0x610030, 0x80000000);
656 657
}

658
void
659
nv50_disp_intr(struct nvkm_subdev *subdev)
660
{
B
Ben Skeggs 已提交
661
	struct nv50_disp *disp = (void *)subdev;
662 663 664
	struct nvkm_device *device = disp->base.engine.subdev.device;
	u32 intr0 = nvkm_rd32(device, 0x610020);
	u32 intr1 = nvkm_rd32(device, 0x610024);
665

666 667
	while (intr0 & 0x001f0000) {
		u32 chid = __ffs(intr0 & 0x001f0000) - 16;
B
Ben Skeggs 已提交
668
		nv50_disp_intr_error(disp, chid);
669
		intr0 &= ~(0x00010000 << chid);
670 671
	}

672 673
	while (intr0 & 0x0000001f) {
		u32 chid = __ffs(intr0 & 0x0000001f);
B
Ben Skeggs 已提交
674
		nv50_disp_chan_uevent_send(disp, chid);
675 676 677
		intr0 &= ~(0x00000001 << chid);
	}

678
	if (intr1 & 0x00000004) {
B
Ben Skeggs 已提交
679
		nvkm_disp_vblank(&disp->base, 0);
680
		nvkm_wr32(device, 0x610024, 0x00000004);
681 682
	}

683
	if (intr1 & 0x00000008) {
B
Ben Skeggs 已提交
684
		nvkm_disp_vblank(&disp->base, 1);
685
		nvkm_wr32(device, 0x610024, 0x00000008);
686 687
	}

688
	if (intr1 & 0x00000070) {
B
Ben Skeggs 已提交
689 690
		disp->super = (intr1 & 0x00000070);
		schedule_work(&disp->supervisor);
691
		nvkm_wr32(device, 0x610024, disp->super);
692
	}
693 694
}

695 696 697 698 699
static const struct nvkm_disp_func
nv50_disp = {
	.root = &nv50_disp_root_oclass,
};

700
static int
701 702 703
nv50_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
	       struct nvkm_oclass *oclass, void *data, u32 size,
	       struct nvkm_object **pobject)
704
{
B
Ben Skeggs 已提交
705
	struct nv50_disp *disp;
706 707
	int ret;

708
	ret = nvkm_disp_create(parent, engine, oclass, 2, "PDISP",
B
Ben Skeggs 已提交
709 710
			       "display", &disp);
	*pobject = nv_object(disp);
711 712 713
	if (ret)
		return ret;

714 715
	disp->base.func = &nv50_disp;

B
Ben Skeggs 已提交
716
	ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &disp->uevent);
717 718 719
	if (ret)
		return ret;

B
Ben Skeggs 已提交
720 721 722 723 724 725 726 727 728 729
	nv_subdev(disp)->intr = nv50_disp_intr;
	INIT_WORK(&disp->supervisor, nv50_disp_intr_supervisor);
	disp->head.nr = 2;
	disp->dac.nr = 3;
	disp->sor.nr = 2;
	disp->pior.nr = 3;
	disp->dac.power = nv50_dac_power;
	disp->dac.sense = nv50_dac_sense;
	disp->sor.power = nv50_sor_power;
	disp->pior.power = nv50_pior_power;
730 731 732
	return 0;
}

733
struct nvkm_oclass *
734 735
nv50_disp_oclass = &(struct nv50_disp_impl) {
	.base.base.handle = NV_ENGINE(DISP, 0x50),
736
	.base.base.ofuncs = &(struct nvkm_ofuncs) {
737
		.ctor = nv50_disp_ctor,
738 739 740
		.dtor = _nvkm_disp_dtor,
		.init = _nvkm_disp_init,
		.fini = _nvkm_disp_fini,
741
	},
742 743 744 745 746
	.base.outp.internal.crt = nv50_dac_output_new,
	.base.outp.internal.tmds = nv50_sor_output_new,
	.base.outp.internal.lvds = nv50_sor_output_new,
	.base.outp.external.tmds = nv50_pior_output_new,
	.base.outp.external.dp = nv50_pior_dp_new,
747
	.base.vblank = &nv50_disp_vblank_func,
748
	.head.scanoutpos = nv50_disp_root_scanoutpos,
749
}.base.base;