/* * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * Copyright (C) 2017 Linaro Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include "core.h" #include "hfi.h" #include "hfi_cmds.h" #include "hfi_venus.h" #define TIMEOUT msecs_to_jiffies(1000) static u32 to_codec_type(u32 pixfmt) { switch (pixfmt) { case V4L2_PIX_FMT_H264: case V4L2_PIX_FMT_H264_NO_SC: return HFI_VIDEO_CODEC_H264; case V4L2_PIX_FMT_H263: return HFI_VIDEO_CODEC_H263; case V4L2_PIX_FMT_MPEG1: return HFI_VIDEO_CODEC_MPEG1; case V4L2_PIX_FMT_MPEG2: return HFI_VIDEO_CODEC_MPEG2; case V4L2_PIX_FMT_MPEG4: return HFI_VIDEO_CODEC_MPEG4; case V4L2_PIX_FMT_VC1_ANNEX_G: case V4L2_PIX_FMT_VC1_ANNEX_L: return HFI_VIDEO_CODEC_VC1; case V4L2_PIX_FMT_VP8: return HFI_VIDEO_CODEC_VP8; case V4L2_PIX_FMT_VP9: return HFI_VIDEO_CODEC_VP9; case V4L2_PIX_FMT_XVID: return HFI_VIDEO_CODEC_DIVX; default: return 0; } } int hfi_core_init(struct venus_core *core) { int ret = 0; mutex_lock(&core->lock); if (core->state >= CORE_INIT) goto unlock; reinit_completion(&core->done); ret = core->ops->core_init(core); if (ret) goto unlock; ret = wait_for_completion_timeout(&core->done, TIMEOUT); if (!ret) { ret = -ETIMEDOUT; goto unlock; } ret = 0; if (core->error != HFI_ERR_NONE) { ret = -EIO; goto unlock; } core->state = CORE_INIT; unlock: mutex_unlock(&core->lock); return ret; } int hfi_core_deinit(struct venus_core *core, bool blocking) { int ret = 0, empty; mutex_lock(&core->lock); if (core->state == CORE_UNINIT) goto unlock; empty = list_empty(&core->instances); if (!empty && !blocking) { ret = -EBUSY; goto unlock; } if (!empty) { mutex_unlock(&core->lock); wait_on_atomic_t(&core->insts_count, atomic_t_wait, TASK_UNINTERRUPTIBLE); mutex_lock(&core->lock); } ret = core->ops->core_deinit(core); if (!ret) core->state = CORE_UNINIT; unlock: mutex_unlock(&core->lock); return ret; } int hfi_core_suspend(struct venus_core *core) { if (core->state != CORE_INIT) return 0; return core->ops->suspend(core); } int hfi_core_resume(struct venus_core *core, bool force) { if (!force && core->state != CORE_INIT) return 0; return core->ops->resume(core); } int hfi_core_trigger_ssr(struct venus_core *core, u32 type) { return core->ops->core_trigger_ssr(core, type); } int hfi_core_ping(struct venus_core *core) { int ret; mutex_lock(&core->lock); ret = core->ops->core_ping(core, 0xbeef); if (ret) goto unlock; ret = wait_for_completion_timeout(&core->done, TIMEOUT); if (!ret) { ret = -ETIMEDOUT; goto unlock; } ret = 0; if (core->error != HFI_ERR_NONE) ret = -ENODEV; unlock: mutex_unlock(&core->lock); return ret; } static int wait_session_msg(struct venus_inst *inst) { int ret; ret = wait_for_completion_timeout(&inst->done, TIMEOUT); if (!ret) return -ETIMEDOUT; if (inst->error != HFI_ERR_NONE) return -EIO; return 0; } int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops) { struct venus_core *core = inst->core; if (!ops) return -EINVAL; inst->state = INST_UNINIT; init_completion(&inst->done); inst->ops = ops; mutex_lock(&core->lock); list_add_tail(&inst->list, &core->instances); atomic_inc(&core->insts_count); mutex_unlock(&core->lock); return 0; } EXPORT_SYMBOL_GPL(hfi_session_create); int hfi_session_init(struct venus_inst *inst, u32 pixfmt) { struct venus_core *core = inst->core; const struct hfi_ops *ops = core->ops; u32 codec; int ret; codec = to_codec_type(pixfmt); reinit_completion(&inst->done); ret = ops->session_init(inst, inst->session_type, codec); if (ret) return ret; ret = wait_session_msg(inst); if (ret) return ret; inst->state = INST_INIT; return 0; } EXPORT_SYMBOL_GPL(hfi_session_init); void hfi_session_destroy(struct venus_inst *inst) { struct venus_core *core = inst->core; mutex_lock(&core->lock); list_del_init(&inst->list); atomic_dec(&core->insts_count); wake_up_atomic_t(&core->insts_count); mutex_unlock(&core->lock); } EXPORT_SYMBOL_GPL(hfi_session_destroy); int hfi_session_deinit(struct venus_inst *inst) { const struct hfi_ops *ops = inst->core->ops; int ret; if (inst->state == INST_UNINIT) return 0; if (inst->state < INST_INIT) return -EINVAL; reinit_completion(&inst->done); ret = ops->session_end(inst); if (ret) return ret; ret = wait_session_msg(inst); if (ret) return ret; inst->state = INST_UNINIT; return 0; } EXPORT_SYMBOL_GPL(hfi_session_deinit); int hfi_session_start(struct venus_inst *inst) { const struct hfi_ops *ops = inst->core->ops; int ret; if (inst->state != INST_LOAD_RESOURCES) return -EINVAL; reinit_completion(&inst->done); ret = ops->session_start(inst); if (ret) return ret; ret = wait_session_msg(inst); if (ret) return ret; inst->state = INST_START; return 0; } int hfi_session_stop(struct venus_inst *inst) { const struct hfi_ops *ops = inst->core->ops; int ret; if (inst->state != INST_START) return -EINVAL; reinit_completion(&inst->done); ret = ops->session_stop(inst); if (ret) return ret; ret = wait_session_msg(inst); if (ret) return ret; inst->state = INST_STOP; return 0; } int hfi_session_continue(struct venus_inst *inst) { struct venus_core *core = inst->core; if (core->res->hfi_version != HFI_VERSION_3XX) return 0; return core->ops->session_continue(inst); } EXPORT_SYMBOL_GPL(hfi_session_continue); int hfi_session_abort(struct venus_inst *inst) { const struct hfi_ops *ops = inst->core->ops; int ret; reinit_completion(&inst->done); ret = ops->session_abort(inst); if (ret) return ret; ret = wait_session_msg(inst); if (ret) return ret; return 0; } int hfi_session_load_res(struct venus_inst *inst) { const struct hfi_ops *ops = inst->core->ops; int ret; if (inst->state != INST_INIT) return -EINVAL; reinit_completion(&inst->done); ret = ops->session_load_res(inst); if (ret) return ret; ret = wait_session_msg(inst); if (ret) return ret; inst->state = INST_LOAD_RESOURCES; return 0; } int hfi_session_unload_res(struct venus_inst *inst) { const struct hfi_ops *ops = inst->core->ops; int ret; if (inst->state != INST_STOP) return -EINVAL; reinit_completion(&inst->done); ret = ops->session_release_res(inst); if (ret) return ret; ret = wait_session_msg(inst); if (ret) return ret; inst->state = INST_RELEASE_RESOURCES; return 0; } int hfi_session_flush(struct venus_inst *inst) { const struct hfi_ops *ops = inst->core->ops; int ret; reinit_completion(&inst->done); ret = ops->session_flush(inst, HFI_FLUSH_ALL); if (ret) return ret; ret = wait_session_msg(inst); if (ret) return ret; return 0; } EXPORT_SYMBOL_GPL(hfi_session_flush); int hfi_session_set_buffers(struct venus_inst *inst, struct hfi_buffer_desc *bd) { const struct hfi_ops *ops = inst->core->ops; return ops->session_set_buffers(inst, bd); } int hfi_session_unset_buffers(struct venus_inst *inst, struct hfi_buffer_desc *bd) { const struct hfi_ops *ops = inst->core->ops; int ret; reinit_completion(&inst->done); ret = ops->session_unset_buffers(inst, bd); if (ret) return ret; if (!bd->response_required) return 0; ret = wait_session_msg(inst); if (ret) return ret; return 0; } int hfi_session_get_property(struct venus_inst *inst, u32 ptype, union hfi_get_property *hprop) { const struct hfi_ops *ops = inst->core->ops; int ret; if (inst->state < INST_INIT || inst->state >= INST_STOP) return -EINVAL; reinit_completion(&inst->done); ret = ops->session_get_property(inst, ptype); if (ret) return ret; ret = wait_session_msg(inst); if (ret) return ret; *hprop = inst->hprop; return 0; } EXPORT_SYMBOL_GPL(hfi_session_get_property); int hfi_session_set_property(struct venus_inst *inst, u32 ptype, void *pdata) { const struct hfi_ops *ops = inst->core->ops; if (inst->state < INST_INIT || inst->state >= INST_STOP) return -EINVAL; return ops->session_set_property(inst, ptype, pdata); } EXPORT_SYMBOL_GPL(hfi_session_set_property); int hfi_session_process_buf(struct venus_inst *inst, struct hfi_frame_data *fd) { const struct hfi_ops *ops = inst->core->ops; if (fd->buffer_type == HFI_BUFFER_INPUT) return ops->session_etb(inst, fd); else if (fd->buffer_type == HFI_BUFFER_OUTPUT) return ops->session_ftb(inst, fd); return -EINVAL; } irqreturn_t hfi_isr_thread(int irq, void *dev_id) { struct venus_core *core = dev_id; return core->ops->isr_thread(core); } irqreturn_t hfi_isr(int irq, void *dev) { struct venus_core *core = dev; return core->ops->isr(core); } int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops) { int ret; if (!ops) return -EINVAL; atomic_set(&core->insts_count, 0); core->core_ops = ops; core->state = CORE_UNINIT; init_completion(&core->done); pkt_set_version(core->res->hfi_version); ret = venus_hfi_create(core); return ret; } void hfi_destroy(struct venus_core *core) { venus_hfi_destroy(core); }