xref: /linux/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c (revision 4de93a086eb0315f0bd8e1d6da40186842670b57)
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 "priv.h"
25 
26 #include <core/client.h>
27 
28 #include <nvif/class.h>
29 #include <nvif/unpack.h>
30 
31 static int
32 nv04_disp_scanoutpos(struct nvkm_object *object, struct nvkm_disp *disp,
33 		     void *data, u32 size, int head)
34 {
35 	const u32 hoff = head * 0x2000;
36 	union {
37 		struct nv04_disp_scanoutpos_v0 v0;
38 	} *args = data;
39 	u32 line;
40 	int ret;
41 
42 	nv_ioctl(object, "disp scanoutpos size %d\n", size);
43 	if (nvif_unpack(args->v0, 0, 0, false)) {
44 		nv_ioctl(object, "disp scanoutpos vers %d\n", args->v0.version);
45 		args->v0.vblanks = nv_rd32(disp, 0x680800 + hoff) & 0xffff;
46 		args->v0.vtotal  = nv_rd32(disp, 0x680804 + hoff) & 0xffff;
47 		args->v0.vblanke = args->v0.vtotal - 1;
48 
49 		args->v0.hblanks = nv_rd32(disp, 0x680820 + hoff) & 0xffff;
50 		args->v0.htotal  = nv_rd32(disp, 0x680824 + hoff) & 0xffff;
51 		args->v0.hblanke = args->v0.htotal - 1;
52 
53 		/*
54 		 * If output is vga instead of digital then vtotal/htotal is
55 		 * invalid so we have to give up and trigger the timestamping
56 		 * fallback in the drm core.
57 		 */
58 		if (!args->v0.vtotal || !args->v0.htotal)
59 			return -ENOTSUPP;
60 
61 		args->v0.time[0] = ktime_to_ns(ktime_get());
62 		line = nv_rd32(disp, 0x600868 + hoff);
63 		args->v0.time[1] = ktime_to_ns(ktime_get());
64 		args->v0.hline = (line & 0xffff0000) >> 16;
65 		args->v0.vline = (line & 0x0000ffff);
66 	} else
67 		return ret;
68 
69 	return 0;
70 }
71 
72 static int
73 nv04_disp_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
74 {
75 	union {
76 		struct nv04_disp_mthd_v0 v0;
77 	} *args = data;
78 	struct nvkm_disp *disp = (void *)object->engine;
79 	int head, ret;
80 
81 	nv_ioctl(object, "disp mthd size %d\n", size);
82 	if (nvif_unpack(args->v0, 0, 0, true)) {
83 		nv_ioctl(object, "disp mthd vers %d mthd %02x head %d\n",
84 			 args->v0.version, args->v0.method, args->v0.head);
85 		mthd = args->v0.method;
86 		head = args->v0.head;
87 	} else
88 		return ret;
89 
90 	if (head < 0 || head >= 2)
91 		return -ENXIO;
92 
93 	switch (mthd) {
94 	case NV04_DISP_SCANOUTPOS:
95 		return nv04_disp_scanoutpos(object, disp, data, size, head);
96 	default:
97 		break;
98 	}
99 
100 	return -EINVAL;
101 }
102 
103 static struct nvkm_ofuncs
104 nv04_disp_ofuncs = {
105 	.ctor = _nvkm_object_ctor,
106 	.dtor = nvkm_object_destroy,
107 	.init = nvkm_object_init,
108 	.fini = nvkm_object_fini,
109 	.mthd = nv04_disp_mthd,
110 	.ntfy = nvkm_disp_ntfy,
111 };
112 
113 static struct nvkm_oclass
114 nv04_disp_sclass[] = {
115 	{ NV04_DISP, &nv04_disp_ofuncs },
116 	{},
117 };
118 
119 /*******************************************************************************
120  * Display engine implementation
121  ******************************************************************************/
122 
123 static void
124 nv04_disp_vblank_init(struct nvkm_event *event, int type, int head)
125 {
126 	struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
127 	nv_wr32(disp, 0x600140 + (head * 0x2000) , 0x00000001);
128 }
129 
130 static void
131 nv04_disp_vblank_fini(struct nvkm_event *event, int type, int head)
132 {
133 	struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
134 	nv_wr32(disp, 0x600140 + (head * 0x2000) , 0x00000000);
135 }
136 
137 static const struct nvkm_event_func
138 nv04_disp_vblank_func = {
139 	.ctor = nvkm_disp_vblank_ctor,
140 	.init = nv04_disp_vblank_init,
141 	.fini = nv04_disp_vblank_fini,
142 };
143 
144 static void
145 nv04_disp_intr(struct nvkm_subdev *subdev)
146 {
147 	struct nvkm_disp *disp = (void *)subdev;
148 	u32 crtc0 = nv_rd32(disp, 0x600100);
149 	u32 crtc1 = nv_rd32(disp, 0x602100);
150 	u32 pvideo;
151 
152 	if (crtc0 & 0x00000001) {
153 		nvkm_disp_vblank(disp, 0);
154 		nv_wr32(disp, 0x600100, 0x00000001);
155 	}
156 
157 	if (crtc1 & 0x00000001) {
158 		nvkm_disp_vblank(disp, 1);
159 		nv_wr32(disp, 0x602100, 0x00000001);
160 	}
161 
162 	if (nv_device(disp)->chipset >= 0x10 &&
163 	    nv_device(disp)->chipset <= 0x40) {
164 		pvideo = nv_rd32(disp, 0x8100);
165 		if (pvideo & ~0x11)
166 			nv_info(disp, "PVIDEO intr: %08x\n", pvideo);
167 		nv_wr32(disp, 0x8100, pvideo);
168 	}
169 }
170 
171 static int
172 nv04_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
173 	       struct nvkm_oclass *oclass, void *data, u32 size,
174 	       struct nvkm_object **pobject)
175 {
176 	struct nvkm_disp *disp;
177 	int ret;
178 
179 	ret = nvkm_disp_create(parent, engine, oclass, 2, "DISPLAY",
180 			       "display", &disp);
181 	*pobject = nv_object(disp);
182 	if (ret)
183 		return ret;
184 
185 	nv_engine(disp)->sclass = nv04_disp_sclass;
186 	nv_subdev(disp)->intr = nv04_disp_intr;
187 	return 0;
188 }
189 
190 struct nvkm_oclass *
191 nv04_disp_oclass = &(struct nvkm_disp_impl) {
192 	.base.handle = NV_ENGINE(DISP, 0x04),
193 	.base.ofuncs = &(struct nvkm_ofuncs) {
194 		.ctor = nv04_disp_ctor,
195 		.dtor = _nvkm_disp_dtor,
196 		.init = _nvkm_disp_init,
197 		.fini = _nvkm_disp_fini,
198 	},
199 	.vblank = &nv04_disp_vblank_func,
200 }.base;
201