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/drmP.h> 27 #include <drm/drm_crtc.h> 28 #include <drm/drm_fourcc.h> 29 30 #include "nouveau_drv.h" 31 32 #include "nouveau_bo.h" 33 #include "nouveau_connector.h" 34 #include "nouveau_display.h" 35 #include "nvreg.h" 36 #include "disp.h" 37 38 struct nouveau_plane { 39 struct drm_plane base; 40 bool flip; 41 struct nouveau_bo *cur; 42 43 struct { 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; 50 } props; 51 52 int colorkey; 53 int contrast; 54 int brightness; 55 int hue; 56 int saturation; 57 int iturbt_709; 58 59 void (*set_params)(struct nouveau_plane *); 60 }; 61 62 static uint32_t formats[] = { 63 DRM_FORMAT_YUYV, 64 DRM_FORMAT_UYVY, 65 DRM_FORMAT_NV12, 66 }; 67 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) 73 */ 74 static inline int 75 sin_mul(int degrees, int factor) 76 { 77 if (degrees > 180) { 78 degrees -= 180; 79 factor *= -1; 80 } 81 return factor * 4 * degrees * (180 - degrees) / 82 (40500 - degrees * (180 - degrees)); 83 } 84 85 /* cos(x) = sin(x + 90) */ 86 static inline int 87 cos_mul(int degrees, int factor) 88 { 89 return sin_mul((degrees + 90) % 360, factor); 90 } 91 92 static int 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) 98 { 99 struct nouveau_drm *drm = nouveau_drm(plane->dev); 100 struct nvif_object *dev = &drm->client.device.object; 101 struct nouveau_plane *nv_plane = 102 container_of(plane, struct nouveau_plane, base); 103 struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); 104 struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 105 struct nouveau_bo *cur = nv_plane->cur; 106 bool flip = nv_plane->flip; 107 int soff = NV_PCRTC0_SIZE * nv_crtc->index; 108 int soff2 = NV_PCRTC0_SIZE * !nv_crtc->index; 109 int format, ret; 110 111 /* Source parameters given in 16.16 fixed point, ignore fractional. */ 112 src_x >>= 16; 113 src_y >>= 16; 114 src_w >>= 16; 115 src_h >>= 16; 116 117 format = ALIGN(src_w * 4, 0x100); 118 119 if (format > 0xffff) 120 return -ERANGE; 121 122 if (drm->client.device.info.chipset >= 0x30) { 123 if (crtc_w < (src_w >> 1) || crtc_h < (src_h >> 1)) 124 return -ERANGE; 125 } else { 126 if (crtc_w < (src_w >> 3) || crtc_h < (src_h >> 3)) 127 return -ERANGE; 128 } 129 130 ret = nouveau_bo_pin(nv_fb->nvbo, TTM_PL_FLAG_VRAM, false); 131 if (ret) 132 return ret; 133 134 nv_plane->cur = nv_fb->nvbo; 135 136 nvif_mask(dev, NV_PCRTC_ENGINE_CTRL + soff, NV_CRTC_FSEL_OVERLAY, NV_CRTC_FSEL_OVERLAY); 137 nvif_mask(dev, NV_PCRTC_ENGINE_CTRL + soff2, NV_CRTC_FSEL_OVERLAY, 0); 138 139 nvif_wr32(dev, NV_PVIDEO_BASE(flip), 0); 140 nvif_wr32(dev, NV_PVIDEO_OFFSET_BUFF(flip), nv_fb->nvbo->bo.offset); 141 nvif_wr32(dev, NV_PVIDEO_SIZE_IN(flip), src_h << 16 | src_w); 142 nvif_wr32(dev, NV_PVIDEO_POINT_IN(flip), src_y << 16 | src_x); 143 nvif_wr32(dev, NV_PVIDEO_DS_DX(flip), (src_w << 20) / crtc_w); 144 nvif_wr32(dev, NV_PVIDEO_DT_DY(flip), (src_h << 20) / crtc_h); 145 nvif_wr32(dev, NV_PVIDEO_POINT_OUT(flip), crtc_y << 16 | crtc_x); 146 nvif_wr32(dev, NV_PVIDEO_SIZE_OUT(flip), crtc_h << 16 | crtc_w); 147 148 if (fb->format->format != DRM_FORMAT_UYVY) 149 format |= NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8; 150 if (fb->format->format == DRM_FORMAT_NV12) 151 format |= NV_PVIDEO_FORMAT_PLANAR; 152 if (nv_plane->iturbt_709) 153 format |= NV_PVIDEO_FORMAT_MATRIX_ITURBT709; 154 if (nv_plane->colorkey & (1 << 24)) 155 format |= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY; 156 157 if (fb->format->format == DRM_FORMAT_NV12) { 158 nvif_wr32(dev, NV_PVIDEO_UVPLANE_BASE(flip), 0); 159 nvif_wr32(dev, NV_PVIDEO_UVPLANE_OFFSET_BUFF(flip), 160 nv_fb->nvbo->bo.offset + fb->offsets[1]); 161 } 162 nvif_wr32(dev, NV_PVIDEO_FORMAT(flip), format); 163 nvif_wr32(dev, NV_PVIDEO_STOP, 0); 164 /* TODO: wait for vblank? */ 165 nvif_wr32(dev, NV_PVIDEO_BUFFER, flip ? 0x10 : 0x1); 166 nv_plane->flip = !flip; 167 168 if (cur) 169 nouveau_bo_unpin(cur); 170 171 return 0; 172 } 173 174 static int 175 nv10_disable_plane(struct drm_plane *plane) 176 { 177 struct nvif_object *dev = &nouveau_drm(plane->dev)->client.device.object; 178 struct nouveau_plane *nv_plane = 179 container_of(plane, struct nouveau_plane, base); 180 181 nvif_wr32(dev, NV_PVIDEO_STOP, 1); 182 if (nv_plane->cur) { 183 nouveau_bo_unpin(nv_plane->cur); 184 nv_plane->cur = NULL; 185 } 186 187 return 0; 188 } 189 190 static void 191 nv_destroy_plane(struct drm_plane *plane) 192 { 193 plane->funcs->disable_plane(plane); 194 drm_plane_cleanup(plane); 195 kfree(plane); 196 } 197 198 static void 199 nv10_set_params(struct nouveau_plane *plane) 200 { 201 struct nvif_object *dev = &nouveau_drm(plane->base.dev)->client.device.object; 202 u32 luma = (plane->brightness - 512) << 16 | plane->contrast; 203 u32 chroma = ((sin_mul(plane->hue, plane->saturation) & 0xffff) << 16) | 204 (cos_mul(plane->hue, plane->saturation) & 0xffff); 205 u32 format = 0; 206 207 nvif_wr32(dev, NV_PVIDEO_LUMINANCE(0), luma); 208 nvif_wr32(dev, NV_PVIDEO_LUMINANCE(1), luma); 209 nvif_wr32(dev, NV_PVIDEO_CHROMINANCE(0), chroma); 210 nvif_wr32(dev, NV_PVIDEO_CHROMINANCE(1), chroma); 211 nvif_wr32(dev, NV_PVIDEO_COLOR_KEY, plane->colorkey & 0xffffff); 212 213 if (plane->cur) { 214 if (plane->iturbt_709) 215 format |= NV_PVIDEO_FORMAT_MATRIX_ITURBT709; 216 if (plane->colorkey & (1 << 24)) 217 format |= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY; 218 nvif_mask(dev, NV_PVIDEO_FORMAT(plane->flip), 219 NV_PVIDEO_FORMAT_MATRIX_ITURBT709 | 220 NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY, 221 format); 222 } 223 } 224 225 static int 226 nv_set_property(struct drm_plane *plane, 227 struct drm_property *property, 228 uint64_t value) 229 { 230 struct nouveau_plane *nv_plane = 231 container_of(plane, struct nouveau_plane, base); 232 233 if (property == nv_plane->props.colorkey) 234 nv_plane->colorkey = value; 235 else if (property == nv_plane->props.contrast) 236 nv_plane->contrast = value; 237 else if (property == nv_plane->props.brightness) 238 nv_plane->brightness = value; 239 else if (property == nv_plane->props.hue) 240 nv_plane->hue = value; 241 else if (property == nv_plane->props.saturation) 242 nv_plane->saturation = value; 243 else if (property == nv_plane->props.iturbt_709) 244 nv_plane->iturbt_709 = value; 245 else 246 return -EINVAL; 247 248 if (nv_plane->set_params) 249 nv_plane->set_params(nv_plane); 250 return 0; 251 } 252 253 static const struct drm_plane_funcs nv10_plane_funcs = { 254 .update_plane = nv10_update_plane, 255 .disable_plane = nv10_disable_plane, 256 .set_property = nv_set_property, 257 .destroy = nv_destroy_plane, 258 }; 259 260 static void 261 nv10_overlay_init(struct drm_device *device) 262 { 263 struct nouveau_drm *drm = nouveau_drm(device); 264 struct nouveau_plane *plane = kzalloc(sizeof(struct nouveau_plane), GFP_KERNEL); 265 unsigned int num_formats = ARRAY_SIZE(formats); 266 int ret; 267 268 if (!plane) 269 return; 270 271 switch (drm->client.device.info.chipset) { 272 case 0x10: 273 case 0x11: 274 case 0x15: 275 case 0x1a: 276 case 0x20: 277 num_formats = 2; 278 break; 279 } 280 281 ret = drm_plane_init(device, &plane->base, 3 /* both crtc's */, 282 &nv10_plane_funcs, 283 formats, num_formats, false); 284 if (ret) 285 goto err; 286 287 /* Set up the plane properties */ 288 plane->props.colorkey = drm_property_create_range( 289 device, 0, "colorkey", 0, 0x01ffffff); 290 plane->props.contrast = drm_property_create_range( 291 device, 0, "contrast", 0, 8192 - 1); 292 plane->props.brightness = drm_property_create_range( 293 device, 0, "brightness", 0, 1024); 294 plane->props.hue = drm_property_create_range( 295 device, 0, "hue", 0, 359); 296 plane->props.saturation = drm_property_create_range( 297 device, 0, "saturation", 0, 8192 - 1); 298 plane->props.iturbt_709 = drm_property_create_range( 299 device, 0, "iturbt_709", 0, 1); 300 if (!plane->props.colorkey || 301 !plane->props.contrast || 302 !plane->props.brightness || 303 !plane->props.hue || 304 !plane->props.saturation || 305 !plane->props.iturbt_709) 306 goto cleanup; 307 308 plane->colorkey = 0; 309 drm_object_attach_property(&plane->base.base, 310 plane->props.colorkey, plane->colorkey); 311 312 plane->contrast = 0x1000; 313 drm_object_attach_property(&plane->base.base, 314 plane->props.contrast, plane->contrast); 315 316 plane->brightness = 512; 317 drm_object_attach_property(&plane->base.base, 318 plane->props.brightness, plane->brightness); 319 320 plane->hue = 0; 321 drm_object_attach_property(&plane->base.base, 322 plane->props.hue, plane->hue); 323 324 plane->saturation = 0x1000; 325 drm_object_attach_property(&plane->base.base, 326 plane->props.saturation, plane->saturation); 327 328 plane->iturbt_709 = 0; 329 drm_object_attach_property(&plane->base.base, 330 plane->props.iturbt_709, plane->iturbt_709); 331 332 plane->set_params = nv10_set_params; 333 nv10_set_params(plane); 334 nv10_disable_plane(&plane->base); 335 return; 336 cleanup: 337 drm_plane_cleanup(&plane->base); 338 err: 339 kfree(plane); 340 NV_ERROR(drm, "Failed to create plane\n"); 341 } 342 343 static int 344 nv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, 345 struct drm_framebuffer *fb, int crtc_x, int crtc_y, 346 unsigned int crtc_w, unsigned int crtc_h, 347 uint32_t src_x, uint32_t src_y, 348 uint32_t src_w, uint32_t src_h) 349 { 350 struct nvif_object *dev = &nouveau_drm(plane->dev)->client.device.object; 351 struct nouveau_plane *nv_plane = 352 container_of(plane, struct nouveau_plane, base); 353 struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); 354 struct nouveau_bo *cur = nv_plane->cur; 355 uint32_t overlay = 1; 356 int brightness = (nv_plane->brightness - 512) * 62 / 512; 357 int pitch, ret, i; 358 359 /* Source parameters given in 16.16 fixed point, ignore fractional. */ 360 src_x >>= 16; 361 src_y >>= 16; 362 src_w >>= 16; 363 src_h >>= 16; 364 365 pitch = ALIGN(src_w * 4, 0x100); 366 367 if (pitch > 0xffff) 368 return -ERANGE; 369 370 /* TODO: Compute an offset? Not sure how to do this for YUYV. */ 371 if (src_x != 0 || src_y != 0) 372 return -ERANGE; 373 374 if (crtc_w < src_w || crtc_h < src_h) 375 return -ERANGE; 376 377 ret = nouveau_bo_pin(nv_fb->nvbo, TTM_PL_FLAG_VRAM, false); 378 if (ret) 379 return ret; 380 381 nv_plane->cur = nv_fb->nvbo; 382 383 nvif_wr32(dev, NV_PVIDEO_OE_STATE, 0); 384 nvif_wr32(dev, NV_PVIDEO_SU_STATE, 0); 385 nvif_wr32(dev, NV_PVIDEO_RM_STATE, 0); 386 387 for (i = 0; i < 2; i++) { 388 nvif_wr32(dev, NV_PVIDEO_BUFF0_START_ADDRESS + 4 * i, 389 nv_fb->nvbo->bo.offset); 390 nvif_wr32(dev, NV_PVIDEO_BUFF0_PITCH_LENGTH + 4 * i, pitch); 391 nvif_wr32(dev, NV_PVIDEO_BUFF0_OFFSET + 4 * i, 0); 392 } 393 nvif_wr32(dev, NV_PVIDEO_WINDOW_START, crtc_y << 16 | crtc_x); 394 nvif_wr32(dev, NV_PVIDEO_WINDOW_SIZE, crtc_h << 16 | crtc_w); 395 nvif_wr32(dev, NV_PVIDEO_STEP_SIZE, 396 (uint32_t)(((src_h - 1) << 11) / (crtc_h - 1)) << 16 | (uint32_t)(((src_w - 1) << 11) / (crtc_w - 1))); 397 398 /* It should be possible to convert hue/contrast to this */ 399 nvif_wr32(dev, NV_PVIDEO_RED_CSC_OFFSET, 0x69 - brightness); 400 nvif_wr32(dev, NV_PVIDEO_GREEN_CSC_OFFSET, 0x3e + brightness); 401 nvif_wr32(dev, NV_PVIDEO_BLUE_CSC_OFFSET, 0x89 - brightness); 402 nvif_wr32(dev, NV_PVIDEO_CSC_ADJUST, 0); 403 404 nvif_wr32(dev, NV_PVIDEO_CONTROL_Y, 0x001); /* (BLUR_ON, LINE_HALF) */ 405 nvif_wr32(dev, NV_PVIDEO_CONTROL_X, 0x111); /* (WEIGHT_HEAVY, SHARPENING_ON, SMOOTHING_ON) */ 406 407 nvif_wr32(dev, NV_PVIDEO_FIFO_BURST_LENGTH, 0x03); 408 nvif_wr32(dev, NV_PVIDEO_FIFO_THRES_SIZE, 0x38); 409 410 nvif_wr32(dev, NV_PVIDEO_KEY, nv_plane->colorkey); 411 412 if (nv_plane->colorkey & (1 << 24)) 413 overlay |= 0x10; 414 if (fb->format->format == DRM_FORMAT_YUYV) 415 overlay |= 0x100; 416 417 nvif_wr32(dev, NV_PVIDEO_OVERLAY, overlay); 418 419 nvif_wr32(dev, NV_PVIDEO_SU_STATE, nvif_rd32(dev, NV_PVIDEO_SU_STATE) ^ (1 << 16)); 420 421 if (cur) 422 nouveau_bo_unpin(cur); 423 424 return 0; 425 } 426 427 static int 428 nv04_disable_plane(struct drm_plane *plane) 429 { 430 struct nvif_object *dev = &nouveau_drm(plane->dev)->client.device.object; 431 struct nouveau_plane *nv_plane = 432 container_of(plane, struct nouveau_plane, base); 433 434 nvif_mask(dev, NV_PVIDEO_OVERLAY, 1, 0); 435 nvif_wr32(dev, NV_PVIDEO_OE_STATE, 0); 436 nvif_wr32(dev, NV_PVIDEO_SU_STATE, 0); 437 nvif_wr32(dev, NV_PVIDEO_RM_STATE, 0); 438 if (nv_plane->cur) { 439 nouveau_bo_unpin(nv_plane->cur); 440 nv_plane->cur = NULL; 441 } 442 443 return 0; 444 } 445 446 static const struct drm_plane_funcs nv04_plane_funcs = { 447 .update_plane = nv04_update_plane, 448 .disable_plane = nv04_disable_plane, 449 .set_property = nv_set_property, 450 .destroy = nv_destroy_plane, 451 }; 452 453 static void 454 nv04_overlay_init(struct drm_device *device) 455 { 456 struct nouveau_drm *drm = nouveau_drm(device); 457 struct nouveau_plane *plane = kzalloc(sizeof(struct nouveau_plane), GFP_KERNEL); 458 int ret; 459 460 if (!plane) 461 return; 462 463 ret = drm_plane_init(device, &plane->base, 1 /* single crtc */, 464 &nv04_plane_funcs, 465 formats, 2, false); 466 if (ret) 467 goto err; 468 469 /* Set up the plane properties */ 470 plane->props.colorkey = drm_property_create_range( 471 device, 0, "colorkey", 0, 0x01ffffff); 472 plane->props.brightness = drm_property_create_range( 473 device, 0, "brightness", 0, 1024); 474 if (!plane->props.colorkey || 475 !plane->props.brightness) 476 goto cleanup; 477 478 plane->colorkey = 0; 479 drm_object_attach_property(&plane->base.base, 480 plane->props.colorkey, plane->colorkey); 481 482 plane->brightness = 512; 483 drm_object_attach_property(&plane->base.base, 484 plane->props.brightness, plane->brightness); 485 486 nv04_disable_plane(&plane->base); 487 return; 488 cleanup: 489 drm_plane_cleanup(&plane->base); 490 err: 491 kfree(plane); 492 NV_ERROR(drm, "Failed to create plane\n"); 493 } 494 495 void 496 nouveau_overlay_init(struct drm_device *device) 497 { 498 struct nvif_device *dev = &nouveau_drm(device)->client.device; 499 if (dev->info.chipset < 0x10) 500 nv04_overlay_init(device); 501 else if (dev->info.chipset <= 0x40) 502 nv10_overlay_init(device); 503 } 504