2 * Copyright 2013 Ilia Mirkin
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 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * Implementation based on the pre-KMS implementation in xf86-video-nouveau,
23 * written by Arthur Huillet.
27 #include <drm/drm_crtc.h>
28 #include <drm/drm_fourcc.h>
30 #include "nouveau_drm.h"
32 #include "nouveau_bo.h"
33 #include "nouveau_connector.h"
34 #include "nouveau_display.h"
38 struct nouveau_plane
{
39 struct drm_plane base
;
41 struct nouveau_bo
*cur
;
44 struct drm_property
*colorkey
;
45 struct drm_property
*contrast
;
46 struct drm_property
*brightness
;
47 struct drm_property
*hue
;
48 struct drm_property
*saturation
;
49 struct drm_property
*iturbt_709
;
59 void (*set_params
)(struct nouveau_plane
*);
62 static uint32_t formats
[] = {
68 /* Sine can be approximated with
69 * http://en.wikipedia.org/wiki/Bhaskara_I's_sine_approximation_formula
70 * sin(x degrees) ~= 4 x (180 - x) / (40500 - x (180 - x) )
71 * Note that this only works for the range [0, 180].
72 * Also note that sin(x) == -sin(x - 180)
75 sin_mul(int degrees
, int factor
)
81 return factor
* 4 * degrees
* (180 - degrees
) /
82 (40500 - degrees
* (180 - degrees
));
85 /* cos(x) = sin(x + 90) */
87 cos_mul(int degrees
, int factor
)
89 return sin_mul((degrees
+ 90) % 360, factor
);
93 nv10_update_plane(struct drm_plane
*plane
, struct drm_crtc
*crtc
,
94 struct drm_framebuffer
*fb
, int crtc_x
, int crtc_y
,
95 unsigned int crtc_w
, unsigned int crtc_h
,
96 uint32_t src_x
, uint32_t src_y
,
97 uint32_t src_w
, uint32_t src_h
)
99 struct nouveau_device
*dev
= nouveau_dev(plane
->dev
);
100 struct nouveau_plane
*nv_plane
= (struct nouveau_plane
*)plane
;
101 struct nouveau_framebuffer
*nv_fb
= nouveau_framebuffer(fb
);
102 struct nouveau_crtc
*nv_crtc
= nouveau_crtc(crtc
);
103 struct nouveau_bo
*cur
= nv_plane
->cur
;
104 bool flip
= nv_plane
->flip
;
105 int soff
= NV_PCRTC0_SIZE
* nv_crtc
->index
;
106 int soff2
= NV_PCRTC0_SIZE
* !nv_crtc
->index
;
109 /* Source parameters given in 16.16 fixed point, ignore fractional. */
115 format
= ALIGN(src_w
* 4, 0x100);
120 if (dev
->chipset
>= 0x30) {
121 if (crtc_w
< (src_w
>> 1) || crtc_h
< (src_h
>> 1))
124 if (crtc_w
< (src_w
>> 3) || crtc_h
< (src_h
>> 3))
128 ret
= nouveau_bo_pin(nv_fb
->nvbo
, TTM_PL_FLAG_VRAM
);
132 nv_plane
->cur
= nv_fb
->nvbo
;
134 nv_mask(dev
, NV_PCRTC_ENGINE_CTRL
+ soff
, NV_CRTC_FSEL_OVERLAY
, NV_CRTC_FSEL_OVERLAY
);
135 nv_mask(dev
, NV_PCRTC_ENGINE_CTRL
+ soff2
, NV_CRTC_FSEL_OVERLAY
, 0);
137 nv_wr32(dev
, NV_PVIDEO_BASE(flip
), 0);
138 nv_wr32(dev
, NV_PVIDEO_OFFSET_BUFF(flip
), nv_fb
->nvbo
->bo
.offset
);
139 nv_wr32(dev
, NV_PVIDEO_SIZE_IN(flip
), src_h
<< 16 | src_w
);
140 nv_wr32(dev
, NV_PVIDEO_POINT_IN(flip
), src_y
<< 16 | src_x
);
141 nv_wr32(dev
, NV_PVIDEO_DS_DX(flip
), (src_w
<< 20) / crtc_w
);
142 nv_wr32(dev
, NV_PVIDEO_DT_DY(flip
), (src_h
<< 20) / crtc_h
);
143 nv_wr32(dev
, NV_PVIDEO_POINT_OUT(flip
), crtc_y
<< 16 | crtc_x
);
144 nv_wr32(dev
, NV_PVIDEO_SIZE_OUT(flip
), crtc_h
<< 16 | crtc_w
);
146 if (fb
->pixel_format
!= DRM_FORMAT_UYVY
)
147 format
|= NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8
;
148 if (fb
->pixel_format
== DRM_FORMAT_NV12
)
149 format
|= NV_PVIDEO_FORMAT_PLANAR
;
150 if (nv_plane
->iturbt_709
)
151 format
|= NV_PVIDEO_FORMAT_MATRIX_ITURBT709
;
152 if (nv_plane
->colorkey
& (1 << 24))
153 format
|= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY
;
155 if (fb
->pixel_format
== DRM_FORMAT_NV12
) {
156 nv_wr32(dev
, NV_PVIDEO_UVPLANE_BASE(flip
), 0);
157 nv_wr32(dev
, NV_PVIDEO_UVPLANE_OFFSET_BUFF(flip
),
158 nv_fb
->nvbo
->bo
.offset
+ fb
->offsets
[1]);
160 nv_wr32(dev
, NV_PVIDEO_FORMAT(flip
), format
);
161 nv_wr32(dev
, NV_PVIDEO_STOP
, 0);
162 /* TODO: wait for vblank? */
163 nv_wr32(dev
, NV_PVIDEO_BUFFER
, flip
? 0x10 : 0x1);
164 nv_plane
->flip
= !flip
;
167 nouveau_bo_unpin(cur
);
173 nv10_disable_plane(struct drm_plane
*plane
)
175 struct nouveau_device
*dev
= nouveau_dev(plane
->dev
);
176 struct nouveau_plane
*nv_plane
= (struct nouveau_plane
*)plane
;
178 nv_wr32(dev
, NV_PVIDEO_STOP
, 1);
180 nouveau_bo_unpin(nv_plane
->cur
);
181 nv_plane
->cur
= NULL
;
188 nv_destroy_plane(struct drm_plane
*plane
)
190 plane
->funcs
->disable_plane(plane
);
191 drm_plane_cleanup(plane
);
196 nv10_set_params(struct nouveau_plane
*plane
)
198 struct nouveau_device
*dev
= nouveau_dev(plane
->base
.dev
);
199 u32 luma
= (plane
->brightness
- 512) << 16 | plane
->contrast
;
200 u32 chroma
= ((sin_mul(plane
->hue
, plane
->saturation
) & 0xffff) << 16) |
201 (cos_mul(plane
->hue
, plane
->saturation
) & 0xffff);
204 nv_wr32(dev
, NV_PVIDEO_LUMINANCE(0), luma
);
205 nv_wr32(dev
, NV_PVIDEO_LUMINANCE(1), luma
);
206 nv_wr32(dev
, NV_PVIDEO_CHROMINANCE(0), chroma
);
207 nv_wr32(dev
, NV_PVIDEO_CHROMINANCE(1), chroma
);
208 nv_wr32(dev
, NV_PVIDEO_COLOR_KEY
, plane
->colorkey
& 0xffffff);
211 if (plane
->iturbt_709
)
212 format
|= NV_PVIDEO_FORMAT_MATRIX_ITURBT709
;
213 if (plane
->colorkey
& (1 << 24))
214 format
|= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY
;
215 nv_mask(dev
, NV_PVIDEO_FORMAT(plane
->flip
),
216 NV_PVIDEO_FORMAT_MATRIX_ITURBT709
|
217 NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY
,
223 nv_set_property(struct drm_plane
*plane
,
224 struct drm_property
*property
,
227 struct nouveau_plane
*nv_plane
= (struct nouveau_plane
*)plane
;
229 if (property
== nv_plane
->props
.colorkey
)
230 nv_plane
->colorkey
= value
;
231 else if (property
== nv_plane
->props
.contrast
)
232 nv_plane
->contrast
= value
;
233 else if (property
== nv_plane
->props
.brightness
)
234 nv_plane
->brightness
= value
;
235 else if (property
== nv_plane
->props
.hue
)
236 nv_plane
->hue
= value
;
237 else if (property
== nv_plane
->props
.saturation
)
238 nv_plane
->saturation
= value
;
239 else if (property
== nv_plane
->props
.iturbt_709
)
240 nv_plane
->iturbt_709
= value
;
244 if (nv_plane
->set_params
)
245 nv_plane
->set_params(nv_plane
);
249 static const struct drm_plane_funcs nv10_plane_funcs
= {
250 .update_plane
= nv10_update_plane
,
251 .disable_plane
= nv10_disable_plane
,
252 .set_property
= nv_set_property
,
253 .destroy
= nv_destroy_plane
,
257 nv10_overlay_init(struct drm_device
*device
)
259 struct nouveau_device
*dev
= nouveau_dev(device
);
260 struct nouveau_plane
*plane
= kzalloc(sizeof(struct nouveau_plane
), GFP_KERNEL
);
261 int num_formats
= ARRAY_SIZE(formats
);
267 switch (dev
->chipset
) {
277 ret
= drm_plane_init(device
, &plane
->base
, 3 /* both crtc's */,
279 formats
, num_formats
, false);
283 /* Set up the plane properties */
284 plane
->props
.colorkey
= drm_property_create_range(
285 device
, 0, "colorkey", 0, 0x01ffffff);
286 plane
->props
.contrast
= drm_property_create_range(
287 device
, 0, "contrast", 0, 8192 - 1);
288 plane
->props
.brightness
= drm_property_create_range(
289 device
, 0, "brightness", 0, 1024);
290 plane
->props
.hue
= drm_property_create_range(
291 device
, 0, "hue", 0, 359);
292 plane
->props
.saturation
= drm_property_create_range(
293 device
, 0, "saturation", 0, 8192 - 1);
294 plane
->props
.iturbt_709
= drm_property_create_range(
295 device
, 0, "iturbt_709", 0, 1);
296 if (!plane
->props
.colorkey
||
297 !plane
->props
.contrast
||
298 !plane
->props
.brightness
||
300 !plane
->props
.saturation
||
301 !plane
->props
.iturbt_709
)
305 drm_object_attach_property(&plane
->base
.base
,
306 plane
->props
.colorkey
, plane
->colorkey
);
308 plane
->contrast
= 0x1000;
309 drm_object_attach_property(&plane
->base
.base
,
310 plane
->props
.contrast
, plane
->contrast
);
312 plane
->brightness
= 512;
313 drm_object_attach_property(&plane
->base
.base
,
314 plane
->props
.brightness
, plane
->brightness
);
317 drm_object_attach_property(&plane
->base
.base
,
318 plane
->props
.hue
, plane
->hue
);
320 plane
->saturation
= 0x1000;
321 drm_object_attach_property(&plane
->base
.base
,
322 plane
->props
.saturation
, plane
->saturation
);
324 plane
->iturbt_709
= 0;
325 drm_object_attach_property(&plane
->base
.base
,
326 plane
->props
.iturbt_709
, plane
->iturbt_709
);
328 plane
->set_params
= nv10_set_params
;
329 nv10_set_params(plane
);
330 nv10_disable_plane(&plane
->base
);
333 drm_plane_cleanup(&plane
->base
);
336 nv_error(dev
, "Failed to create plane\n");
340 nv04_update_plane(struct drm_plane
*plane
, struct drm_crtc
*crtc
,
341 struct drm_framebuffer
*fb
, int crtc_x
, int crtc_y
,
342 unsigned int crtc_w
, unsigned int crtc_h
,
343 uint32_t src_x
, uint32_t src_y
,
344 uint32_t src_w
, uint32_t src_h
)
346 struct nouveau_device
*dev
= nouveau_dev(plane
->dev
);
347 struct nouveau_plane
*nv_plane
= (struct nouveau_plane
*)plane
;
348 struct nouveau_framebuffer
*nv_fb
= nouveau_framebuffer(fb
);
349 struct nouveau_bo
*cur
= nv_plane
->cur
;
350 uint32_t overlay
= 1;
351 int brightness
= (nv_plane
->brightness
- 512) * 62 / 512;
354 /* Source parameters given in 16.16 fixed point, ignore fractional. */
360 pitch
= ALIGN(src_w
* 4, 0x100);
365 /* TODO: Compute an offset? Not sure how to do this for YUYV. */
366 if (src_x
!= 0 || src_y
!= 0)
369 if (crtc_w
< src_w
|| crtc_h
< src_h
)
372 ret
= nouveau_bo_pin(nv_fb
->nvbo
, TTM_PL_FLAG_VRAM
);
376 nv_plane
->cur
= nv_fb
->nvbo
;
378 nv_wr32(dev
, NV_PVIDEO_OE_STATE
, 0);
379 nv_wr32(dev
, NV_PVIDEO_SU_STATE
, 0);
380 nv_wr32(dev
, NV_PVIDEO_RM_STATE
, 0);
382 for (i
= 0; i
< 2; i
++) {
383 nv_wr32(dev
, NV_PVIDEO_BUFF0_START_ADDRESS
+ 4 * i
,
384 nv_fb
->nvbo
->bo
.offset
);
385 nv_wr32(dev
, NV_PVIDEO_BUFF0_PITCH_LENGTH
+ 4 * i
, pitch
);
386 nv_wr32(dev
, NV_PVIDEO_BUFF0_OFFSET
+ 4 * i
, 0);
388 nv_wr32(dev
, NV_PVIDEO_WINDOW_START
, crtc_y
<< 16 | crtc_x
);
389 nv_wr32(dev
, NV_PVIDEO_WINDOW_SIZE
, crtc_h
<< 16 | crtc_w
);
390 nv_wr32(dev
, NV_PVIDEO_STEP_SIZE
,
391 (uint32_t)(((src_h
- 1) << 11) / (crtc_h
- 1)) << 16 | (uint32_t)(((src_w
- 1) << 11) / (crtc_w
- 1)));
393 /* It should be possible to convert hue/contrast to this */
394 nv_wr32(dev
, NV_PVIDEO_RED_CSC_OFFSET
, 0x69 - brightness
);
395 nv_wr32(dev
, NV_PVIDEO_GREEN_CSC_OFFSET
, 0x3e + brightness
);
396 nv_wr32(dev
, NV_PVIDEO_BLUE_CSC_OFFSET
, 0x89 - brightness
);
397 nv_wr32(dev
, NV_PVIDEO_CSC_ADJUST
, 0);
399 nv_wr32(dev
, NV_PVIDEO_CONTROL_Y
, 0x001); /* (BLUR_ON, LINE_HALF) */
400 nv_wr32(dev
, NV_PVIDEO_CONTROL_X
, 0x111); /* (WEIGHT_HEAVY, SHARPENING_ON, SMOOTHING_ON) */
402 nv_wr32(dev
, NV_PVIDEO_FIFO_BURST_LENGTH
, 0x03);
403 nv_wr32(dev
, NV_PVIDEO_FIFO_THRES_SIZE
, 0x38);
405 nv_wr32(dev
, NV_PVIDEO_KEY
, nv_plane
->colorkey
);
407 if (nv_plane
->colorkey
& (1 << 24))
409 if (fb
->pixel_format
== DRM_FORMAT_YUYV
)
412 nv_wr32(dev
, NV_PVIDEO_OVERLAY
, overlay
);
414 nv_wr32(dev
, NV_PVIDEO_SU_STATE
, nv_rd32(dev
, NV_PVIDEO_SU_STATE
) ^ (1 << 16));
417 nouveau_bo_unpin(cur
);
423 nv04_disable_plane(struct drm_plane
*plane
)
425 struct nouveau_device
*dev
= nouveau_dev(plane
->dev
);
426 struct nouveau_plane
*nv_plane
= (struct nouveau_plane
*)plane
;
428 nv_mask(dev
, NV_PVIDEO_OVERLAY
, 1, 0);
429 nv_wr32(dev
, NV_PVIDEO_OE_STATE
, 0);
430 nv_wr32(dev
, NV_PVIDEO_SU_STATE
, 0);
431 nv_wr32(dev
, NV_PVIDEO_RM_STATE
, 0);
433 nouveau_bo_unpin(nv_plane
->cur
);
434 nv_plane
->cur
= NULL
;
440 static const struct drm_plane_funcs nv04_plane_funcs
= {
441 .update_plane
= nv04_update_plane
,
442 .disable_plane
= nv04_disable_plane
,
443 .set_property
= nv_set_property
,
444 .destroy
= nv_destroy_plane
,
448 nv04_overlay_init(struct drm_device
*device
)
450 struct nouveau_device
*dev
= nouveau_dev(device
);
451 struct nouveau_plane
*plane
= kzalloc(sizeof(struct nouveau_plane
), GFP_KERNEL
);
457 ret
= drm_plane_init(device
, &plane
->base
, 1 /* single crtc */,
463 /* Set up the plane properties */
464 plane
->props
.colorkey
= drm_property_create_range(
465 device
, 0, "colorkey", 0, 0x01ffffff);
466 plane
->props
.brightness
= drm_property_create_range(
467 device
, 0, "brightness", 0, 1024);
468 if (!plane
->props
.colorkey
||
469 !plane
->props
.brightness
)
473 drm_object_attach_property(&plane
->base
.base
,
474 plane
->props
.colorkey
, plane
->colorkey
);
476 plane
->brightness
= 512;
477 drm_object_attach_property(&plane
->base
.base
,
478 plane
->props
.brightness
, plane
->brightness
);
480 nv04_disable_plane(&plane
->base
);
483 drm_plane_cleanup(&plane
->base
);
486 nv_error(dev
, "Failed to create plane\n");
490 nouveau_overlay_init(struct drm_device
*device
)
492 struct nouveau_device
*dev
= nouveau_dev(device
);
493 if (dev
->chipset
< 0x10)
494 nv04_overlay_init(device
);
495 else if (dev
->chipset
<= 0x40)
496 nv10_overlay_init(device
);