0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023 #include <linux/uaccess.h>
0024
0025 #include <drm/drm_atomic.h>
0026 #include <drm/drm_color_mgmt.h>
0027 #include <drm/drm_crtc.h>
0028 #include <drm/drm_device.h>
0029 #include <drm/drm_drv.h>
0030 #include <drm/drm_print.h>
0031
0032 #include "drm_crtc_internal.h"
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134 u64 drm_color_ctm_s31_32_to_qm_n(u64 user_input, u32 m, u32 n)
0135 {
0136 u64 mag = (user_input & ~BIT_ULL(63)) >> (32 - n);
0137 bool negative = !!(user_input & BIT_ULL(63));
0138 s64 val;
0139
0140 WARN_ON(m > 32 || n > 32);
0141
0142 val = clamp_val(mag, 0, negative ?
0143 BIT_ULL(n + m - 1) : BIT_ULL(n + m - 1) - 1);
0144
0145 return negative ? -val : val;
0146 }
0147 EXPORT_SYMBOL(drm_color_ctm_s31_32_to_qm_n);
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164 void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,
0165 uint degamma_lut_size,
0166 bool has_ctm,
0167 uint gamma_lut_size)
0168 {
0169 struct drm_device *dev = crtc->dev;
0170 struct drm_mode_config *config = &dev->mode_config;
0171
0172 if (degamma_lut_size) {
0173 drm_object_attach_property(&crtc->base,
0174 config->degamma_lut_property, 0);
0175 drm_object_attach_property(&crtc->base,
0176 config->degamma_lut_size_property,
0177 degamma_lut_size);
0178 }
0179
0180 if (has_ctm)
0181 drm_object_attach_property(&crtc->base,
0182 config->ctm_property, 0);
0183
0184 if (gamma_lut_size) {
0185 drm_object_attach_property(&crtc->base,
0186 config->gamma_lut_property, 0);
0187 drm_object_attach_property(&crtc->base,
0188 config->gamma_lut_size_property,
0189 gamma_lut_size);
0190 }
0191 }
0192 EXPORT_SYMBOL(drm_crtc_enable_color_mgmt);
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206 int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
0207 int gamma_size)
0208 {
0209 uint16_t *r_base, *g_base, *b_base;
0210 int i;
0211
0212 crtc->gamma_size = gamma_size;
0213
0214 crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3,
0215 GFP_KERNEL);
0216 if (!crtc->gamma_store) {
0217 crtc->gamma_size = 0;
0218 return -ENOMEM;
0219 }
0220
0221 r_base = crtc->gamma_store;
0222 g_base = r_base + gamma_size;
0223 b_base = g_base + gamma_size;
0224 for (i = 0; i < gamma_size; i++) {
0225 r_base[i] = i << 8;
0226 g_base[i] = i << 8;
0227 b_base[i] = i << 8;
0228 }
0229
0230
0231 return 0;
0232 }
0233 EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
0234
0235
0236
0237
0238
0239
0240
0241
0242 static bool drm_crtc_supports_legacy_gamma(struct drm_crtc *crtc)
0243 {
0244 u32 gamma_id = crtc->dev->mode_config.gamma_lut_property->base.id;
0245 u32 degamma_id = crtc->dev->mode_config.degamma_lut_property->base.id;
0246
0247 if (!crtc->gamma_size)
0248 return false;
0249
0250 if (crtc->funcs->gamma_set)
0251 return true;
0252
0253 return !!(drm_mode_obj_find_prop_id(&crtc->base, gamma_id) ||
0254 drm_mode_obj_find_prop_id(&crtc->base, degamma_id));
0255 }
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265
0266
0267
0268
0269
0270
0271
0272
0273
0274
0275 static int drm_crtc_legacy_gamma_set(struct drm_crtc *crtc,
0276 u16 *red, u16 *green, u16 *blue,
0277 u32 size,
0278 struct drm_modeset_acquire_ctx *ctx)
0279 {
0280 struct drm_device *dev = crtc->dev;
0281 struct drm_atomic_state *state;
0282 struct drm_crtc_state *crtc_state;
0283 struct drm_property_blob *blob;
0284 struct drm_color_lut *blob_data;
0285 u32 gamma_id = dev->mode_config.gamma_lut_property->base.id;
0286 u32 degamma_id = dev->mode_config.degamma_lut_property->base.id;
0287 bool use_gamma_lut;
0288 int i, ret = 0;
0289 bool replaced;
0290
0291 if (crtc->funcs->gamma_set)
0292 return crtc->funcs->gamma_set(crtc, red, green, blue, size, ctx);
0293
0294 if (drm_mode_obj_find_prop_id(&crtc->base, gamma_id))
0295 use_gamma_lut = true;
0296 else if (drm_mode_obj_find_prop_id(&crtc->base, degamma_id))
0297 use_gamma_lut = false;
0298 else
0299 return -ENODEV;
0300
0301 state = drm_atomic_state_alloc(crtc->dev);
0302 if (!state)
0303 return -ENOMEM;
0304
0305 blob = drm_property_create_blob(dev,
0306 sizeof(struct drm_color_lut) * size,
0307 NULL);
0308 if (IS_ERR(blob)) {
0309 ret = PTR_ERR(blob);
0310 blob = NULL;
0311 goto fail;
0312 }
0313
0314
0315 blob_data = blob->data;
0316 for (i = 0; i < size; i++) {
0317 blob_data[i].red = red[i];
0318 blob_data[i].green = green[i];
0319 blob_data[i].blue = blue[i];
0320 }
0321
0322 state->acquire_ctx = ctx;
0323 crtc_state = drm_atomic_get_crtc_state(state, crtc);
0324 if (IS_ERR(crtc_state)) {
0325 ret = PTR_ERR(crtc_state);
0326 goto fail;
0327 }
0328
0329
0330 replaced = drm_property_replace_blob(&crtc_state->degamma_lut,
0331 use_gamma_lut ? NULL : blob);
0332 replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);
0333 replaced |= drm_property_replace_blob(&crtc_state->gamma_lut,
0334 use_gamma_lut ? blob : NULL);
0335 crtc_state->color_mgmt_changed |= replaced;
0336
0337 ret = drm_atomic_commit(state);
0338
0339 fail:
0340 drm_atomic_state_put(state);
0341 drm_property_blob_put(blob);
0342 return ret;
0343 }
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355
0356
0357
0358
0359 int drm_mode_gamma_set_ioctl(struct drm_device *dev,
0360 void *data, struct drm_file *file_priv)
0361 {
0362 struct drm_mode_crtc_lut *crtc_lut = data;
0363 struct drm_crtc *crtc;
0364 void *r_base, *g_base, *b_base;
0365 int size;
0366 struct drm_modeset_acquire_ctx ctx;
0367 int ret = 0;
0368
0369 if (!drm_core_check_feature(dev, DRIVER_MODESET))
0370 return -EOPNOTSUPP;
0371
0372 crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
0373 if (!crtc)
0374 return -ENOENT;
0375
0376 if (!drm_crtc_supports_legacy_gamma(crtc))
0377 return -ENOSYS;
0378
0379
0380 if (crtc_lut->gamma_size != crtc->gamma_size)
0381 return -EINVAL;
0382
0383 DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
0384
0385 size = crtc_lut->gamma_size * (sizeof(uint16_t));
0386 r_base = crtc->gamma_store;
0387 if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
0388 ret = -EFAULT;
0389 goto out;
0390 }
0391
0392 g_base = r_base + size;
0393 if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
0394 ret = -EFAULT;
0395 goto out;
0396 }
0397
0398 b_base = g_base + size;
0399 if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
0400 ret = -EFAULT;
0401 goto out;
0402 }
0403
0404 ret = drm_crtc_legacy_gamma_set(crtc, r_base, g_base, b_base,
0405 crtc->gamma_size, &ctx);
0406
0407 out:
0408 DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
0409 return ret;
0410
0411 }
0412
0413
0414
0415
0416
0417
0418
0419
0420
0421
0422
0423
0424
0425
0426
0427
0428 int drm_mode_gamma_get_ioctl(struct drm_device *dev,
0429 void *data, struct drm_file *file_priv)
0430 {
0431 struct drm_mode_crtc_lut *crtc_lut = data;
0432 struct drm_crtc *crtc;
0433 void *r_base, *g_base, *b_base;
0434 int size;
0435 int ret = 0;
0436
0437 if (!drm_core_check_feature(dev, DRIVER_MODESET))
0438 return -EOPNOTSUPP;
0439
0440 crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
0441 if (!crtc)
0442 return -ENOENT;
0443
0444
0445 if (crtc_lut->gamma_size != crtc->gamma_size)
0446 return -EINVAL;
0447
0448 drm_modeset_lock(&crtc->mutex, NULL);
0449 size = crtc_lut->gamma_size * (sizeof(uint16_t));
0450 r_base = crtc->gamma_store;
0451 if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
0452 ret = -EFAULT;
0453 goto out;
0454 }
0455
0456 g_base = r_base + size;
0457 if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
0458 ret = -EFAULT;
0459 goto out;
0460 }
0461
0462 b_base = g_base + size;
0463 if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
0464 ret = -EFAULT;
0465 goto out;
0466 }
0467 out:
0468 drm_modeset_unlock(&crtc->mutex);
0469 return ret;
0470 }
0471
0472 static const char * const color_encoding_name[] = {
0473 [DRM_COLOR_YCBCR_BT601] = "ITU-R BT.601 YCbCr",
0474 [DRM_COLOR_YCBCR_BT709] = "ITU-R BT.709 YCbCr",
0475 [DRM_COLOR_YCBCR_BT2020] = "ITU-R BT.2020 YCbCr",
0476 };
0477
0478 static const char * const color_range_name[] = {
0479 [DRM_COLOR_YCBCR_FULL_RANGE] = "YCbCr full range",
0480 [DRM_COLOR_YCBCR_LIMITED_RANGE] = "YCbCr limited range",
0481 };
0482
0483
0484
0485
0486
0487
0488
0489
0490 const char *drm_get_color_encoding_name(enum drm_color_encoding encoding)
0491 {
0492 if (WARN_ON(encoding >= ARRAY_SIZE(color_encoding_name)))
0493 return "unknown";
0494
0495 return color_encoding_name[encoding];
0496 }
0497
0498
0499
0500
0501
0502
0503
0504
0505 const char *drm_get_color_range_name(enum drm_color_range range)
0506 {
0507 if (WARN_ON(range >= ARRAY_SIZE(color_range_name)))
0508 return "unknown";
0509
0510 return color_range_name[range];
0511 }
0512
0513
0514
0515
0516
0517
0518
0519
0520
0521
0522
0523
0524
0525
0526
0527 int drm_plane_create_color_properties(struct drm_plane *plane,
0528 u32 supported_encodings,
0529 u32 supported_ranges,
0530 enum drm_color_encoding default_encoding,
0531 enum drm_color_range default_range)
0532 {
0533 struct drm_device *dev = plane->dev;
0534 struct drm_property *prop;
0535 struct drm_prop_enum_list enum_list[max_t(int, DRM_COLOR_ENCODING_MAX,
0536 DRM_COLOR_RANGE_MAX)];
0537 int i, len;
0538
0539 if (WARN_ON(supported_encodings == 0 ||
0540 (supported_encodings & -BIT(DRM_COLOR_ENCODING_MAX)) != 0 ||
0541 (supported_encodings & BIT(default_encoding)) == 0))
0542 return -EINVAL;
0543
0544 if (WARN_ON(supported_ranges == 0 ||
0545 (supported_ranges & -BIT(DRM_COLOR_RANGE_MAX)) != 0 ||
0546 (supported_ranges & BIT(default_range)) == 0))
0547 return -EINVAL;
0548
0549 len = 0;
0550 for (i = 0; i < DRM_COLOR_ENCODING_MAX; i++) {
0551 if ((supported_encodings & BIT(i)) == 0)
0552 continue;
0553
0554 enum_list[len].type = i;
0555 enum_list[len].name = color_encoding_name[i];
0556 len++;
0557 }
0558
0559 prop = drm_property_create_enum(dev, 0, "COLOR_ENCODING",
0560 enum_list, len);
0561 if (!prop)
0562 return -ENOMEM;
0563 plane->color_encoding_property = prop;
0564 drm_object_attach_property(&plane->base, prop, default_encoding);
0565 if (plane->state)
0566 plane->state->color_encoding = default_encoding;
0567
0568 len = 0;
0569 for (i = 0; i < DRM_COLOR_RANGE_MAX; i++) {
0570 if ((supported_ranges & BIT(i)) == 0)
0571 continue;
0572
0573 enum_list[len].type = i;
0574 enum_list[len].name = color_range_name[i];
0575 len++;
0576 }
0577
0578 prop = drm_property_create_enum(dev, 0, "COLOR_RANGE",
0579 enum_list, len);
0580 if (!prop)
0581 return -ENOMEM;
0582 plane->color_range_property = prop;
0583 drm_object_attach_property(&plane->base, prop, default_range);
0584 if (plane->state)
0585 plane->state->color_range = default_range;
0586
0587 return 0;
0588 }
0589 EXPORT_SYMBOL(drm_plane_create_color_properties);
0590
0591
0592
0593
0594
0595
0596
0597
0598
0599
0600
0601
0602 int drm_color_lut_check(const struct drm_property_blob *lut, u32 tests)
0603 {
0604 const struct drm_color_lut *entry;
0605 int i;
0606
0607 if (!lut || !tests)
0608 return 0;
0609
0610 entry = lut->data;
0611 for (i = 0; i < drm_color_lut_size(lut); i++) {
0612 if (tests & DRM_COLOR_LUT_EQUAL_CHANNELS) {
0613 if (entry[i].red != entry[i].blue ||
0614 entry[i].red != entry[i].green) {
0615 DRM_DEBUG_KMS("All LUT entries must have equal r/g/b\n");
0616 return -EINVAL;
0617 }
0618 }
0619
0620 if (i > 0 && tests & DRM_COLOR_LUT_NON_DECREASING) {
0621 if (entry[i].red < entry[i - 1].red ||
0622 entry[i].green < entry[i - 1].green ||
0623 entry[i].blue < entry[i - 1].blue) {
0624 DRM_DEBUG_KMS("LUT entries must never decrease.\n");
0625 return -EINVAL;
0626 }
0627 }
0628 }
0629
0630 return 0;
0631 }
0632 EXPORT_SYMBOL(drm_color_lut_check);