// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. */ #include #include #include #include "vpu_cmd.h" #include "vpu_cmn.h" #include "vpu_algo.h" #include "vpu_debug.h" static uint32_t prop_info_data_length; const size_t g_vpu_prop_type_size[VPU_NUM_PROP_TYPES] = { [VPU_PROP_TYPE_CHAR] = sizeof(char), [VPU_PROP_TYPE_INT32] = sizeof(int32_t), [VPU_PROP_TYPE_FLOAT] = sizeof(int32_t), [VPU_PROP_TYPE_INT64] = sizeof(int64_t), [VPU_PROP_TYPE_DOUBLE] = sizeof(int64_t) }; struct vpu_prop_desc g_vpu_prop_descs[VPU_NUM_PROPS] = { #define INS_PROP(id, name, type, count, access) \ { VPU_PROP_ ## id, VPU_PROP_TYPE_ ## type, \ VPU_PROP_ACCESS_ ## access, 0, count, name } INS_PROP(RESERVED, "reserved", INT32, 256, RDONLY), #undef INS_PROP }; /* called by vpu_init(), calculating prop_info_data_length */ void vpu_init_algo(void) { int i = 0; unsigned int offset = 0; unsigned int prop_data_length; struct vpu_prop_desc *prop_desc; for (i = 0; i < VPU_NUM_PROPS; i++) { prop_desc = &g_vpu_prop_descs[i]; prop_desc->offset = offset; prop_data_length = prop_desc->count * g_vpu_prop_type_size[prop_desc->type]; offset += prop_data_length; } /* the total length = last offset + last data length */ prop_info_data_length = offset; vpu_alg_debug("%s: prop_info_data_length: %x\n", __func__, prop_info_data_length); } static struct __vpu_algo *vpu_alg_get(struct vpu_algo_list *al, const char *name, struct __vpu_algo *alg) { if (alg) { kref_get(&alg->ref); goto out; } if (!name) goto out; /* search from tail, so that existing algorithm can be * overidden by dynamic loaded ones. **/ spin_lock(&al->lock); list_for_each_entry_reverse(alg, &al->a, list) { if (!strcmp(alg->a.name, name)) { /* found, reference count++ */ kref_get(&alg->ref); goto unlock; } } alg = NULL; unlock: spin_unlock(&al->lock); out: if (alg) vpu_alg_debug("%s: vpu%d: %s: %s: ref: %d builtin: %d\n", __func__, al->vd->id, al->name, alg->a.name, kref_read(&alg->ref), alg->builtin); else vpu_alg_debug("%s: vpu%d: %s: %s was not found\n", __func__, al->vd->id, al->name, name); return alg; } static void vpu_alg_release(struct kref *ref) { struct __vpu_algo *alg = container_of(ref, struct __vpu_algo, ref); struct vpu_algo_list *al = alg->al; spin_lock(&al->lock); list_del(&alg->list); al->cnt--; spin_unlock(&al->lock); vpu_alg_debug("%s: vpu%d: %s: %s, algo_cnt: %d builtin: %d\n", __func__, al->vd->id, al->name, alg->a.name, al->cnt, alg->builtin); /* free __vpu_algo memory */ vpu_alg_free(container_of(ref, struct __vpu_algo, ref)); } static void vpu_alg_put(struct __vpu_algo *alg) { vpu_alg_debug("%s: vpu%d: %s: %s: ref: %d builtin: %d\n", __func__, alg->al->vd->id, alg->al->name, alg->a.name, kref_read(&alg->ref), alg->builtin); kref_put(&alg->ref, alg->al->ops->release); } /** * vpu_alg_unload() - unload currently loaded algorithm of * command priority "prio" from vpu * @vd: vpu device * @prio: command priority * * vpu_cmd_lock() must be locked before calling this function */ static void vpu_alg_unload(struct vpu_algo_list *al, int prio) { struct __vpu_algo *alg; if (!al) return; alg = vpu_cmd_alg(al->vd, prio); if (!alg) return; al->ops->put(alg); vpu_cmd_alg_clr(al->vd, prio); } /** * vpu_alg_load() - load an algortihm for normal priority(0) * d2d execution * @vd: vpu device * * Automatically unload currently loaded algortihm, and * load given one. * vpu_cmd_lock() must be locked before calling this function */ static int vpu_alg_load(struct vpu_algo_list *al, const char *name, struct __vpu_algo *alg, int prio) { int ret = 0; alg = al->ops->get(al, name, alg); if (!alg) { vpu_alg_debug("%s: vpu%d: %s: \"%s\" was not found\n", __func__, al->vd->id, al->name, name); return -ENOENT; } al->ops->unload(al, prio); if (al->ops->hw_init) { ret = al->ops->hw_init(al, alg); if (ret) { pr_info("%s: vpu%d: %s: hw_init: %d\n", __func__, al->vd->id, al->name, ret); al->ops->put(alg); goto out; } } vpu_cmd_alg_set(al->vd, prio, alg); out: return ret; } struct __vpu_algo *vpu_alg_alloc(struct vpu_algo_list *al) { struct __vpu_algo *algo; algo = kzalloc(sizeof(struct __vpu_algo) + prop_info_data_length, GFP_KERNEL); if (!algo) return NULL; algo->a.info.ptr = (uintptr_t) algo + sizeof(struct __vpu_algo); algo->a.info.length = prop_info_data_length; algo->builtin = false; algo->al = al; INIT_LIST_HEAD(&algo->list); kref_init(&algo->ref); /* init count = 1 */ return algo; } void vpu_alg_free(struct __vpu_algo *alg) { struct vpu_device *vd = alg->al->vd; struct device *dev = vd->dev; struct vpu_mem_ops *mops = vd_mops(vd); mops->free(dev, &alg->prog); mops->free(dev, &alg->iram); kfree(alg); } int vpu_alloc_request(struct vpu_request **rreq) { struct vpu_request *req; req = kzalloc(sizeof(struct vpu_request), GFP_KERNEL); if (!req) return -ENOMEM; *rreq = req; return 0; } int vpu_free_request(struct vpu_request *req) { if (req != NULL) kfree(req); return 0; } struct vpu_algo_ops vpu_normal_aops = { .load = vpu_alg_load, .unload = vpu_alg_unload, .get = vpu_alg_get, .put = vpu_alg_put, .release = vpu_alg_release, .hw_init = vpu_hw_alg_init, }; struct vpu_algo_ops vpu_prelaod_aops = { .load = vpu_alg_load, .unload = vpu_alg_unload, .get = vpu_alg_get, .put = vpu_alg_put, .release = vpu_alg_release, .hw_init = NULL, };