• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: Ben Skeggs
23  */
24 #include "rootnv50.h"
25 #include "dmacnv50.h"
26 
27 #include <core/client.h>
28 #include <core/ramht.h>
29 #include <subdev/timer.h>
30 
31 #include <nvif/class.h>
32 #include <nvif/unpack.h>
33 
34 int
nv50_disp_root_scanoutpos(NV50_DISP_MTHD_V0)35 nv50_disp_root_scanoutpos(NV50_DISP_MTHD_V0)
36 {
37 	struct nvkm_device *device = disp->base.engine.subdev.device;
38 	const u32 blanke = nvkm_rd32(device, 0x610aec + (head * 0x540));
39 	const u32 blanks = nvkm_rd32(device, 0x610af4 + (head * 0x540));
40 	const u32 total  = nvkm_rd32(device, 0x610afc + (head * 0x540));
41 	union {
42 		struct nv04_disp_scanoutpos_v0 v0;
43 	} *args = data;
44 	int ret;
45 
46 	nvif_ioctl(object, "disp scanoutpos size %d\n", size);
47 	if (nvif_unpack(args->v0, 0, 0, false)) {
48 		nvif_ioctl(object, "disp scanoutpos vers %d\n",
49 			   args->v0.version);
50 		args->v0.vblanke = (blanke & 0xffff0000) >> 16;
51 		args->v0.hblanke = (blanke & 0x0000ffff);
52 		args->v0.vblanks = (blanks & 0xffff0000) >> 16;
53 		args->v0.hblanks = (blanks & 0x0000ffff);
54 		args->v0.vtotal  = ( total & 0xffff0000) >> 16;
55 		args->v0.htotal  = ( total & 0x0000ffff);
56 		args->v0.time[0] = ktime_to_ns(ktime_get());
57 		args->v0.vline = /* vline read locks hline */
58 			nvkm_rd32(device, 0x616340 + (head * 0x800)) & 0xffff;
59 		args->v0.time[1] = ktime_to_ns(ktime_get());
60 		args->v0.hline =
61 			nvkm_rd32(device, 0x616344 + (head * 0x800)) & 0xffff;
62 	} else
63 		return ret;
64 
65 	return 0;
66 }
67 
68 int
nv50_disp_root_mthd_(struct nvkm_object * object,u32 mthd,void * data,u32 size)69 nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
70 {
71 	union {
72 		struct nv50_disp_mthd_v0 v0;
73 		struct nv50_disp_mthd_v1 v1;
74 	} *args = data;
75 	struct nv50_disp_root *root = nv50_disp_root(object);
76 	struct nv50_disp *disp = root->disp;
77 	const struct nv50_disp_func *func = disp->func;
78 	struct nvkm_output *outp = NULL;
79 	struct nvkm_output *temp;
80 	u16 type, mask = 0;
81 	int head, ret;
82 
83 	if (mthd != NV50_DISP_MTHD)
84 		return -EINVAL;
85 
86 	nvif_ioctl(object, "disp mthd size %d\n", size);
87 	if (nvif_unpack(args->v0, 0, 0, true)) {
88 		nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n",
89 			   args->v0.version, args->v0.method, args->v0.head);
90 		mthd = args->v0.method;
91 		head = args->v0.head;
92 	} else
93 	if (nvif_unpack(args->v1, 1, 1, true)) {
94 		nvif_ioctl(object, "disp mthd vers %d mthd %02x "
95 				   "type %04x mask %04x\n",
96 			   args->v1.version, args->v1.method,
97 			   args->v1.hasht, args->v1.hashm);
98 		mthd = args->v1.method;
99 		type = args->v1.hasht;
100 		mask = args->v1.hashm;
101 		head = ffs((mask >> 8) & 0x0f) - 1;
102 	} else
103 		return ret;
104 
105 	if (head < 0 || head >= disp->base.head.nr)
106 		return -ENXIO;
107 
108 	if (mask) {
109 		list_for_each_entry(temp, &disp->base.outp, head) {
110 			if ((temp->info.hasht         == type) &&
111 			    (temp->info.hashm & mask) == mask) {
112 				outp = temp;
113 				break;
114 			}
115 		}
116 		if (outp == NULL)
117 			return -ENXIO;
118 	}
119 
120 	switch (mthd) {
121 	case NV50_DISP_SCANOUTPOS:
122 		return func->head.scanoutpos(object, disp, data, size, head);
123 	default:
124 		break;
125 	}
126 
127 	switch (mthd * !!outp) {
128 	case NV50_DISP_MTHD_V1_DAC_PWR:
129 		return func->dac.power(object, disp, data, size, head, outp);
130 	case NV50_DISP_MTHD_V1_DAC_LOAD:
131 		return func->dac.sense(object, disp, data, size, head, outp);
132 	case NV50_DISP_MTHD_V1_SOR_PWR:
133 		return func->sor.power(object, disp, data, size, head, outp);
134 	case NV50_DISP_MTHD_V1_SOR_HDA_ELD:
135 		if (!func->sor.hda_eld)
136 			return -ENODEV;
137 		return func->sor.hda_eld(object, disp, data, size, head, outp);
138 	case NV50_DISP_MTHD_V1_SOR_HDMI_PWR:
139 		if (!func->sor.hdmi)
140 			return -ENODEV;
141 		return func->sor.hdmi(object, disp, data, size, head, outp);
142 	case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: {
143 		union {
144 			struct nv50_disp_sor_lvds_script_v0 v0;
145 		} *args = data;
146 		nvif_ioctl(object, "disp sor lvds script size %d\n", size);
147 		if (nvif_unpack(args->v0, 0, 0, false)) {
148 			nvif_ioctl(object, "disp sor lvds script "
149 					   "vers %d name %04x\n",
150 				   args->v0.version, args->v0.script);
151 			disp->sor.lvdsconf = args->v0.script;
152 			return 0;
153 		} else
154 			return ret;
155 	}
156 		break;
157 	case NV50_DISP_MTHD_V1_SOR_DP_PWR: {
158 		struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
159 		union {
160 			struct nv50_disp_sor_dp_pwr_v0 v0;
161 		} *args = data;
162 		nvif_ioctl(object, "disp sor dp pwr size %d\n", size);
163 		if (nvif_unpack(args->v0, 0, 0, false)) {
164 			nvif_ioctl(object, "disp sor dp pwr vers %d state %d\n",
165 				   args->v0.version, args->v0.state);
166 			if (args->v0.state == 0) {
167 				nvkm_notify_put(&outpdp->irq);
168 				outpdp->func->lnk_pwr(outpdp, 0);
169 				atomic_set(&outpdp->lt.done, 0);
170 				return 0;
171 			} else
172 			if (args->v0.state != 0) {
173 				nvkm_output_dp_train(&outpdp->base, 0, true);
174 				return 0;
175 			}
176 		} else
177 			return ret;
178 	}
179 		break;
180 	case NV50_DISP_MTHD_V1_PIOR_PWR:
181 		if (!func->pior.power)
182 			return -ENODEV;
183 		return func->pior.power(object, disp, data, size, head, outp);
184 	default:
185 		break;
186 	}
187 
188 	return -EINVAL;
189 }
190 
191 static int
nv50_disp_root_dmac_new_(const struct nvkm_oclass * oclass,void * data,u32 size,struct nvkm_object ** pobject)192 nv50_disp_root_dmac_new_(const struct nvkm_oclass *oclass,
193 			 void *data, u32 size, struct nvkm_object **pobject)
194 {
195 	const struct nv50_disp_dmac_oclass *sclass = oclass->priv;
196 	struct nv50_disp_root *root = nv50_disp_root(oclass->parent);
197 	return sclass->ctor(sclass->func, sclass->mthd, root, sclass->chid,
198 			    oclass, data, size, pobject);
199 }
200 
201 static int
nv50_disp_root_pioc_new_(const struct nvkm_oclass * oclass,void * data,u32 size,struct nvkm_object ** pobject)202 nv50_disp_root_pioc_new_(const struct nvkm_oclass *oclass,
203 			 void *data, u32 size, struct nvkm_object **pobject)
204 {
205 	const struct nv50_disp_pioc_oclass *sclass = oclass->priv;
206 	struct nv50_disp_root *root = nv50_disp_root(oclass->parent);
207 	return sclass->ctor(sclass->func, sclass->mthd, root, sclass->chid,
208 			    oclass, data, size, pobject);
209 }
210 
211 static int
nv50_disp_root_child_get_(struct nvkm_object * object,int index,struct nvkm_oclass * sclass)212 nv50_disp_root_child_get_(struct nvkm_object *object, int index,
213 			  struct nvkm_oclass *sclass)
214 {
215 	struct nv50_disp_root *root = nv50_disp_root(object);
216 
217 	if (index < ARRAY_SIZE(root->func->dmac)) {
218 		sclass->base = root->func->dmac[index]->base;
219 		sclass->priv = root->func->dmac[index];
220 		sclass->ctor = nv50_disp_root_dmac_new_;
221 		return 0;
222 	}
223 
224 	index -= ARRAY_SIZE(root->func->dmac);
225 
226 	if (index < ARRAY_SIZE(root->func->pioc)) {
227 		sclass->base = root->func->pioc[index]->base;
228 		sclass->priv = root->func->pioc[index];
229 		sclass->ctor = nv50_disp_root_pioc_new_;
230 		return 0;
231 	}
232 
233 	return -EINVAL;
234 }
235 
236 static int
nv50_disp_root_fini_(struct nvkm_object * object,bool suspend)237 nv50_disp_root_fini_(struct nvkm_object *object, bool suspend)
238 {
239 	struct nv50_disp_root *root = nv50_disp_root(object);
240 	root->func->fini(root);
241 	return 0;
242 }
243 
244 static int
nv50_disp_root_init_(struct nvkm_object * object)245 nv50_disp_root_init_(struct nvkm_object *object)
246 {
247 	struct nv50_disp_root *root = nv50_disp_root(object);
248 	return root->func->init(root);
249 }
250 
251 static void *
nv50_disp_root_dtor_(struct nvkm_object * object)252 nv50_disp_root_dtor_(struct nvkm_object *object)
253 {
254 	struct nv50_disp_root *root = nv50_disp_root(object);
255 	nvkm_ramht_del(&root->ramht);
256 	nvkm_gpuobj_del(&root->instmem);
257 	return root;
258 }
259 
260 static const struct nvkm_object_func
261 nv50_disp_root_ = {
262 	.dtor = nv50_disp_root_dtor_,
263 	.init = nv50_disp_root_init_,
264 	.fini = nv50_disp_root_fini_,
265 	.mthd = nv50_disp_root_mthd_,
266 	.ntfy = nvkm_disp_ntfy,
267 	.sclass = nv50_disp_root_child_get_,
268 };
269 
270 int
nv50_disp_root_new_(const struct nv50_disp_root_func * func,struct nvkm_disp * base,const struct nvkm_oclass * oclass,void * data,u32 size,struct nvkm_object ** pobject)271 nv50_disp_root_new_(const struct nv50_disp_root_func *func,
272 		    struct nvkm_disp *base, const struct nvkm_oclass *oclass,
273 		    void *data, u32 size, struct nvkm_object **pobject)
274 {
275 	struct nv50_disp *disp = nv50_disp(base);
276 	struct nv50_disp_root *root;
277 	struct nvkm_device *device = disp->base.engine.subdev.device;
278 	int ret;
279 
280 	if (!(root = kzalloc(sizeof(*root), GFP_KERNEL)))
281 		return -ENOMEM;
282 	*pobject = &root->object;
283 
284 	nvkm_object_ctor(&nv50_disp_root_, oclass, &root->object);
285 	root->func = func;
286 	root->disp = disp;
287 
288 	ret = nvkm_gpuobj_new(disp->base.engine.subdev.device, 0x10000, 0x10000,
289 			      false, NULL, &root->instmem);
290 	if (ret)
291 		return ret;
292 
293 	return nvkm_ramht_new(device, 0x1000, 0, root->instmem, &root->ramht);
294 }
295 
296 void
nv50_disp_root_fini(struct nv50_disp_root * root)297 nv50_disp_root_fini(struct nv50_disp_root *root)
298 {
299 	struct nvkm_device *device = root->disp->base.engine.subdev.device;
300 	/* disable all interrupts */
301 	nvkm_wr32(device, 0x610024, 0x00000000);
302 	nvkm_wr32(device, 0x610020, 0x00000000);
303 }
304 
305 int
nv50_disp_root_init(struct nv50_disp_root * root)306 nv50_disp_root_init(struct nv50_disp_root *root)
307 {
308 	struct nv50_disp *disp = root->disp;
309 	struct nvkm_device *device = disp->base.engine.subdev.device;
310 	u32 tmp;
311 	int i;
312 
313 	/* The below segments of code copying values from one register to
314 	 * another appear to inform EVO of the display capabilities or
315 	 * something similar.  NFI what the 0x614004 caps are for..
316 	 */
317 	tmp = nvkm_rd32(device, 0x614004);
318 	nvkm_wr32(device, 0x610184, tmp);
319 
320 	/* ... CRTC caps */
321 	for (i = 0; i < disp->base.head.nr; i++) {
322 		tmp = nvkm_rd32(device, 0x616100 + (i * 0x800));
323 		nvkm_wr32(device, 0x610190 + (i * 0x10), tmp);
324 		tmp = nvkm_rd32(device, 0x616104 + (i * 0x800));
325 		nvkm_wr32(device, 0x610194 + (i * 0x10), tmp);
326 		tmp = nvkm_rd32(device, 0x616108 + (i * 0x800));
327 		nvkm_wr32(device, 0x610198 + (i * 0x10), tmp);
328 		tmp = nvkm_rd32(device, 0x61610c + (i * 0x800));
329 		nvkm_wr32(device, 0x61019c + (i * 0x10), tmp);
330 	}
331 
332 	/* ... DAC caps */
333 	for (i = 0; i < disp->func->dac.nr; i++) {
334 		tmp = nvkm_rd32(device, 0x61a000 + (i * 0x800));
335 		nvkm_wr32(device, 0x6101d0 + (i * 0x04), tmp);
336 	}
337 
338 	/* ... SOR caps */
339 	for (i = 0; i < disp->func->sor.nr; i++) {
340 		tmp = nvkm_rd32(device, 0x61c000 + (i * 0x800));
341 		nvkm_wr32(device, 0x6101e0 + (i * 0x04), tmp);
342 	}
343 
344 	/* ... PIOR caps */
345 	for (i = 0; i < disp->func->pior.nr; i++) {
346 		tmp = nvkm_rd32(device, 0x61e000 + (i * 0x800));
347 		nvkm_wr32(device, 0x6101f0 + (i * 0x04), tmp);
348 	}
349 
350 	/* steal display away from vbios, or something like that */
351 	if (nvkm_rd32(device, 0x610024) & 0x00000100) {
352 		nvkm_wr32(device, 0x610024, 0x00000100);
353 		nvkm_mask(device, 0x6194e8, 0x00000001, 0x00000000);
354 		if (nvkm_msec(device, 2000,
355 			if (!(nvkm_rd32(device, 0x6194e8) & 0x00000002))
356 				break;
357 		) < 0)
358 			return -EBUSY;
359 	}
360 
361 	/* point at display engine memory area (hash table, objects) */
362 	nvkm_wr32(device, 0x610010, (root->instmem->addr >> 8) | 9);
363 
364 	/* enable supervisor interrupts, disable everything else */
365 	nvkm_wr32(device, 0x61002c, 0x00000370);
366 	nvkm_wr32(device, 0x610028, 0x00000000);
367 	return 0;
368 }
369 
370 static const struct nv50_disp_root_func
371 nv50_disp_root = {
372 	.init = nv50_disp_root_init,
373 	.fini = nv50_disp_root_fini,
374 	.dmac = {
375 		&nv50_disp_core_oclass,
376 		&nv50_disp_base_oclass,
377 		&nv50_disp_ovly_oclass,
378 	},
379 	.pioc = {
380 		&nv50_disp_oimm_oclass,
381 		&nv50_disp_curs_oclass,
382 	},
383 };
384 
385 static int
nv50_disp_root_new(struct nvkm_disp * disp,const struct nvkm_oclass * oclass,void * data,u32 size,struct nvkm_object ** pobject)386 nv50_disp_root_new(struct nvkm_disp *disp, const struct nvkm_oclass *oclass,
387 		   void *data, u32 size, struct nvkm_object **pobject)
388 {
389 	return nv50_disp_root_new_(&nv50_disp_root, disp, oclass,
390 				   data, size, pobject);
391 }
392 
393 const struct nvkm_disp_oclass
394 nv50_disp_root_oclass = {
395 	.base.oclass = NV50_DISP,
396 	.base.minver = -1,
397 	.base.maxver = -1,
398 	.ctor = nv50_disp_root_new,
399 };
400