sorg94.c 8.7 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 "ior.h"
25 26
#include "nv50.h"

27 28
#include <subdev/timer.h>

29
static inline u32
30
g94_sor_soff(struct nvkm_output_dp *outp)
31
{
32
	return (ffs(outp->base.info.or) - 1) * 0x800;
33 34
}

35
static inline u32
36
g94_sor_loff(struct nvkm_output_dp *outp)
37
{
38
	return g94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
39 40
}

41 42 43
/*******************************************************************************
 * DisplayPort
 ******************************************************************************/
44
u32
45
g94_sor_dp_lane_map(struct nvkm_device *device, u8 lane)
46
{
47
	static const u8 gm100[] = { 0, 8, 16, 24 };
48
	static const u8 mcp89[] = { 24, 16, 8, 0 }; /* thanks, apple.. */
49
	static const u8   g94[] = { 16, 8, 0, 24 };
50
	if (device->chipset >= 0x110)
51
		return gm100[lane];
52
	if (device->chipset == 0xaf)
53 54
		return mcp89[lane];
	return g94[lane];
55 56
}

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
static int
g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
{
	struct nvkm_device *device = outp->base.disp->engine.subdev.device;
	struct nvkm_bios *bios = device->bios;
	const u32 shift = g94_sor_dp_lane_map(device, ln);
	const u32 loff = g94_sor_loff(outp);
	u32 addr, data[3];
	u8  ver, hdr, cnt, len;
	struct nvbios_dpout info;
	struct nvbios_dpcfg ocfg;

	addr = nvbios_dpout_match(bios, outp->base.info.hasht,
					outp->base.info.hashm,
				  &ver, &hdr, &cnt, &len, &info);
	if (!addr)
		return -ENODEV;

	addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
				  &ver, &hdr, &cnt, &len, &ocfg);
	if (!addr)
		return -EINVAL;

	data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
	data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
	data[2] = nvkm_rd32(device, 0x61c130 + loff);
	if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
		data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
	nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
	nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
	nvkm_wr32(device, 0x61c130 + loff, data[2]);
	return 0;
}

91
static int
92
g94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
93
{
94
	struct nvkm_device *device = outp->base.disp->engine.subdev.device;
95
	const u32 loff = g94_sor_loff(outp);
96
	nvkm_mask(device, 0x61c10c + loff, 0x0f000000, pattern << 24);
97 98 99
	return 0;
}

100
int
101
g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
102
{
103
	struct nvkm_device *device = outp->base.disp->engine.subdev.device;
104 105
	const u32 soff = g94_sor_soff(outp);
	const u32 loff = g94_sor_loff(outp);
106 107 108
	u32 mask = 0, i;

	for (i = 0; i < nr; i++)
109
		mask |= 1 << (g94_sor_dp_lane_map(device, i) >> 3);
110

111 112
	nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask);
	nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000);
113 114 115 116
	nvkm_msec(device, 2000,
		if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000))
			break;
	);
117 118 119
	return 0;
}

120
static int
121
g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
122
{
123
	struct nvkm_device *device = outp->base.disp->engine.subdev.device;
124 125
	const u32 soff = g94_sor_soff(outp);
	const u32 loff = g94_sor_loff(outp);
126 127 128
	u32 dpctrl = 0x00000000;
	u32 clksor = 0x00000000;

129 130
	dpctrl |= ((1 << nr) - 1) << 16;
	if (ef)
131
		dpctrl |= 0x00004000;
132
	if (bw > 0x06)
133 134
		clksor |= 0x00040000;

135 136
	nvkm_mask(device, 0x614300 + soff, 0x000c0000, clksor);
	nvkm_mask(device, 0x61c10c + loff, 0x001f4000, dpctrl);
137 138 139
	return 0;
}

140 141
static const struct nvkm_output_dp_func
g94_sor_dp_func = {
142 143 144 145
	.pattern = g94_sor_dp_pattern,
	.lnk_pwr = g94_sor_dp_lnk_pwr,
	.lnk_ctl = g94_sor_dp_lnk_ctl,
	.drv_ctl = g94_sor_dp_drv_ctl,
146
};
147 148 149 150 151 152 153

int
g94_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
	       struct nvkm_output **poutp)
{
	return nvkm_output_dp_new_(&g94_sor_dp_func, disp, index, dcbE, poutp);
}
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279

