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 <drm/drm_connector.h>
23#include <drm/drm_mode_config.h>
24#include <drm/drm_vblank.h>
25#include "nouveau_drv.h"
26#include "nouveau_bios.h"
27#include "nouveau_connector.h"
28#include "head.h"
29#include "core.h"
30#include "crc.h"
31
32#include <nvif/push507c.h>
33
34#include <nvhw/class/cl907d.h>
35
36int
37head907d_or(struct nv50_head *head, struct nv50_head_atom *asyh)
38{
39	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
40	const int i = head->base.index;
41	int ret;
42
43	if ((ret = PUSH_WAIT(push, 3)))
44		return ret;
45
46	PUSH_MTHD(push, NV907D, HEAD_SET_CONTROL_OUTPUT_RESOURCE(i),
47		  NVVAL(NV907D, HEAD_SET_CONTROL_OUTPUT_RESOURCE, CRC_MODE, asyh->or.crc_raster) |
48		  NVVAL(NV907D, HEAD_SET_CONTROL_OUTPUT_RESOURCE, HSYNC_POLARITY, asyh->or.nhsync) |
49		  NVVAL(NV907D, HEAD_SET_CONTROL_OUTPUT_RESOURCE, VSYNC_POLARITY, asyh->or.nvsync) |
50		  NVVAL(NV907D, HEAD_SET_CONTROL_OUTPUT_RESOURCE, PIXEL_DEPTH, asyh->or.depth),
51
52				HEAD_SET_CONTROL(i), 0x31ec6000 | head->base.index << 25 |
53		  NVVAL(NV907D, HEAD_SET_CONTROL, STRUCTURE, asyh->mode.interlace));
54	return 0;
55}
56
57int
58head907d_procamp(struct nv50_head *head, struct nv50_head_atom *asyh)
59{
60	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
61	const int i = head->base.index;
62	int ret;
63
64	if ((ret = PUSH_WAIT(push, 2)))
65		return ret;
66
67	PUSH_MTHD(push, NV907D, HEAD_SET_PROCAMP(i),
68		  NVDEF(NV907D, HEAD_SET_PROCAMP, COLOR_SPACE, RGB) |
69		  NVDEF(NV907D, HEAD_SET_PROCAMP, CHROMA_LPF, AUTO) |
70		  NVVAL(NV907D, HEAD_SET_PROCAMP, SAT_COS, asyh->procamp.sat.cos) |
71		  NVVAL(NV907D, HEAD_SET_PROCAMP, SAT_SINE, asyh->procamp.sat.sin) |
72		  NVDEF(NV907D, HEAD_SET_PROCAMP, DYNAMIC_RANGE, VESA) |
73		  NVDEF(NV907D, HEAD_SET_PROCAMP, RANGE_COMPRESSION, DISABLE));
74	return 0;
75}
76
77static int
78head907d_dither(struct nv50_head *head, struct nv50_head_atom *asyh)
79{
80	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
81	const int i = head->base.index;
82	int ret;
83
84	if ((ret = PUSH_WAIT(push, 2)))
85		return ret;
86
87	PUSH_MTHD(push, NV907D, HEAD_SET_DITHER_CONTROL(i),
88		  NVVAL(NV907D, HEAD_SET_DITHER_CONTROL, ENABLE, asyh->dither.enable) |
89		  NVVAL(NV907D, HEAD_SET_DITHER_CONTROL, BITS, asyh->dither.bits) |
90		  NVVAL(NV907D, HEAD_SET_DITHER_CONTROL, MODE, asyh->dither.mode) |
91		  NVVAL(NV907D, HEAD_SET_DITHER_CONTROL, PHASE, 0));
92	return 0;
93}
94
95int
96head907d_ovly(struct nv50_head *head, struct nv50_head_atom *asyh)
97{
98	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
99	const int i = head->base.index;
100	u32 bounds = 0;
101	int ret;
102
103	if (asyh->ovly.cpp) {
104		switch (asyh->ovly.cpp) {
105		case 8: bounds |= NVDEF(NV907D, HEAD_SET_OVERLAY_USAGE_BOUNDS, PIXEL_DEPTH, BPP_64); break;
106		case 4: bounds |= NVDEF(NV907D, HEAD_SET_OVERLAY_USAGE_BOUNDS, PIXEL_DEPTH, BPP_32); break;
107		case 2: bounds |= NVDEF(NV907D, HEAD_SET_OVERLAY_USAGE_BOUNDS, PIXEL_DEPTH, BPP_16); break;
108		default:
109			WARN_ON(1);
110			break;
111		}
112		bounds |= NVDEF(NV907D, HEAD_SET_OVERLAY_USAGE_BOUNDS, USABLE, TRUE);
113	} else {
114		bounds |= NVDEF(NV907D, HEAD_SET_OVERLAY_USAGE_BOUNDS, PIXEL_DEPTH, BPP_16);
115	}
116
117	if ((ret = PUSH_WAIT(push, 2)))
118		return ret;
119
120	PUSH_MTHD(push, NV907D, HEAD_SET_OVERLAY_USAGE_BOUNDS(i), bounds);
121	return 0;
122}
123
124static int
125head907d_base(struct nv50_head *head, struct nv50_head_atom *asyh)
126{
127	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
128	const int i = head->base.index;
129	u32 bounds = 0;
130	int ret;
131
132	if (asyh->base.cpp) {
133		switch (asyh->base.cpp) {
134		case 8: bounds |= NVDEF(NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_64); break;
135		case 4: bounds |= NVDEF(NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_32); break;
136		case 2: bounds |= NVDEF(NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_16); break;
137		case 1: bounds |= NVDEF(NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_8); break;
138		default:
139			WARN_ON(1);
140			break;
141		}
142		bounds |= NVDEF(NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, USABLE, TRUE);
143	}
144
145	if ((ret = PUSH_WAIT(push, 2)))
146		return ret;
147
148	PUSH_MTHD(push, NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS(i), bounds);
149	return 0;
150}
151
152int
153head907d_curs_clr(struct nv50_head *head)
154{
155	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
156	const int i = head->base.index;
157	int ret;
158
159	if ((ret = PUSH_WAIT(push, 4)))
160		return ret;
161
162	PUSH_MTHD(push, NV907D, HEAD_SET_CONTROL_CURSOR(i),
163		  NVDEF(NV907D, HEAD_SET_CONTROL_CURSOR, ENABLE, DISABLE) |
164		  NVDEF(NV907D, HEAD_SET_CONTROL_CURSOR, FORMAT, A8R8G8B8) |
165		  NVDEF(NV907D, HEAD_SET_CONTROL_CURSOR, SIZE, W64_H64));
166
167	PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_CURSOR(i), 0x00000000);
168	return 0;
169}
170
171int
172head907d_curs_set(struct nv50_head *head, struct nv50_head_atom *asyh)
173{
174	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
175	const int i = head->base.index;
176	int ret;
177
178	if ((ret = PUSH_WAIT(push, 5)))
179		return ret;
180
181	PUSH_MTHD(push, NV907D, HEAD_SET_CONTROL_CURSOR(i),
182		  NVDEF(NV907D, HEAD_SET_CONTROL_CURSOR, ENABLE, ENABLE) |
183		  NVVAL(NV907D, HEAD_SET_CONTROL_CURSOR, FORMAT, asyh->curs.format) |
184		  NVVAL(NV907D, HEAD_SET_CONTROL_CURSOR, SIZE, asyh->curs.layout) |
185		  NVVAL(NV907D, HEAD_SET_CONTROL_CURSOR, HOT_SPOT_X, 0) |
186		  NVVAL(NV907D, HEAD_SET_CONTROL_CURSOR, HOT_SPOT_Y, 0) |
187		  NVDEF(NV907D, HEAD_SET_CONTROL_CURSOR, COMPOSITION, ALPHA_BLEND),
188
189				HEAD_SET_OFFSET_CURSOR(i), asyh->curs.offset >> 8);
190
191	PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_CURSOR(i), asyh->curs.handle);
192	return 0;
193}
194
195int
196head907d_core_clr(struct nv50_head *head)
197{
198	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
199	const int i = head->base.index;
200	int ret;
201
202	if ((ret = PUSH_WAIT(push, 2)))
203		return ret;
204
205	PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMAS_ISO(i), 0x00000000);
206	return 0;
207}
208
209int
210head907d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
211{
212	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
213	const int i = head->base.index;
214	int ret;
215
216	if ((ret = PUSH_WAIT(push, 9)))
217		return ret;
218
219	PUSH_MTHD(push, NV907D, HEAD_SET_OFFSET(i),
220		  NVVAL(NV907D, HEAD_SET_OFFSET, ORIGIN, asyh->core.offset >> 8));
221
222	PUSH_MTHD(push, NV907D, HEAD_SET_SIZE(i),
223		  NVVAL(NV907D, HEAD_SET_SIZE, WIDTH, asyh->core.w) |
224		  NVVAL(NV907D, HEAD_SET_SIZE, HEIGHT, asyh->core.h),
225
226				HEAD_SET_STORAGE(i),
227		  NVVAL(NV907D, HEAD_SET_STORAGE, BLOCK_HEIGHT, asyh->core.blockh) |
228		  NVVAL(NV907D, HEAD_SET_STORAGE, PITCH, asyh->core.pitch >> 8) |
229		  NVVAL(NV907D, HEAD_SET_STORAGE, PITCH, asyh->core.blocks) |
230		  NVVAL(NV907D, HEAD_SET_STORAGE, MEMORY_LAYOUT, asyh->core.layout),
231
232				HEAD_SET_PARAMS(i),
233		  NVVAL(NV907D, HEAD_SET_PARAMS, FORMAT, asyh->core.format) |
234		  NVDEF(NV907D, HEAD_SET_PARAMS, SUPER_SAMPLE, X1_AA) |
235		  NVDEF(NV907D, HEAD_SET_PARAMS, GAMMA, LINEAR),
236
237				HEAD_SET_CONTEXT_DMAS_ISO(i),
238		  NVVAL(NV907D, HEAD_SET_CONTEXT_DMAS_ISO, HANDLE, asyh->core.handle));
239
240	PUSH_MTHD(push, NV907D, HEAD_SET_VIEWPORT_POINT_IN(i),
241		  NVVAL(NV907D, HEAD_SET_VIEWPORT_POINT_IN, X, asyh->core.x) |
242		  NVVAL(NV907D, HEAD_SET_VIEWPORT_POINT_IN, Y, asyh->core.y));
243	return 0;
244}
245
246int
247head907d_olut_clr(struct nv50_head *head)
248{
249	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
250	const int i = head->base.index;
251	int ret;
252
253	if ((ret = PUSH_WAIT(push, 4)))
254		return ret;
255
256	PUSH_MTHD(push, NV907D, HEAD_SET_OUTPUT_LUT_LO(i),
257		  NVDEF(NV907D, HEAD_SET_OUTPUT_LUT_LO, ENABLE, DISABLE));
258
259	PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_LUT(i), 0x00000000);
260	return 0;
261}
262
263int
264head907d_olut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
265{
266	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
267	const int i = head->base.index;
268	int ret;
269
270	if ((ret = PUSH_WAIT(push, 5)))
271		return ret;
272
273	PUSH_MTHD(push, NV907D, HEAD_SET_OUTPUT_LUT_LO(i),
274		  NVDEF(NV907D, HEAD_SET_OUTPUT_LUT_LO, ENABLE, ENABLE) |
275		  NVVAL(NV907D, HEAD_SET_OUTPUT_LUT_LO, MODE, asyh->olut.mode) |
276		  NVDEF(NV907D, HEAD_SET_OUTPUT_LUT_LO, NEVER_YIELD_TO_BASE, DISABLE),
277
278				HEAD_SET_OUTPUT_LUT_HI(i),
279		  NVVAL(NV907D, HEAD_SET_OUTPUT_LUT_HI, ORIGIN, asyh->olut.offset >> 8));
280
281	PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_LUT(i), asyh->olut.handle);
282	return 0;
283}
284
285void
286head907d_olut_load(struct drm_color_lut *in, int size, void __iomem *mem)
287{
288	for (; size--; in++, mem += 8) {
289		writew(drm_color_lut_extract(in->  red, 14) + 0x6000, mem + 0);
290		writew(drm_color_lut_extract(in->green, 14) + 0x6000, mem + 2);
291		writew(drm_color_lut_extract(in-> blue, 14) + 0x6000, mem + 4);
292	}
293
294	/* INTERPOLATE modes require a "next" entry to interpolate with,
295	 * so we replicate the last entry to deal with this for now.
296	 */
297	writew(readw(mem - 8), mem + 0);
298	writew(readw(mem - 6), mem + 2);
299	writew(readw(mem - 4), mem + 4);
300}
301
302bool
303head907d_olut(struct nv50_head *head, struct nv50_head_atom *asyh, int size)
304{
305	if (size != 256 && size != 1024)
306		return false;
307
308	if (size == 1024)
309		asyh->olut.mode = NV907D_HEAD_SET_OUTPUT_LUT_LO_MODE_INTERPOLATE_1025_UNITY_RANGE;
310	else
311		asyh->olut.mode = NV907D_HEAD_SET_OUTPUT_LUT_LO_MODE_INTERPOLATE_257_UNITY_RANGE;
312
313	asyh->olut.load = head907d_olut_load;
314	return true;
315}
316
317bool head907d_ilut_check(int size)
318{
319	return size == 256 || size == 1024;
320}
321
322int
323head907d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
324{
325	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
326	struct nv50_head_mode *m = &asyh->mode;
327	const int i = head->base.index;
328	int ret;
329
330	if ((ret = PUSH_WAIT(push, 13)))
331		return ret;
332
333	PUSH_MTHD(push, NV907D, HEAD_SET_OVERSCAN_COLOR(i),
334		  NVVAL(NV907D, HEAD_SET_OVERSCAN_COLOR, RED, 0) |
335		  NVVAL(NV907D, HEAD_SET_OVERSCAN_COLOR, GRN, 0) |
336		  NVVAL(NV907D, HEAD_SET_OVERSCAN_COLOR, BLU, 0),
337
338				HEAD_SET_RASTER_SIZE(i),
339		  NVVAL(NV907D, HEAD_SET_RASTER_SIZE, WIDTH, m->h.active) |
340		  NVVAL(NV907D, HEAD_SET_RASTER_SIZE, HEIGHT, m->v.active),
341
342				HEAD_SET_RASTER_SYNC_END(i),
343		  NVVAL(NV907D, HEAD_SET_RASTER_SYNC_END, X, m->h.synce) |
344		  NVVAL(NV907D, HEAD_SET_RASTER_SYNC_END, Y, m->v.synce),
345
346				HEAD_SET_RASTER_BLANK_END(i),
347		  NVVAL(NV907D, HEAD_SET_RASTER_BLANK_END, X, m->h.blanke) |
348		  NVVAL(NV907D, HEAD_SET_RASTER_BLANK_END, Y, m->v.blanke),
349
350				HEAD_SET_RASTER_BLANK_START(i),
351		  NVVAL(NV907D, HEAD_SET_RASTER_BLANK_START, X, m->h.blanks) |
352		  NVVAL(NV907D, HEAD_SET_RASTER_BLANK_START, Y, m->v.blanks),
353
354				HEAD_SET_RASTER_VERT_BLANK2(i),
355		  NVVAL(NV907D, HEAD_SET_RASTER_VERT_BLANK2, YSTART, m->v.blank2s) |
356		  NVVAL(NV907D, HEAD_SET_RASTER_VERT_BLANK2, YEND, m->v.blank2e));
357
358	PUSH_MTHD(push, NV907D, HEAD_SET_DEFAULT_BASE_COLOR(i),
359		  NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, RED, 0) |
360		  NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, GREEN, 0) |
361		  NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, BLUE, 0));
362
363	PUSH_MTHD(push, NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY(i),
364		  NVVAL(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY, HERTZ, m->clock * 1000) |
365		  NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY, ADJ1000DIV1001, FALSE),
366
367				HEAD_SET_PIXEL_CLOCK_CONFIGURATION(i),
368		  NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_CONFIGURATION, MODE, CLK_CUSTOM) |
369		  NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_CONFIGURATION, NOT_DRIVER, FALSE) |
370		  NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_CONFIGURATION, ENABLE_HOPPING, FALSE),
371
372				HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX(i),
373		  NVVAL(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, HERTZ, m->clock * 1000) |
374		  NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, ADJ1000DIV1001, FALSE));
375	return 0;
376}
377
378int
379head907d_view(struct nv50_head *head, struct nv50_head_atom *asyh)
380{
381	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
382	const int i = head->base.index;
383	int ret;
384
385	if ((ret = PUSH_WAIT(push, 8)))
386		return ret;
387
388	PUSH_MTHD(push, NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER(i),
389		  NVDEF(NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER, VERTICAL_TAPS, TAPS_1) |
390		  NVDEF(NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER, HORIZONTAL_TAPS, TAPS_1) |
391		  NVVAL(NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER, HRESPONSE_BIAS, 0) |
392		  NVVAL(NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER, VRESPONSE_BIAS, 0));
393
394	PUSH_MTHD(push, NV907D, HEAD_SET_VIEWPORT_SIZE_IN(i),
395		  NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_IN, WIDTH, asyh->view.iW) |
396		  NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_IN, HEIGHT, asyh->view.iH));
397
398	PUSH_MTHD(push, NV907D, HEAD_SET_VIEWPORT_SIZE_OUT(i),
399		  NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT, WIDTH, asyh->view.oW) |
400		  NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT, HEIGHT, asyh->view.oH),
401
402				HEAD_SET_VIEWPORT_SIZE_OUT_MIN(i),
403		  NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT_MIN, WIDTH, asyh->view.oW) |
404		  NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT_MIN, HEIGHT, asyh->view.oH),
405
406				HEAD_SET_VIEWPORT_SIZE_OUT_MAX(i),
407		  NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT_MAX, WIDTH, asyh->view.oW) |
408		  NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT_MAX, HEIGHT, asyh->view.oH));
409	return 0;
410}
411
412const struct nv50_head_func
413head907d = {
414	.view = head907d_view,
415	.mode = head907d_mode,
416	.olut = head907d_olut,
417	.ilut_check = head907d_ilut_check,
418	.olut_size = 1024,
419	.olut_set = head907d_olut_set,
420	.olut_clr = head907d_olut_clr,
421	.core_calc = head507d_core_calc,
422	.core_set = head907d_core_set,
423	.core_clr = head907d_core_clr,
424	.curs_layout = head507d_curs_layout,
425	.curs_format = head507d_curs_format,
426	.curs_set = head907d_curs_set,
427	.curs_clr = head907d_curs_clr,
428	.base = head907d_base,
429	.ovly = head907d_ovly,
430	.dither = head907d_dither,
431	.procamp = head907d_procamp,
432	.or = head907d_or,
433};
434