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.
26 #include <drm/drm_crtc.h>
27 #include <drm/drm_fourcc.h>
29 #include "nouveau_drv.h"
31 #include "nouveau_bo.h"
32 #include "nouveau_connector.h"
33 #include "nouveau_display.h"
37 struct nouveau_plane
{
38 struct drm_plane base
;
40 struct nouveau_bo
*cur
;
43 struct drm_property
*colorkey
;
44 struct drm_property
*contrast
;
45 struct drm_property
*brightness
;
46 struct drm_property
*hue
;
47 struct drm_property
*saturation
;
55 enum drm_color_encoding color_encoding
;
57 void (*set_params
)(struct nouveau_plane
*);
60 static uint32_t formats
[] = {
67 /* Sine can be approximated with
68 * http://en.wikipedia.org/wiki/Bhaskara_I's_sine_approximation_formula
69 * sin(x degrees) ~= 4 x (180 - x) / (40500 - x (180 - x) )
70 * Note that this only works for the range [0, 180].
71 * Also note that sin(x) == -sin(x - 180)
74 sin_mul(int degrees
, int factor
)
80 return factor
* 4 * degrees
* (180 - degrees
) /
81 (40500 - degrees
* (180 - degrees
));
84 /* cos(x) = sin(x + 90) */
86 cos_mul(int degrees
, int factor
)
88 return sin_mul((degrees
+ 90) % 360, factor
);
92 verify_scaling(const struct drm_framebuffer
*fb
, uint8_t shift
,
93 uint32_t src_x
, uint32_t src_y
, uint32_t src_w
, uint32_t src_h
,
94 uint32_t crtc_w
, uint32_t crtc_h
)
96 if (crtc_w
< (src_w
>> shift
) || crtc_h
< (src_h
>> shift
)) {
97 DRM_DEBUG_KMS("Unsuitable framebuffer scaling: %dx%d -> %dx%d\n",
98 src_w
, src_h
, crtc_w
, crtc_h
);
102 if (src_x
!= 0 || src_y
!= 0) {
103 DRM_DEBUG_KMS("Unsuitable framebuffer offset: %d,%d\n",
112 nv10_update_plane(struct drm_plane
*plane
, struct drm_crtc
*crtc
,
113 struct drm_framebuffer
*fb
, int crtc_x
, int crtc_y
,
114 unsigned int crtc_w
, unsigned int crtc_h
,
115 uint32_t src_x
, uint32_t src_y
,
116 uint32_t src_w
, uint32_t src_h
,
117 struct drm_modeset_acquire_ctx
*ctx
)
119 struct nouveau_drm
*drm
= nouveau_drm(plane
->dev
);
120 struct nvif_object
*dev
= &drm
->client
.device
.object
;
121 struct nouveau_plane
*nv_plane
=
122 container_of(plane
, struct nouveau_plane
, base
);
123 struct nouveau_framebuffer
*nv_fb
= nouveau_framebuffer(fb
);
124 struct nouveau_crtc
*nv_crtc
= nouveau_crtc(crtc
);
125 struct nouveau_bo
*cur
= nv_plane
->cur
;
126 bool flip
= nv_plane
->flip
;
127 int soff
= NV_PCRTC0_SIZE
* nv_crtc
->index
;
128 int soff2
= NV_PCRTC0_SIZE
* !nv_crtc
->index
;
129 unsigned shift
= drm
->client
.device
.info
.chipset
>= 0x30 ? 1 : 3;
133 /* Source parameters given in 16.16 fixed point, ignore fractional. */
139 ret
= verify_scaling(fb
, shift
, 0, 0, src_w
, src_h
, crtc_w
, crtc_h
);
143 ret
= nouveau_bo_pin(nv_fb
->nvbo
, TTM_PL_FLAG_VRAM
, false);
147 nv_plane
->cur
= nv_fb
->nvbo
;
149 nvif_mask(dev
, NV_PCRTC_ENGINE_CTRL
+ soff
, NV_CRTC_FSEL_OVERLAY
, NV_CRTC_FSEL_OVERLAY
);
150 nvif_mask(dev
, NV_PCRTC_ENGINE_CTRL
+ soff2
, NV_CRTC_FSEL_OVERLAY
, 0);
152 nvif_wr32(dev
, NV_PVIDEO_BASE(flip
), 0);
153 nvif_wr32(dev
, NV_PVIDEO_OFFSET_BUFF(flip
), nv_fb
->nvbo
->bo
.offset
);
154 nvif_wr32(dev
, NV_PVIDEO_SIZE_IN(flip
), src_h
<< 16 | src_w
);
155 nvif_wr32(dev
, NV_PVIDEO_POINT_IN(flip
), src_y
<< 16 | src_x
);
156 nvif_wr32(dev
, NV_PVIDEO_DS_DX(flip
), (src_w
<< 20) / crtc_w
);
157 nvif_wr32(dev
, NV_PVIDEO_DT_DY(flip
), (src_h
<< 20) / crtc_h
);
158 nvif_wr32(dev
, NV_PVIDEO_POINT_OUT(flip
), crtc_y
<< 16 | crtc_x
);
159 nvif_wr32(dev
, NV_PVIDEO_SIZE_OUT(flip
), crtc_h
<< 16 | crtc_w
);
161 if (fb
->format
->format
== DRM_FORMAT_YUYV
||
162 fb
->format
->format
== DRM_FORMAT_NV12
)
163 format
|= NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8
;
164 if (fb
->format
->format
== DRM_FORMAT_NV12
||
165 fb
->format
->format
== DRM_FORMAT_NV21
)
166 format
|= NV_PVIDEO_FORMAT_PLANAR
;
167 if (nv_plane
->color_encoding
== DRM_COLOR_YCBCR_BT709
)
168 format
|= NV_PVIDEO_FORMAT_MATRIX_ITURBT709
;
169 if (nv_plane
->colorkey
& (1 << 24))
170 format
|= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY
;
172 if (format
& NV_PVIDEO_FORMAT_PLANAR
) {
173 nvif_wr32(dev
, NV_PVIDEO_UVPLANE_BASE(flip
), 0);
174 nvif_wr32(dev
, NV_PVIDEO_UVPLANE_OFFSET_BUFF(flip
),
175 nv_fb
->nvbo
->bo
.offset
+ fb
->offsets
[1]);
177 nvif_wr32(dev
, NV_PVIDEO_FORMAT(flip
), format
| fb
->pitches
[0]);
178 nvif_wr32(dev
, NV_PVIDEO_STOP
, 0);
179 /* TODO: wait for vblank? */
180 nvif_wr32(dev
, NV_PVIDEO_BUFFER
, flip
? 0x10 : 0x1);
181 nv_plane
->flip
= !flip
;
184 nouveau_bo_unpin(cur
);
190 nv10_disable_plane(struct drm_plane
*plane
,
191 struct drm_modeset_acquire_ctx
*ctx
)
193 struct nvif_object
*dev
= &nouveau_drm(plane
->dev
)->client
.device
.object
;
194 struct nouveau_plane
*nv_plane
=
195 container_of(plane
, struct nouveau_plane
, base
);
197 nvif_wr32(dev
, NV_PVIDEO_STOP
, 1);
199 nouveau_bo_unpin(nv_plane
->cur
);
200 nv_plane
->cur
= NULL
;
207 nv_destroy_plane(struct drm_plane
*plane
)
209 drm_plane_force_disable(plane
);
210 drm_plane_cleanup(plane
);
215 nv10_set_params(struct nouveau_plane
*plane
)
217 struct nvif_object
*dev
= &nouveau_drm(plane
->base
.dev
)->client
.device
.object
;
218 u32 luma
= (plane
->brightness
- 512) << 16 | plane
->contrast
;
219 u32 chroma
= ((sin_mul(plane
->hue
, plane
->saturation
) & 0xffff) << 16) |
220 (cos_mul(plane
->hue
, plane
->saturation
) & 0xffff);
223 nvif_wr32(dev
, NV_PVIDEO_LUMINANCE(0), luma
);
224 nvif_wr32(dev
, NV_PVIDEO_LUMINANCE(1), luma
);
225 nvif_wr32(dev
, NV_PVIDEO_CHROMINANCE(0), chroma
);
226 nvif_wr32(dev
, NV_PVIDEO_CHROMINANCE(1), chroma
);
227 nvif_wr32(dev
, NV_PVIDEO_COLOR_KEY
, plane
->colorkey
& 0xffffff);
230 if (plane
->color_encoding
== DRM_COLOR_YCBCR_BT709
)
231 format
|= NV_PVIDEO_FORMAT_MATRIX_ITURBT709
;
232 if (plane
->colorkey
& (1 << 24))
233 format
|= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY
;
234 nvif_mask(dev
, NV_PVIDEO_FORMAT(plane
->flip
),
235 NV_PVIDEO_FORMAT_MATRIX_ITURBT709
|
236 NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY
,
242 nv_set_property(struct drm_plane
*plane
,
243 struct drm_property
*property
,
246 struct nouveau_plane
*nv_plane
=
247 container_of(plane
, struct nouveau_plane
, base
);
249 if (property
== nv_plane
->props
.colorkey
)
250 nv_plane
->colorkey
= value
;
251 else if (property
== nv_plane
->props
.contrast
)
252 nv_plane
->contrast
= value
;
253 else if (property
== nv_plane
->props
.brightness
)
254 nv_plane
->brightness
= value
;
255 else if (property
== nv_plane
->props
.hue
)
256 nv_plane
->hue
= value
;
257 else if (property
== nv_plane
->props
.saturation
)
258 nv_plane
->saturation
= value
;
259 else if (property
== nv_plane
->base
.color_encoding_property
)
260 nv_plane
->color_encoding
= value
;
264 if (nv_plane
->set_params
)
265 nv_plane
->set_params(nv_plane
);
269 static const struct drm_plane_funcs nv10_plane_funcs
= {
270 .update_plane
= nv10_update_plane
,
271 .disable_plane
= nv10_disable_plane
,
272 .set_property
= nv_set_property
,
273 .destroy
= nv_destroy_plane
,
277 nv10_overlay_init(struct drm_device
*device
)
279 struct nouveau_drm
*drm
= nouveau_drm(device
);
280 struct nouveau_plane
*plane
= kzalloc(sizeof(struct nouveau_plane
), GFP_KERNEL
);
281 unsigned int num_formats
= ARRAY_SIZE(formats
);
287 switch (drm
->client
.device
.info
.chipset
) {
297 ret
= drm_plane_init(device
, &plane
->base
, 3 /* both crtc's */,
299 formats
, num_formats
, false);
303 /* Set up the plane properties */
304 plane
->props
.colorkey
= drm_property_create_range(
305 device
, 0, "colorkey", 0, 0x01ffffff);
306 plane
->props
.contrast
= drm_property_create_range(
307 device
, 0, "contrast", 0, 8192 - 1);
308 plane
->props
.brightness
= drm_property_create_range(
309 device
, 0, "brightness", 0, 1024);
310 plane
->props
.hue
= drm_property_create_range(
311 device
, 0, "hue", 0, 359);
312 plane
->props
.saturation
= drm_property_create_range(
313 device
, 0, "saturation", 0, 8192 - 1);
314 if (!plane
->props
.colorkey
||
315 !plane
->props
.contrast
||
316 !plane
->props
.brightness
||
318 !plane
->props
.saturation
)
322 drm_object_attach_property(&plane
->base
.base
,
323 plane
->props
.colorkey
, plane
->colorkey
);
325 plane
->contrast
= 0x1000;
326 drm_object_attach_property(&plane
->base
.base
,
327 plane
->props
.contrast
, plane
->contrast
);
329 plane
->brightness
= 512;
330 drm_object_attach_property(&plane
->base
.base
,
331 plane
->props
.brightness
, plane
->brightness
);
334 drm_object_attach_property(&plane
->base
.base
,
335 plane
->props
.hue
, plane
->hue
);
337 plane
->saturation
= 0x1000;
338 drm_object_attach_property(&plane
->base
.base
,
339 plane
->props
.saturation
, plane
->saturation
);
341 plane
->color_encoding
= DRM_COLOR_YCBCR_BT601
;
342 drm_plane_create_color_properties(&plane
->base
,
343 BIT(DRM_COLOR_YCBCR_BT601
) |
344 BIT(DRM_COLOR_YCBCR_BT709
),
345 BIT(DRM_COLOR_YCBCR_LIMITED_RANGE
),
346 DRM_COLOR_YCBCR_BT601
,
347 DRM_COLOR_YCBCR_LIMITED_RANGE
);
349 plane
->set_params
= nv10_set_params
;
350 nv10_set_params(plane
);
351 drm_plane_force_disable(&plane
->base
);
354 drm_plane_cleanup(&plane
->base
);
357 NV_ERROR(drm
, "Failed to create plane\n");
361 nv04_update_plane(struct drm_plane
*plane
, struct drm_crtc
*crtc
,
362 struct drm_framebuffer
*fb
, int crtc_x
, int crtc_y
,
363 unsigned int crtc_w
, unsigned int crtc_h
,
364 uint32_t src_x
, uint32_t src_y
,
365 uint32_t src_w
, uint32_t src_h
,
366 struct drm_modeset_acquire_ctx
*ctx
)
368 struct nvif_object
*dev
= &nouveau_drm(plane
->dev
)->client
.device
.object
;
369 struct nouveau_plane
*nv_plane
=
370 container_of(plane
, struct nouveau_plane
, base
);
371 struct nouveau_framebuffer
*nv_fb
= nouveau_framebuffer(fb
);
372 struct nouveau_bo
*cur
= nv_plane
->cur
;
373 uint32_t overlay
= 1;
374 int brightness
= (nv_plane
->brightness
- 512) * 62 / 512;
377 /* Source parameters given in 16.16 fixed point, ignore fractional. */
383 ret
= verify_scaling(fb
, 0, src_x
, src_y
, src_w
, src_h
, crtc_w
, crtc_h
);
387 ret
= nouveau_bo_pin(nv_fb
->nvbo
, TTM_PL_FLAG_VRAM
, false);
391 nv_plane
->cur
= nv_fb
->nvbo
;
393 nvif_wr32(dev
, NV_PVIDEO_OE_STATE
, 0);
394 nvif_wr32(dev
, NV_PVIDEO_SU_STATE
, 0);
395 nvif_wr32(dev
, NV_PVIDEO_RM_STATE
, 0);
397 for (i
= 0; i
< 2; i
++) {
398 nvif_wr32(dev
, NV_PVIDEO_BUFF0_START_ADDRESS
+ 4 * i
,
399 nv_fb
->nvbo
->bo
.offset
);
400 nvif_wr32(dev
, NV_PVIDEO_BUFF0_PITCH_LENGTH
+ 4 * i
,
402 nvif_wr32(dev
, NV_PVIDEO_BUFF0_OFFSET
+ 4 * i
, 0);
404 nvif_wr32(dev
, NV_PVIDEO_WINDOW_START
, crtc_y
<< 16 | crtc_x
);
405 nvif_wr32(dev
, NV_PVIDEO_WINDOW_SIZE
, crtc_h
<< 16 | crtc_w
);
406 nvif_wr32(dev
, NV_PVIDEO_STEP_SIZE
,
407 (uint32_t)(((src_h
- 1) << 11) / (crtc_h
- 1)) << 16 | (uint32_t)(((src_w
- 1) << 11) / (crtc_w
- 1)));
409 /* It should be possible to convert hue/contrast to this */
410 nvif_wr32(dev
, NV_PVIDEO_RED_CSC_OFFSET
, 0x69 - brightness
);
411 nvif_wr32(dev
, NV_PVIDEO_GREEN_CSC_OFFSET
, 0x3e + brightness
);
412 nvif_wr32(dev
, NV_PVIDEO_BLUE_CSC_OFFSET
, 0x89 - brightness
);
413 nvif_wr32(dev
, NV_PVIDEO_CSC_ADJUST
, 0);
415 nvif_wr32(dev
, NV_PVIDEO_CONTROL_Y
, 0x001); /* (BLUR_ON, LINE_HALF) */
416 nvif_wr32(dev
, NV_PVIDEO_CONTROL_X
, 0x111); /* (WEIGHT_HEAVY, SHARPENING_ON, SMOOTHING_ON) */
418 nvif_wr32(dev
, NV_PVIDEO_FIFO_BURST_LENGTH
, 0x03);
419 nvif_wr32(dev
, NV_PVIDEO_FIFO_THRES_SIZE
, 0x38);
421 nvif_wr32(dev
, NV_PVIDEO_KEY
, nv_plane
->colorkey
);
423 if (nv_plane
->colorkey
& (1 << 24))
425 if (fb
->format
->format
== DRM_FORMAT_YUYV
)
428 nvif_wr32(dev
, NV_PVIDEO_OVERLAY
, overlay
);
430 nvif_wr32(dev
, NV_PVIDEO_SU_STATE
, nvif_rd32(dev
, NV_PVIDEO_SU_STATE
) ^ (1 << 16));
433 nouveau_bo_unpin(cur
);
439 nv04_disable_plane(struct drm_plane
*plane
,
440 struct drm_modeset_acquire_ctx
*ctx
)
442 struct nvif_object
*dev
= &nouveau_drm(plane
->dev
)->client
.device
.object
;
443 struct nouveau_plane
*nv_plane
=
444 container_of(plane
, struct nouveau_plane
, base
);
446 nvif_mask(dev
, NV_PVIDEO_OVERLAY
, 1, 0);
447 nvif_wr32(dev
, NV_PVIDEO_OE_STATE
, 0);
448 nvif_wr32(dev
, NV_PVIDEO_SU_STATE
, 0);
449 nvif_wr32(dev
, NV_PVIDEO_RM_STATE
, 0);
451 nouveau_bo_unpin(nv_plane
->cur
);
452 nv_plane
->cur
= NULL
;
458 static const struct drm_plane_funcs nv04_plane_funcs
= {
459 .update_plane
= nv04_update_plane
,
460 .disable_plane
= nv04_disable_plane
,
461 .set_property
= nv_set_property
,
462 .destroy
= nv_destroy_plane
,
466 nv04_overlay_init(struct drm_device
*device
)
468 struct nouveau_drm
*drm
= nouveau_drm(device
);
469 struct nouveau_plane
*plane
= kzalloc(sizeof(struct nouveau_plane
), GFP_KERNEL
);
475 ret
= drm_plane_init(device
, &plane
->base
, 1 /* single crtc */,
481 /* Set up the plane properties */
482 plane
->props
.colorkey
= drm_property_create_range(
483 device
, 0, "colorkey", 0, 0x01ffffff);
484 plane
->props
.brightness
= drm_property_create_range(
485 device
, 0, "brightness", 0, 1024);
486 if (!plane
->props
.colorkey
||
487 !plane
->props
.brightness
)
491 drm_object_attach_property(&plane
->base
.base
,
492 plane
->props
.colorkey
, plane
->colorkey
);
494 plane
->brightness
= 512;
495 drm_object_attach_property(&plane
->base
.base
,
496 plane
->props
.brightness
, plane
->brightness
);
498 drm_plane_force_disable(&plane
->base
);
501 drm_plane_cleanup(&plane
->base
);
504 NV_ERROR(drm
, "Failed to create plane\n");
508 nouveau_overlay_init(struct drm_device
*device
)
510 struct nvif_device
*dev
= &nouveau_drm(device
)->client
.device
;
511 if (dev
->info
.chipset
< 0x10)
512 nv04_overlay_init(device
);
513 else if (dev
->info
.chipset
<= 0x40)
514 nv10_overlay_init(device
);