1 /* $NetBSD: nouveau_dispnv04_overlay.c,v 1.5 2021/12/18 23:45:32 riastradh Exp $ */ 2 3 /* 4 * Copyright 2013 Ilia Mirkin 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 21 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 * 24 * Implementation based on the pre-KMS implementation in xf86-video-nouveau, 25 * written by Arthur Huillet. 26 */ 27 28 #include <sys/cdefs.h> 29 __KERNEL_RCSID(0, "$NetBSD: nouveau_dispnv04_overlay.c,v 1.5 2021/12/18 23:45:32 riastradh Exp $"); 30 31 #include <drm/drm_crtc.h> 32 #include <drm/drm_fourcc.h> 33 34 #include "nouveau_drv.h" 35 36 #include "nouveau_bo.h" 37 #include "nouveau_connector.h" 38 #include "nouveau_display.h" 39 #include "nvreg.h" 40 #include "disp.h" 41 #include <linux/nbsd-namespace.h> 42 43 struct nouveau_plane { 44 struct drm_plane base; 45 bool flip; 46 struct nouveau_bo *cur; 47 48 struct { 49 struct drm_property *colorkey; 50 struct drm_property *contrast; 51 struct drm_property *brightness; 52 struct drm_property *hue; 53 struct drm_property *saturation; 54 } props; 55 56 int colorkey; 57 int contrast; 58 int brightness; 59 int hue; 60 int saturation; 61 enum drm_color_encoding color_encoding; 62 63 void (*set_params)(struct nouveau_plane *); 64 }; 65 66 static const uint32_t formats[] = { 67 DRM_FORMAT_YUYV, 68 DRM_FORMAT_UYVY, 69 DRM_FORMAT_NV12, 70 DRM_FORMAT_NV21, 71 }; 72 73 /* Sine can be approximated with 74 * http://en.wikipedia.org/wiki/Bhaskara_I's_sine_approximation_formula 75 * sin(x degrees) ~= 4 x (180 - x) / (40500 - x (180 - x) ) 76 * Note that this only works for the range [0, 180]. 77 * Also note that sin(x) == -sin(x - 180) 78 */ 79 static inline int 80 sin_mul(int degrees, int factor) 81 { 82 if (degrees > 180) { 83 degrees -= 180; 84 factor *= -1; 85 } 86 return factor * 4 * degrees * (180 - degrees) / 87 (40500 - degrees * (180 - degrees)); 88 } 89 90 /* cos(x) = sin(x + 90) */ 91 static inline int 92 cos_mul(int degrees, int factor) 93 { 94 return sin_mul((degrees + 90) % 360, factor); 95 } 96 97 static int 98 verify_scaling(const struct drm_framebuffer *fb, uint8_t shift, 99 uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h, 100 uint32_t crtc_w, uint32_t crtc_h) 101 { 102 if (crtc_w < (src_w >> shift) || crtc_h < (src_h >> shift)) { 103 DRM_DEBUG_KMS("Unsuitable framebuffer scaling: %dx%d -> %dx%d\n", 104 src_w, src_h, crtc_w, crtc_h); 105 return -ERANGE; 106 } 107 108 if (src_x != 0 || src_y != 0) { 109 DRM_DEBUG_KMS("Unsuitable framebuffer offset: %d,%d\n", 110 src_x, src_y); 111 return -ERANGE; 112 } 113 114 return 0; 115 } 116 117 static int 118 nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, 119 struct drm_framebuffer *fb, int crtc_x, int crtc_y, 120 unsigned int crtc_w, unsigned int crtc_h, 121 uint32_t src_x, uint32_t src_y, 122 uint32_t src_w, uint32_t src_h, 123 struct drm_modeset_acquire_ctx *ctx) 124 { 125 struct nouveau_drm *drm = nouveau_drm(plane->dev); 126 struct nvif_object *dev = &drm->client.device.object; 127 struct nouveau_plane *nv_plane = 128 container_of(plane, struct nouveau_plane, base); 129 struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); 130 struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 131 struct nouveau_bo *cur = nv_plane->cur; 132 bool flip = nv_plane->flip; 133 int soff = NV_PCRTC0_SIZE * nv_crtc->index; 134 int soff2 = NV_PCRTC0_SIZE * !nv_crtc->index; 135 unsigned shift = drm->client.device.info.chipset >= 0x30 ? 1 : 3; 136 unsigned format = 0; 137 int ret; 138 139 /* Source parameters given in 16.16 fixed point, ignore fractional. */ 140 src_x >>= 16; 141 src_y >>= 16; 142 src_w >>= 16; 143 src_h >>= 16; 144 145 ret = verify_scaling(fb, shift, 0, 0, src_w, src_h, crtc_w, crtc_h); 146 if (ret) 147 return ret; 148 149 ret = nouveau_bo_pin(nv_fb->nvbo, TTM_PL_FLAG_VRAM, false); 150 if (ret) 151 return ret; 152 153 nv_plane->cur = nv_fb->nvbo; 154 155 nvif_mask(dev, NV_PCRTC_ENGINE_CTRL + soff, NV_CRTC_FSEL_OVERLAY, NV_CRTC_FSEL_OVERLAY); 156 nvif_mask(dev, NV_PCRTC_ENGINE_CTRL + soff2, NV_CRTC_FSEL_OVERLAY, 0); 157 158 nvif_wr32(dev, NV_PVIDEO_BASE(flip), 0); 159 nvif_wr32(dev, NV_PVIDEO_OFFSET_BUFF(flip), nv_fb->nvbo->bo.offset); 160 nvif_wr32(dev, NV_PVIDEO_SIZE_IN(flip), src_h << 16 | src_w); 161 nvif_wr32(dev, NV_PVIDEO_POINT_IN(flip), src_y << 16 | src_x); 162 nvif_wr32(dev, NV_PVIDEO_DS_DX(flip), (src_w << 20) / crtc_w); 163 nvif_wr32(dev, NV_PVIDEO_DT_DY(flip), (src_h << 20) / crtc_h); 164 nvif_wr32(dev, NV_PVIDEO_POINT_OUT(flip), crtc_y << 16 | crtc_x); 165 nvif_wr32(dev, NV_PVIDEO_SIZE_OUT(flip), crtc_h << 16 | crtc_w); 166 167 if (fb->format->format == DRM_FORMAT_YUYV || 168 fb->format->format == DRM_FORMAT_NV12) 169 format |= NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8; 170 if (fb->format->format == DRM_FORMAT_NV12 || 171 fb->format->format == DRM_FORMAT_NV21) 172 format |= NV_PVIDEO_FORMAT_PLANAR; 173 if (nv_plane->color_encoding == DRM_COLOR_YCBCR_BT709) 174 format |= NV_PVIDEO_FORMAT_MATRIX_ITURBT709; 175 if (nv_plane->colorkey & (1 << 24)) 176 format |= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY; 177 178 if (format & NV_PVIDEO_FORMAT_PLANAR) { 179 nvif_wr32(dev, NV_PVIDEO_UVPLANE_BASE(flip), 0); 180 nvif_wr32(dev, NV_PVIDEO_UVPLANE_OFFSET_BUFF(flip), 181 nv_fb->nvbo->bo.offset + fb->offsets[1]); 182 } 183 nvif_wr32(dev, NV_PVIDEO_FORMAT(flip), format | fb->pitches[0]); 184 nvif_wr32(dev, NV_PVIDEO_STOP, 0); 185 /* TODO: wait for vblank? */ 186 nvif_wr32(dev, NV_PVIDEO_BUFFER, flip ? 0x10 : 0x1); 187 nv_plane->flip = !flip; 188 189 if (cur) 190 nouveau_bo_unpin(cur); 191 192 return 0; 193 } 194 195 static int 196 nv10_disable_plane(struct drm_plane *plane, 197 struct drm_modeset_acquire_ctx *ctx) 198 { 199 struct nvif_object *dev = &nouveau_drm(plane->dev)->client.device.object; 200 struct nouveau_plane *nv_plane = 201 container_of(plane, struct nouveau_plane, base); 202 203 nvif_wr32(dev, NV_PVIDEO_STOP, 1); 204 if (nv_plane->cur) { 205 nouveau_bo_unpin(nv_plane->cur); 206 nv_plane->cur = NULL; 207 } 208 209 return 0; 210 } 211 212 static void 213 nv_destroy_plane(struct drm_plane *plane) 214 { 215 drm_plane_force_disable(plane); 216 drm_plane_cleanup(plane); 217 kfree(plane); 218 } 219 220 static void 221 nv10_set_params(struct nouveau_plane *plane) 222 { 223 struct nvif_object *dev = &nouveau_drm(plane->base.dev)->client.device.object; 224 u32 luma = (plane->brightness - 512) << 16 | plane->contrast; 225 u32 chroma = ((sin_mul(plane->hue, plane->saturation) & 0xffff) << 16) | 226 (cos_mul(plane->hue, plane->saturation) & 0xffff); 227 u32 format = 0; 228 229 nvif_wr32(dev, NV_PVIDEO_LUMINANCE(0), luma); 230 nvif_wr32(dev, NV_PVIDEO_LUMINANCE(1), luma); 231 nvif_wr32(dev, NV_PVIDEO_CHROMINANCE(0), chroma); 232 nvif_wr32(dev, NV_PVIDEO_CHROMINANCE(1), chroma); 233 nvif_wr32(dev, NV_PVIDEO_COLOR_KEY, plane->colorkey & 0xffffff); 234 235 if (plane->cur) { 236 if (plane->color_encoding == DRM_COLOR_YCBCR_BT709) 237 format |= NV_PVIDEO_FORMAT_MATRIX_ITURBT709; 238 if (plane->colorkey & (1 << 24)) 239 format |= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY; 240 nvif_mask(dev, NV_PVIDEO_FORMAT(plane->flip), 241 NV_PVIDEO_FORMAT_MATRIX_ITURBT709 | 242 NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY, 243 format); 244 } 245 } 246 247 static int 248 nv_set_property(struct drm_plane *plane, 249 struct drm_property *property, 250 uint64_t value) 251 { 252 struct nouveau_plane *nv_plane = 253 container_of(plane, struct nouveau_plane, base); 254 255 if (property == nv_plane->props.colorkey) 256 nv_plane->colorkey = value; 257 else if (property == nv_plane->props.contrast) 258 nv_plane->contrast = value; 259 else if (property == nv_plane->props.brightness) 260 nv_plane->brightness = value; 261 else if (property == nv_plane->props.hue) 262 nv_plane->hue = value; 263 else if (property == nv_plane->props.saturation) 264 nv_plane->saturation = value; 265 else if (property == nv_plane->base.color_encoding_property) 266 nv_plane->color_encoding = value; 267 else 268 return -EINVAL; 269 270 if (nv_plane->set_params) 271 nv_plane->set_params(nv_plane); 272 return 0; 273 } 274 275 static const struct drm_plane_funcs nv10_plane_funcs = { 276 .update_plane = nv10_update_plane, 277 .disable_plane = nv10_disable_plane, 278 .set_property = nv_set_property, 279 .destroy = nv_destroy_plane, 280 }; 281 282 static void 283 nv10_overlay_init(struct drm_device *device) 284 { 285 struct nouveau_drm *drm = nouveau_drm(device); 286 struct nouveau_plane *plane = kzalloc(sizeof(struct nouveau_plane), GFP_KERNEL); 287 unsigned int num_formats = ARRAY_SIZE(formats); 288 int ret; 289 290 if (!plane) 291 return; 292 293 switch (drm->client.device.info.chipset) { 294 case 0x10: 295 case 0x11: 296 case 0x15: 297 case 0x1a: 298 case 0x20: 299 num_formats = 2; 300 break; 301 } 302 303 ret = drm_plane_init(device, &plane->base, 3 /* both crtc's */, 304 &nv10_plane_funcs, 305 formats, num_formats, false); 306 if (ret) 307 goto err; 308 309 /* Set up the plane properties */ 310 plane->props.colorkey = drm_property_create_range( 311 device, 0, "colorkey", 0, 0x01ffffff); 312 plane->props.contrast = drm_property_create_range( 313 device, 0, "contrast", 0, 8192 - 1); 314 plane->props.brightness = drm_property_create_range( 315 device, 0, "brightness", 0, 1024); 316 plane->props.hue = drm_property_create_range( 317 device, 0, "hue", 0, 359); 318 plane->props.saturation = drm_property_create_range( 319 device, 0, "saturation", 0, 8192 - 1); 320 if (!plane->props.colorkey || 321 !plane->props.contrast || 322 !plane->props.brightness || 323 !plane->props.hue || 324 !plane->props.saturation) 325 goto cleanup; 326 327 plane->colorkey = 0; 328 drm_object_attach_property(&plane->base.base, 329 plane->props.colorkey, plane->colorkey); 330 331 plane->contrast = 0x1000; 332 drm_object_attach_property(&plane->base.base, 333 plane->props.contrast, plane->contrast); 334 335 plane->brightness = 512; 336 drm_object_attach_property(&plane->base.base, 337 plane->props.brightness, plane->brightness); 338 339 plane->hue = 0; 340 drm_object_attach_property(&plane->base.base, 341 plane->props.hue, plane->hue); 342 343 plane->saturation = 0x1000; 344 drm_object_attach_property(&plane->base.base, 345 plane->props.saturation, plane->saturation); 346 347 plane->color_encoding = DRM_COLOR_YCBCR_BT601; 348 drm_plane_create_color_properties(&plane->base, 349 BIT(DRM_COLOR_YCBCR_BT601) | 350 BIT(DRM_COLOR_YCBCR_BT709), 351 BIT(DRM_COLOR_YCBCR_LIMITED_RANGE), 352 DRM_COLOR_YCBCR_BT601, 353 DRM_COLOR_YCBCR_LIMITED_RANGE); 354 355 plane->set_params = nv10_set_params; 356 nv10_set_params(plane); 357 drm_plane_force_disable(&plane->base); 358 return; 359 cleanup: 360 drm_plane_cleanup(&plane->base); 361 err: 362 kfree(plane); 363 NV_ERROR(drm, "Failed to create plane\n"); 364 } 365 366 static int 367 nv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, 368 struct drm_framebuffer *fb, int crtc_x, int crtc_y, 369 unsigned int crtc_w, unsigned int crtc_h, 370 uint32_t src_x, uint32_t src_y, 371 uint32_t src_w, uint32_t src_h, 372 struct drm_modeset_acquire_ctx *ctx) 373 { 374 struct nvif_object *dev = &nouveau_drm(plane->dev)->client.device.object; 375 struct nouveau_plane *nv_plane = 376 container_of(plane, struct nouveau_plane, base); 377 struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); 378 struct nouveau_bo *cur = nv_plane->cur; 379 uint32_t overlay = 1; 380 int brightness = (nv_plane->brightness - 512) * 62 / 512; 381 int ret, i; 382 383 /* Source parameters given in 16.16 fixed point, ignore fractional. */ 384 src_x >>= 16; 385 src_y >>= 16; 386 src_w >>= 16; 387 src_h >>= 16; 388 389 ret = verify_scaling(fb, 0, src_x, src_y, src_w, src_h, crtc_w, crtc_h); 390 if (ret) 391 return ret; 392 393 ret = nouveau_bo_pin(nv_fb->nvbo, TTM_PL_FLAG_VRAM, false); 394 if (ret) 395 return ret; 396 397 nv_plane->cur = nv_fb->nvbo; 398 399 nvif_wr32(dev, NV_PVIDEO_OE_STATE, 0); 400 nvif_wr32(dev, NV_PVIDEO_SU_STATE, 0); 401 nvif_wr32(dev, NV_PVIDEO_RM_STATE, 0); 402 403 for (i = 0; i < 2; i++) { 404 nvif_wr32(dev, NV_PVIDEO_BUFF0_START_ADDRESS + 4 * i, 405 nv_fb->nvbo->bo.offset); 406 nvif_wr32(dev, NV_PVIDEO_BUFF0_PITCH_LENGTH + 4 * i, 407 fb->pitches[0]); 408 nvif_wr32(dev, NV_PVIDEO_BUFF0_OFFSET + 4 * i, 0); 409 } 410 nvif_wr32(dev, NV_PVIDEO_WINDOW_START, crtc_y << 16 | crtc_x); 411 nvif_wr32(dev, NV_PVIDEO_WINDOW_SIZE, crtc_h << 16 | crtc_w); 412 nvif_wr32(dev, NV_PVIDEO_STEP_SIZE, 413 (uint32_t)(((src_h - 1) << 11) / (crtc_h - 1)) << 16 | (uint32_t)(((src_w - 1) << 11) / (crtc_w - 1))); 414 415 /* It should be possible to convert hue/contrast to this */ 416 nvif_wr32(dev, NV_PVIDEO_RED_CSC_OFFSET, 0x69 - brightness); 417 nvif_wr32(dev, NV_PVIDEO_GREEN_CSC_OFFSET, 0x3e + brightness); 418 nvif_wr32(dev, NV_PVIDEO_BLUE_CSC_OFFSET, 0x89 - brightness); 419 nvif_wr32(dev, NV_PVIDEO_CSC_ADJUST, 0); 420 421 nvif_wr32(dev, NV_PVIDEO_CONTROL_Y, 0x001); /* (BLUR_ON, LINE_HALF) */ 422 nvif_wr32(dev, NV_PVIDEO_CONTROL_X, 0x111); /* (WEIGHT_HEAVY, SHARPENING_ON, SMOOTHING_ON) */ 423 424 nvif_wr32(dev, NV_PVIDEO_FIFO_BURST_LENGTH, 0x03); 425 nvif_wr32(dev, NV_PVIDEO_FIFO_THRES_SIZE, 0x38); 426 427 nvif_wr32(dev, NV_PVIDEO_KEY, nv_plane->colorkey); 428 429 if (nv_plane->colorkey & (1 << 24)) 430 overlay |= 0x10; 431 if (fb->format->format == DRM_FORMAT_YUYV) 432 overlay |= 0x100; 433 434 nvif_wr32(dev, NV_PVIDEO_OVERLAY, overlay); 435 436 nvif_wr32(dev, NV_PVIDEO_SU_STATE, nvif_rd32(dev, NV_PVIDEO_SU_STATE) ^ (1 << 16)); 437 438 if (cur) 439 nouveau_bo_unpin(cur); 440 441 return 0; 442 } 443 444 static int 445 nv04_disable_plane(struct drm_plane *plane, 446 struct drm_modeset_acquire_ctx *ctx) 447 { 448 struct nvif_object *dev = &nouveau_drm(plane->dev)->client.device.object; 449 struct nouveau_plane *nv_plane = 450 container_of(plane, struct nouveau_plane, base); 451 452 nvif_mask(dev, NV_PVIDEO_OVERLAY, 1, 0); 453 nvif_wr32(dev, NV_PVIDEO_OE_STATE, 0); 454 nvif_wr32(dev, NV_PVIDEO_SU_STATE, 0); 455 nvif_wr32(dev, NV_PVIDEO_RM_STATE, 0); 456 if (nv_plane->cur) { 457 nouveau_bo_unpin(nv_plane->cur); 458 nv_plane->cur = NULL; 459 } 460 461 return 0; 462 } 463 464 static const struct drm_plane_funcs nv04_plane_funcs = { 465 .update_plane = nv04_update_plane, 466 .disable_plane = nv04_disable_plane, 467 .set_property = nv_set_property, 468 .destroy = nv_destroy_plane, 469 }; 470 471 static void 472 nv04_overlay_init(struct drm_device *device) 473 { 474 struct nouveau_drm *drm = nouveau_drm(device); 475 struct nouveau_plane *plane = kzalloc(sizeof(struct nouveau_plane), GFP_KERNEL); 476 int ret; 477 478 if (!plane) 479 return; 480 481 ret = drm_plane_init(device, &plane->base, 1 /* single crtc */, 482 &nv04_plane_funcs, 483 formats, 2, false); 484 if (ret) 485 goto err; 486 487 /* Set up the plane properties */ 488 plane->props.colorkey = drm_property_create_range( 489 device, 0, "colorkey", 0, 0x01ffffff); 490 plane->props.brightness = drm_property_create_range( 491 device, 0, "brightness", 0, 1024); 492 if (!plane->props.colorkey || 493 !plane->props.brightness) 494 goto cleanup; 495 496 plane->colorkey = 0; 497 drm_object_attach_property(&plane->base.base, 498 plane->props.colorkey, plane->colorkey); 499 500 plane->brightness = 512; 501 drm_object_attach_property(&plane->base.base, 502 plane->props.brightness, plane->brightness); 503 504 drm_plane_force_disable(&plane->base); 505 return; 506 cleanup: 507 drm_plane_cleanup(&plane->base); 508 err: 509 kfree(plane); 510 NV_ERROR(drm, "Failed to create plane\n"); 511 } 512 513 void 514 nouveau_overlay_init(struct drm_device *device) 515 { 516 struct nvif_device *dev = &nouveau_drm(device)->client.device; 517 if (dev->info.chipset < 0x10) 518 nv04_overlay_init(device); 519 else if (dev->info.chipset <= 0x40) 520 nv10_overlay_init(device); 521 } 522