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