xref: /openbmc/linux/drivers/gpu/drm/nouveau/dispnv50/head507d.c (revision 2aa934ca04bc93e7cf5133cb44a751be13b9df9e)
1 /*
2  * Copyright 2018 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 #include "head.h"
23 #include "core.h"
24 
25 #include <nvif/push507c.h>
26 
27 #include <nvhw/class/cl507d.h>
28 
29 int
30 head507d_procamp(struct nv50_head *head, struct nv50_head_atom *asyh)
31 {
32 	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
33 	const int i = head->base.index;
34 	int ret;
35 
36 	if ((ret = PUSH_WAIT(push, 2)))
37 		return ret;
38 
39 	PUSH_NVSQ(push, NV507D, 0x08a8 + (i * 0x400), asyh->procamp.sat.sin << 20 |
40 						      asyh->procamp.sat.cos << 8);
41 	return 0;
42 }
43 
44 int
45 head507d_dither(struct nv50_head *head, struct nv50_head_atom *asyh)
46 {
47 	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
48 	const int i = head->base.index;
49 	int ret;
50 
51 	if ((ret = PUSH_WAIT(push, 2)))
52 		return ret;
53 
54 	PUSH_NVSQ(push, NV507D, 0x08a0 + (i * 0x400), asyh->dither.mode << 3 |
55 						      asyh->dither.bits << 1 |
56 						      asyh->dither.enable);
57 	return 0;
58 }
59 
60 int
61 head507d_ovly(struct nv50_head *head, struct nv50_head_atom *asyh)
62 {
63 	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
64 	const int i = head->base.index;
65 	u32 bounds = 0;
66 	int ret;
67 
68 	if (asyh->ovly.cpp) {
69 		switch (asyh->ovly.cpp) {
70 		case 4: bounds |= 0x00000300; break;
71 		case 2: bounds |= 0x00000100; break;
72 		default:
73 			WARN_ON(1);
74 			break;
75 		}
76 		bounds |= 0x00000001;
77 	} else {
78 		bounds |= 0x00000100;
79 	}
80 
81 	if ((ret = PUSH_WAIT(push, 2)))
82 		return ret;
83 
84 	PUSH_NVSQ(push, NV507D, 0x0904 + (i * 0x400), bounds);
85 	return 0;
86 }
87 
88 int
89 head507d_base(struct nv50_head *head, struct nv50_head_atom *asyh)
90 {
91 	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
92 	const int i = head->base.index;
93 	u32 bounds = 0;
94 	int ret;
95 
96 	if (asyh->base.cpp) {
97 		switch (asyh->base.cpp) {
98 		case 8: bounds |= NVDEF(NV507D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_64); break;
99 		case 4: bounds |= NVDEF(NV507D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_32); break;
100 		case 2: bounds |= NVDEF(NV507D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_16); break;
101 		case 1: bounds |= NVDEF(NV507D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_8); break;
102 		default:
103 			WARN_ON(1);
104 			break;
105 		}
106 		bounds |= NVDEF(NV507D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, USABLE, TRUE);
107 	}
108 
109 	if ((ret = PUSH_WAIT(push, 2)))
110 		return ret;
111 
112 	PUSH_MTHD(push, NV507D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS(i), bounds);
113 	return 0;
114 }
115 
116 static int
117 head507d_curs_clr(struct nv50_head *head)
118 {
119 	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
120 	const int i = head->base.index;
121 	int ret;
122 
123 	if ((ret = PUSH_WAIT(push, 2)))
124 		return ret;
125 
126 	PUSH_MTHD(push, NV507D, HEAD_SET_CONTROL_CURSOR(i),
127 		  NVDEF(NV507D, HEAD_SET_CONTROL_CURSOR, ENABLE, DISABLE) |
128 		  NVDEF(NV507D, HEAD_SET_CONTROL_CURSOR, FORMAT, A8R8G8B8) |
129 		  NVDEF(NV507D, HEAD_SET_CONTROL_CURSOR, SIZE, W64_H64));
130 	return 0;
131 }
132 
133 static int
134 head507d_curs_set(struct nv50_head *head, struct nv50_head_atom *asyh)
135 {
136 	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
137 	const int i = head->base.index;
138 	int ret;
139 
140 	if ((ret = PUSH_WAIT(push, 3)))
141 		return ret;
142 
143 	PUSH_MTHD(push, NV507D, HEAD_SET_CONTROL_CURSOR(i),
144 		  NVDEF(NV507D, HEAD_SET_CONTROL_CURSOR, ENABLE, ENABLE) |
145 		  NVVAL(NV507D, HEAD_SET_CONTROL_CURSOR, FORMAT, asyh->curs.format) |
146 		  NVVAL(NV507D, HEAD_SET_CONTROL_CURSOR, SIZE, asyh->curs.layout) |
147 		  NVVAL(NV507D, HEAD_SET_CONTROL_CURSOR, HOT_SPOT_X, 0) |
148 		  NVVAL(NV507D, HEAD_SET_CONTROL_CURSOR, HOT_SPOT_Y, 0) |
149 		  NVDEF(NV507D, HEAD_SET_CONTROL_CURSOR, COMPOSITION, ALPHA_BLEND) |
150 		  NVDEF(NV507D, HEAD_SET_CONTROL_CURSOR, SUB_OWNER, NONE),
151 
152 				HEAD_SET_OFFSET_CURSOR(i), asyh->curs.offset >> 8);
153 	return 0;
154 }
155 
156 int
157 head507d_curs_format(struct nv50_head *head, struct nv50_wndw_atom *asyw,
158 		     struct nv50_head_atom *asyh)
159 {
160 	switch (asyw->image.format) {
161 	case 0xcf: asyh->curs.format = NV507D_HEAD_SET_CONTROL_CURSOR_FORMAT_A8R8G8B8; break;
162 	default:
163 		WARN_ON(1);
164 		return -EINVAL;
165 	}
166 	return 0;
167 }
168 
169 int
170 head507d_curs_layout(struct nv50_head *head, struct nv50_wndw_atom *asyw,
171 		     struct nv50_head_atom *asyh)
172 {
173 	switch (asyw->image.w) {
174 	case 32: asyh->curs.layout = NV507D_HEAD_SET_CONTROL_CURSOR_SIZE_W32_H32; break;
175 	case 64: asyh->curs.layout = NV507D_HEAD_SET_CONTROL_CURSOR_SIZE_W64_H64; break;
176 	default:
177 		return -EINVAL;
178 	}
179 	return 0;
180 }
181 
182 int
183 head507d_core_clr(struct nv50_head *head)
184 {
185 	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
186 	const int i = head->base.index;
187 	int ret;
188 
189 	if ((ret = PUSH_WAIT(push, 2)))
190 		return ret;
191 
192 	PUSH_MTHD(push, NV507D, HEAD_SET_CONTEXT_DMA_ISO(i), 0x00000000);
193 	return 0;
194 }
195 
196 static int
197 head507d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
198 {
199 	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
200 	const int i = head->base.index;
201 	int ret;
202 
203 	if ((ret = PUSH_WAIT(push, 9)))
204 		return ret;
205 
206 	PUSH_MTHD(push, NV507D, HEAD_SET_OFFSET(i, 0),
207 		  NVVAL(NV507D, HEAD_SET_OFFSET, ORIGIN, asyh->core.offset >> 8));
208 
209 	PUSH_MTHD(push, NV507D, HEAD_SET_SIZE(i),
210 		  NVVAL(NV507D, HEAD_SET_SIZE, WIDTH, asyh->core.w) |
211 		  NVVAL(NV507D, HEAD_SET_SIZE, HEIGHT, asyh->core.h),
212 
213 				HEAD_SET_STORAGE(i),
214 		  NVVAL(NV507D, HEAD_SET_STORAGE, BLOCK_HEIGHT, asyh->core.blockh) |
215 		  NVVAL(NV507D, HEAD_SET_STORAGE, PITCH, asyh->core.pitch >> 8) |
216 		  NVVAL(NV507D, HEAD_SET_STORAGE, PITCH, asyh->core.blocks) |
217 		  NVVAL(NV507D, HEAD_SET_STORAGE, MEMORY_LAYOUT, asyh->core.layout),
218 
219 				HEAD_SET_PARAMS(i),
220 		  NVVAL(NV507D, HEAD_SET_PARAMS, FORMAT, asyh->core.format) |
221 		  NVVAL(NV507D, HEAD_SET_PARAMS, KIND, asyh->core.kind) |
222 		  NVDEF(NV507D, HEAD_SET_PARAMS, PART_STRIDE, PARTSTRIDE_256),
223 
224 				HEAD_SET_CONTEXT_DMA_ISO(i),
225 		  NVVAL(NV507D, HEAD_SET_CONTEXT_DMA_ISO, HANDLE, asyh->core.handle));
226 
227 	PUSH_MTHD(push, NV507D, HEAD_SET_VIEWPORT_POINT_IN(i, 0),
228 		  NVVAL(NV507D, HEAD_SET_VIEWPORT_POINT_IN, X, asyh->core.x) |
229 		  NVVAL(NV507D, HEAD_SET_VIEWPORT_POINT_IN, Y, asyh->core.y));
230 
231 	/* EVO will complain with INVALID_STATE if we have an
232 	 * active cursor and (re)specify HeadSetContextDmaIso
233 	 * without also updating HeadSetOffsetCursor.
234 	 */
235 	asyh->set.curs = asyh->curs.visible;
236 	asyh->set.olut = asyh->olut.handle != 0;
237 	return 0;
238 }
239 
240 void
241 head507d_core_calc(struct nv50_head *head, struct nv50_head_atom *asyh)
242 {
243 	struct nv50_disp *disp = nv50_disp(head->base.base.dev);
244 	if ((asyh->core.visible = (asyh->base.cpp != 0))) {
245 		asyh->core.x = asyh->base.x;
246 		asyh->core.y = asyh->base.y;
247 		asyh->core.w = asyh->base.w;
248 		asyh->core.h = asyh->base.h;
249 	} else
250 	if ((asyh->core.visible = (asyh->ovly.cpp != 0)) ||
251 	    (asyh->core.visible = asyh->curs.visible)) {
252 		/*XXX: We need to either find some way of having the
253 		 *     primary base layer appear black, while still
254 		 *     being able to display the other layers, or we
255 		 *     need to allocate a dummy black surface here.
256 		 */
257 		asyh->core.x = 0;
258 		asyh->core.y = 0;
259 		asyh->core.w = asyh->state.mode.hdisplay;
260 		asyh->core.h = asyh->state.mode.vdisplay;
261 	}
262 	asyh->core.handle = disp->core->chan.vram.handle;
263 	asyh->core.offset = 0;
264 	asyh->core.format = NV507D_HEAD_SET_PARAMS_FORMAT_A8R8G8B8;
265 	asyh->core.kind = NV507D_HEAD_SET_PARAMS_KIND_KIND_PITCH;
266 	asyh->core.layout = NV507D_HEAD_SET_STORAGE_MEMORY_LAYOUT_PITCH;
267 	asyh->core.blockh = NV507D_HEAD_SET_STORAGE_BLOCK_HEIGHT_ONE_GOB;
268 	asyh->core.blocks = 0;
269 	asyh->core.pitch = ALIGN(asyh->core.w, 64) * 4;
270 }
271 
272 static int
273 head507d_olut_clr(struct nv50_head *head)
274 {
275 	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
276 	const int i = head->base.index;
277 	int ret;
278 
279 	if ((ret = PUSH_WAIT(push, 2)))
280 		return ret;
281 
282 	PUSH_MTHD(push, NV507D, HEAD_SET_BASE_LUT_LO(i),
283 		  NVDEF(NV507D, HEAD_SET_BASE_LUT_LO, ENABLE, DISABLE));
284 	return 0;
285 }
286 
287 static int
288 head507d_olut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
289 {
290 	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
291 	const int i = head->base.index;
292 	int ret;
293 
294 	if ((ret = PUSH_WAIT(push, 3)))
295 		return ret;
296 
297 	PUSH_MTHD(push, NV507D, HEAD_SET_BASE_LUT_LO(i),
298 		  NVDEF(NV507D, HEAD_SET_BASE_LUT_LO, ENABLE, ENABLE) |
299 		  NVVAL(NV507D, HEAD_SET_BASE_LUT_LO, MODE, asyh->olut.mode) |
300 		  NVVAL(NV507D, HEAD_SET_BASE_LUT_LO, ORIGIN, 0),
301 
302 				HEAD_SET_BASE_LUT_HI(i),
303 		  NVVAL(NV507D, HEAD_SET_BASE_LUT_HI, ORIGIN, asyh->olut.offset >> 8));
304 	return 0;
305 }
306 
307 static void
308 head507d_olut_load(struct drm_color_lut *in, int size, void __iomem *mem)
309 {
310 	for (; size--; in++, mem += 8) {
311 		writew(drm_color_lut_extract(in->  red, 11) << 3, mem + 0);
312 		writew(drm_color_lut_extract(in->green, 11) << 3, mem + 2);
313 		writew(drm_color_lut_extract(in-> blue, 11) << 3, mem + 4);
314 	}
315 
316 	/* INTERPOLATE modes require a "next" entry to interpolate with,
317 	 * so we replicate the last entry to deal with this for now.
318 	 */
319 	writew(readw(mem - 8), mem + 0);
320 	writew(readw(mem - 6), mem + 2);
321 	writew(readw(mem - 4), mem + 4);
322 }
323 
324 bool
325 head507d_olut(struct nv50_head *head, struct nv50_head_atom *asyh, int size)
326 {
327 	if (size != 256)
328 		return false;
329 
330 	if (asyh->base.cpp == 1)
331 		asyh->olut.mode = NV507D_HEAD_SET_BASE_LUT_LO_MODE_LORES;
332 	else
333 		asyh->olut.mode = NV507D_HEAD_SET_BASE_LUT_LO_MODE_HIRES;
334 
335 	asyh->olut.load = head507d_olut_load;
336 	return true;
337 }
338 
339 int
340 head507d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
341 {
342 	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
343 	struct nv50_head_mode *m = &asyh->mode;
344 	const int i = head->base.index;
345 	int ret;
346 
347 	if ((ret = PUSH_WAIT(push, 13)))
348 		return ret;
349 
350 	PUSH_MTHD(push, NV507D, HEAD_SET_PIXEL_CLOCK(i),
351 		  NVVAL(NV507D, HEAD_SET_PIXEL_CLOCK, FREQUENCY, m->clock) |
352 		  NVDEF(NV507D, HEAD_SET_PIXEL_CLOCK, MODE, CLK_CUSTOM) |
353 		  NVDEF(NV507D, HEAD_SET_PIXEL_CLOCK, ADJ1000DIV1001, FALSE) |
354 		  NVDEF(NV507D, HEAD_SET_PIXEL_CLOCK, NOT_DRIVER, FALSE),
355 
356 				HEAD_SET_CONTROL(i),
357 		  NVVAL(NV507D, HEAD_SET_CONTROL, STRUCTURE, m->interlace));
358 
359 	PUSH_MTHD(push, NV507D, HEAD_SET_OVERSCAN_COLOR(i),
360 		  NVVAL(NV507D, HEAD_SET_OVERSCAN_COLOR, RED, 0) |
361 		  NVVAL(NV507D, HEAD_SET_OVERSCAN_COLOR, GRN, 0) |
362 		  NVVAL(NV507D, HEAD_SET_OVERSCAN_COLOR, BLU, 0),
363 
364 				HEAD_SET_RASTER_SIZE(i),
365 		  NVVAL(NV507D, HEAD_SET_RASTER_SIZE, WIDTH, m->h.active) |
366 		  NVVAL(NV507D, HEAD_SET_RASTER_SIZE, HEIGHT, m->v.active),
367 
368 				HEAD_SET_RASTER_SYNC_END(i),
369 		  NVVAL(NV507D, HEAD_SET_RASTER_SYNC_END, X, m->h.synce) |
370 		  NVVAL(NV507D, HEAD_SET_RASTER_SYNC_END, Y, m->v.synce),
371 
372 				HEAD_SET_RASTER_BLANK_END(i),
373 		  NVVAL(NV507D, HEAD_SET_RASTER_BLANK_END, X, m->h.blanke) |
374 		  NVVAL(NV507D, HEAD_SET_RASTER_BLANK_END, Y, m->v.blanke),
375 
376 				HEAD_SET_RASTER_BLANK_START(i),
377 		  NVVAL(NV507D, HEAD_SET_RASTER_BLANK_START, X, m->h.blanks) |
378 		  NVVAL(NV507D, HEAD_SET_RASTER_BLANK_START, Y, m->v.blanks),
379 
380 				HEAD_SET_RASTER_VERT_BLANK2(i),
381 		  NVVAL(NV507D, HEAD_SET_RASTER_VERT_BLANK2, YSTART, m->v.blank2s) |
382 		  NVVAL(NV507D, HEAD_SET_RASTER_VERT_BLANK2, YEND, m->v.blank2e),
383 
384 				HEAD_SET_RASTER_VERT_BLANK_DMI(i),
385 		  NVVAL(NV507D, HEAD_SET_RASTER_VERT_BLANK_DMI, DURATION, m->v.blankus));
386 
387 	PUSH_MTHD(push, NV507D, HEAD_SET_DEFAULT_BASE_COLOR(i),
388 		  NVVAL(NV507D, HEAD_SET_DEFAULT_BASE_COLOR, RED, 0) |
389 		  NVVAL(NV507D, HEAD_SET_DEFAULT_BASE_COLOR, GREEN, 0) |
390 		  NVVAL(NV507D, HEAD_SET_DEFAULT_BASE_COLOR, BLUE, 0));
391 	return 0;
392 }
393 
394 int
395 head507d_view(struct nv50_head *head, struct nv50_head_atom *asyh)
396 {
397 	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
398 	const int i = head->base.index;
399 	int ret;
400 
401 	if ((ret = PUSH_WAIT(push, 7)))
402 		return ret;
403 
404 	PUSH_MTHD(push, NV507D, HEAD_SET_CONTROL_OUTPUT_SCALER(i),
405 		  NVDEF(NV507D, HEAD_SET_CONTROL_OUTPUT_SCALER, VERTICAL_TAPS, TAPS_1) |
406 		  NVDEF(NV507D, HEAD_SET_CONTROL_OUTPUT_SCALER, HORIZONTAL_TAPS, TAPS_1) |
407 		  NVVAL(NV507D, HEAD_SET_CONTROL_OUTPUT_SCALER, HRESPONSE_BIAS, 0) |
408 		  NVVAL(NV507D, HEAD_SET_CONTROL_OUTPUT_SCALER, VRESPONSE_BIAS, 0));
409 
410 	PUSH_MTHD(push, NV507D, HEAD_SET_VIEWPORT_SIZE_IN(i),
411 		  NVVAL(NV507D, HEAD_SET_VIEWPORT_SIZE_IN, WIDTH, asyh->view.iW) |
412 		  NVVAL(NV507D, HEAD_SET_VIEWPORT_SIZE_IN, HEIGHT, asyh->view.iH));
413 
414 	PUSH_MTHD(push, NV507D, HEAD_SET_VIEWPORT_SIZE_OUT(i),
415 		  NVVAL(NV507D, HEAD_SET_VIEWPORT_SIZE_OUT, WIDTH, asyh->view.oW) |
416 		  NVVAL(NV507D, HEAD_SET_VIEWPORT_SIZE_OUT, HEIGHT, asyh->view.oH),
417 
418 				HEAD_SET_VIEWPORT_SIZE_OUT_MIN(i),
419 		  NVVAL(NV507D, HEAD_SET_VIEWPORT_SIZE_OUT_MIN, WIDTH, asyh->view.oW) |
420 		  NVVAL(NV507D, HEAD_SET_VIEWPORT_SIZE_OUT_MIN, HEIGHT, asyh->view.oH));
421 	return 0;
422 }
423 
424 const struct nv50_head_func
425 head507d = {
426 	.view = head507d_view,
427 	.mode = head507d_mode,
428 	.olut = head507d_olut,
429 	.olut_size = 256,
430 	.olut_set = head507d_olut_set,
431 	.olut_clr = head507d_olut_clr,
432 	.core_calc = head507d_core_calc,
433 	.core_set = head507d_core_set,
434 	.core_clr = head507d_core_clr,
435 	.curs_layout = head507d_curs_layout,
436 	.curs_format = head507d_curs_format,
437 	.curs_set = head507d_curs_set,
438 	.curs_clr = head507d_curs_clr,
439 	.base = head507d_base,
440 	.ovly = head507d_ovly,
441 	.dither = head507d_dither,
442 	.procamp = head507d_procamp,
443 };
444