18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Fuzhou Rockchip Electronics Co.Ltd
48c2ecf20Sopenharmony_ci * Author: Jacob Chen <jacob-chen@iotwrt.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
108c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
118c2ecf20Sopenharmony_ci#include <media/v4l2-mem2mem.h>
128c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-sg.h>
138c2ecf20Sopenharmony_ci#include <media/videobuf2-v4l2.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "rga-hw.h"
168c2ecf20Sopenharmony_ci#include "rga.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic int
198c2ecf20Sopenharmony_cirga_queue_setup(struct vb2_queue *vq,
208c2ecf20Sopenharmony_ci		unsigned int *nbuffers, unsigned int *nplanes,
218c2ecf20Sopenharmony_ci		unsigned int sizes[], struct device *alloc_devs[])
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	struct rga_ctx *ctx = vb2_get_drv_priv(vq);
248c2ecf20Sopenharmony_ci	struct rga_frame *f = rga_get_frame(ctx, vq->type);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	if (IS_ERR(f))
278c2ecf20Sopenharmony_ci		return PTR_ERR(f);
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	if (*nplanes)
308c2ecf20Sopenharmony_ci		return sizes[0] < f->size ? -EINVAL : 0;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	sizes[0] = f->size;
338c2ecf20Sopenharmony_ci	*nplanes = 1;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return 0;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int rga_buf_prepare(struct vb2_buffer *vb)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
418c2ecf20Sopenharmony_ci	struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	if (IS_ERR(f))
448c2ecf20Sopenharmony_ci		return PTR_ERR(f);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	vb2_set_plane_payload(vb, 0, f->size);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	return 0;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void rga_buf_queue(struct vb2_buffer *vb)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
548c2ecf20Sopenharmony_ci	struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic void rga_buf_return_buffers(struct vb2_queue *q,
608c2ecf20Sopenharmony_ci				   enum vb2_buffer_state state)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct rga_ctx *ctx = vb2_get_drv_priv(q);
638c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	for (;;) {
668c2ecf20Sopenharmony_ci		if (V4L2_TYPE_IS_OUTPUT(q->type))
678c2ecf20Sopenharmony_ci			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
688c2ecf20Sopenharmony_ci		else
698c2ecf20Sopenharmony_ci			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
708c2ecf20Sopenharmony_ci		if (!vbuf)
718c2ecf20Sopenharmony_ci			break;
728c2ecf20Sopenharmony_ci		v4l2_m2m_buf_done(vbuf, state);
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct rga_ctx *ctx = vb2_get_drv_priv(q);
798c2ecf20Sopenharmony_ci	struct rockchip_rga *rga = ctx->rga;
808c2ecf20Sopenharmony_ci	int ret;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	ret = pm_runtime_resume_and_get(rga->dev);
838c2ecf20Sopenharmony_ci	if (ret < 0) {
848c2ecf20Sopenharmony_ci		rga_buf_return_buffers(q, VB2_BUF_STATE_QUEUED);
858c2ecf20Sopenharmony_ci		return ret;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	return 0;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void rga_buf_stop_streaming(struct vb2_queue *q)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct rga_ctx *ctx = vb2_get_drv_priv(q);
948c2ecf20Sopenharmony_ci	struct rockchip_rga *rga = ctx->rga;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	rga_buf_return_buffers(q, VB2_BUF_STATE_ERROR);
978c2ecf20Sopenharmony_ci	pm_runtime_put(rga->dev);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ciconst struct vb2_ops rga_qops = {
1018c2ecf20Sopenharmony_ci	.queue_setup = rga_queue_setup,
1028c2ecf20Sopenharmony_ci	.buf_prepare = rga_buf_prepare,
1038c2ecf20Sopenharmony_ci	.buf_queue = rga_buf_queue,
1048c2ecf20Sopenharmony_ci	.wait_prepare = vb2_ops_wait_prepare,
1058c2ecf20Sopenharmony_ci	.wait_finish = vb2_ops_wait_finish,
1068c2ecf20Sopenharmony_ci	.start_streaming = rga_buf_start_streaming,
1078c2ecf20Sopenharmony_ci	.stop_streaming = rga_buf_stop_streaming,
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/* RGA MMU is a 1-Level MMU, so it can't be used through the IOMMU API.
1118c2ecf20Sopenharmony_ci * We use it more like a scatter-gather list.
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_civoid rga_buf_map(struct vb2_buffer *vb)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
1168c2ecf20Sopenharmony_ci	struct rockchip_rga *rga = ctx->rga;
1178c2ecf20Sopenharmony_ci	struct sg_table *sgt;
1188c2ecf20Sopenharmony_ci	struct scatterlist *sgl;
1198c2ecf20Sopenharmony_ci	unsigned int *pages;
1208c2ecf20Sopenharmony_ci	unsigned int address, len, i, p;
1218c2ecf20Sopenharmony_ci	unsigned int mapped_size = 0;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
1248c2ecf20Sopenharmony_ci		pages = rga->src_mmu_pages;
1258c2ecf20Sopenharmony_ci	else
1268c2ecf20Sopenharmony_ci		pages = rga->dst_mmu_pages;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/* Create local MMU table for RGA */
1298c2ecf20Sopenharmony_ci	sgt = vb2_plane_cookie(vb, 0);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	for_each_sg(sgt->sgl, sgl, sgt->nents, i) {
1328c2ecf20Sopenharmony_ci		len = sg_dma_len(sgl) >> PAGE_SHIFT;
1338c2ecf20Sopenharmony_ci		address = sg_phys(sgl);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		for (p = 0; p < len; p++) {
1368c2ecf20Sopenharmony_ci			dma_addr_t phys = address +
1378c2ecf20Sopenharmony_ci					  ((dma_addr_t)p << PAGE_SHIFT);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci			pages[mapped_size + p] = phys;
1408c2ecf20Sopenharmony_ci		}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci		mapped_size += len;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* sync local MMU table for RGA */
1468c2ecf20Sopenharmony_ci	dma_sync_single_for_device(rga->dev, virt_to_phys(pages),
1478c2ecf20Sopenharmony_ci				   8 * PAGE_SIZE, DMA_BIDIRECTIONAL);
1488c2ecf20Sopenharmony_ci}
149