static bool
nv50_disp_dptmds_war(struct nvkm_device *device)
{
	switch (device->chipset) {
	case 0x94:
	case 0x96:
	case 0x98:
		return true;
	default:
		break;
	}
	return false;
}

static bool
nv50_disp_dptmds_war_needed(struct nv50_disp *disp, struct dcb_output *outp)
{
	struct nvkm_device *device = disp->base.engine.subdev.device;
	const u32 soff = __ffs(outp->or) * 0x800;
	if (nv50_disp_dptmds_war(device) && outp->type == DCB_OUTPUT_TMDS) {
		switch (nvkm_rd32(device, 0x614300 + soff) & 0x00030000) {
		case 0x00000000:
		case 0x00030000:
			return true;
		default:
			break;
		}
	}
	return false;

}

void
nv50_disp_update_sppll1(struct nv50_disp *disp)
{
	struct nvkm_device *device = disp->base.engine.subdev.device;
	bool used = false;
	int sor;

	if (!nv50_disp_dptmds_war(device))
		return;

	for (sor = 0; sor < disp->func->sor.nr; sor++) {
		u32 clksor = nvkm_rd32(device, 0x614300 + (sor * 0x800));
		switch (clksor & 0x03000000) {
		case 0x02000000:
		case 0x03000000:
			used = true;
			break;
		default:
			break;
		}
	}

	if (used)
		return;

	nvkm_mask(device, 0x00e840, 0x80000000, 0x00000000);
}

void
nv50_disp_dptmds_war_3(struct nv50_disp *disp, struct dcb_output *outp)
{
	struct nvkm_device *device = disp->base.engine.subdev.device;
	const u32 soff = __ffs(outp->or) * 0x800;
	u32 sorpwr;

	if (!nv50_disp_dptmds_war_needed(disp, outp))
		return;

	sorpwr = nvkm_rd32(device, 0x61c004 + soff);
	if (sorpwr & 0x00000001) {
		u32 seqctl = nvkm_rd32(device, 0x61c030 + soff);
		u32  pd_pc = (seqctl & 0x00000f00) >> 8;
		u32  pu_pc =  seqctl & 0x0000000f;

		nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x1f008000);

		nvkm_msec(device, 2000,
			if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
				break;
		);
		nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000);
		nvkm_msec(device, 2000,
			if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
				break;
		);

		nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x00002000);
		nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f000000);
	}

	nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000000);
	nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x00000000);

	if (sorpwr & 0x00000001) {
		nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000001);
	}
}

void
nv50_disp_dptmds_war_2(struct nv50_disp *disp, struct dcb_output *outp)
{
	struct nvkm_device *device = disp->base.engine.subdev.device;
	const u32 soff = __ffs(outp->or) * 0x800;

	if (!nv50_disp_dptmds_war_needed(disp, outp))
		return;

	nvkm_mask(device, 0x00e840, 0x80000000, 0x80000000);
	nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x03000000);
	nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000001);

	nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x00000000);
	nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x14000000);
	nvkm_usec(device, 400, NVKM_DELAY);
	nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x00000000);
	nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x01000000);

	if (nvkm_rd32(device, 0x61c004 + soff) & 0x00000001) {
		u32 seqctl = nvkm_rd32(device, 0x61c030 + soff);
		u32  pu_pc = seqctl & 0x0000000f;
		nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f008000);
	}
}
280

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
void
g94_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state)
{
	struct nvkm_device *device = sor->disp->engine.subdev.device;
	const u32 coff = sor->id * 8 + (state == &sor->arm) * 4;
	u32 ctrl = nvkm_rd32(device, 0x610794 + coff);

	state->proto_evo = (ctrl & 0x00000f00) >> 8;
	switch (state->proto_evo) {
	case 0: state->proto = LVDS; state->link = 1; break;
	case 1: state->proto = TMDS; state->link = 1; break;
	case 2: state->proto = TMDS; state->link = 2; break;
	case 5: state->proto = TMDS; state->link = 3; break;
	case 8: state->proto =   DP; state->link = 1; break;
	case 9: state->proto =   DP; state->link = 2; break;
	default:
		state->proto = UNKNOWN;
		break;
	}

	state->head = ctrl & 0x00000003;
}

304 305
static const struct nvkm_ior_func
g94_sor = {
306
	.state = g94_sor_state,
307 308 309 310 311 312 313
};

int
g94_sor_new(struct nvkm_disp *disp, int id)
{
	return nvkm_ior_new_(&g94_sor, disp, SOR, id);
}