Files
linux/drivers/media/platform/qcom/venus/hfi.c
Thomas Gleixner 97fb5e8d9b treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 284
Based on 1 normalized pattern(s):

  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

extracted by the scancode license scanner the SPDX license identifier

  GPL-2.0-only

has been chosen to replace the boilerplate/reference in 294 file(s).

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Allison Randal <allison@lohutok.net>
Reviewed-by: Alexios Zavras <alexios.zavras@intel.com>
Cc: linux-spdx@vger.kernel.org
Link: https://lkml.kernel.org/r/20190529141900.825281744@linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-06-05 17:36:37 +02:00

511 lines
9.2 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
* Copyright (C) 2017 Linaro Ltd.
*/
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/completion.h>
#include <linux/platform_device.h>
#include <linux/videodev2.h>
#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;
case V4L2_PIX_FMT_HEVC:
return HFI_VIDEO_CODEC_HEVC;
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_var_event(&core->insts_count,
!atomic_read(&core->insts_count));
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;
int ret;
inst->hfi_codec = to_codec_type(pixfmt);
reinit_completion(&inst->done);
ret = ops->session_init(inst, inst->session_type, inst->hfi_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);
if (atomic_dec_and_test(&core->insts_count))
wake_up_var(&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_1XX)
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 ||
fd->buffer_type == HFI_BUFFER_OUTPUT2)
return ops->session_ftb(inst, fd);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(hfi_session_process_buf);
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);
}