1 /* 2 * Copyright 2013 Ilia Mirkin 3 * 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: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 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 20 * SOFTWARE. 21 * 22 * Implementation based on the pre-KMS implementation in xf86-video-nouveau, 23 * written by Arthur Huillet. 24 */ 25 26 #include <drm/drm_crtc.h> 27 #include <drm/drm_fourcc.h> 28 29 #include "nouveau_drv.h" 30 31 #include "nouveau_bo.h" 32 #include "nouveau_connector.h" 33 #include "nouveau_display.h" 34 #include "nvreg.h" 35 #include "disp.h" 36 37 struct nouveau_plane { 38 struct drm_plane base; 39 bool flip; 40 struct nouveau_bo *cur; 41 42 struct { 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; 48 } props; 49 50 int colorkey; 51 int contrast; 52 int brightness; 53 int hue; 54 int saturation; 55 enum drm_color_encoding color_encoding; 56 57 void (*set_params)(struct nouveau_plane *); 58 }; 59 60 static uint32_t formats[] = { 61 DRM_FORMAT_YUYV, 62 DRM_FORMAT_UYVY, 63 DRM_FORMAT_NV12, 64 DRM_FORMAT_NV21, 65 }; 66 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) 72 */ 73 static inline int 74 sin_mul(int degrees, int factor) 75 { 76 if (degrees > 180) { 77 degrees -= 180; 78 factor *= -1; 79 } 80 return factor * 4 * degrees * (180 - degrees) / 81 (40500 - degrees * (180 - degrees)); 82 } 83 84 /* cos(x) = sin(x + 90) */ 85 static inline int 86 cos_mul(int degrees, int factor) 87 { 88 return sin_mul((degrees + 90) % 360, factor); 89 } 90 91 static int 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) 95 { 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); 99 return -ERANGE; 100 } 101 102 if (src_x != 0 || src_y != 0) { 103 DRM_DEBUG_KMS("Unsuitable framebuffer offset: %d,%d\n", 104 src_x, src_y); 105 return -ERANGE; 106 } 107 108 return 0; 109 } 110 111 static int 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) 118 { 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; 130 unsigned format = 0; 131 int ret; 132 133 /* Source parameters given in 16.16 fixed point, ignore fractional. */ 134 src_x >>= 16; 135 src_y >>= 16; 136 src_w >>= 16; 137 src_h >>= 16; 138 139 ret = verify_scaling(fb, shift, 0, 0, src_w, src_h, crtc_w, crtc_h); 140 if (ret) 141 return ret; 142 143 ret = nouveau_bo_pin(nv_fb->nvbo, TTM_PL_FLAG_VRAM, false); 144 if (ret) 145 return ret; 146 147 nv_plane->cur = nv_fb->nvbo; 148 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); 151 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); 160 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; 171 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]); 176 } 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; 182 183 if (cur) 184 nouveau_bo_unpin(cur); 185 186 return 0; 187 } 188 189 static int 190 nv10_disable_plane(struct drm_plane *plane, 191 struct drm_modeset_acquire_ctx *ctx) 192 { 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); 196 197 nvif_wr32(dev, NV_PVIDEO_STOP, 1); 198 if (nv_plane->cur) { 199 nouveau_bo_unpin(nv_plane->cur); 200 nv_plane->cur = NULL; 201 } 202 203 return 0; 204 } 205 206 static void 207 nv_destroy_plane(struct drm_plane *plane) 208 { 209 drm_plane_force_disable(plane); 210 drm_plane_cleanup(plane); 211 kfree(plane); 212 } 213 214 static void 215 nv10_set_params(struct nouveau_plane *plane) 216 { 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); 221 u32 format = 0; 222 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); 228 229 if (plane->cur) { 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, 237 format); 238 } 239 } 240 241 static int 242 nv_set_property(struct drm_plane *plane, 243 struct drm_property *property, 244 uint64_t value) 245 { 246 struct nouveau_plane *nv_plane = 247 container_of(plane, struct nouveau_plane, base); 248 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; 261 else 262 return -EINVAL; 263 264 if (nv_plane->set_params) 265 nv_plane->set_params(nv_plane); 266 return 0; 267 } 268 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, 274 }; 275 276 static void 277 nv10_overlay_init(struct drm_device *device) 278 { 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); 282 int ret; 283 284 if (!plane) 285 return; 286 287 switch (drm->client.device.info.chipset) { 288 case 0x10: 289 case 0x11: 290 case 0x15: 291 case 0x1a: 292 case 0x20: 293 num_formats = 2; 294 break; 295 } 296 297 ret = drm_plane_init(device, &plane->base, 3 /* both crtc's */, 298 &nv10_plane_funcs, 299 formats, num_formats, false); 300 if (ret) 301 goto err; 302 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 || 317 !plane->props.hue || 318 !plane->props.saturation) 319 goto cleanup; 320 321 plane->colorkey = 0; 322 drm_object_attach_property(&plane->base.base, 323 plane->props.colorkey, plane->colorkey); 324 325 plane->contrast = 0x1000; 326 drm_object_attach_property(&plane->base.base, 327 plane->props.contrast, plane->contrast); 328 329 plane->brightness = 512; 330 drm_object_attach_property(&plane->base.base, 331 plane->props.brightness, plane->brightness); 332 333 plane->hue = 0; 334 drm_object_attach_property(&plane->base.base, 335 plane->props.hue, plane->hue); 336 337 plane->saturation = 0x1000; 338 drm_object_attach_property(&plane->base.base, 339 plane->props.saturation, plane->saturation); 340 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); 348 349 plane->set_params = nv10_set_params; 350 nv10_set_params(plane); 351 drm_plane_force_disable(&plane->base); 352 return; 353 cleanup: 354 drm_plane_cleanup(&plane->base); 355 err: 356 kfree(plane); 357 NV_ERROR(drm, "Failed to create plane\n"); 358 } 359 360 static int 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) 367 { 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; 375 int ret, i; 376 377 /* Source parameters given in 16.16 fixed point, ignore fractional. */ 378 src_x >>= 16; 379 src_y >>= 16; 380 src_w >>= 16; 381 src_h >>= 16; 382 383 ret = verify_scaling(fb, 0, src_x, src_y, src_w, src_h, crtc_w, crtc_h); 384 if (ret) 385 return ret; 386 387 ret = nouveau_bo_pin(nv_fb->nvbo, TTM_PL_FLAG_VRAM, false); 388 if (ret) 389 return ret; 390 391 nv_plane->cur = nv_fb->nvbo; 392 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); 396 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, 401 fb->pitches[0]); 402 nvif_wr32(dev, NV_PVIDEO_BUFF0_OFFSET + 4 * i, 0); 403 } 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))); 408 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); 414 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) */ 417 418 nvif_wr32(dev, NV_PVIDEO_FIFO_BURST_LENGTH, 0x03); 419 nvif_wr32(dev, NV_PVIDEO_FIFO_THRES_SIZE, 0x38); 420 421 nvif_wr32(dev, NV_PVIDEO_KEY, nv_plane->colorkey); 422 423 if (nv_plane->colorkey & (1 << 24)) 424 overlay |= 0x10; 425 if (fb->format->format == DRM_FORMAT_YUYV) 426 overlay |= 0x100; 427 428 nvif_wr32(dev, NV_PVIDEO_OVERLAY, overlay); 429 430 nvif_wr32(dev, NV_PVIDEO_SU_STATE, nvif_rd32(dev, NV_PVIDEO_SU_STATE) ^ (1 << 16)); 431 432 if (cur) 433 nouveau_bo_unpin(cur); 434 435 return 0; 436 } 437 438 static int 439 nv04_disable_plane(struct drm_plane *plane, 440 struct drm_modeset_acquire_ctx *ctx) 441 { 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); 445 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); 450 if (nv_plane->cur) { 451 nouveau_bo_unpin(nv_plane->cur); 452 nv_plane->cur = NULL; 453 } 454 455 return 0; 456 } 457 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, 463 }; 464 465 static void 466 nv04_overlay_init(struct drm_device *device) 467 { 468 struct nouveau_drm *drm = nouveau_drm(device); 469 struct nouveau_plane *plane = kzalloc(sizeof(struct nouveau_plane), GFP_KERNEL); 470 int ret; 471 472 if (!plane) 473 return; 474 475 ret = drm_plane_init(device, &plane->base, 1 /* single crtc */, 476 &nv04_plane_funcs, 477 formats, 2, false); 478 if (ret) 479 goto err; 480 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) 488 goto cleanup; 489 490 plane->colorkey = 0; 491 drm_object_attach_property(&plane->base.base, 492 plane->props.colorkey, plane->colorkey); 493 494 plane->brightness = 512; 495 drm_object_attach_property(&plane->base.base, 496 plane->props.brightness, plane->brightness); 497 498 drm_plane_force_disable(&plane->base); 499 return; 500 cleanup: 501 drm_plane_cleanup(&plane->base); 502 err: 503 kfree(plane); 504 NV_ERROR(drm, "Failed to create plane\n"); 505 } 506 507 void 508 nouveau_overlay_init(struct drm_device *device) 509 { 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); 515 } 516