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 "atom.h"
24#include "core.h"
25
26#include <nvif/pushc37b.h>
27
28#include <nvhw/class/clc37d.h>
29
30static int
31headc37d_or(struct nv50_head *head, struct nv50_head_atom *asyh)
32{
33	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
34	const int i = head->base.index;
35	u8 depth;
36	int ret;
37
38	/*XXX: This is a dirty hack until OR depth handling is
39	 *     improved later for deep colour etc.
40	 */
41	switch (asyh->or.depth) {
42	case 6: depth = 5; break;
43	case 5: depth = 4; break;
44	case 2: depth = 1; break;
45	case 0:	depth = 4; break;
46	default:
47		depth = asyh->or.depth;
48		WARN_ON(1);
49		break;
50	}
51
52	if ((ret = PUSH_WAIT(push, 2)))
53		return ret;
54
55	PUSH_MTHD(push, NVC37D, HEAD_SET_CONTROL_OUTPUT_RESOURCE(i),
56		  NVVAL(NVC37D, HEAD_SET_CONTROL_OUTPUT_RESOURCE, CRC_MODE, asyh->or.crc_raster) |
57		  NVVAL(NVC37D, HEAD_SET_CONTROL_OUTPUT_RESOURCE, HSYNC_POLARITY, asyh->or.nhsync) |
58		  NVVAL(NVC37D, HEAD_SET_CONTROL_OUTPUT_RESOURCE, VSYNC_POLARITY, asyh->or.nvsync) |
59		  NVVAL(NVC37D, HEAD_SET_CONTROL_OUTPUT_RESOURCE, PIXEL_DEPTH, depth) |
60		  NVDEF(NVC37D, HEAD_SET_CONTROL_OUTPUT_RESOURCE, COLOR_SPACE_OVERRIDE, DISABLE));
61	return 0;
62}
63
64static int
65headc37d_procamp(struct nv50_head *head, struct nv50_head_atom *asyh)
66{
67	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
68	const int i = head->base.index;
69	int ret;
70
71	if ((ret = PUSH_WAIT(push, 2)))
72		return ret;
73
74	PUSH_MTHD(push, NVC37D, HEAD_SET_PROCAMP(i),
75		  NVDEF(NVC37D, HEAD_SET_PROCAMP, COLOR_SPACE, RGB) |
76		  NVDEF(NVC37D, HEAD_SET_PROCAMP, CHROMA_LPF, DISABLE) |
77		  NVVAL(NVC37D, HEAD_SET_PROCAMP, SAT_COS, asyh->procamp.sat.cos) |
78		  NVVAL(NVC37D, HEAD_SET_PROCAMP, SAT_SINE, asyh->procamp.sat.sin) |
79		  NVDEF(NVC37D, HEAD_SET_PROCAMP, DYNAMIC_RANGE, VESA) |
80		  NVDEF(NVC37D, HEAD_SET_PROCAMP, RANGE_COMPRESSION, DISABLE) |
81		  NVDEF(NVC37D, HEAD_SET_PROCAMP, BLACK_LEVEL, GRAPHICS));
82	return 0;
83}
84
85int
86headc37d_dither(struct nv50_head *head, struct nv50_head_atom *asyh)
87{
88	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
89	const int i = head->base.index;
90	int ret;
91
92	if ((ret = PUSH_WAIT(push, 2)))
93		return ret;
94
95	PUSH_MTHD(push, NVC37D, HEAD_SET_DITHER_CONTROL(i),
96		  NVVAL(NVC37D, HEAD_SET_DITHER_CONTROL, ENABLE, asyh->dither.enable) |
97		  NVVAL(NVC37D, HEAD_SET_DITHER_CONTROL, BITS, asyh->dither.bits) |
98		  NVDEF(NVC37D, HEAD_SET_DITHER_CONTROL, OFFSET_ENABLE, DISABLE) |
99		  NVVAL(NVC37D, HEAD_SET_DITHER_CONTROL, MODE, asyh->dither.mode) |
100		  NVVAL(NVC37D, HEAD_SET_DITHER_CONTROL, PHASE, 0));
101	return 0;
102}
103
104int
105headc37d_curs_clr(struct nv50_head *head)
106{
107	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
108	const int i = head->base.index;
109	int ret;
110
111	if ((ret = PUSH_WAIT(push, 4)))
112		return ret;
113
114	PUSH_MTHD(push, NVC37D, HEAD_SET_CONTROL_CURSOR(i),
115		  NVDEF(NVC37D, HEAD_SET_CONTROL_CURSOR, ENABLE, DISABLE) |
116		  NVDEF(NVC37D, HEAD_SET_CONTROL_CURSOR, FORMAT, A8R8G8B8));
117
118	PUSH_MTHD(push, NVC37D, HEAD_SET_CONTEXT_DMA_CURSOR(i, 0), 0x00000000);
119	return 0;
120}
121
122int
123headc37d_curs_set(struct nv50_head *head, struct nv50_head_atom *asyh)
124{
125	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
126	const int i = head->base.index;
127	int ret;
128
129	if ((ret = PUSH_WAIT(push, 7)))
130		return ret;
131
132	PUSH_MTHD(push, NVC37D, HEAD_SET_CONTROL_CURSOR(i),
133		  NVDEF(NVC37D, HEAD_SET_CONTROL_CURSOR, ENABLE, ENABLE) |
134		  NVVAL(NVC37D, HEAD_SET_CONTROL_CURSOR, FORMAT, asyh->curs.format) |
135		  NVVAL(NVC37D, HEAD_SET_CONTROL_CURSOR, SIZE, asyh->curs.layout) |
136		  NVVAL(NVC37D, HEAD_SET_CONTROL_CURSOR, HOT_SPOT_X, 0) |
137		  NVVAL(NVC37D, HEAD_SET_CONTROL_CURSOR, HOT_SPOT_Y, 0) |
138		  NVDEF(NVC37D, HEAD_SET_CONTROL_CURSOR, DE_GAMMA, NONE),
139
140				HEAD_SET_CONTROL_CURSOR_COMPOSITION(i),
141		  NVVAL(NVC37D, HEAD_SET_CONTROL_CURSOR_COMPOSITION, K1, 0xff) |
142		  NVDEF(NVC37D, HEAD_SET_CONTROL_CURSOR_COMPOSITION, CURSOR_COLOR_FACTOR_SELECT,
143								     K1) |
144		  NVDEF(NVC37D, HEAD_SET_CONTROL_CURSOR_COMPOSITION, VIEWPORT_COLOR_FACTOR_SELECT,
145								     NEG_K1_TIMES_SRC) |
146		  NVDEF(NVC37D, HEAD_SET_CONTROL_CURSOR_COMPOSITION, MODE, BLEND));
147
148	PUSH_MTHD(push, NVC37D, HEAD_SET_CONTEXT_DMA_CURSOR(i, 0), asyh->curs.handle);
149	PUSH_MTHD(push, NVC37D, HEAD_SET_OFFSET_CURSOR(i, 0), asyh->curs.offset >> 8);
150	return 0;
151}
152
153int
154headc37d_curs_format(struct nv50_head *head, struct nv50_wndw_atom *asyw,
155		     struct nv50_head_atom *asyh)
156{
157	asyh->curs.format = asyw->image.format;
158	return 0;
159}
160
161static int
162headc37d_olut_clr(struct nv50_head *head)
163{
164	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
165	const int i = head->base.index;
166	int ret;
167
168	if ((ret = PUSH_WAIT(push, 2)))
169		return ret;
170
171	PUSH_MTHD(push, NVC37D, HEAD_SET_CONTEXT_DMA_OUTPUT_LUT(i), 0x00000000);
172	return 0;
173}
174
175static int
176headc37d_olut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
177{
178	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
179	const int i = head->base.index;
180	int ret;
181
182	if ((ret = PUSH_WAIT(push, 4)))
183		return ret;
184
185	PUSH_MTHD(push, NVC37D, HEAD_SET_CONTROL_OUTPUT_LUT(i),
186		  NVVAL(NVC37D, HEAD_SET_CONTROL_OUTPUT_LUT, SIZE, asyh->olut.size) |
187		  NVVAL(NVC37D, HEAD_SET_CONTROL_OUTPUT_LUT, RANGE, asyh->olut.range) |
188		  NVVAL(NVC37D, HEAD_SET_CONTROL_OUTPUT_LUT, OUTPUT_MODE, asyh->olut.output_mode),
189
190				HEAD_SET_OFFSET_OUTPUT_LUT(i), asyh->olut.offset >> 8,
191				HEAD_SET_CONTEXT_DMA_OUTPUT_LUT(i), asyh->olut.handle);
192	return 0;
193}
194
195static bool
196headc37d_olut(struct nv50_head *head, struct nv50_head_atom *asyh, int size)
197{
198	if (size != 256 && size != 1024)
199		return false;
200
201	asyh->olut.size = size == 1024 ? NVC37D_HEAD_SET_CONTROL_OUTPUT_LUT_SIZE_SIZE_1025 :
202					 NVC37D_HEAD_SET_CONTROL_OUTPUT_LUT_SIZE_SIZE_257;
203	asyh->olut.range = NVC37D_HEAD_SET_CONTROL_OUTPUT_LUT_RANGE_UNITY;
204	asyh->olut.output_mode = NVC37D_HEAD_SET_CONTROL_OUTPUT_LUT_OUTPUT_MODE_INTERPOLATE;
205	asyh->olut.load = head907d_olut_load;
206	return true;
207}
208
209static int
210headc37d_mode(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	struct nv50_head_mode *m = &asyh->mode;
214	const int i = head->base.index;
215	int ret;
216
217	if ((ret = PUSH_WAIT(push, 15)))
218		return ret;
219
220	PUSH_MTHD(push, NVC37D, HEAD_SET_RASTER_SIZE(i),
221		  NVVAL(NVC37D, HEAD_SET_RASTER_SIZE, WIDTH, m->h.active) |
222		  NVVAL(NVC37D, HEAD_SET_RASTER_SIZE, HEIGHT, m->v.active),
223
224				HEAD_SET_RASTER_SYNC_END(i),
225		  NVVAL(NVC37D, HEAD_SET_RASTER_SYNC_END, X, m->h.synce) |
226		  NVVAL(NVC37D, HEAD_SET_RASTER_SYNC_END, Y, m->v.synce),
227
228				HEAD_SET_RASTER_BLANK_END(i),
229		  NVVAL(NVC37D, HEAD_SET_RASTER_BLANK_END, X, m->h.blanke) |
230		  NVVAL(NVC37D, HEAD_SET_RASTER_BLANK_END, Y, m->v.blanke),
231
232				HEAD_SET_RASTER_BLANK_START(i),
233		  NVVAL(NVC37D, HEAD_SET_RASTER_BLANK_START, X, m->h.blanks) |
234		  NVVAL(NVC37D, HEAD_SET_RASTER_BLANK_START, Y, m->v.blanks));
235
236	//XXX:
237	PUSH_NVSQ(push, NVC37D, 0x2074 + (i * 0x400), m->v.blank2e << 16 | m->v.blank2s);
238	PUSH_NVSQ(push, NVC37D, 0x2008 + (i * 0x400), m->interlace);
239
240	PUSH_MTHD(push, NVC37D, HEAD_SET_PIXEL_CLOCK_FREQUENCY(i),
241		  NVVAL(NVC37D, HEAD_SET_PIXEL_CLOCK_FREQUENCY, HERTZ, m->clock * 1000));
242
243	PUSH_MTHD(push, NVC37D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX(i),
244		  NVVAL(NVC37D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, HERTZ, m->clock * 1000));
245
246	/*XXX: HEAD_USAGE_BOUNDS, doesn't belong here. */
247	PUSH_MTHD(push, NVC37D, HEAD_SET_HEAD_USAGE_BOUNDS(i),
248		  NVDEF(NVC37D, HEAD_SET_HEAD_USAGE_BOUNDS, CURSOR, USAGE_W256_H256) |
249		  NVDEF(NVC37D, HEAD_SET_HEAD_USAGE_BOUNDS, OUTPUT_LUT, USAGE_1025) |
250		  NVDEF(NVC37D, HEAD_SET_HEAD_USAGE_BOUNDS, UPSCALING_ALLOWED, TRUE));
251	return 0;
252}
253
254int
255headc37d_view(struct nv50_head *head, struct nv50_head_atom *asyh)
256{
257	struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
258	const int i = head->base.index;
259	int ret;
260
261	if ((ret = PUSH_WAIT(push, 4)))
262		return ret;
263
264	PUSH_MTHD(push, NVC37D, HEAD_SET_VIEWPORT_SIZE_IN(i),
265		  NVVAL(NVC37D, HEAD_SET_VIEWPORT_SIZE_IN, WIDTH, asyh->view.iW) |
266		  NVVAL(NVC37D, HEAD_SET_VIEWPORT_SIZE_IN, HEIGHT, asyh->view.iH));
267
268	PUSH_MTHD(push, NVC37D, HEAD_SET_VIEWPORT_SIZE_OUT(i),
269		  NVVAL(NVC37D, HEAD_SET_VIEWPORT_SIZE_OUT, WIDTH, asyh->view.oW) |
270		  NVVAL(NVC37D, HEAD_SET_VIEWPORT_SIZE_OUT, HEIGHT, asyh->view.oH));
271	return 0;
272}
273
274void
275headc37d_static_wndw_map(struct nv50_head *head, struct nv50_head_atom *asyh)
276{
277	int i, end;
278
279	for (i = head->base.index * 2, end = i + 2; i < end; i++)
280		asyh->wndw.owned |= BIT(i);
281}
282
283const struct nv50_head_func
284headc37d = {
285	.view = headc37d_view,
286	.mode = headc37d_mode,
287	.olut = headc37d_olut,
288	.ilut_check = head907d_ilut_check,
289	.olut_size = 1024,
290	.olut_set = headc37d_olut_set,
291	.olut_clr = headc37d_olut_clr,
292	.curs_layout = head917d_curs_layout,
293	.curs_format = headc37d_curs_format,
294	.curs_set = headc37d_curs_set,
295	.curs_clr = headc37d_curs_clr,
296	.dither = headc37d_dither,
297	.procamp = headc37d_procamp,
298	.or = headc37d_or,
299	.static_wndw_map = headc37d_static_wndw_map,
300};
301