// SPDX-License-Identifier: GPL-2.0
/*
* Hantro VPU codec driver
*
* Copyright (C) 2018 Collabora, Ltd.
* Copyright 2018 Google LLC.
* Tomasz Figa <tfiga@chromium.org>
*
* Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/workqueue.h>
#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-vmalloc.h>
#include "hantro_v4l2.h"
#include "hantro.h"
#include "hantro_hw.h"
#define DRIVER_NAME "hantro-vpu"
int hantro_debug;
module_param_named(debug, hantro_debug, int, 0644);
MODULE_PARM_DESC(debug,
"Debug level - higher value produces more verbose messages");
void *hantro_get_ctrl(struct hantro_ctx *ctx, u32 id)
{
struct v4l2_ctrl *ctrl;
ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, id);
return ctrl ? ctrl->p_cur.p : NULL;
}
dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts)
{
struct vb2_queue *q = v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx);
struct vb2_buffer *buf;
int index;
index = vb2_find_timestamp(q, ts, 0);
if (index < 0)
return 0;
buf = vb2_get_buffer(q, index);
return hantro_get_dec_buf_addr(ctx, buf);
}
static int
hantro_enc_buf_finish(struct hantro_ctx *ctx, struct vb2_buffer *buf,
unsigned int bytesused)
{
size_t avail_size;
avail_size = vb2_plane_size(buf, 0) - ctx->vpu_dst_fmt->header_size;
if (bytesused > avail_size)
return -EINVAL;
/*
* The bounce buffer is only for the JPEG encoder.
* TODO: Rework the JPEG encoder to eliminate the need
* for a bounce buffer.
*/
if (ctx->jpeg_enc.bounce_buffer.cpu) {
memcpy(vb2_plane_vaddr(buf, 0) +
ctx->vpu_dst_fmt->header_size,
ctx->jpeg_enc.bounce_buffer.cpu, bytesused);
}
buf->planes[0].bytesused =
ctx->vpu_dst_fmt->header_size + bytesused;
return 0;
}
static int
hantro_dec_buf_finish(struct hantro_ctx *ctx, struct vb2_buffer *buf,
unsigned int bytesused)
{
/* For decoders set bytesused as per the output picture. */
buf->planes[0].bytesused = ctx->dst_fmt.plane_fmt[0].sizeimage;
return 0;
}
static void hantro_job_finish(struct hantro_dev *vpu,
struct hantro_ctx *ctx,
unsigned int bytesused,
enum vb2_buffer_state result)
{
struct vb2_v4l2_buffer *src, *dst;
int ret;
pm_runtime_mark_last_busy(vpu->dev);
pm_runtime_put_autosuspend(vpu->dev);
clk_bulk_disable(vpu->variant->num_clocks, vpu->clocks);
src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
if (WARN_ON(!src))
return;
if (WARN_ON(!dst))
return;
src->sequence = ctx->sequence_out++;
dst->sequence = ctx->sequence_cap++;
ret = ctx->buf_finish(ctx, &dst->vb2_buf, bytesused);
if (ret)
result = VB2_BUF_STATE_ERROR;
v4l2_m2m_buf_done(src, result);
v4l2_m2m_buf_done(dst, result);
v4l2_m2m_job_finish(vpu->m2m_dev, ctx->fh.m2m_ctx);
}
void hantro_irq_done(struct hantro_dev *vpu, unsigned int bytesused,
enum vb2_buffer_state result)
{
struct hantro_ctx *ctx =
v4l2_m2m_get_curr_priv(vpu->m2m_dev);
/*
* If cancel_delayed_work returns false
* the timeout expired. The watchdog is running,
* and will take care of finishing the job.
*/
if (cancel_delayed_work(&vpu->watchdog_work))
hantro_job_finish(vpu, ctx, bytesused, result);
}
void hantro_watchdog(struct work_struct *work)
{
struct hantro_dev *vpu;
struct hantro_ctx *ctx;
vpu = container_of(to_delayed_work(work),
struct hantro_dev, watchdog_work);
ctx = v4l2_m2m_get_curr_priv(vpu->m2m_dev);
if (ctx) {
vpu_err("frame processing timed out!\n");
ctx->codec_ops->reset(ctx);
hantro_job_finish(vpu, ctx, 0, VB2_BUF_STATE_ERROR);
}
}
void hantro_start_prepare_run(struct hantro_ctx *ctx)
{
struct vb2_v4l2_buffer *src_buf;
src_buf = hantro_get_src_buf(ctx);
v4l2_ctrl_request_setup(