WIP FPC-III support
[linux/fpc-iii.git] / drivers / gpu / drm / nouveau / dispnv50 / head907d.c
blob8f860e9c52247c8a178393820148a09d4b9074b7
1 /*
2 * Copyright 2018 Red Hat Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
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"
32 #include <nvif/push507c.h>
34 #include <nvhw/class/cl907d.h>
36 int
37 head907d_or(struct nv50_head *head, struct nv50_head_atom *asyh)
39 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
40 const int i = head->base.index;
41 int ret;
43 if ((ret = PUSH_WAIT(push, 3)))
44 return ret;
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),
52 HEAD_SET_CONTROL(i), 0x31ec6000 | head->base.index << 25 |
53 NVVAL(NV907D, HEAD_SET_CONTROL, STRUCTURE, asyh->mode.interlace));
54 return 0;
57 int
58 head907d_procamp(struct nv50_head *head, struct nv50_head_atom *asyh)
60 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
61 const int i = head->base.index;
62 int ret;
64 if ((ret = PUSH_WAIT(push, 2)))
65 return ret;
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;
77 static int
78 head907d_dither(struct nv50_head *head, struct nv50_head_atom *asyh)
80 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
81 const int i = head->base.index;
82 int ret;
84 if ((ret = PUSH_WAIT(push, 2)))
85 return ret;
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;
95 int
96 head907d_ovly(struct nv50_head *head, struct nv50_head_atom *asyh)
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;
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;
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);
117 if ((ret = PUSH_WAIT(push, 2)))
118 return ret;
120 PUSH_MTHD(push, NV907D, HEAD_SET_OVERLAY_USAGE_BOUNDS(i), bounds);
121 return 0;
124 static int
125 head907d_base(struct nv50_head *head, struct nv50_head_atom *asyh)
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;
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;
142 bounds |= NVDEF(NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, USABLE, TRUE);
145 if ((ret = PUSH_WAIT(push, 2)))
146 return ret;
148 PUSH_MTHD(push, NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS(i), bounds);
149 return 0;
153 head907d_curs_clr(struct nv50_head *head)
155 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
156 const int i = head->base.index;
157 int ret;
159 if ((ret = PUSH_WAIT(push, 4)))
160 return ret;
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));
167 PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_CURSOR(i), 0x00000000);
168 return 0;
172 head907d_curs_set(struct nv50_head *head, struct nv50_head_atom *asyh)
174 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
175 const int i = head->base.index;
176 int ret;
178 if ((ret = PUSH_WAIT(push, 5)))
179 return ret;
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),
189 HEAD_SET_OFFSET_CURSOR(i), asyh->curs.offset >> 8);
191 PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_CURSOR(i), asyh->curs.handle);
192 return 0;
196 head907d_core_clr(struct nv50_head *head)
198 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
199 const int i = head->base.index;
200 int ret;
202 if ((ret = PUSH_WAIT(push, 2)))
203 return ret;
205 PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMAS_ISO(i), 0x00000000);
206 return 0;
210 head907d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
212 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
213 const int i = head->base.index;
214 int ret;
216 if ((ret = PUSH_WAIT(push, 9)))
217 return ret;
219 PUSH_MTHD(push, NV907D, HEAD_SET_OFFSET(i),
220 NVVAL(NV907D, HEAD_SET_OFFSET, ORIGIN, asyh->core.offset >> 8));
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),
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),
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),
237 HEAD_SET_CONTEXT_DMAS_ISO(i),
238 NVVAL(NV907D, HEAD_SET_CONTEXT_DMAS_ISO, HANDLE, asyh->core.handle));
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;
247 head907d_olut_clr(struct nv50_head *head)
249 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
250 const int i = head->base.index;
251 int ret;
253 if ((ret = PUSH_WAIT(push, 4)))
254 return ret;
256 PUSH_MTHD(push, NV907D, HEAD_SET_OUTPUT_LUT_LO(i),
257 NVDEF(NV907D, HEAD_SET_OUTPUT_LUT_LO, ENABLE, DISABLE));
259 PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_LUT(i), 0x00000000);
260 return 0;
264 head907d_olut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
266 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
267 const int i = head->base.index;
268 int ret;
270 if ((ret = PUSH_WAIT(push, 5)))
271 return ret;
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),
278 HEAD_SET_OUTPUT_LUT_HI(i),
279 NVVAL(NV907D, HEAD_SET_OUTPUT_LUT_HI, ORIGIN, asyh->olut.offset >> 8));
281 PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_LUT(i), asyh->olut.handle);
282 return 0;
285 void
286 head907d_olut_load(struct drm_color_lut *in, int size, void __iomem *mem)
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);
294 /* INTERPOLATE modes require a "next" entry to interpolate with,
295 * so we replicate the last entry to deal with this for now.
297 writew(readw(mem - 8), mem + 0);
298 writew(readw(mem - 6), mem + 2);
299 writew(readw(mem - 4), mem + 4);
302 bool
303 head907d_olut(struct nv50_head *head, struct nv50_head_atom *asyh, int size)
305 if (size != 256 && size != 1024)
306 return false;
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;
313 asyh->olut.load = head907d_olut_load;
314 return true;
318 head907d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
320 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
321 struct nv50_head_mode *m = &asyh->mode;
322 const int i = head->base.index;
323 int ret;
325 if ((ret = PUSH_WAIT(push, 14)))
326 return ret;
328 PUSH_MTHD(push, NV907D, HEAD_SET_OVERSCAN_COLOR(i),
329 NVVAL(NV907D, HEAD_SET_OVERSCAN_COLOR, RED, 0) |
330 NVVAL(NV907D, HEAD_SET_OVERSCAN_COLOR, GRN, 0) |
331 NVVAL(NV907D, HEAD_SET_OVERSCAN_COLOR, BLU, 0),
333 HEAD_SET_RASTER_SIZE(i),
334 NVVAL(NV907D, HEAD_SET_RASTER_SIZE, WIDTH, m->h.active) |
335 NVVAL(NV907D, HEAD_SET_RASTER_SIZE, HEIGHT, m->v.active),
337 HEAD_SET_RASTER_SYNC_END(i),
338 NVVAL(NV907D, HEAD_SET_RASTER_SYNC_END, X, m->h.synce) |
339 NVVAL(NV907D, HEAD_SET_RASTER_SYNC_END, Y, m->v.synce),
341 HEAD_SET_RASTER_BLANK_END(i),
342 NVVAL(NV907D, HEAD_SET_RASTER_BLANK_END, X, m->h.blanke) |
343 NVVAL(NV907D, HEAD_SET_RASTER_BLANK_END, Y, m->v.blanke),
345 HEAD_SET_RASTER_BLANK_START(i),
346 NVVAL(NV907D, HEAD_SET_RASTER_BLANK_START, X, m->h.blanks) |
347 NVVAL(NV907D, HEAD_SET_RASTER_BLANK_START, Y, m->v.blanks),
349 HEAD_SET_RASTER_VERT_BLANK2(i),
350 NVVAL(NV907D, HEAD_SET_RASTER_VERT_BLANK2, YSTART, m->v.blank2s) |
351 NVVAL(NV907D, HEAD_SET_RASTER_VERT_BLANK2, YEND, m->v.blank2e));
353 PUSH_MTHD(push, NV907D, HEAD_SET_DEFAULT_BASE_COLOR(i),
354 NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, RED, 0) |
355 NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, GREEN, 0) |
356 NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, BLUE, 0),
358 HEAD_SET_CRC_CONTROL(i),
359 NVDEF(NV907D, HEAD_SET_CRC_CONTROL, CONTROLLING_CHANNEL, CORE) |
360 NVDEF(NV907D, HEAD_SET_CRC_CONTROL, EXPECT_BUFFER_COLLAPSE, FALSE) |
361 NVDEF(NV907D, HEAD_SET_CRC_CONTROL, TIMESTAMP_MODE, FALSE) |
362 NVDEF(NV907D, HEAD_SET_CRC_CONTROL, PRIMARY_OUTPUT, NONE) |
363 NVDEF(NV907D, HEAD_SET_CRC_CONTROL, SECONDARY_OUTPUT, NONE));
365 PUSH_MTHD(push, NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY(i),
366 NVVAL(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY, HERTZ, m->clock * 1000) |
367 NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY, ADJ1000DIV1001, FALSE),
369 HEAD_SET_PIXEL_CLOCK_CONFIGURATION(i),
370 NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_CONFIGURATION, MODE, CLK_CUSTOM) |
371 NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_CONFIGURATION, NOT_DRIVER, FALSE) |
372 NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_CONFIGURATION, ENABLE_HOPPING, FALSE),
374 HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX(i),
375 NVVAL(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, HERTZ, m->clock * 1000) |
376 NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, ADJ1000DIV1001, FALSE));
377 return 0;
381 head907d_view(struct nv50_head *head, struct nv50_head_atom *asyh)
383 struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
384 const int i = head->base.index;
385 int ret;
387 if ((ret = PUSH_WAIT(push, 8)))
388 return ret;
390 PUSH_MTHD(push, NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER(i),
391 NVDEF(NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER, VERTICAL_TAPS, TAPS_1) |
392 NVDEF(NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER, HORIZONTAL_TAPS, TAPS_1) |
393 NVVAL(NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER, HRESPONSE_BIAS, 0) |
394 NVVAL(NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER, VRESPONSE_BIAS, 0));
396 PUSH_MTHD(push, NV907D, HEAD_SET_VIEWPORT_SIZE_IN(i),
397 NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_IN, WIDTH, asyh->view.iW) |
398 NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_IN, HEIGHT, asyh->view.iH));
400 PUSH_MTHD(push, NV907D, HEAD_SET_VIEWPORT_SIZE_OUT(i),
401 NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT, WIDTH, asyh->view.oW) |
402 NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT, HEIGHT, asyh->view.oH),
404 HEAD_SET_VIEWPORT_SIZE_OUT_MIN(i),
405 NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT_MIN, WIDTH, asyh->view.oW) |
406 NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT_MIN, HEIGHT, asyh->view.oH),
408 HEAD_SET_VIEWPORT_SIZE_OUT_MAX(i),
409 NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT_MAX, WIDTH, asyh->view.oW) |
410 NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT_MAX, HEIGHT, asyh->view.oH));
411 return 0;
414 const struct nv50_head_func
415 head907d = {
416 .view = head907d_view,
417 .mode = head907d_mode,
418 .olut = head907d_olut,
419 .olut_size = 1024,
420 .olut_set = head907d_olut_set,
421 .olut_clr = head907d_olut_clr,
422 .core_calc = head507d_core_calc,
423 .core_set = head907d_core_set,
424 .core_clr = head907d_core_clr,
425 .curs_layout = head507d_curs_layout,
426 .curs_format = head507d_curs_format,
427 .curs_set = head907d_curs_set,
428 .curs_clr = head907d_curs_clr,
429 .base = head907d_base,
430 .ovly = head907d_ovly,
431 .dither = head907d_dither,
432 .procamp = head907d_procamp,
433 .or = head907d_or,