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