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/export.h>
0024 #include <linux/uaccess.h>
0025
0026 #include <drm/drm_atomic.h>
0027 #include <drm/drm_atomic_uapi.h>
0028 #include <drm/drm_auth.h>
0029 #include <drm/drm_debugfs.h>
0030 #include <drm/drm_drv.h>
0031 #include <drm/drm_file.h>
0032 #include <drm/drm_fourcc.h>
0033 #include <drm/drm_framebuffer.h>
0034 #include <drm/drm_gem.h>
0035 #include <drm/drm_print.h>
0036 #include <drm/drm_util.h>
0037
0038 #include "drm_crtc_internal.h"
0039 #include "drm_internal.h"
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 int drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y,
0077 uint32_t src_w, uint32_t src_h,
0078 const struct drm_framebuffer *fb)
0079 {
0080 unsigned int fb_width, fb_height;
0081
0082 fb_width = fb->width << 16;
0083 fb_height = fb->height << 16;
0084
0085
0086 if (src_w > fb_width ||
0087 src_x > fb_width - src_w ||
0088 src_h > fb_height ||
0089 src_y > fb_height - src_h) {
0090 DRM_DEBUG_KMS("Invalid source coordinates "
0091 "%u.%06ux%u.%06u+%u.%06u+%u.%06u (fb %ux%u)\n",
0092 src_w >> 16, ((src_w & 0xffff) * 15625) >> 10,
0093 src_h >> 16, ((src_h & 0xffff) * 15625) >> 10,
0094 src_x >> 16, ((src_x & 0xffff) * 15625) >> 10,
0095 src_y >> 16, ((src_y & 0xffff) * 15625) >> 10,
0096 fb->width, fb->height);
0097 return -ENOSPC;
0098 }
0099
0100 return 0;
0101 }
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117 int drm_mode_addfb(struct drm_device *dev, struct drm_mode_fb_cmd *or,
0118 struct drm_file *file_priv)
0119 {
0120 struct drm_mode_fb_cmd2 r = {};
0121 int ret;
0122
0123 if (!drm_core_check_feature(dev, DRIVER_MODESET))
0124 return -EOPNOTSUPP;
0125
0126 r.pixel_format = drm_driver_legacy_fb_format(dev, or->bpp, or->depth);
0127 if (r.pixel_format == DRM_FORMAT_INVALID) {
0128 DRM_DEBUG("bad {bpp:%d, depth:%d}\n", or->bpp, or->depth);
0129 return -EINVAL;
0130 }
0131
0132
0133 r.fb_id = or->fb_id;
0134 r.width = or->width;
0135 r.height = or->height;
0136 r.pitches[0] = or->pitch;
0137 r.handles[0] = or->handle;
0138
0139 ret = drm_mode_addfb2(dev, &r, file_priv);
0140 if (ret)
0141 return ret;
0142
0143 or->fb_id = r.fb_id;
0144
0145 return 0;
0146 }
0147
0148 int drm_mode_addfb_ioctl(struct drm_device *dev,
0149 void *data, struct drm_file *file_priv)
0150 {
0151 return drm_mode_addfb(dev, data, file_priv);
0152 }
0153
0154 static int fb_plane_width(int width,
0155 const struct drm_format_info *format, int plane)
0156 {
0157 if (plane == 0)
0158 return width;
0159
0160 return DIV_ROUND_UP(width, format->hsub);
0161 }
0162
0163 static int fb_plane_height(int height,
0164 const struct drm_format_info *format, int plane)
0165 {
0166 if (plane == 0)
0167 return height;
0168
0169 return DIV_ROUND_UP(height, format->vsub);
0170 }
0171
0172 static int framebuffer_check(struct drm_device *dev,
0173 const struct drm_mode_fb_cmd2 *r)
0174 {
0175 const struct drm_format_info *info;
0176 int i;
0177
0178
0179 if (!__drm_format_info(r->pixel_format)) {
0180 DRM_DEBUG_KMS("bad framebuffer format %p4cc\n",
0181 &r->pixel_format);
0182 return -EINVAL;
0183 }
0184
0185 if (r->width == 0) {
0186 DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width);
0187 return -EINVAL;
0188 }
0189
0190 if (r->height == 0) {
0191 DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height);
0192 return -EINVAL;
0193 }
0194
0195
0196 info = drm_get_format_info(dev, r);
0197
0198 for (i = 0; i < info->num_planes; i++) {
0199 unsigned int width = fb_plane_width(r->width, info, i);
0200 unsigned int height = fb_plane_height(r->height, info, i);
0201 unsigned int block_size = info->char_per_block[i];
0202 u64 min_pitch = drm_format_info_min_pitch(info, i, width);
0203
0204 if (!block_size && (r->modifier[i] == DRM_FORMAT_MOD_LINEAR)) {
0205 DRM_DEBUG_KMS("Format requires non-linear modifier for plane %d\n", i);
0206 return -EINVAL;
0207 }
0208
0209 if (!r->handles[i]) {
0210 DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i);
0211 return -EINVAL;
0212 }
0213
0214 if (min_pitch > UINT_MAX)
0215 return -ERANGE;
0216
0217 if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
0218 return -ERANGE;
0219
0220 if (block_size && r->pitches[i] < min_pitch) {
0221 DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
0222 return -EINVAL;
0223 }
0224
0225 if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) {
0226 DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n",
0227 r->modifier[i], i);
0228 return -EINVAL;
0229 }
0230
0231 if (r->flags & DRM_MODE_FB_MODIFIERS &&
0232 r->modifier[i] != r->modifier[0]) {
0233 DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n",
0234 r->modifier[i], i);
0235 return -EINVAL;
0236 }
0237
0238
0239 switch (r->modifier[i]) {
0240 case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE:
0241
0242
0243
0244 if (r->pixel_format != DRM_FORMAT_NV12 ||
0245 width % 128 || height % 32 ||
0246 r->pitches[i] % 128) {
0247 DRM_DEBUG_KMS("bad modifier data for plane %d\n", i);
0248 return -EINVAL;
0249 }
0250 break;
0251
0252 default:
0253 break;
0254 }
0255 }
0256
0257 for (i = info->num_planes; i < 4; i++) {
0258 if (r->modifier[i]) {
0259 DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i);
0260 return -EINVAL;
0261 }
0262
0263
0264 if (!(r->flags & DRM_MODE_FB_MODIFIERS))
0265 continue;
0266
0267 if (r->handles[i]) {
0268 DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i);
0269 return -EINVAL;
0270 }
0271
0272 if (r->pitches[i]) {
0273 DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i);
0274 return -EINVAL;
0275 }
0276
0277 if (r->offsets[i]) {
0278 DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i);
0279 return -EINVAL;
0280 }
0281 }
0282
0283 return 0;
0284 }
0285
0286 struct drm_framebuffer *
0287 drm_internal_framebuffer_create(struct drm_device *dev,
0288 const struct drm_mode_fb_cmd2 *r,
0289 struct drm_file *file_priv)
0290 {
0291 struct drm_mode_config *config = &dev->mode_config;
0292 struct drm_framebuffer *fb;
0293 int ret;
0294
0295 if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) {
0296 DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags);
0297 return ERR_PTR(-EINVAL);
0298 }
0299
0300 if ((config->min_width > r->width) || (r->width > config->max_width)) {
0301 DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",
0302 r->width, config->min_width, config->max_width);
0303 return ERR_PTR(-EINVAL);
0304 }
0305 if ((config->min_height > r->height) || (r->height > config->max_height)) {
0306 DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",
0307 r->height, config->min_height, config->max_height);
0308 return ERR_PTR(-EINVAL);
0309 }
0310
0311 if (r->flags & DRM_MODE_FB_MODIFIERS &&
0312 dev->mode_config.fb_modifiers_not_supported) {
0313 DRM_DEBUG_KMS("driver does not support fb modifiers\n");
0314 return ERR_PTR(-EINVAL);
0315 }
0316
0317 ret = framebuffer_check(dev, r);
0318 if (ret)
0319 return ERR_PTR(ret);
0320
0321 fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
0322 if (IS_ERR(fb)) {
0323 DRM_DEBUG_KMS("could not create framebuffer\n");
0324 return fb;
0325 }
0326
0327 return fb;
0328 }
0329 EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_internal_framebuffer_create);
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346 int drm_mode_addfb2(struct drm_device *dev,
0347 void *data, struct drm_file *file_priv)
0348 {
0349 struct drm_mode_fb_cmd2 *r = data;
0350 struct drm_framebuffer *fb;
0351
0352 if (!drm_core_check_feature(dev, DRIVER_MODESET))
0353 return -EOPNOTSUPP;
0354
0355 fb = drm_internal_framebuffer_create(dev, r, file_priv);
0356 if (IS_ERR(fb))
0357 return PTR_ERR(fb);
0358
0359 DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
0360 r->fb_id = fb->base.id;
0361
0362
0363 mutex_lock(&file_priv->fbs_lock);
0364 list_add(&fb->filp_head, &file_priv->fbs);
0365 mutex_unlock(&file_priv->fbs_lock);
0366
0367 return 0;
0368 }
0369
0370 int drm_mode_addfb2_ioctl(struct drm_device *dev,
0371 void *data, struct drm_file *file_priv)
0372 {
0373 #ifdef __BIG_ENDIAN
0374 if (!dev->mode_config.quirk_addfb_prefer_host_byte_order) {
0375
0376
0377
0378
0379
0380
0381
0382
0383
0384
0385
0386
0387 DRM_DEBUG_KMS("addfb2 broken on bigendian");
0388 return -EOPNOTSUPP;
0389 }
0390 #endif
0391 return drm_mode_addfb2(dev, data, file_priv);
0392 }
0393
0394 struct drm_mode_rmfb_work {
0395 struct work_struct work;
0396 struct list_head fbs;
0397 };
0398
0399 static void drm_mode_rmfb_work_fn(struct work_struct *w)
0400 {
0401 struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work);
0402
0403 while (!list_empty(&arg->fbs)) {
0404 struct drm_framebuffer *fb =
0405 list_first_entry(&arg->fbs, typeof(*fb), filp_head);
0406
0407 drm_dbg_kms(fb->dev,
0408 "Removing [FB:%d] from all active usage due to RMFB ioctl\n",
0409 fb->base.id);
0410 list_del_init(&fb->filp_head);
0411 drm_framebuffer_remove(fb);
0412 }
0413 }
0414
0415
0416
0417
0418
0419
0420
0421
0422
0423
0424
0425
0426
0427
0428 int drm_mode_rmfb(struct drm_device *dev, u32 fb_id,
0429 struct drm_file *file_priv)
0430 {
0431 struct drm_framebuffer *fb = NULL;
0432 struct drm_framebuffer *fbl = NULL;
0433 int found = 0;
0434
0435 if (!drm_core_check_feature(dev, DRIVER_MODESET))
0436 return -EOPNOTSUPP;
0437
0438 fb = drm_framebuffer_lookup(dev, file_priv, fb_id);
0439 if (!fb)
0440 return -ENOENT;
0441
0442 mutex_lock(&file_priv->fbs_lock);
0443 list_for_each_entry(fbl, &file_priv->fbs, filp_head)
0444 if (fb == fbl)
0445 found = 1;
0446 if (!found) {
0447 mutex_unlock(&file_priv->fbs_lock);
0448 goto fail_unref;
0449 }
0450
0451 list_del_init(&fb->filp_head);
0452 mutex_unlock(&file_priv->fbs_lock);
0453
0454
0455 drm_framebuffer_put(fb);
0456
0457
0458
0459
0460
0461
0462
0463
0464 if (drm_framebuffer_read_refcount(fb) > 1) {
0465 struct drm_mode_rmfb_work arg;
0466
0467 INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
0468 INIT_LIST_HEAD(&arg.fbs);
0469 list_add_tail(&fb->filp_head, &arg.fbs);
0470
0471 schedule_work(&arg.work);
0472 flush_work(&arg.work);
0473 destroy_work_on_stack(&arg.work);
0474 } else
0475 drm_framebuffer_put(fb);
0476
0477 return 0;
0478
0479 fail_unref:
0480 drm_framebuffer_put(fb);
0481 return -ENOENT;
0482 }
0483
0484 int drm_mode_rmfb_ioctl(struct drm_device *dev,
0485 void *data, struct drm_file *file_priv)
0486 {
0487 uint32_t *fb_id = data;
0488
0489 return drm_mode_rmfb(dev, *fb_id, file_priv);
0490 }
0491
0492
0493
0494
0495
0496
0497
0498
0499
0500
0501
0502
0503
0504
0505 int drm_mode_getfb(struct drm_device *dev,
0506 void *data, struct drm_file *file_priv)
0507 {
0508 struct drm_mode_fb_cmd *r = data;
0509 struct drm_framebuffer *fb;
0510 int ret;
0511
0512 if (!drm_core_check_feature(dev, DRIVER_MODESET))
0513 return -EOPNOTSUPP;
0514
0515 fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
0516 if (!fb)
0517 return -ENOENT;
0518
0519
0520 if (fb->format->num_planes > 1) {
0521 ret = -EINVAL;
0522 goto out;
0523 }
0524
0525 if (!fb->funcs->create_handle) {
0526 ret = -ENODEV;
0527 goto out;
0528 }
0529
0530 r->height = fb->height;
0531 r->width = fb->width;
0532 r->depth = fb->format->depth;
0533 r->bpp = fb->format->cpp[0] * 8;
0534 r->pitch = fb->pitches[0];
0535
0536
0537
0538
0539
0540
0541 if (!drm_is_current_master(file_priv) && !capable(CAP_SYS_ADMIN)) {
0542 r->handle = 0;
0543 ret = 0;
0544 goto out;
0545 }
0546
0547 ret = fb->funcs->create_handle(fb, file_priv, &r->handle);
0548
0549 out:
0550 drm_framebuffer_put(fb);
0551 return ret;
0552 }
0553
0554
0555
0556
0557
0558
0559
0560
0561
0562
0563
0564
0565
0566
0567 int drm_mode_getfb2_ioctl(struct drm_device *dev,
0568 void *data, struct drm_file *file_priv)
0569 {
0570 struct drm_mode_fb_cmd2 *r = data;
0571 struct drm_framebuffer *fb;
0572 unsigned int i;
0573 int ret;
0574
0575 if (!drm_core_check_feature(dev, DRIVER_MODESET))
0576 return -EINVAL;
0577
0578 fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
0579 if (!fb)
0580 return -ENOENT;
0581
0582
0583
0584
0585
0586 if (!fb->obj[0] &&
0587 (fb->format->num_planes > 1 || !fb->funcs->create_handle)) {
0588 ret = -ENODEV;
0589 goto out;
0590 }
0591
0592 r->height = fb->height;
0593 r->width = fb->width;
0594 r->pixel_format = fb->format->format;
0595
0596 r->flags = 0;
0597 if (!dev->mode_config.fb_modifiers_not_supported)
0598 r->flags |= DRM_MODE_FB_MODIFIERS;
0599
0600 for (i = 0; i < ARRAY_SIZE(r->handles); i++) {
0601 r->handles[i] = 0;
0602 r->pitches[i] = 0;
0603 r->offsets[i] = 0;
0604 r->modifier[i] = 0;
0605 }
0606
0607 for (i = 0; i < fb->format->num_planes; i++) {
0608 r->pitches[i] = fb->pitches[i];
0609 r->offsets[i] = fb->offsets[i];
0610 if (!dev->mode_config.fb_modifiers_not_supported)
0611 r->modifier[i] = fb->modifier;
0612 }
0613
0614
0615
0616
0617
0618
0619 if (!drm_is_current_master(file_priv) && !capable(CAP_SYS_ADMIN)) {
0620 ret = 0;
0621 goto out;
0622 }
0623
0624 for (i = 0; i < fb->format->num_planes; i++) {
0625 int j;
0626
0627
0628
0629
0630 for (j = 0; j < i; j++) {
0631 if (fb->obj[i] == fb->obj[j]) {
0632 r->handles[i] = r->handles[j];
0633 break;
0634 }
0635 }
0636
0637 if (r->handles[i])
0638 continue;
0639
0640 if (fb->obj[i]) {
0641 ret = drm_gem_handle_create(file_priv, fb->obj[i],
0642 &r->handles[i]);
0643 } else {
0644 WARN_ON(i > 0);
0645 ret = fb->funcs->create_handle(fb, file_priv,
0646 &r->handles[i]);
0647 }
0648
0649 if (ret != 0)
0650 goto out;
0651 }
0652
0653 out:
0654 if (ret != 0) {
0655
0656 for (i = 0; i < ARRAY_SIZE(r->handles); i++) {
0657 int j;
0658
0659 if (r->handles[i])
0660 drm_gem_handle_delete(file_priv, r->handles[i]);
0661
0662
0663
0664
0665 for (j = i + 1; j < ARRAY_SIZE(r->handles); j++) {
0666 if (r->handles[j] == r->handles[i])
0667 r->handles[j] = 0;
0668 }
0669 }
0670 }
0671
0672 drm_framebuffer_put(fb);
0673 return ret;
0674 }
0675
0676
0677
0678
0679
0680
0681
0682
0683
0684
0685
0686
0687
0688
0689
0690
0691
0692
0693
0694
0695 int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
0696 void *data, struct drm_file *file_priv)
0697 {
0698 struct drm_clip_rect __user *clips_ptr;
0699 struct drm_clip_rect *clips = NULL;
0700 struct drm_mode_fb_dirty_cmd *r = data;
0701 struct drm_framebuffer *fb;
0702 unsigned flags;
0703 int num_clips;
0704 int ret;
0705
0706 if (!drm_core_check_feature(dev, DRIVER_MODESET))
0707 return -EOPNOTSUPP;
0708
0709 fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
0710 if (!fb)
0711 return -ENOENT;
0712
0713 num_clips = r->num_clips;
0714 clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
0715
0716 if (!num_clips != !clips_ptr) {
0717 ret = -EINVAL;
0718 goto out_err1;
0719 }
0720
0721 flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags;
0722
0723
0724 if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) {
0725 ret = -EINVAL;
0726 goto out_err1;
0727 }
0728
0729 if (num_clips && clips_ptr) {
0730 if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) {
0731 ret = -EINVAL;
0732 goto out_err1;
0733 }
0734 clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL);
0735 if (!clips) {
0736 ret = -ENOMEM;
0737 goto out_err1;
0738 }
0739
0740 ret = copy_from_user(clips, clips_ptr,
0741 num_clips * sizeof(*clips));
0742 if (ret) {
0743 ret = -EFAULT;
0744 goto out_err2;
0745 }
0746 }
0747
0748 if (fb->funcs->dirty) {
0749 ret = fb->funcs->dirty(fb, file_priv, flags, r->color,
0750 clips, num_clips);
0751 } else {
0752 ret = -ENOSYS;
0753 }
0754
0755 out_err2:
0756 kfree(clips);
0757 out_err1:
0758 drm_framebuffer_put(fb);
0759
0760 return ret;
0761 }
0762
0763
0764
0765
0766
0767
0768
0769
0770
0771
0772
0773
0774 void drm_fb_release(struct drm_file *priv)
0775 {
0776 struct drm_framebuffer *fb, *tfb;
0777 struct drm_mode_rmfb_work arg;
0778
0779 INIT_LIST_HEAD(&arg.fbs);
0780
0781
0782
0783
0784
0785
0786
0787
0788
0789
0790
0791 list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
0792 if (drm_framebuffer_read_refcount(fb) > 1) {
0793 list_move_tail(&fb->filp_head, &arg.fbs);
0794 } else {
0795 list_del_init(&fb->filp_head);
0796
0797
0798 drm_framebuffer_put(fb);
0799 }
0800 }
0801
0802 if (!list_empty(&arg.fbs)) {
0803 INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
0804
0805 schedule_work(&arg.work);
0806 flush_work(&arg.work);
0807 destroy_work_on_stack(&arg.work);
0808 }
0809 }
0810
0811 void drm_framebuffer_free(struct kref *kref)
0812 {
0813 struct drm_framebuffer *fb =
0814 container_of(kref, struct drm_framebuffer, base.refcount);
0815 struct drm_device *dev = fb->dev;
0816
0817
0818
0819
0820
0821 drm_mode_object_unregister(dev, &fb->base);
0822
0823 fb->funcs->destroy(fb);
0824 }
0825
0826
0827
0828
0829
0830
0831
0832
0833
0834
0835
0836
0837
0838
0839
0840
0841
0842
0843
0844 int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
0845 const struct drm_framebuffer_funcs *funcs)
0846 {
0847 int ret;
0848
0849 if (WARN_ON_ONCE(fb->dev != dev || !fb->format))
0850 return -EINVAL;
0851
0852 INIT_LIST_HEAD(&fb->filp_head);
0853
0854 fb->funcs = funcs;
0855 strcpy(fb->comm, current->comm);
0856
0857 ret = __drm_mode_object_add(dev, &fb->base, DRM_MODE_OBJECT_FB,
0858 false, drm_framebuffer_free);
0859 if (ret)
0860 goto out;
0861
0862 mutex_lock(&dev->mode_config.fb_lock);
0863 dev->mode_config.num_fb++;
0864 list_add(&fb->head, &dev->mode_config.fb_list);
0865 mutex_unlock(&dev->mode_config.fb_lock);
0866
0867 drm_mode_object_register(dev, &fb->base);
0868 out:
0869 return ret;
0870 }
0871 EXPORT_SYMBOL(drm_framebuffer_init);
0872
0873
0874
0875
0876
0877
0878
0879
0880
0881
0882
0883 struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
0884 struct drm_file *file_priv,
0885 uint32_t id)
0886 {
0887 struct drm_mode_object *obj;
0888 struct drm_framebuffer *fb = NULL;
0889
0890 obj = __drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_FB);
0891 if (obj)
0892 fb = obj_to_fb(obj);
0893 return fb;
0894 }
0895 EXPORT_SYMBOL(drm_framebuffer_lookup);
0896
0897
0898
0899
0900
0901
0902
0903
0904
0905
0906
0907
0908
0909
0910
0911 void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
0912 {
0913 struct drm_device *dev;
0914
0915 if (!fb)
0916 return;
0917
0918 dev = fb->dev;
0919
0920
0921 drm_mode_object_unregister(dev, &fb->base);
0922 }
0923 EXPORT_SYMBOL(drm_framebuffer_unregister_private);
0924
0925
0926
0927
0928
0929
0930
0931
0932
0933
0934
0935
0936
0937
0938
0939
0940
0941
0942 void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
0943 {
0944 struct drm_device *dev = fb->dev;
0945
0946 mutex_lock(&dev->mode_config.fb_lock);
0947 list_del(&fb->head);
0948 dev->mode_config.num_fb--;
0949 mutex_unlock(&dev->mode_config.fb_lock);
0950 }
0951 EXPORT_SYMBOL(drm_framebuffer_cleanup);
0952
0953 static int atomic_remove_fb(struct drm_framebuffer *fb)
0954 {
0955 struct drm_modeset_acquire_ctx ctx;
0956 struct drm_device *dev = fb->dev;
0957 struct drm_atomic_state *state;
0958 struct drm_plane *plane;
0959 struct drm_connector *conn __maybe_unused;
0960 struct drm_connector_state *conn_state;
0961 int i, ret;
0962 unsigned plane_mask;
0963 bool disable_crtcs = false;
0964
0965 retry_disable:
0966 drm_modeset_acquire_init(&ctx, 0);
0967
0968 state = drm_atomic_state_alloc(dev);
0969 if (!state) {
0970 ret = -ENOMEM;
0971 goto out;
0972 }
0973 state->acquire_ctx = &ctx;
0974
0975 retry:
0976 plane_mask = 0;
0977 ret = drm_modeset_lock_all_ctx(dev, &ctx);
0978 if (ret)
0979 goto unlock;
0980
0981 drm_for_each_plane(plane, dev) {
0982 struct drm_plane_state *plane_state;
0983
0984 if (plane->state->fb != fb)
0985 continue;
0986
0987 drm_dbg_kms(dev,
0988 "Disabling [PLANE:%d:%s] because [FB:%d] is removed\n",
0989 plane->base.id, plane->name, fb->base.id);
0990
0991 plane_state = drm_atomic_get_plane_state(state, plane);
0992 if (IS_ERR(plane_state)) {
0993 ret = PTR_ERR(plane_state);
0994 goto unlock;
0995 }
0996
0997 if (disable_crtcs && plane_state->crtc->primary == plane) {
0998 struct drm_crtc_state *crtc_state;
0999
1000 drm_dbg_kms(dev,
1001 "Disabling [CRTC:%d:%s] because [FB:%d] is removed\n",
1002 plane_state->crtc->base.id,
1003 plane_state->crtc->name, fb->base.id);
1004
1005 crtc_state = drm_atomic_get_existing_crtc_state(state, plane_state->crtc);
1006
1007 ret = drm_atomic_add_affected_connectors(state, plane_state->crtc);
1008 if (ret)
1009 goto unlock;
1010
1011 crtc_state->active = false;
1012 ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
1013 if (ret)
1014 goto unlock;
1015 }
1016
1017 drm_atomic_set_fb_for_plane(plane_state, NULL);
1018 ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
1019 if (ret)
1020 goto unlock;
1021
1022 plane_mask |= drm_plane_mask(plane);
1023 }
1024
1025
1026 for_each_new_connector_in_state(state, conn, conn_state, i) {
1027 ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
1028
1029 if (ret)
1030 goto unlock;
1031 }
1032
1033 if (plane_mask)
1034 ret = drm_atomic_commit(state);
1035
1036 unlock:
1037 if (ret == -EDEADLK) {
1038 drm_atomic_state_clear(state);
1039 drm_modeset_backoff(&ctx);
1040 goto retry;
1041 }
1042
1043 drm_atomic_state_put(state);
1044
1045 out:
1046 drm_modeset_drop_locks(&ctx);
1047 drm_modeset_acquire_fini(&ctx);
1048
1049 if (ret == -EINVAL && !disable_crtcs) {
1050 disable_crtcs = true;
1051 goto retry_disable;
1052 }
1053
1054 return ret;
1055 }
1056
1057 static void legacy_remove_fb(struct drm_framebuffer *fb)
1058 {
1059 struct drm_device *dev = fb->dev;
1060 struct drm_crtc *crtc;
1061 struct drm_plane *plane;
1062
1063 drm_modeset_lock_all(dev);
1064
1065 drm_for_each_crtc(crtc, dev) {
1066 if (crtc->primary->fb == fb) {
1067 drm_dbg_kms(dev,
1068 "Disabling [CRTC:%d:%s] because [FB:%d] is removed\n",
1069 crtc->base.id, crtc->name, fb->base.id);
1070
1071
1072 if (drm_crtc_force_disable(crtc))
1073 DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
1074 }
1075 }
1076
1077 drm_for_each_plane(plane, dev) {
1078 if (plane->fb == fb) {
1079 drm_dbg_kms(dev,
1080 "Disabling [PLANE:%d:%s] because [FB:%d] is removed\n",
1081 plane->base.id, plane->name, fb->base.id);
1082 drm_plane_force_disable(plane);
1083 }
1084 }
1085 drm_modeset_unlock_all(dev);
1086 }
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100 void drm_framebuffer_remove(struct drm_framebuffer *fb)
1101 {
1102 struct drm_device *dev;
1103
1104 if (!fb)
1105 return;
1106
1107 dev = fb->dev;
1108
1109 WARN_ON(!list_empty(&fb->filp_head));
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126 if (drm_framebuffer_read_refcount(fb) > 1) {
1127 if (drm_drv_uses_atomic_modeset(dev)) {
1128 int ret = atomic_remove_fb(fb);
1129
1130 WARN(ret, "atomic remove_fb failed with %i\n", ret);
1131 } else
1132 legacy_remove_fb(fb);
1133 }
1134
1135 drm_framebuffer_put(fb);
1136 }
1137 EXPORT_SYMBOL(drm_framebuffer_remove);
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148 int drm_framebuffer_plane_width(int width,
1149 const struct drm_framebuffer *fb, int plane)
1150 {
1151 if (plane >= fb->format->num_planes)
1152 return 0;
1153
1154 return fb_plane_width(width, fb->format, plane);
1155 }
1156 EXPORT_SYMBOL(drm_framebuffer_plane_width);
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167 int drm_framebuffer_plane_height(int height,
1168 const struct drm_framebuffer *fb, int plane)
1169 {
1170 if (plane >= fb->format->num_planes)
1171 return 0;
1172
1173 return fb_plane_height(height, fb->format, plane);
1174 }
1175 EXPORT_SYMBOL(drm_framebuffer_plane_height);
1176
1177 void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent,
1178 const struct drm_framebuffer *fb)
1179 {
1180 unsigned int i;
1181
1182 drm_printf_indent(p, indent, "allocated by = %s\n", fb->comm);
1183 drm_printf_indent(p, indent, "refcount=%u\n",
1184 drm_framebuffer_read_refcount(fb));
1185 drm_printf_indent(p, indent, "format=%p4cc\n", &fb->format->format);
1186 drm_printf_indent(p, indent, "modifier=0x%llx\n", fb->modifier);
1187 drm_printf_indent(p, indent, "size=%ux%u\n", fb->width, fb->height);
1188 drm_printf_indent(p, indent, "layers:\n");
1189
1190 for (i = 0; i < fb->format->num_planes; i++) {
1191 drm_printf_indent(p, indent + 1, "size[%u]=%dx%d\n", i,
1192 drm_framebuffer_plane_width(fb->width, fb, i),
1193 drm_framebuffer_plane_height(fb->height, fb, i));
1194 drm_printf_indent(p, indent + 1, "pitch[%u]=%u\n", i, fb->pitches[i]);
1195 drm_printf_indent(p, indent + 1, "offset[%u]=%u\n", i, fb->offsets[i]);
1196 drm_printf_indent(p, indent + 1, "obj[%u]:%s\n", i,
1197 fb->obj[i] ? "" : "(null)");
1198 if (fb->obj[i])
1199 drm_gem_print_info(p, indent + 2, fb->obj[i]);
1200 }
1201 }
1202
1203 #ifdef CONFIG_DEBUG_FS
1204 static int drm_framebuffer_info(struct seq_file *m, void *data)
1205 {
1206 struct drm_info_node *node = m->private;
1207 struct drm_device *dev = node->minor->dev;
1208 struct drm_printer p = drm_seq_file_printer(m);
1209 struct drm_framebuffer *fb;
1210
1211 mutex_lock(&dev->mode_config.fb_lock);
1212 drm_for_each_fb(fb, dev) {
1213 drm_printf(&p, "framebuffer[%u]:\n", fb->base.id);
1214 drm_framebuffer_print_info(&p, 1, fb);
1215 }
1216 mutex_unlock(&dev->mode_config.fb_lock);
1217
1218 return 0;
1219 }
1220
1221 static const struct drm_info_list drm_framebuffer_debugfs_list[] = {
1222 { "framebuffer", drm_framebuffer_info, 0 },
1223 };
1224
1225 void drm_framebuffer_debugfs_init(struct drm_minor *minor)
1226 {
1227 drm_debugfs_create_files(drm_framebuffer_debugfs_list,
1228 ARRAY_SIZE(drm_framebuffer_debugfs_list),
1229 minor->debugfs_root, minor);
1230 }
1231 #endif