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