root/drivers/gpu/drm/drm_color_mgmt.c

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

DEFINITIONS

This source file includes following definitions.
  1. drm_color_lut_extract
  2. drm_crtc_enable_color_mgmt
  3. drm_mode_crtc_set_gamma_size
  4. drm_mode_gamma_set_ioctl
  5. drm_mode_gamma_get_ioctl
  6. drm_get_color_encoding_name
  7. drm_get_color_range_name
  8. drm_plane_create_color_properties
  9. drm_color_lut_check

   1 /*
   2  * Copyright (c) 2016 Intel Corporation
   3  *
   4  * Permission to use, copy, modify, distribute, and sell this software and its
   5  * documentation for any purpose is hereby granted without fee, provided that
   6  * the above copyright notice appear in all copies and that both that copyright
   7  * notice and this permission notice appear in supporting documentation, and
   8  * that the name of the copyright holders not be used in advertising or
   9  * publicity pertaining to distribution of the software without specific,
  10  * written prior permission.  The copyright holders make no representations
  11  * about the suitability of this software for any purpose.  It is provided "as
  12  * is" without express or implied warranty.
  13  *
  14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
  16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  20  * OF THIS SOFTWARE.
  21  */
  22 
  23 #include <linux/uaccess.h>
  24 
  25 #include <drm/drm_color_mgmt.h>
  26 #include <drm/drm_crtc.h>
  27 #include <drm/drm_device.h>
  28 #include <drm/drm_drv.h>
  29 #include <drm/drm_print.h>
  30 
  31 #include "drm_crtc_internal.h"
  32 
  33 /**
  34  * DOC: overview
  35  *
  36  * Color management or color space adjustments is supported through a set of 5
  37  * properties on the &drm_crtc object. They are set up by calling
  38  * drm_crtc_enable_color_mgmt().
  39  *
  40  * "DEGAMMA_LUT”:
  41  *      Blob property to set the degamma lookup table (LUT) mapping pixel data
  42  *      from the framebuffer before it is given to the transformation matrix.
  43  *      The data is interpreted as an array of &struct drm_color_lut elements.
  44  *      Hardware might choose not to use the full precision of the LUT elements
  45  *      nor use all the elements of the LUT (for example the hardware might
  46  *      choose to interpolate between LUT[0] and LUT[4]).
  47  *
  48  *      Setting this to NULL (blob property value set to 0) means a
  49  *      linear/pass-thru gamma table should be used. This is generally the
  50  *      driver boot-up state too. Drivers can access this blob through
  51  *      &drm_crtc_state.degamma_lut.
  52  *
  53  * “DEGAMMA_LUT_SIZE”:
  54  *      Unsinged range property to give the size of the lookup table to be set
  55  *      on the DEGAMMA_LUT property (the size depends on the underlying
  56  *      hardware). If drivers support multiple LUT sizes then they should
  57  *      publish the largest size, and sub-sample smaller sized LUTs (e.g. for
  58  *      split-gamma modes) appropriately.
  59  *
  60  * “CTM”:
  61  *      Blob property to set the current transformation matrix (CTM) apply to
  62  *      pixel data after the lookup through the degamma LUT and before the
  63  *      lookup through the gamma LUT. The data is interpreted as a struct
  64  *      &drm_color_ctm.
  65  *
  66  *      Setting this to NULL (blob property value set to 0) means a
  67  *      unit/pass-thru matrix should be used. This is generally the driver
  68  *      boot-up state too. Drivers can access the blob for the color conversion
  69  *      matrix through &drm_crtc_state.ctm.
  70  *
  71  * “GAMMA_LUT”:
  72  *      Blob property to set the gamma lookup table (LUT) mapping pixel data
  73  *      after the transformation matrix to data sent to the connector. The
  74  *      data is interpreted as an array of &struct drm_color_lut elements.
  75  *      Hardware might choose not to use the full precision of the LUT elements
  76  *      nor use all the elements of the LUT (for example the hardware might
  77  *      choose to interpolate between LUT[0] and LUT[4]).
  78  *
  79  *      Setting this to NULL (blob property value set to 0) means a
  80  *      linear/pass-thru gamma table should be used. This is generally the
  81  *      driver boot-up state too. Drivers can access this blob through
  82  *      &drm_crtc_state.gamma_lut.
  83  *
  84  * “GAMMA_LUT_SIZE”:
  85  *      Unsigned range property to give the size of the lookup table to be set
  86  *      on the GAMMA_LUT property (the size depends on the underlying hardware).
  87  *      If drivers support multiple LUT sizes then they should publish the
  88  *      largest size, and sub-sample smaller sized LUTs (e.g. for split-gamma
  89  *      modes) appropriately.
  90  *
  91  * There is also support for a legacy gamma table, which is set up by calling
  92  * drm_mode_crtc_set_gamma_size(). Drivers which support both should use
  93  * drm_atomic_helper_legacy_gamma_set() to alias the legacy gamma ramp with the
  94  * "GAMMA_LUT" property above.
  95  *
  96  * Support for different non RGB color encodings is controlled through
  97  * &drm_plane specific COLOR_ENCODING and COLOR_RANGE properties. They
  98  * are set up by calling drm_plane_create_color_properties().
  99  *
 100  * "COLOR_ENCODING"
 101  *      Optional plane enum property to support different non RGB
 102  *      color encodings. The driver can provide a subset of standard
 103  *      enum values supported by the DRM plane.
 104  *
 105  * "COLOR_RANGE"
 106  *      Optional plane enum property to support different non RGB
 107  *      color parameter ranges. The driver can provide a subset of
 108  *      standard enum values supported by the DRM plane.
 109  */
 110 
 111 /**
 112  * drm_color_lut_extract - clamp and round LUT entries
 113  * @user_input: input value
 114  * @bit_precision: number of bits the hw LUT supports
 115  *
 116  * Extract a degamma/gamma LUT value provided by user (in the form of
 117  * &drm_color_lut entries) and round it to the precision supported by the
 118  * hardware.
 119  */
 120 uint32_t drm_color_lut_extract(uint32_t user_input, uint32_t bit_precision)
 121 {
 122         uint32_t val = user_input;
 123         uint32_t max = 0xffff >> (16 - bit_precision);
 124 
 125         /* Round only if we're not using full precision. */
 126         if (bit_precision < 16) {
 127                 val += 1UL << (16 - bit_precision - 1);
 128                 val >>= 16 - bit_precision;
 129         }
 130 
 131         return clamp_val(val, 0, max);
 132 }
 133 EXPORT_SYMBOL(drm_color_lut_extract);
 134 
 135 /**
 136  * drm_crtc_enable_color_mgmt - enable color management properties
 137  * @crtc: DRM CRTC
 138  * @degamma_lut_size: the size of the degamma lut (before CSC)
 139  * @has_ctm: whether to attach ctm_property for CSC matrix
 140  * @gamma_lut_size: the size of the gamma lut (after CSC)
 141  *
 142  * This function lets the driver enable the color correction
 143  * properties on a CRTC. This includes 3 degamma, csc and gamma
 144  * properties that userspace can set and 2 size properties to inform
 145  * the userspace of the lut sizes. Each of the properties are
 146  * optional. The gamma and degamma properties are only attached if
 147  * their size is not 0 and ctm_property is only attached if has_ctm is
 148  * true.
 149  *
 150  * Drivers should use drm_atomic_helper_legacy_gamma_set() to implement the
 151  * legacy &drm_crtc_funcs.gamma_set callback.
 152  */
 153 void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,
 154                                 uint degamma_lut_size,
 155                                 bool has_ctm,
 156                                 uint gamma_lut_size)
 157 {
 158         struct drm_device *dev = crtc->dev;
 159         struct drm_mode_config *config = &dev->mode_config;
 160 
 161         if (degamma_lut_size) {
 162                 drm_object_attach_property(&crtc->base,
 163                                            config->degamma_lut_property, 0);
 164                 drm_object_attach_property(&crtc->base,
 165                                            config->degamma_lut_size_property,
 166                                            degamma_lut_size);
 167         }
 168 
 169         if (has_ctm)
 170                 drm_object_attach_property(&crtc->base,
 171                                            config->ctm_property, 0);
 172 
 173         if (gamma_lut_size) {
 174                 drm_object_attach_property(&crtc->base,
 175                                            config->gamma_lut_property, 0);
 176                 drm_object_attach_property(&crtc->base,
 177                                            config->gamma_lut_size_property,
 178                                            gamma_lut_size);
 179         }
 180 }
 181 EXPORT_SYMBOL(drm_crtc_enable_color_mgmt);
 182 
 183 /**
 184  * drm_mode_crtc_set_gamma_size - set the gamma table size
 185  * @crtc: CRTC to set the gamma table size for
 186  * @gamma_size: size of the gamma table
 187  *
 188  * Drivers which support gamma tables should set this to the supported gamma
 189  * table size when initializing the CRTC. Currently the drm core only supports a
 190  * fixed gamma table size.
 191  *
 192  * Returns:
 193  * Zero on success, negative errno on failure.
 194  */
 195 int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
 196                                  int gamma_size)
 197 {
 198         uint16_t *r_base, *g_base, *b_base;
 199         int i;
 200 
 201         crtc->gamma_size = gamma_size;
 202 
 203         crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3,
 204                                     GFP_KERNEL);
 205         if (!crtc->gamma_store) {
 206                 crtc->gamma_size = 0;
 207                 return -ENOMEM;
 208         }
 209 
 210         r_base = crtc->gamma_store;
 211         g_base = r_base + gamma_size;
 212         b_base = g_base + gamma_size;
 213         for (i = 0; i < gamma_size; i++) {
 214                 r_base[i] = i << 8;
 215                 g_base[i] = i << 8;
 216                 b_base[i] = i << 8;
 217         }
 218 
 219 
 220         return 0;
 221 }
 222 EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
 223 
 224 /**
 225  * drm_mode_gamma_set_ioctl - set the gamma table
 226  * @dev: DRM device
 227  * @data: ioctl data
 228  * @file_priv: DRM file info
 229  *
 230  * Set the gamma table of a CRTC to the one passed in by the user. Userspace can
 231  * inquire the required gamma table size through drm_mode_gamma_get_ioctl.
 232  *
 233  * Called by the user via ioctl.
 234  *
 235  * Returns:
 236  * Zero on success, negative errno on failure.
 237  */
 238 int drm_mode_gamma_set_ioctl(struct drm_device *dev,
 239                              void *data, struct drm_file *file_priv)
 240 {
 241         struct drm_mode_crtc_lut *crtc_lut = data;
 242         struct drm_crtc *crtc;
 243         void *r_base, *g_base, *b_base;
 244         int size;
 245         struct drm_modeset_acquire_ctx ctx;
 246         int ret = 0;
 247 
 248         if (!drm_core_check_feature(dev, DRIVER_MODESET))
 249                 return -EOPNOTSUPP;
 250 
 251         crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
 252         if (!crtc)
 253                 return -ENOENT;
 254 
 255         if (crtc->funcs->gamma_set == NULL)
 256                 return -ENOSYS;
 257 
 258         /* memcpy into gamma store */
 259         if (crtc_lut->gamma_size != crtc->gamma_size)
 260                 return -EINVAL;
 261 
 262         DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
 263 
 264         size = crtc_lut->gamma_size * (sizeof(uint16_t));
 265         r_base = crtc->gamma_store;
 266         if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
 267                 ret = -EFAULT;
 268                 goto out;
 269         }
 270 
 271         g_base = r_base + size;
 272         if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
 273                 ret = -EFAULT;
 274                 goto out;
 275         }
 276 
 277         b_base = g_base + size;
 278         if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
 279                 ret = -EFAULT;
 280                 goto out;
 281         }
 282 
 283         ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base,
 284                                      crtc->gamma_size, &ctx);
 285 
 286 out:
 287         DRM_MODESET_LOCK_ALL_END(ctx, ret);
 288         return ret;
 289 
 290 }
 291 
 292 /**
 293  * drm_mode_gamma_get_ioctl - get the gamma table
 294  * @dev: DRM device
 295  * @data: ioctl data
 296  * @file_priv: DRM file info
 297  *
 298  * Copy the current gamma table into the storage provided. This also provides
 299  * the gamma table size the driver expects, which can be used to size the
 300  * allocated storage.
 301  *
 302  * Called by the user via ioctl.
 303  *
 304  * Returns:
 305  * Zero on success, negative errno on failure.
 306  */
 307 int drm_mode_gamma_get_ioctl(struct drm_device *dev,
 308                              void *data, struct drm_file *file_priv)
 309 {
 310         struct drm_mode_crtc_lut *crtc_lut = data;
 311         struct drm_crtc *crtc;
 312         void *r_base, *g_base, *b_base;
 313         int size;
 314         int ret = 0;
 315 
 316         if (!drm_core_check_feature(dev, DRIVER_MODESET))
 317                 return -EOPNOTSUPP;
 318 
 319         crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
 320         if (!crtc)
 321                 return -ENOENT;
 322 
 323         /* memcpy into gamma store */
 324         if (crtc_lut->gamma_size != crtc->gamma_size)
 325                 return -EINVAL;
 326 
 327         drm_modeset_lock(&crtc->mutex, NULL);
 328         size = crtc_lut->gamma_size * (sizeof(uint16_t));
 329         r_base = crtc->gamma_store;
 330         if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
 331                 ret = -EFAULT;
 332                 goto out;
 333         }
 334 
 335         g_base = r_base + size;
 336         if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
 337                 ret = -EFAULT;
 338                 goto out;
 339         }
 340 
 341         b_base = g_base + size;
 342         if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
 343                 ret = -EFAULT;
 344                 goto out;
 345         }
 346 out:
 347         drm_modeset_unlock(&crtc->mutex);
 348         return ret;
 349 }
 350 
 351 static const char * const color_encoding_name[] = {
 352         [DRM_COLOR_YCBCR_BT601] = "ITU-R BT.601 YCbCr",
 353         [DRM_COLOR_YCBCR_BT709] = "ITU-R BT.709 YCbCr",
 354         [DRM_COLOR_YCBCR_BT2020] = "ITU-R BT.2020 YCbCr",
 355 };
 356 
 357 static const char * const color_range_name[] = {
 358         [DRM_COLOR_YCBCR_FULL_RANGE] = "YCbCr full range",
 359         [DRM_COLOR_YCBCR_LIMITED_RANGE] = "YCbCr limited range",
 360 };
 361 
 362 /**
 363  * drm_get_color_encoding_name - return a string for color encoding
 364  * @encoding: color encoding to compute name of
 365  *
 366  * In contrast to the other drm_get_*_name functions this one here returns a
 367  * const pointer and hence is threadsafe.
 368  */
 369 const char *drm_get_color_encoding_name(enum drm_color_encoding encoding)
 370 {
 371         if (WARN_ON(encoding >= ARRAY_SIZE(color_encoding_name)))
 372                 return "unknown";
 373 
 374         return color_encoding_name[encoding];
 375 }
 376 
 377 /**
 378  * drm_get_color_range_name - return a string for color range
 379  * @range: color range to compute name of
 380  *
 381  * In contrast to the other drm_get_*_name functions this one here returns a
 382  * const pointer and hence is threadsafe.
 383  */
 384 const char *drm_get_color_range_name(enum drm_color_range range)
 385 {
 386         if (WARN_ON(range >= ARRAY_SIZE(color_range_name)))
 387                 return "unknown";
 388 
 389         return color_range_name[range];
 390 }
 391 
 392 /**
 393  * drm_plane_create_color_properties - color encoding related plane properties
 394  * @plane: plane object
 395  * @supported_encodings: bitfield indicating supported color encodings
 396  * @supported_ranges: bitfileld indicating supported color ranges
 397  * @default_encoding: default color encoding
 398  * @default_range: default color range
 399  *
 400  * Create and attach plane specific COLOR_ENCODING and COLOR_RANGE
 401  * properties to @plane. The supported encodings and ranges should
 402  * be provided in supported_encodings and supported_ranges bitmasks.
 403  * Each bit set in the bitmask indicates that its number as enum
 404  * value is supported.
 405  */
 406 int drm_plane_create_color_properties(struct drm_plane *plane,
 407                                       u32 supported_encodings,
 408                                       u32 supported_ranges,
 409                                       enum drm_color_encoding default_encoding,
 410                                       enum drm_color_range default_range)
 411 {
 412         struct drm_device *dev = plane->dev;
 413         struct drm_property *prop;
 414         struct drm_prop_enum_list enum_list[max_t(int, DRM_COLOR_ENCODING_MAX,
 415                                                        DRM_COLOR_RANGE_MAX)];
 416         int i, len;
 417 
 418         if (WARN_ON(supported_encodings == 0 ||
 419                     (supported_encodings & -BIT(DRM_COLOR_ENCODING_MAX)) != 0 ||
 420                     (supported_encodings & BIT(default_encoding)) == 0))
 421                 return -EINVAL;
 422 
 423         if (WARN_ON(supported_ranges == 0 ||
 424                     (supported_ranges & -BIT(DRM_COLOR_RANGE_MAX)) != 0 ||
 425                     (supported_ranges & BIT(default_range)) == 0))
 426                 return -EINVAL;
 427 
 428         len = 0;
 429         for (i = 0; i < DRM_COLOR_ENCODING_MAX; i++) {
 430                 if ((supported_encodings & BIT(i)) == 0)
 431                         continue;
 432 
 433                 enum_list[len].type = i;
 434                 enum_list[len].name = color_encoding_name[i];
 435                 len++;
 436         }
 437 
 438         prop = drm_property_create_enum(dev, 0, "COLOR_ENCODING",
 439                                         enum_list, len);
 440         if (!prop)
 441                 return -ENOMEM;
 442         plane->color_encoding_property = prop;
 443         drm_object_attach_property(&plane->base, prop, default_encoding);
 444         if (plane->state)
 445                 plane->state->color_encoding = default_encoding;
 446 
 447         len = 0;
 448         for (i = 0; i < DRM_COLOR_RANGE_MAX; i++) {
 449                 if ((supported_ranges & BIT(i)) == 0)
 450                         continue;
 451 
 452                 enum_list[len].type = i;
 453                 enum_list[len].name = color_range_name[i];
 454                 len++;
 455         }
 456 
 457         prop = drm_property_create_enum(dev, 0, "COLOR_RANGE",
 458                                         enum_list, len);
 459         if (!prop)
 460                 return -ENOMEM;
 461         plane->color_range_property = prop;
 462         drm_object_attach_property(&plane->base, prop, default_range);
 463         if (plane->state)
 464                 plane->state->color_range = default_range;
 465 
 466         return 0;
 467 }
 468 EXPORT_SYMBOL(drm_plane_create_color_properties);
 469 
 470 /**
 471  * drm_color_lut_check - check validity of lookup table
 472  * @lut: property blob containing LUT to check
 473  * @tests: bitmask of tests to run
 474  *
 475  * Helper to check whether a userspace-provided lookup table is valid and
 476  * satisfies hardware requirements.  Drivers pass a bitmask indicating which of
 477  * the tests in &drm_color_lut_tests should be performed.
 478  *
 479  * Returns 0 on success, -EINVAL on failure.
 480  */
 481 int drm_color_lut_check(const struct drm_property_blob *lut, u32 tests)
 482 {
 483         const struct drm_color_lut *entry;
 484         int i;
 485 
 486         if (!lut || !tests)
 487                 return 0;
 488 
 489         entry = lut->data;
 490         for (i = 0; i < drm_color_lut_size(lut); i++) {
 491                 if (tests & DRM_COLOR_LUT_EQUAL_CHANNELS) {
 492                         if (entry[i].red != entry[i].blue ||
 493                             entry[i].red != entry[i].green) {
 494                                 DRM_DEBUG_KMS("All LUT entries must have equal r/g/b\n");
 495                                 return -EINVAL;
 496                         }
 497                 }
 498 
 499                 if (i > 0 && tests & DRM_COLOR_LUT_NON_DECREASING) {
 500                         if (entry[i].red < entry[i - 1].red ||
 501                             entry[i].green < entry[i - 1].green ||
 502                             entry[i].blue < entry[i - 1].blue) {
 503                                 DRM_DEBUG_KMS("LUT entries must never decrease.\n");
 504                                 return -EINVAL;
 505                         }
 506                 }
 507         }
 508 
 509         return 0;
 510 }
 511 EXPORT_SYMBOL(drm_color_lut_check);

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