1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. 4 * Author: James.Qian.Wang <james.qian.wang@arm.com> 5 * 6 */ 7 #include <drm/drm_gem.h> 8 #include <drm/drm_gem_framebuffer_helper.h> 9 #include <drm/drm_fb_cma_helper.h> 10 #include <drm/drm_gem_cma_helper.h> 11 #include "komeda_framebuffer.h" 12 #include "komeda_dev.h" 13 14 static void komeda_fb_destroy(struct drm_framebuffer *fb) 15 { 16 struct komeda_fb *kfb = to_kfb(fb); 17 u32 i; 18 19 for (i = 0; i < fb->format->num_planes; i++) 20 drm_gem_object_put_unlocked(fb->obj[i]); 21 22 drm_framebuffer_cleanup(fb); 23 kfree(kfb); 24 } 25 26 static int komeda_fb_create_handle(struct drm_framebuffer *fb, 27 struct drm_file *file, u32 *handle) 28 { 29 return drm_gem_handle_create(file, fb->obj[0], handle); 30 } 31 32 static const struct drm_framebuffer_funcs komeda_fb_funcs = { 33 .destroy = komeda_fb_destroy, 34 .create_handle = komeda_fb_create_handle, 35 }; 36 37 static int 38 komeda_fb_none_afbc_size_check(struct komeda_dev *mdev, struct komeda_fb *kfb, 39 struct drm_file *file, 40 const struct drm_mode_fb_cmd2 *mode_cmd) 41 { 42 struct drm_framebuffer *fb = &kfb->base; 43 struct drm_gem_object *obj; 44 u32 min_size = 0; 45 u32 i; 46 47 for (i = 0; i < fb->format->num_planes; i++) { 48 obj = drm_gem_object_lookup(file, mode_cmd->handles[i]); 49 if (!obj) { 50 DRM_DEBUG_KMS("Failed to lookup GEM object\n"); 51 fb->obj[i] = NULL; 52 53 return -ENOENT; 54 } 55 56 kfb->aligned_w = fb->width / (i ? fb->format->hsub : 1); 57 kfb->aligned_h = fb->height / (i ? fb->format->vsub : 1); 58 59 if (fb->pitches[i] % mdev->chip.bus_width) { 60 DRM_DEBUG_KMS("Pitch[%d]: 0x%x doesn't align to 0x%x\n", 61 i, fb->pitches[i], mdev->chip.bus_width); 62 drm_gem_object_put_unlocked(obj); 63 fb->obj[i] = NULL; 64 65 return -EINVAL; 66 } 67 68 min_size = ((kfb->aligned_h / kfb->format_caps->tile_size - 1) 69 * fb->pitches[i]) 70 + (kfb->aligned_w * fb->format->cpp[i] 71 * kfb->format_caps->tile_size) 72 + fb->offsets[i]; 73 74 if (obj->size < min_size) { 75 DRM_DEBUG_KMS("Fail to check none afbc fb size.\n"); 76 drm_gem_object_put_unlocked(obj); 77 fb->obj[i] = NULL; 78 79 return -EINVAL; 80 } 81 82 fb->obj[i] = obj; 83 } 84 85 if (fb->format->num_planes == 3) { 86 if (fb->pitches[1] != fb->pitches[2]) { 87 DRM_DEBUG_KMS("The pitch[1] and [2] are not same\n"); 88 return -EINVAL; 89 } 90 } 91 92 return 0; 93 } 94 95 struct drm_framebuffer * 96 komeda_fb_create(struct drm_device *dev, struct drm_file *file, 97 const struct drm_mode_fb_cmd2 *mode_cmd) 98 { 99 struct komeda_dev *mdev = dev->dev_private; 100 struct komeda_fb *kfb; 101 int ret = 0, i; 102 103 kfb = kzalloc(sizeof(*kfb), GFP_KERNEL); 104 if (!kfb) 105 return ERR_PTR(-ENOMEM); 106 107 kfb->format_caps = komeda_get_format_caps(&mdev->fmt_tbl, 108 mode_cmd->pixel_format, 109 mode_cmd->modifier[0]); 110 if (!kfb->format_caps) { 111 DRM_DEBUG_KMS("FMT %x is not supported.\n", 112 mode_cmd->pixel_format); 113 kfree(kfb); 114 return ERR_PTR(-EINVAL); 115 } 116 117 drm_helper_mode_fill_fb_struct(dev, &kfb->base, mode_cmd); 118 119 ret = komeda_fb_none_afbc_size_check(mdev, kfb, file, mode_cmd); 120 if (ret < 0) 121 goto err_cleanup; 122 123 ret = drm_framebuffer_init(dev, &kfb->base, &komeda_fb_funcs); 124 if (ret < 0) { 125 DRM_DEBUG_KMS("failed to initialize fb\n"); 126 127 goto err_cleanup; 128 } 129 130 return &kfb->base; 131 132 err_cleanup: 133 for (i = 0; i < kfb->base.format->num_planes; i++) 134 drm_gem_object_put_unlocked(kfb->base.obj[i]); 135 136 kfree(kfb); 137 return ERR_PTR(ret); 138 } 139 140 dma_addr_t 141 komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane) 142 { 143 struct drm_framebuffer *fb = &kfb->base; 144 const struct drm_gem_cma_object *obj; 145 u32 plane_x, plane_y, cpp, pitch, offset; 146 147 if (plane >= fb->format->num_planes) { 148 DRM_DEBUG_KMS("Out of max plane num.\n"); 149 return -EINVAL; 150 } 151 152 obj = drm_fb_cma_get_gem_obj(fb, plane); 153 154 offset = fb->offsets[plane]; 155 if (!fb->modifier) { 156 plane_x = x / (plane ? fb->format->hsub : 1); 157 plane_y = y / (plane ? fb->format->vsub : 1); 158 cpp = fb->format->cpp[plane]; 159 pitch = fb->pitches[plane]; 160 offset += plane_x * cpp * kfb->format_caps->tile_size + 161 (plane_y * pitch) / kfb->format_caps->tile_size; 162 } 163 164 return obj->paddr + offset; 165 } 166