/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * Copyright (C) 2016 NextThing Co
 * Copyright (C) 2016-2019 Bootlin
 *
 * Author: Maxime Ripard <maxime.ripard@bootlin.com>
 */

#ifndef _SUN4I_CSI_H_
#define _SUN4I_CSI_H_

#include <media/media-device.h>
#include <media/v4l2-async.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
#include <media/videobuf2-core.h>

#define CSI_EN_REG			0x00

#define CSI_CFG_REG			0x04
#define CSI_CFG_INPUT_FMT(fmt)			((fmt) << 20)
#define CSI_CFG_OUTPUT_FMT(fmt)			((fmt) << 16)
#define CSI_CFG_YUV_DATA_SEQ(seq)		((seq) << 8)
#define CSI_CFG_VREF_POL(pol)			((pol) << 2)
#define CSI_CFG_HREF_POL(pol)			((pol) << 1)
#define CSI_CFG_PCLK_POL(pol)			((pol) << 0)

#define CSI_CPT_CTRL_REG		0x08
#define CSI_CPT_CTRL_VIDEO_START		BIT(1)
#define CSI_CPT_CTRL_IMAGE_START		BIT(0)

#define CSI_BUF_ADDR_REG(fifo, buf)	(0x10 + (0x8 * (fifo)) + (0x4 * (buf)))

#define CSI_BUF_CTRL_REG		0x28
#define CSI_BUF_CTRL_DBN			BIT(2)
#define CSI_BUF_CTRL_DBS			BIT(1)
#define CSI_BUF_CTRL_DBE			BIT(0)

#define CSI_INT_EN_REG			0x30
#define CSI_INT_FRM_DONE			BIT(1)
#define CSI_INT_CPT_DONE			BIT(0)

#define CSI_INT_STA_REG			0x34

#define CSI_WIN_CTRL_W_REG		0x40
#define CSI_WIN_CTRL_W_ACTIVE(w)		((w) << 16)

#define CSI_WIN_CTRL_H_REG		0x44
#define CSI_WIN_CTRL_H_ACTIVE(h)		((h) << 16)

#define CSI_BUF_LEN_REG			0x48

#define CSI_MAX_BUFFER		2
#define CSI_MAX_HEIGHT		8192U
#define CSI_MAX_WIDTH		8192U

enum csi_input {
	CSI_INPUT_RAW	= 0,
	CSI_INPUT_BT656	= 2,
	CSI_INPUT_YUV	= 3,
};

enum csi_output_raw {
	CSI_OUTPUT_RAW_PASSTHROUGH = 0,
};

enum csi_output_yuv {
	CSI_OUTPUT_YUV_422_PLANAR	= 0,
	CSI_OUTPUT_YUV_420_PLANAR	= 1,
	CSI_OUTPUT_YUV_422_UV		= 4,
	CSI_OUTPUT_YUV_420_UV		= 5,
	CSI_OUTPUT_YUV_422_MACRO	= 8,
	CSI_OUTPUT_YUV_420_MACRO	= 9,
};

enum csi_yuv_data_seq {
	CSI_YUV_DATA_SEQ_YUYV	= 0,
	CSI_YUV_DATA_SEQ_YVYU	= 1,
	CSI_YUV_DATA_SEQ_UYVY	= 2,
	CSI_YUV_DATA_SEQ_VYUY	= 3,
};

enum csi_subdev_pads {
	CSI_SUBDEV_SINK,
	CSI_SUBDEV_SOURCE,

	CSI_SUBDEV_PADS,
};

extern const struct v4l2_subdev_ops sun4i_csi_subdev_ops;
extern const struct v4l2_subdev_internal_ops sun4i_csi_subdev_internal_ops;

struct sun4i_csi_format {
	u32			mbus;
	u32			fourcc;
	enum csi_input		input;
	u32			output;
	unsigned int		num_planes;
	u8			bpp[3];
	unsigned int		hsub;
	unsigned int		vsub;
};

const struct sun4i_csi_format *sun4i_csi_find_format(const u32 *fourcc,
						     const u32 *mbus);

struct sun4i_csi {
	/* Device resources */
	struct device			*dev;

	const struct sun4i_csi_traits	*traits;

	void __iomem			*regs;
	struct clk			*bus_clk;
	struct clk			*isp_clk;
	struct clk			*ram_clk;
	struct reset_control		*rst;

	struct vb2_v4l2_buffer		*current_buf[CSI_MAX_BUFFER];

	struct {
		size_t			size;
		void			*vaddr;
		dma_addr_t		paddr;
	} scratch;

	struct v4l2_mbus_config_parallel	bus;

	/* Main Device */
	struct v4l2_device		v4l;
	struct media_device		mdev;
	struct video_device		vdev;
	struct media_pad		vdev_pad;
	struct v4l2_pix_format_mplane	fmt;

	/* Local subdev */
	struct v4l2_subdev		subdev;
	struct media_pad		subdev_pads[CSI_SUBDEV_PADS];
	struct v4l2_mbus_framefmt	subdev_fmt;

	/* V4L2 Async variables */
	struct v4l2_async_notifier	notifier;
	struct v4l2_subdev		*src_subdev;
	int				src_pad;

	/* V4L2 variables */
	struct mutex			lock;

	/* Videobuf2 */
	struct vb2_queue		queue;
	struct list_head		buf_list;
	spinlock_t			qlock;
	unsigned int			sequence;
};

int sun4i_csi_dma_register(struct sun4i_csi *csi, int irq);
void sun4i_csi_dma_unregister(struct sun4i_csi *csi);

int sun4i_csi_v4l2_register(struct sun4i_csi *csi);

#endif /* _SUN4I_CSI_H_ */
