| // SPDX-License-Identifier: GPL-2.0-only or MIT | |
| /* Copyright 2025 Arm, Ltd. */ | |
| #include <linux/err.h> | |
| #include <linux/overflow.h> | |
| #include <linux/slab.h> | |
| #include <drm/ethosu_accel.h> | |
| #include "ethosu_device.h" | |
| #include "ethosu_gem.h" | |
| static void ethosu_gem_free_object(struct drm_gem_object *obj) | |
| { | |
| struct ethosu_gem_object *bo = to_ethosu_bo(obj); | |
| kfree(bo->info); | |
| drm_gem_free_mmap_offset(&bo->base.base); | |
| drm_gem_dma_free(&bo->base); | |
| } | |
| static int ethosu_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) | |
| { | |
| struct ethosu_gem_object *bo = to_ethosu_bo(obj); | |
| /* Don't allow mmap on objects that have the NO_MMAP flag set. */ | |
| if (bo->flags & DRM_ETHOSU_BO_NO_MMAP) | |
| return -EINVAL; | |
| return drm_gem_dma_object_mmap(obj, vma); | |
| } | |
| static const struct drm_gem_object_funcs ethosu_gem_funcs = { | |
| .free = ethosu_gem_free_object, | |
| .print_info = drm_gem_dma_object_print_info, | |
| .get_sg_table = drm_gem_dma_object_get_sg_table, | |
| .vmap = drm_gem_dma_object_vmap, | |
| .mmap = ethosu_gem_mmap, | |
| .vm_ops = &drm_gem_dma_vm_ops, | |
| }; | |
| /** | |
| * ethosu_gem_create_object - Implementation of driver->gem_create_object. | |
| * @ddev: DRM device | |
| * @size: Size in bytes of the memory the object will reference | |
| * | |
| * This lets the GEM helpers allocate object structs for us, and keep | |
| * our BO stats correct. | |
| */ | |
| struct drm_gem_object *ethosu_gem_create_object(struct drm_device *ddev, size_t size) | |
| { | |
| struct ethosu_gem_object *obj; | |
| obj = kzalloc_obj(*obj); | |
| if (!obj) | |
| return ERR_PTR(-ENOMEM); | |
| obj->base.base.funcs = ðosu_gem_funcs; | |
| return &obj->base.base; | |
| } | |
| /** | |
| * ethosu_gem_create_with_handle() - Create a GEM object and attach it to a handle. | |
| * @file: DRM file. | |
| * @ddev: DRM device. | |
| * @size: Size of the GEM object to allocate. | |
| * @flags: Combination of drm_ethosu_bo_flags flags. | |
| * @handle: Pointer holding the handle pointing to the new GEM object. | |
| * | |
| * Return: Zero on success | |
| */ | |
| int ethosu_gem_create_with_handle(struct drm_file *file, | |
| struct drm_device *ddev, | |
| u64 *size, u32 flags, u32 *handle) | |
| { | |
| struct drm_gem_dma_object *mem; | |
| struct ethosu_gem_object *bo; | |
| int ret; | |
| mem = drm_gem_dma_create(ddev, *size); | |
| if (IS_ERR(mem)) | |
| return PTR_ERR(mem); | |
| bo = to_ethosu_bo(&mem->base); | |
| bo->flags = flags; | |
| /* | |
| * Allocate an id of idr table where the obj is registered | |
| * and handle has the id what user can see. | |
| */ | |
| ret = drm_gem_handle_create(file, &mem->base, handle); | |
| if (!ret) | |
| *size = bo->base.base.size; | |
| /* drop reference from allocate - handle holds it now. */ | |
| drm_gem_object_put(&mem->base); | |
| return ret; | |
| } | |
| struct dma { | |
| s8 region; | |
| u64 len; | |
| u64 offset; | |
| s64 stride[2]; | |
| }; | |
| struct dma_state { | |
| u16 size0; | |
| u16 size1; | |
| s8 mode; | |
| struct dma src; | |
| struct dma dst; | |
| }; | |
| struct buffer { | |
| u64 base; | |
| u32 length; | |
| s8 region; | |
| }; | |
| struct feat_matrix { | |
| u64 base[4]; | |
| s64 stride_x; | |
| s64 stride_y; | |
| s64 stride_c; | |
| s8 region; | |
| u8 broadcast; | |
| u16 stride_kernel; | |
| u16 precision; | |
| u16 depth; | |
| u16 width; | |
| u16 width0; | |
| u16 height[3]; | |
| u8 pad_top; | |
| u8 pad_left; | |
| u8 pad_bottom; | |
| u8 pad_right; | |
| }; | |
| struct cmd_state { | |
| struct dma_state dma; | |
| struct buffer scale[2]; | |
| struct buffer weight[4]; | |
| struct feat_matrix ofm; | |
| struct feat_matrix ifm; | |
| struct feat_matrix ifm2; | |
| }; | |
| static void cmd_state_init(struct cmd_state *st) | |
| { | |
| /* Initialize to all 1s to detect missing setup */ | |
| memset(st, 0xff, sizeof(*st)); | |
| } | |
| static u64 cmd_to_addr(u32 *cmd) | |
| { | |
| return (((u64)cmd[0] & 0xff0000) << 16) | cmd[1]; | |
| } | |
| static u64 dma_length(struct ethosu_validated_cmdstream_info *info, | |
| struct dma_state *dma_st, struct dma *dma) | |
| { | |
| s8 mode = dma_st->mode; | |
| u64 len = dma->len; | |
| if (len == U64_MAX) | |
| return U64_MAX; | |
| if (mode >= 1) { | |
| if (dma->stride[0] < 0 && (u64)(-dma->stride[0]) > len) | |
| return U64_MAX; | |
| len += dma->stride[0]; | |
| if (check_mul_overflow(len, (u64)dma_st->size0, &len)) | |
| return U64_MAX; | |
| } | |
| if (mode == 2) { | |
| if (dma->stride[1] < 0 && (u64)(-dma->stride[1]) > len) | |
| return U64_MAX; | |
| len += dma->stride[1]; | |
| if (check_mul_overflow(len, (u64)dma_st->size1, &len)) | |
| return U64_MAX; | |
| } | |
| if (dma->region >= 0) { | |
| u64 end; | |
| if (check_add_overflow(len, dma->offset, &end)) | |
| return U64_MAX; | |
| info->region_size[dma->region] = max(info->region_size[dma->region], end); | |
| } | |
| return len; | |
| } | |
| static bool feat_matrix_chained(struct ethosu_device *edev, struct feat_matrix *fm) | |
| { | |
| u32 storage = fm->precision >> 14; | |
| return !ethosu_is_u65(edev) && storage == 2; | |
| } | |
| static u64 feat_matrix_length(struct ethosu_device *edev, | |
| struct ethosu_validated_cmdstream_info *info, | |
| struct feat_matrix *fm, | |
| u32 x, u32 y, u32 c, bool ofm) | |
| { | |
| u32 element_size, storage = ethosu_is_u65(edev) ? 0 : fm->precision >> 14; | |
| int tile = 0; | |
| u64 addr; | |
| if (fm->region < 0) | |
| return U64_MAX; | |
| if (feat_matrix_chained(edev, fm)) | |
| return 0; | |
| switch (storage) { | |
| case 0: | |
| if (x >= fm->width0 + 1) { | |
| x -= fm->width0 + 1; | |
| tile += 1; | |
| } | |
| if (y >= fm->height[tile] + 1) { | |
| y -= fm->height[tile] + 1; | |
| tile += 2; | |
| } | |
| break; | |
| case 1: | |
| if (y >= fm->height[1] + 1) { | |
| y -= fm->height[1] + 1; | |
| tile = 2; | |
| } else if (y >= fm->height[0] + 1) { | |
| y -= fm->height[0] + 1; | |
| tile = 1; | |
| } | |
| break; | |
| default: | |
| return U64_MAX; | |
| } | |
| if (fm->base[tile] == U64_MAX) | |
| return U64_MAX; | |
| addr = fm->base[tile] + y * fm->stride_y; | |
| switch ((fm->precision >> 6) & 0x3) { // format | |
| case 0: //nhwc: | |
| element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3); | |
| addr += x * fm->stride_x + c * element_size; | |
| break; | |
| case 1: //nhcwb16: | |
| element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3); | |
| addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size; | |
| break; | |
| } | |
| info->region_size[fm->region] = max(info->region_size[fm->region], addr + 1); | |
| return addr; | |
| } | |
| static int calc_sizes(struct drm_device *ddev, | |
| struct ethosu_validated_cmdstream_info *info, | |
| u16 op, struct cmd_state *st, | |
| bool ifm, bool ifm2, bool weight, bool scale) | |
| { | |
| struct ethosu_device *edev = to_ethosu_device(ddev); | |
| u64 len; | |
| if (ifm) { | |
| if (st->ifm.stride_kernel == U16_MAX) | |
| return -EINVAL; | |
| u32 stride_y = ((st->ifm.stride_kernel >> 8) & 0x2) + | |
| ((st->ifm.stride_kernel >> 1) & 0x1) + 1; | |
| u32 stride_x = ((st->ifm.stride_kernel >> 5) & 0x2) + | |
| (st->ifm.stride_kernel & 0x1) + 1; | |
| s32 ifm_height = st->ofm.height[2] * stride_y + | |
| st->ifm.height[2] - (st->ifm.pad_top + st->ifm.pad_bottom); | |
| s32 ifm_width = st->ofm.width * stride_x + | |
| st->ifm.width - (st->ifm.pad_left + st->ifm.pad_right); | |
| if (ifm_height < 0 || ifm_width < 0) | |
| return -EINVAL; | |
| len = feat_matrix_length(edev, info, &st->ifm, ifm_width, | |
| ifm_height, st->ifm.depth, false); | |
| dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", | |
| op, st->ifm.region, st->ifm.base[0], len); | |
| if (len == U64_MAX) | |
| return -EINVAL; | |
| } | |
| if (ifm2) { | |
| len = feat_matrix_length(edev, info, &st->ifm2, st->ifm.depth, | |
| 0, st->ofm.depth, false); | |
| dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", | |
| op, st->ifm2.region, st->ifm2.base[0], len); | |
| if (len == U64_MAX) | |
| return -EINVAL; | |
| } | |
| if (weight) { | |
| dev_dbg(ddev->dev, "op %d: W:%d:0x%llx-0x%llx\n", | |
| op, st->weight[0].region, st->weight[0].base, | |
| st->weight[0].base + st->weight[0].length - 1); | |
| if (st->weight[0].region < 0 || st->weight[0].base == U64_MAX || | |
| st->weight[0].length == U32_MAX) | |
| return -EINVAL; | |
| info->region_size[st->weight[0].region] = | |
| max(info->region_size[st->weight[0].region], | |
| st->weight[0].base + st->weight[0].length); | |
| } | |
| if (scale) { | |
| dev_dbg(ddev->dev, "op %d: S:%d:0x%llx-0x%llx\n", | |
| op, st->scale[0].region, st->scale[0].base, | |
| st->scale[0].base + st->scale[0].length - 1); | |
| if (st->scale[0].region < 0 || st->scale[0].base == U64_MAX || | |
| st->scale[0].length == U32_MAX) | |
| return -EINVAL; | |
| info->region_size[st->scale[0].region] = | |
| max(info->region_size[st->scale[0].region], | |
| st->scale[0].base + st->scale[0].length); | |
| } | |
| len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width, | |
| st->ofm.height[2], st->ofm.depth, true); | |
| dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", | |
| op, st->ofm.region, st->ofm.base[0], len); | |
| if (len == U64_MAX) | |
| return -EINVAL; | |
| if (!feat_matrix_chained(edev, &st->ofm)) | |
| info->output_region[st->ofm.region] = true; | |
| return 0; | |
| } | |
| static int calc_sizes_elemwise(struct drm_device *ddev, | |
| struct ethosu_validated_cmdstream_info *info, | |
| u16 op, struct cmd_state *st, | |
| bool ifm, bool ifm2) | |
| { | |
| struct ethosu_device *edev = to_ethosu_device(ddev); | |
| u32 height, width, depth; | |
| u64 len; | |
| if (ifm) { | |
| height = st->ifm.broadcast & 0x1 ? 0 : st->ofm.height[2]; | |
| width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width; | |
| depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth; | |
| len = feat_matrix_length(edev, info, &st->ifm, width, | |
| height, depth, false); | |
| dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", | |
| op, st->ifm.region, st->ifm.base[0], len); | |
| if (len == U64_MAX) | |
| return -EINVAL; | |
| } | |
| if (ifm2) { | |
| height = st->ifm2.broadcast & 0x1 ? 0 : st->ofm.height[2]; | |
| width = st->ifm2.broadcast & 0x2 ? 0 : st->ofm.width; | |
| depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth; | |
| len = feat_matrix_length(edev, info, &st->ifm2, width, | |
| height, depth, false); | |
| dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", | |
| op, st->ifm2.region, st->ifm2.base[0], len); | |
| if (len == U64_MAX) | |
| return -EINVAL; | |
| } | |
| len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width, | |
| st->ofm.height[2], st->ofm.depth, true); | |
| dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", | |
| op, st->ofm.region, st->ofm.base[0], len); | |
| if (len == U64_MAX) | |
| return -EINVAL; | |
| if (!feat_matrix_chained(edev, &st->ofm)) | |
| info->output_region[st->ofm.region] = true; | |
| return 0; | |
| } | |
| static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, | |
| u32 __user *ucmds, | |
| struct ethosu_gem_object *bo, | |
| u32 size) | |
| { | |
| struct ethosu_validated_cmdstream_info __free(kfree) *info = kzalloc_obj(*info); | |
| struct ethosu_device *edev = to_ethosu_device(ddev); | |
| u32 *bocmds = bo->base.vaddr; | |
| bool ends_with_stop = false; | |
| struct cmd_state st; | |
| int i, ret; | |
| if (!info) | |
| return -ENOMEM; | |
| info->cmd_size = size; | |
| cmd_state_init(&st); | |
| for (i = 0; i < size / 4; i++) { | |
| bool use_ifm, use_ifm2, use_scale; | |
| u64 dstlen, srclen; | |
| u16 cmd, param; | |
| u32 cmds[2]; | |
| u64 addr; | |
| if (get_user(cmds[0], ucmds++)) | |
| return -EFAULT; | |
| bocmds[i] = cmds[0]; | |
| cmd = cmds[0]; | |
| param = cmds[0] >> 16; | |
| if (cmd & 0x4000) { | |
| if (get_user(cmds[1], ucmds++)) | |
| return -EFAULT; | |
| i++; | |
| if (i >= size / 4) | |
| return -EINVAL; | |
| bocmds[i] = cmds[1]; | |
| addr = cmd_to_addr(cmds); | |
| } | |
| switch (cmd) { | |
| case NPU_OP_STOP: | |
| if (i != size / 4 - 1) | |
| return -EINVAL; | |
| ends_with_stop = true; | |
| break; | |
| case NPU_OP_DMA_START: | |
| srclen = dma_length(info, &st.dma, &st.dma.src); | |
| dstlen = dma_length(info, &st.dma, &st.dma.dst); | |
| if (srclen == U64_MAX || dstlen == U64_MAX) | |
| return -EINVAL; | |
| if (st.dma.dst.region >= 0) | |
| info->output_region[st.dma.dst.region] = true; | |
| dev_dbg(ddev->dev, "cmd: DMA SRC:%d:0x%llx+0x%llx DST:%d:0x%llx+0x%llx\n", | |
| st.dma.src.region, st.dma.src.offset, srclen, | |
| st.dma.dst.region, st.dma.dst.offset, dstlen); | |
| break; | |
| case NPU_OP_CONV: | |
| case NPU_OP_DEPTHWISE: | |
| use_ifm2 = param & 0x1; // weights_ifm2 | |
| use_scale = !(st.ofm.precision & 0x100); | |
| ret = calc_sizes(ddev, info, cmd, &st, true, use_ifm2, | |
| !use_ifm2, use_scale); | |
| if (ret) | |
| return ret; | |
| break; | |
| case NPU_OP_POOL: | |
| use_ifm = param != 0x4; // pooling mode | |
| use_scale = !(st.ofm.precision & 0x100); | |
| ret = calc_sizes(ddev, info, cmd, &st, use_ifm, false, | |
| false, use_scale); | |
| if (ret) | |
| return ret; | |
| break; | |
| case NPU_OP_ELEMENTWISE: | |
| use_scale = ethosu_is_u65(edev) ? | |
| (st.ifm2.broadcast & 0x80) : | |
| (st.ifm2.broadcast == 8); | |
| use_ifm2 = !(use_scale || (param == 5) || | |
| (param == 6) || (param == 7) || (param == 0x24)); | |
| use_ifm = st.ifm.broadcast != 8; | |
| ret = calc_sizes_elemwise(ddev, info, cmd, &st, use_ifm, use_ifm2); | |
| if (ret) | |
| return ret; | |
| break; | |
| case NPU_OP_RESIZE: // U85 only | |
| return -EINVAL; | |
| case NPU_SET_KERNEL_WIDTH_M1: | |
| st.ifm.width = param; | |
| break; | |
| case NPU_SET_KERNEL_HEIGHT_M1: | |
| st.ifm.height[2] = param; | |
| break; | |
| case NPU_SET_KERNEL_STRIDE: | |
| st.ifm.stride_kernel = param; | |
| break; | |
| case NPU_SET_IFM_PAD_TOP: | |
| st.ifm.pad_top = param & 0x7f; | |
| break; | |
| case NPU_SET_IFM_PAD_LEFT: | |
| st.ifm.pad_left = param & 0x7f; | |
| break; | |
| case NPU_SET_IFM_PAD_RIGHT: | |
| st.ifm.pad_right = param & 0xff; | |
| break; | |
| case NPU_SET_IFM_PAD_BOTTOM: | |
| st.ifm.pad_bottom = param & 0xff; | |
| break; | |
| case NPU_SET_IFM_DEPTH_M1: | |
| st.ifm.depth = param; | |
| break; | |
| case NPU_SET_IFM_PRECISION: | |
| st.ifm.precision = param; | |
| break; | |
| case NPU_SET_IFM_BROADCAST: | |
| st.ifm.broadcast = param; | |
| break; | |
| case NPU_SET_IFM_REGION: | |
| st.ifm.region = param & 0x7; | |
| break; | |
| case NPU_SET_IFM_WIDTH0_M1: | |
| st.ifm.width0 = param; | |
| break; | |
| case NPU_SET_IFM_HEIGHT0_M1: | |
| st.ifm.height[0] = param; | |
| break; | |
| case NPU_SET_IFM_HEIGHT1_M1: | |
| st.ifm.height[1] = param; | |
| break; | |
| case NPU_SET_IFM_BASE0: | |
| case NPU_SET_IFM_BASE1: | |
| case NPU_SET_IFM_BASE2: | |
| case NPU_SET_IFM_BASE3: | |
| st.ifm.base[cmd & 0x3] = addr; | |
| break; | |
| case NPU_SET_IFM_STRIDE_X: | |
| st.ifm.stride_x = addr; | |
| break; | |
| case NPU_SET_IFM_STRIDE_Y: | |
| st.ifm.stride_y = addr; | |
| break; | |
| case NPU_SET_IFM_STRIDE_C: | |
| st.ifm.stride_c = addr; | |
| break; | |
| case NPU_SET_OFM_WIDTH_M1: | |
| st.ofm.width = param; | |
| break; | |
| case NPU_SET_OFM_HEIGHT_M1: | |
| st.ofm.height[2] = param; | |
| break; | |
| case NPU_SET_OFM_DEPTH_M1: | |
| st.ofm.depth = param; | |
| break; | |
| case NPU_SET_OFM_PRECISION: | |
| st.ofm.precision = param; | |
| break; | |
| case NPU_SET_OFM_REGION: | |
| st.ofm.region = param & 0x7; | |
| break; | |
| case NPU_SET_OFM_WIDTH0_M1: | |
| st.ofm.width0 = param; | |
| break; | |
| case NPU_SET_OFM_HEIGHT0_M1: | |
| st.ofm.height[0] = param; | |
| break; | |
| case NPU_SET_OFM_HEIGHT1_M1: | |
| st.ofm.height[1] = param; | |
| break; | |
| case NPU_SET_OFM_BASE0: | |
| case NPU_SET_OFM_BASE1: | |
| case NPU_SET_OFM_BASE2: | |
| case NPU_SET_OFM_BASE3: | |
| st.ofm.base[cmd & 0x3] = addr; | |
| break; | |
| case NPU_SET_OFM_STRIDE_X: | |
| st.ofm.stride_x = addr; | |
| break; | |
| case NPU_SET_OFM_STRIDE_Y: | |
| st.ofm.stride_y = addr; | |
| break; | |
| case NPU_SET_OFM_STRIDE_C: | |
| st.ofm.stride_c = addr; | |
| break; | |
| case NPU_SET_IFM2_BROADCAST: | |
| st.ifm2.broadcast = param; | |
| break; | |
| case NPU_SET_IFM2_PRECISION: | |
| st.ifm2.precision = param; | |
| break; | |
| case NPU_SET_IFM2_REGION: | |
| st.ifm2.region = param & 0x7; | |
| break; | |
| case NPU_SET_IFM2_WIDTH0_M1: | |
| st.ifm2.width0 = param; | |
| break; | |
| case NPU_SET_IFM2_HEIGHT0_M1: | |
| st.ifm2.height[0] = param; | |
| break; | |
| case NPU_SET_IFM2_HEIGHT1_M1: | |
| st.ifm2.height[1] = param; | |
| break; | |
| case NPU_SET_IFM2_BASE0: | |
| case NPU_SET_IFM2_BASE1: | |
| case NPU_SET_IFM2_BASE2: | |
| case NPU_SET_IFM2_BASE3: | |
| st.ifm2.base[cmd & 0x3] = addr; | |
| break; | |
| case NPU_SET_IFM2_STRIDE_X: | |
| st.ifm2.stride_x = addr; | |
| break; | |
| case NPU_SET_IFM2_STRIDE_Y: | |
| st.ifm2.stride_y = addr; | |
| break; | |
| case NPU_SET_IFM2_STRIDE_C: | |
| st.ifm2.stride_c = addr; | |
| break; | |
| case NPU_SET_WEIGHT_REGION: | |
| st.weight[0].region = param & 0x7; | |
| break; | |
| case NPU_SET_SCALE_REGION: | |
| st.scale[0].region = param & 0x7; | |
| break; | |
| case NPU_SET_WEIGHT_BASE: | |
| st.weight[0].base = addr; | |
| break; | |
| case NPU_SET_WEIGHT_LENGTH: | |
| st.weight[0].length = cmds[1]; | |
| break; | |
| case NPU_SET_SCALE_BASE: | |
| st.scale[0].base = addr; | |
| break; | |
| case NPU_SET_SCALE_LENGTH: | |
| st.scale[0].length = cmds[1]; | |
| break; | |
| case NPU_SET_WEIGHT1_BASE: | |
| st.weight[1].base = addr; | |
| break; | |
| case NPU_SET_WEIGHT1_LENGTH: | |
| st.weight[1].length = cmds[1]; | |
| break; | |
| case NPU_SET_SCALE1_BASE: // NPU_SET_WEIGHT2_BASE (U85) | |
| if (ethosu_is_u65(edev)) | |
| st.scale[1].base = addr; | |
| else | |
| st.weight[2].base = addr; | |
| break; | |
| case NPU_SET_SCALE1_LENGTH: // NPU_SET_WEIGHT2_LENGTH (U85) | |
| if (ethosu_is_u65(edev)) | |
| st.scale[1].length = cmds[1]; | |
| else | |
| st.weight[2].length = cmds[1]; | |
| break; | |
| case NPU_SET_WEIGHT3_BASE: | |
| st.weight[3].base = addr; | |
| break; | |
| case NPU_SET_WEIGHT3_LENGTH: | |
| st.weight[3].length = cmds[1]; | |
| break; | |
| case NPU_SET_DMA0_SRC_REGION: | |
| if (param & 0x100) | |
| st.dma.src.region = -1; | |
| else | |
| st.dma.src.region = param & 0x7; | |
| st.dma.mode = (param >> 9) & 0x3; | |
| break; | |
| case NPU_SET_DMA0_DST_REGION: | |
| if (param & 0x100) | |
| st.dma.dst.region = -1; | |
| else | |
| st.dma.dst.region = param & 0x7; | |
| break; | |
| case NPU_SET_DMA0_SIZE0: | |
| st.dma.size0 = param; | |
| break; | |
| case NPU_SET_DMA0_SIZE1: | |
| st.dma.size1 = param; | |
| break; | |
| case NPU_SET_DMA0_SRC_STRIDE0: | |
| st.dma.src.stride[0] = ((s64)addr << 24) >> 24; | |
| break; | |
| case NPU_SET_DMA0_SRC_STRIDE1: | |
| st.dma.src.stride[1] = ((s64)addr << 24) >> 24; | |
| break; | |
| case NPU_SET_DMA0_DST_STRIDE0: | |
| st.dma.dst.stride[0] = ((s64)addr << 24) >> 24; | |
| break; | |
| case NPU_SET_DMA0_DST_STRIDE1: | |
| st.dma.dst.stride[1] = ((s64)addr << 24) >> 24; | |
| break; | |
| case NPU_SET_DMA0_SRC: | |
| st.dma.src.offset = addr; | |
| break; | |
| case NPU_SET_DMA0_DST: | |
| st.dma.dst.offset = addr; | |
| break; | |
| case NPU_SET_DMA0_LEN: | |
| st.dma.src.len = st.dma.dst.len = addr; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| if (!ends_with_stop) | |
| return -EINVAL; | |
| for (i = 0; i < NPU_BASEP_REGION_MAX; i++) { | |
| if (!info->region_size[i]) | |
| continue; | |
| dev_dbg(ddev->dev, "region %d max size: 0x%llx\n", | |
| i, info->region_size[i]); | |
| } | |
| bo->info = no_free_ptr(info); | |
| return 0; | |
| } | |
| /** | |
| * ethosu_gem_cmdstream_create() - Create a GEM object and attach it to a handle. | |
| * @file: DRM file. | |
| * @ddev: DRM device. | |
| * @exclusive_vm: Exclusive VM. Not NULL if the GEM object can't be shared. | |
| * @size: Size of the GEM object to allocate. | |
| * @flags: Combination of drm_ethosu_bo_flags flags. | |
| * @handle: Pointer holding the handle pointing to the new GEM object. | |
| * | |
| * Return: Zero on success | |
| */ | |
| int ethosu_gem_cmdstream_create(struct drm_file *file, | |
| struct drm_device *ddev, | |
| u32 size, u64 data, u32 flags, u32 *handle) | |
| { | |
| int ret; | |
| struct drm_gem_dma_object *mem; | |
| struct ethosu_gem_object *bo; | |
| mem = drm_gem_dma_create(ddev, size); | |
| if (IS_ERR(mem)) | |
| return PTR_ERR(mem); | |
| bo = to_ethosu_bo(&mem->base); | |
| bo->flags = flags; | |
| ret = ethosu_gem_cmdstream_copy_and_validate(ddev, | |
| (void __user *)(uintptr_t)data, | |
| bo, size); | |
| if (ret) | |
| goto fail; | |
| /* | |
| * Allocate an id of idr table where the obj is registered | |
| * and handle has the id what user can see. | |
| */ | |
| ret = drm_gem_handle_create(file, &mem->base, handle); | |
| fail: | |
| /* drop reference from allocate - handle holds it now. */ | |
| drm_gem_object_put(&mem->base); | |
| return ret; | |
| } |