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"
32 #include <nvif/push507c.h>
34 #include <nvhw/class/cl907d.h>
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
;
43 if ((ret
= PUSH_WAIT(push
, 3)))
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
));
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
;
64 if ((ret
= PUSH_WAIT(push
, 2)))
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
));
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
;
84 if ((ret
= PUSH_WAIT(push
, 2)))
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));
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
;
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;
112 bounds
|= NVDEF(NV907D
, HEAD_SET_OVERLAY_USAGE_BOUNDS
, USABLE
, TRUE
);
114 bounds
|= NVDEF(NV907D
, HEAD_SET_OVERLAY_USAGE_BOUNDS
, PIXEL_DEPTH
, BPP_16
);
117 if ((ret
= PUSH_WAIT(push
, 2)))
120 PUSH_MTHD(push
, NV907D
, HEAD_SET_OVERLAY_USAGE_BOUNDS(i
), bounds
);
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
;
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;
142 bounds
|= NVDEF(NV907D
, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS
, USABLE
, TRUE
);
145 if ((ret
= PUSH_WAIT(push
, 2)))
148 PUSH_MTHD(push
, NV907D
, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS(i
), bounds
);
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
;
159 if ((ret
= PUSH_WAIT(push
, 4)))
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);
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
;
178 if ((ret
= PUSH_WAIT(push
, 5)))
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
);
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
;
202 if ((ret
= PUSH_WAIT(push
, 2)))
205 PUSH_MTHD(push
, NV907D
, HEAD_SET_CONTEXT_DMAS_ISO(i
), 0x00000000);
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
;
216 if ((ret
= PUSH_WAIT(push
, 9)))
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
),
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
),
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
));
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
;
253 if ((ret
= PUSH_WAIT(push
, 4)))
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);
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
;
270 if ((ret
= PUSH_WAIT(push
, 5)))
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
);
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);
303 head907d_olut(struct nv50_head
*head
, struct nv50_head_atom
*asyh
, int size
)
305 if (size
!= 256 && size
!= 1024)
309 asyh
->olut
.mode
= NV907D_HEAD_SET_OUTPUT_LUT_LO_MODE_INTERPOLATE_1025_UNITY_RANGE
;
311 asyh
->olut
.mode
= NV907D_HEAD_SET_OUTPUT_LUT_LO_MODE_INTERPOLATE_257_UNITY_RANGE
;
313 asyh
->olut
.load
= head907d_olut_load
;
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
;
325 if ((ret
= PUSH_WAIT(push
, 14)))
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
));
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
;
387 if ((ret
= PUSH_WAIT(push
, 8)))
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
));
414 const struct nv50_head_func
416 .view
= head907d_view
,
417 .mode
= head907d_mode
,
418 .olut
= head907d_olut
,
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
,