|
| 1 | +/* |
| 2 | + * Copyright 2019 The FydeOS Authors. All rights reserved. |
| 3 | + * Use of this source code is governed by a BSD-style license that can be |
| 4 | + * found in the LICENSE file. |
| 5 | + * Author: Yang Tsao <yang@fydeos.io> |
| 6 | + */ |
| 7 | +#ifdef DRV_V3D |
| 8 | +#include <errno.h> |
| 9 | +#include <stdio.h> |
| 10 | +#include <string.h> |
| 11 | +#include <sys/mman.h> |
| 12 | +#include <xf86drm.h> |
| 13 | + |
| 14 | +#include "drv_priv.h" |
| 15 | +#include "helpers.h" |
| 16 | +#include "util.h" |
| 17 | +#include "external/v3d_drm.h" |
| 18 | +#define ARC_CALLOC (1<<7) |
| 19 | +#define MAX2( A, B ) ( (A)>(B) ? (A) : (B) ) |
| 20 | + |
| 21 | +#define DEBUG 1 |
| 22 | + |
| 23 | +enum v3d_tiling_mode { |
| 24 | + /* Untiled resources. Not valid as texture inputs. */ |
| 25 | + V3D_TILING_RASTER, |
| 26 | + |
| 27 | + /* Single line of u-tiles. */ |
| 28 | + V3D_TILING_LINEARTILE, |
| 29 | + |
| 30 | + /* Departure from standard 4-UIF block column format. */ |
| 31 | + V3D_TILING_UBLINEAR_1_COLUMN, |
| 32 | + |
| 33 | + /* Departure from standard 4-UIF block column format. */ |
| 34 | + V3D_TILING_UBLINEAR_2_COLUMN, |
| 35 | + |
| 36 | + /* Normal tiling format: grouped in 4x4 UIFblocks, each of which is |
| 37 | + * split 2x2 into utiles. |
| 38 | + */ |
| 39 | + V3D_TILING_UIF_NO_XOR, |
| 40 | + |
| 41 | + /* Normal tiling format: grouped in 4x4 UIFblocks, each of which is |
| 42 | + * split 2x2 into utiles. |
| 43 | + */ |
| 44 | + V3D_TILING_UIF_XOR, |
| 45 | +}; |
| 46 | + |
| 47 | +static const uint32_t render_target_formats[] = { DRM_FORMAT_RGB565, |
| 48 | + DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_XBGR8888 }; |
| 49 | + |
| 50 | +static const uint32_t texture_target_formats[] = { |
| 51 | + DRM_FORMAT_YVU420, DRM_FORMAT_NV12, DRM_FORMAT_YVU420_ANDROID, DRM_FORMAT_R8 |
| 52 | + }; |
| 53 | + |
| 54 | +static int v3d_init(struct driver *drv) { |
| 55 | + uint64_t render_use_flags = BO_USE_RENDER_MASK | BO_USE_SCANOUT; |
| 56 | + uint64_t texture_use_flags = BO_USE_TEXTURE_MASK | BO_USE_HW_VIDEO_DECODER; |
| 57 | + uint64_t sw_flags = (BO_USE_RENDERSCRIPT | BO_USE_SW_MASK | BO_USE_LINEAR | BO_USE_FRONT_RENDERING); |
| 58 | + struct format_metadata metadata; |
| 59 | + drv_add_combinations(drv, render_target_formats, ARRAY_SIZE(render_target_formats), |
| 60 | + &LINEAR_METADATA, render_use_flags); |
| 61 | + drv_add_combinations(drv, texture_target_formats, ARRAY_SIZE(texture_target_formats), |
| 62 | + &LINEAR_METADATA, BO_USE_TEXTURE_MASK); |
| 63 | + drv_modify_combination(drv, DRM_FORMAT_YVU420, &LINEAR_METADATA, |
| 64 | + BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE | BO_USE_SCANOUT | |
| 65 | + BO_USE_HW_VIDEO_DECODER | BO_USE_HW_VIDEO_ENCODER); |
| 66 | + drv_modify_combination(drv, DRM_FORMAT_NV12, &LINEAR_METADATA, |
| 67 | + BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE | BO_USE_SCANOUT | |
| 68 | + BO_USE_HW_VIDEO_DECODER | BO_USE_HW_VIDEO_ENCODER); |
| 69 | + drv_modify_combination(drv, DRM_FORMAT_R8, &LINEAR_METADATA, BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE | BO_USE_SCANOUT | |
| 70 | + BO_USE_HW_VIDEO_DECODER | BO_USE_HW_VIDEO_ENCODER); |
| 71 | + |
| 72 | + metadata.tiling = V3D_TILING_UIF_NO_XOR; |
| 73 | + metadata.priority = 2; |
| 74 | + metadata.modifier = DRM_FORMAT_MOD_BROADCOM_UIF; |
| 75 | + render_use_flags &= ~sw_flags; |
| 76 | + texture_use_flags &= ~sw_flags; |
| 77 | + drv_add_combinations(drv, render_target_formats, ARRAY_SIZE(render_target_formats), |
| 78 | + &metadata, BO_USE_RENDER_MASK); |
| 79 | + drv_add_combinations(drv, texture_target_formats, ARRAY_SIZE(texture_target_formats), |
| 80 | + &metadata, texture_use_flags); |
| 81 | + drv_log("v3d driver init.\n"); |
| 82 | + return drv_modify_linear_combinations(drv); |
| 83 | +} |
| 84 | + |
| 85 | +static unsigned u_minify(unsigned value, unsigned levels) { |
| 86 | + return MAX2(1, value >> levels); |
| 87 | +} |
| 88 | + |
| 89 | +static bool util_is_power_of_two_or_zero(unsigned v) { |
| 90 | + return (v & (v - 1)) == 0; |
| 91 | +} |
| 92 | + |
| 93 | +static unsigned util_next_power_of_two(unsigned x) { |
| 94 | + unsigned val = x; |
| 95 | + |
| 96 | + if (x <= 1) |
| 97 | + return 1; |
| 98 | + |
| 99 | + if (util_is_power_of_two_or_zero(x)) |
| 100 | + return x; |
| 101 | + |
| 102 | + val--; |
| 103 | + val = (val >> 1) | val; |
| 104 | + val = (val >> 2) | val; |
| 105 | + val = (val >> 4) | val; |
| 106 | + val = (val >> 8) | val; |
| 107 | + val = (val >> 16) | val; |
| 108 | + val++; |
| 109 | + return val; |
| 110 | +} |
| 111 | + |
| 112 | +static uint32_t v3d_utile_width(int cpp) { |
| 113 | + switch (cpp) { |
| 114 | + case 1: |
| 115 | + case 2: |
| 116 | + return 8; |
| 117 | + case 4: |
| 118 | + case 8: |
| 119 | + return 4; |
| 120 | + case 16: |
| 121 | + return 2; |
| 122 | + default: |
| 123 | + return 4; |
| 124 | + } |
| 125 | +} |
| 126 | + |
| 127 | +/** Return the height in pixels of a 64-byte microtile. */ |
| 128 | +static uint32_t v3d_utile_height(int cpp) { |
| 129 | + switch (cpp) { |
| 130 | + case 1: |
| 131 | + return 8; |
| 132 | + case 2: |
| 133 | + case 4: |
| 134 | + return 4; |
| 135 | + case 8: |
| 136 | + case 16: |
| 137 | + return 2; |
| 138 | + default: |
| 139 | + return 4; |
| 140 | + } |
| 141 | +} |
| 142 | + |
| 143 | +#define V3D_UIFCFG_PAGE_SIZE 4096 |
| 144 | +#define V3D_UBLOCK_SIZE 64 |
| 145 | +#define V3D_UIFBLOCK_SIZE (4 * V3D_UBLOCK_SIZE) |
| 146 | +#define V3D_UIFBLOCK_ROW_SIZE (4 * V3D_UIFBLOCK_SIZE) |
| 147 | +#define PAGE_UB_ROWS (V3D_UIFCFG_PAGE_SIZE / V3D_UIFBLOCK_ROW_SIZE) |
| 148 | +#define PAGE_UB_ROWS_TIMES_1_5 ((PAGE_UB_ROWS * 3) >> 1) |
| 149 | +#define V3D_UIFCFG_PAGE_SIZE 4096 |
| 150 | +#define V3D_UIFCFG_BANKS 8 |
| 151 | +#define V3D_PAGE_CACHE_SIZE (V3D_UIFCFG_PAGE_SIZE * V3D_UIFCFG_BANKS) |
| 152 | +#define PAGE_CACHE_UB_ROWS (V3D_PAGE_CACHE_SIZE / V3D_UIFBLOCK_ROW_SIZE) |
| 153 | +#define PAGE_CACHE_MINUS_1_5_UB_ROWS (PAGE_CACHE_UB_ROWS - PAGE_UB_ROWS_TIMES_1_5) |
| 154 | + |
| 155 | +static uint32_t v3d_get_ub_pad(int cpp, uint32_t height) { |
| 156 | + uint32_t utile_h = v3d_utile_height(cpp); |
| 157 | + uint32_t uif_block_h = utile_h * 2; |
| 158 | + uint32_t height_ub = height / uif_block_h; |
| 159 | + uint32_t height_offset_in_pc = height_ub % PAGE_CACHE_UB_ROWS; |
| 160 | + if (height_offset_in_pc == 0) |
| 161 | + return 0; |
| 162 | + if (height_offset_in_pc < PAGE_UB_ROWS_TIMES_1_5) { |
| 163 | + if (height_ub < PAGE_CACHE_UB_ROWS) |
| 164 | + return 0; |
| 165 | + else |
| 166 | + return PAGE_UB_ROWS_TIMES_1_5 - height_offset_in_pc; |
| 167 | + } |
| 168 | + if (height_offset_in_pc > PAGE_CACHE_MINUS_1_5_UB_ROWS) |
| 169 | + return PAGE_CACHE_UB_ROWS - height_offset_in_pc; |
| 170 | + return 0; |
| 171 | +} |
| 172 | + |
| 173 | +static int v3d_bo_create_for_modifiers(struct bo *bo, uint32_t width, uint32_t height, |
| 174 | + uint32_t format, uint64_t modifier) { |
| 175 | + int ret; |
| 176 | + size_t plane; |
| 177 | + struct drm_v3d_create_bo bo_create; |
| 178 | + int cpp = 4; |
| 179 | + //bool uif_top = true; |
| 180 | + uint32_t pot_width = 2 * util_next_power_of_two(u_minify(width, 1)); |
| 181 | + uint32_t pot_height = 2 * util_next_power_of_two(u_minify(height, 1)); |
| 182 | + uint32_t pot_depth = 2 * util_next_power_of_two(u_minify(1, 1)); |
| 183 | + uint32_t utile_w = v3d_utile_width(cpp); |
| 184 | + uint32_t utile_h = v3d_utile_height(cpp); |
| 185 | + uint32_t uif_block_w = utile_w * 2; |
| 186 | + uint32_t uif_block_h = utile_h * 2; |
| 187 | + uint32_t block_width = 1; |
| 188 | + uint32_t block_height = 1; |
| 189 | + uint32_t offset = 0; |
| 190 | + uint32_t level_width, level_height, level_depth; |
| 191 | + size_t num_planes = drv_num_planes_from_format(format); |
| 192 | + uint32_t page_align_offset = 0; |
| 193 | + |
| 194 | + for (size_t i = 0; i < num_planes; i++) { |
| 195 | + if ( i < 2) { |
| 196 | + level_width = u_minify(width, i); |
| 197 | + level_height = u_minify(height, i); |
| 198 | + } else { |
| 199 | + level_width = u_minify(pot_width, i); |
| 200 | + level_height = u_minify(pot_height, i); |
| 201 | + } |
| 202 | + if (i < 1) |
| 203 | + level_depth = u_minify(1, i); |
| 204 | + else |
| 205 | + level_depth = u_minify(pot_depth, i); |
| 206 | + level_width = DIV_ROUND_UP(level_width, block_width); |
| 207 | + level_height = DIV_ROUND_UP(level_height, block_height); |
| 208 | + if (modifier == DRM_FORMAT_MOD_LINEAR) { |
| 209 | + bo->meta.tiling = V3D_TILING_RASTER; |
| 210 | + level_width = ALIGN(level_width, 8); |
| 211 | + } else { |
| 212 | + level_width = ALIGN(level_width, 4 * uif_block_w); |
| 213 | + level_height = ALIGN(level_height, uif_block_h); |
| 214 | + level_height += v3d_get_ub_pad(cpp, level_height) * uif_block_h; |
| 215 | + if ((level_height / uif_block_h) % (V3D_PAGE_CACHE_SIZE / |
| 216 | + V3D_UIFBLOCK_ROW_SIZE) == 0) { |
| 217 | + bo->meta.tiling = V3D_TILING_UIF_XOR; |
| 218 | + } else { |
| 219 | + bo->meta.tiling = V3D_TILING_UIF_NO_XOR; |
| 220 | + } |
| 221 | + } |
| 222 | + level_width = ALIGN(level_width * 12 / 10, 64); |
| 223 | + level_height = ALIGN(level_height * 12 / 10, 64); |
| 224 | + bo->meta.offsets[i] = offset; |
| 225 | + bo->meta.strides[i] = level_width * cpp; |
| 226 | + bo->meta.sizes[i] = bo->meta.strides[i] * level_height * level_depth; |
| 227 | + if (i == 1 && |
| 228 | + level_width > 4 * uif_block_w && |
| 229 | + level_height > PAGE_CACHE_MINUS_1_5_UB_ROWS * uif_block_h) { |
| 230 | + bo->meta.sizes[i] = ALIGN(bo->meta.sizes[i], |
| 231 | + V3D_UIFCFG_PAGE_SIZE); |
| 232 | + } |
| 233 | + offset += bo->meta.sizes[i]; |
| 234 | + } |
| 235 | + |
| 236 | + page_align_offset = (ALIGN(offset, 4096) - offset); |
| 237 | + if (page_align_offset) { |
| 238 | + offset += page_align_offset; |
| 239 | + for (size_t i = 0; i < num_planes; i++) |
| 240 | + bo->meta.offsets[i] += page_align_offset; |
| 241 | + } |
| 242 | + bo->meta.format_modifier = modifier; |
| 243 | + bo->meta.total_size = offset; |
| 244 | + |
| 245 | + memset(&bo_create, 0, sizeof(bo_create)); |
| 246 | + |
| 247 | + //if (height * width > 1) // don't bother vc4 if only need 1 page. |
| 248 | + bo_create.flags = ARC_CALLOC; |
| 249 | + //else |
| 250 | + // bo_create.flags = 0; |
| 251 | + |
| 252 | + bo_create.size = bo->meta.total_size; |
| 253 | + bo->meta.format_modifier = modifier; |
| 254 | + ret = drmIoctl(bo->drv->fd, DRM_IOCTL_V3D_CREATE_BO, &bo_create); |
| 255 | + if (ret) { |
| 256 | + drv_log("DRM_IOCTL_V3D_CREATE_BO failed (size=%zu), ret=%d, format:%s\n", bo->meta.total_size, ret, getDrmFormatString(format)); |
| 257 | + return -errno; |
| 258 | + } |
| 259 | + |
| 260 | + for (plane = 0; plane < bo->meta.num_planes; plane++) |
| 261 | + bo->handles[plane].u32 = bo_create.handle; |
| 262 | +#ifdef DEBUG |
| 263 | + drv_log("create bo handler:0x%x, size:%zu, format:%s, width:%u, height:%u\n", |
| 264 | + bo_create.handle, bo->meta.total_size, getDrmFormatString(format), width, height); |
| 265 | +#endif |
| 266 | + return 0; |
| 267 | +} |
| 268 | + |
| 269 | +static int v3d_bo_create_with_modifiers(struct bo *bo, uint32_t width, uint32_t height, |
| 270 | + uint32_t format, const uint64_t *modifiers, uint32_t count) { |
| 271 | + static const uint64_t modifier_order[] = { |
| 272 | + DRM_FORMAT_MOD_BROADCOM_UIF, |
| 273 | + DRM_FORMAT_MOD_LINEAR, |
| 274 | + }; |
| 275 | + uint64_t modifier = |
| 276 | + drv_pick_modifier(modifiers, count, modifier_order, ARRAY_SIZE(modifier_order)); |
| 277 | + return v3d_bo_create_for_modifiers(bo, width, height, format, modifier/*modifier*/); |
| 278 | +} |
| 279 | + |
| 280 | +static int v3d_bo_create(struct bo *bo, uint32_t width, uint32_t height, uint32_t format, |
| 281 | + uint64_t use_flags) { |
| 282 | + return v3d_bo_create_for_modifiers(bo, width, height, format, DRM_FORMAT_MOD_LINEAR/*modifier*/); |
| 283 | +} |
| 284 | + |
| 285 | +static void *v3d_bo_map(struct bo *bo, struct vma *vma, size_t plane, uint32_t map_flags) { |
| 286 | + int ret; |
| 287 | + struct drm_v3d_mmap_bo bo_map; |
| 288 | + memset(&bo_map, 0, sizeof(bo_map)); |
| 289 | + bo_map.handle = bo->handles[0].u32; |
| 290 | + |
| 291 | + ret = drmIoctl(bo->drv->fd, DRM_IOCTL_V3D_MMAP_BO, &bo_map); |
| 292 | + if (ret) { |
| 293 | + drv_log("DRM_V3D_MMAP_BO failed\n"); |
| 294 | + return MAP_FAILED; |
| 295 | + } |
| 296 | + vma->length = bo->meta.total_size; |
| 297 | + return mmap(NULL, bo->meta.total_size, drv_get_prot(map_flags), MAP_SHARED, bo->drv->fd, |
| 298 | + bo_map.offset); |
| 299 | +} |
| 300 | + |
| 301 | +static uint32_t v3d_resolve_format(struct driver *drv, uint32_t format, uint64_t use_flags) |
| 302 | +{ |
| 303 | + switch (format) { |
| 304 | + case DRM_FORMAT_FLEX_IMPLEMENTATION_DEFINED: |
| 305 | + if (use_flags & (BO_USE_CAMERA_WRITE | BO_USE_CAMERA_READ)) |
| 306 | + return DRM_FORMAT_NV12; |
| 307 | + |
| 308 | + /*HACK: See b/28671744 */ |
| 309 | + return DRM_FORMAT_XBGR8888; |
| 310 | + case DRM_FORMAT_FLEX_YCbCr_420_888: |
| 311 | + // TODO(hiroh): Switch to use NV12 for video decoder on MT8173 as well. |
| 312 | + if (use_flags & (BO_USE_HW_VIDEO_DECODER)) { |
| 313 | + return DRM_FORMAT_NV12; |
| 314 | + } |
| 315 | + if (use_flags & |
| 316 | + (BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE | BO_USE_HW_VIDEO_ENCODER)) { |
| 317 | + return DRM_FORMAT_NV12; |
| 318 | + } |
| 319 | + return DRM_FORMAT_YVU420; |
| 320 | + default: |
| 321 | + return format; |
| 322 | + } |
| 323 | +} |
| 324 | + |
| 325 | +const struct backend backend_v3d = { |
| 326 | + .name = "v3d", |
| 327 | + .init = v3d_init, |
| 328 | + .bo_create = v3d_bo_create, |
| 329 | + .bo_create_with_modifiers = v3d_bo_create_with_modifiers, |
| 330 | + .bo_import = drv_prime_bo_import, |
| 331 | + .bo_destroy = drv_gem_bo_destroy, |
| 332 | + .bo_map = v3d_bo_map, |
| 333 | + .bo_unmap = drv_bo_munmap, |
| 334 | + .resolve_format = v3d_resolve_format, |
| 335 | +}; |
| 336 | + |
| 337 | +#endif // DEV_V3D |
0 commit comments