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(dev: 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(dev: 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(dev: 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(dev: 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(dev: 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(dev: 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(dev: 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(dev: 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(dev: 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(dev: 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(dev: 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(val: drm_color_lut_extract(user_input: in-> red, bit_precision: 14) + 0x6000, addr: mem + 0);
290 writew(val: drm_color_lut_extract(user_input: in->green, bit_precision: 14) + 0x6000, addr: mem + 2);
291 writew(val: drm_color_lut_extract(user_input: in-> blue, bit_precision: 14) + 0x6000, addr: 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(addr: mem - 8), addr: mem + 0);
298 writew(readw(addr: mem - 6), addr: mem + 2);
299 writew(readw(addr: mem - 4), addr: 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(dev: 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(dev: 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

source code of linux/drivers/gpu/drm/nouveau/dispnv50/head907d.c