root/drivers/gpu/drm/nouveau/dispnv04/overlay.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sin_mul
  2. cos_mul
  3. verify_scaling
  4. nv10_update_plane
  5. nv10_disable_plane
  6. nv_destroy_plane
  7. nv10_set_params
  8. nv_set_property
  9. nv10_overlay_init
  10. nv04_update_plane
  11. nv04_disable_plane
  12. nv04_overlay_init
  13. nouveau_overlay_init

   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 }

/* [<][>][^][v][top][bottom][index][help] */