162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Samsung S5P/EXYNOS SoC series MIPI-CSI receiver driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
662306a36Sopenharmony_ci * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/clk.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/errno.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <linux/irq.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/memory.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/of.h>
2062306a36Sopenharmony_ci#include <linux/of_graph.h>
2162306a36Sopenharmony_ci#include <linux/phy/phy.h>
2262306a36Sopenharmony_ci#include <linux/platform_device.h>
2362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2462306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2562306a36Sopenharmony_ci#include <linux/sizes.h>
2662306a36Sopenharmony_ci#include <linux/slab.h>
2762306a36Sopenharmony_ci#include <linux/spinlock.h>
2862306a36Sopenharmony_ci#include <linux/videodev2.h>
2962306a36Sopenharmony_ci#include <media/drv-intf/exynos-fimc.h>
3062306a36Sopenharmony_ci#include <media/v4l2-fwnode.h>
3162306a36Sopenharmony_ci#include <media/v4l2-subdev.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include "mipi-csis.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int debug;
3662306a36Sopenharmony_cimodule_param(debug, int, 0644);
3762306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level (0-2)");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* Register map definition */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* CSIS global control */
4262306a36Sopenharmony_ci#define S5PCSIS_CTRL			0x00
4362306a36Sopenharmony_ci#define S5PCSIS_CTRL_DPDN_DEFAULT	(0 << 31)
4462306a36Sopenharmony_ci#define S5PCSIS_CTRL_DPDN_SWAP		(1UL << 31)
4562306a36Sopenharmony_ci#define S5PCSIS_CTRL_ALIGN_32BIT	(1 << 20)
4662306a36Sopenharmony_ci#define S5PCSIS_CTRL_UPDATE_SHADOW	(1 << 16)
4762306a36Sopenharmony_ci#define S5PCSIS_CTRL_WCLK_EXTCLK	(1 << 8)
4862306a36Sopenharmony_ci#define S5PCSIS_CTRL_RESET		(1 << 4)
4962306a36Sopenharmony_ci#define S5PCSIS_CTRL_ENABLE		(1 << 0)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* D-PHY control */
5262306a36Sopenharmony_ci#define S5PCSIS_DPHYCTRL		0x04
5362306a36Sopenharmony_ci#define S5PCSIS_DPHYCTRL_HSS_MASK	(0x1f << 27)
5462306a36Sopenharmony_ci#define S5PCSIS_DPHYCTRL_ENABLE		(0x1f << 0)
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define S5PCSIS_CONFIG			0x08
5762306a36Sopenharmony_ci#define S5PCSIS_CFG_FMT_YCBCR422_8BIT	(0x1e << 2)
5862306a36Sopenharmony_ci#define S5PCSIS_CFG_FMT_RAW8		(0x2a << 2)
5962306a36Sopenharmony_ci#define S5PCSIS_CFG_FMT_RAW10		(0x2b << 2)
6062306a36Sopenharmony_ci#define S5PCSIS_CFG_FMT_RAW12		(0x2c << 2)
6162306a36Sopenharmony_ci/* User defined formats, x = 1...4 */
6262306a36Sopenharmony_ci#define S5PCSIS_CFG_FMT_USER(x)		((0x30 + x - 1) << 2)
6362306a36Sopenharmony_ci#define S5PCSIS_CFG_FMT_MASK		(0x3f << 2)
6462306a36Sopenharmony_ci#define S5PCSIS_CFG_NR_LANE_MASK	3
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* Interrupt mask */
6762306a36Sopenharmony_ci#define S5PCSIS_INTMSK			0x10
6862306a36Sopenharmony_ci#define S5PCSIS_INTMSK_EVEN_BEFORE	(1UL << 31)
6962306a36Sopenharmony_ci#define S5PCSIS_INTMSK_EVEN_AFTER	(1 << 30)
7062306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ODD_BEFORE	(1 << 29)
7162306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ODD_AFTER	(1 << 28)
7262306a36Sopenharmony_ci#define S5PCSIS_INTMSK_FRAME_START	(1 << 27)
7362306a36Sopenharmony_ci#define S5PCSIS_INTMSK_FRAME_END	(1 << 26)
7462306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_SOT_HS	(1 << 12)
7562306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_LOST_FS	(1 << 5)
7662306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_LOST_FE	(1 << 4)
7762306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_OVER		(1 << 3)
7862306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_ECC		(1 << 2)
7962306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_CRC		(1 << 1)
8062306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_UNKNOWN	(1 << 0)
8162306a36Sopenharmony_ci#define S5PCSIS_INTMSK_EXYNOS4_EN_ALL	0xf000103f
8262306a36Sopenharmony_ci#define S5PCSIS_INTMSK_EXYNOS5_EN_ALL	0xfc00103f
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/* Interrupt source */
8562306a36Sopenharmony_ci#define S5PCSIS_INTSRC			0x14
8662306a36Sopenharmony_ci#define S5PCSIS_INTSRC_EVEN_BEFORE	(1UL << 31)
8762306a36Sopenharmony_ci#define S5PCSIS_INTSRC_EVEN_AFTER	(1 << 30)
8862306a36Sopenharmony_ci#define S5PCSIS_INTSRC_EVEN		(0x3 << 30)
8962306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ODD_BEFORE	(1 << 29)
9062306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ODD_AFTER	(1 << 28)
9162306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ODD		(0x3 << 28)
9262306a36Sopenharmony_ci#define S5PCSIS_INTSRC_NON_IMAGE_DATA	(0xf << 28)
9362306a36Sopenharmony_ci#define S5PCSIS_INTSRC_FRAME_START	(1 << 27)
9462306a36Sopenharmony_ci#define S5PCSIS_INTSRC_FRAME_END	(1 << 26)
9562306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_SOT_HS	(0xf << 12)
9662306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_LOST_FS	(1 << 5)
9762306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_LOST_FE	(1 << 4)
9862306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_OVER		(1 << 3)
9962306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_ECC		(1 << 2)
10062306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_CRC		(1 << 1)
10162306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_UNKNOWN	(1 << 0)
10262306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERRORS		0xf03f
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/* Pixel resolution */
10562306a36Sopenharmony_ci#define S5PCSIS_RESOL			0x2c
10662306a36Sopenharmony_ci#define CSIS_MAX_PIX_WIDTH		0xffff
10762306a36Sopenharmony_ci#define CSIS_MAX_PIX_HEIGHT		0xffff
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/* Non-image packet data buffers */
11062306a36Sopenharmony_ci#define S5PCSIS_PKTDATA_ODD		0x2000
11162306a36Sopenharmony_ci#define S5PCSIS_PKTDATA_EVEN		0x3000
11262306a36Sopenharmony_ci#define S5PCSIS_PKTDATA_SIZE		SZ_4K
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cienum {
11562306a36Sopenharmony_ci	CSIS_CLK_MUX,
11662306a36Sopenharmony_ci	CSIS_CLK_GATE,
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic char *csi_clock_name[] = {
12062306a36Sopenharmony_ci	[CSIS_CLK_MUX]  = "sclk_csis",
12162306a36Sopenharmony_ci	[CSIS_CLK_GATE] = "csis",
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci#define NUM_CSIS_CLOCKS	ARRAY_SIZE(csi_clock_name)
12462306a36Sopenharmony_ci#define DEFAULT_SCLK_CSIS_FREQ	166000000UL
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic const char * const csis_supply_name[] = {
12762306a36Sopenharmony_ci	"vddcore",  /* CSIS Core (1.0V, 1.1V or 1.2V) supply */
12862306a36Sopenharmony_ci	"vddio",    /* CSIS I/O and PLL (1.8V) supply */
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci#define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name)
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cienum {
13362306a36Sopenharmony_ci	ST_POWERED	= 1,
13462306a36Sopenharmony_ci	ST_STREAMING	= 2,
13562306a36Sopenharmony_ci	ST_SUSPENDED	= 4,
13662306a36Sopenharmony_ci};
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistruct s5pcsis_event {
13962306a36Sopenharmony_ci	u32 mask;
14062306a36Sopenharmony_ci	const char * const name;
14162306a36Sopenharmony_ci	unsigned int counter;
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic const struct s5pcsis_event s5pcsis_events[] = {
14562306a36Sopenharmony_ci	/* Errors */
14662306a36Sopenharmony_ci	{ S5PCSIS_INTSRC_ERR_SOT_HS,	"SOT Error" },
14762306a36Sopenharmony_ci	{ S5PCSIS_INTSRC_ERR_LOST_FS,	"Lost Frame Start Error" },
14862306a36Sopenharmony_ci	{ S5PCSIS_INTSRC_ERR_LOST_FE,	"Lost Frame End Error" },
14962306a36Sopenharmony_ci	{ S5PCSIS_INTSRC_ERR_OVER,	"FIFO Overflow Error" },
15062306a36Sopenharmony_ci	{ S5PCSIS_INTSRC_ERR_ECC,	"ECC Error" },
15162306a36Sopenharmony_ci	{ S5PCSIS_INTSRC_ERR_CRC,	"CRC Error" },
15262306a36Sopenharmony_ci	{ S5PCSIS_INTSRC_ERR_UNKNOWN,	"Unknown Error" },
15362306a36Sopenharmony_ci	/* Non-image data receive events */
15462306a36Sopenharmony_ci	{ S5PCSIS_INTSRC_EVEN_BEFORE,	"Non-image data before even frame" },
15562306a36Sopenharmony_ci	{ S5PCSIS_INTSRC_EVEN_AFTER,	"Non-image data after even frame" },
15662306a36Sopenharmony_ci	{ S5PCSIS_INTSRC_ODD_BEFORE,	"Non-image data before odd frame" },
15762306a36Sopenharmony_ci	{ S5PCSIS_INTSRC_ODD_AFTER,	"Non-image data after odd frame" },
15862306a36Sopenharmony_ci	/* Frame start/end */
15962306a36Sopenharmony_ci	{ S5PCSIS_INTSRC_FRAME_START,	"Frame Start" },
16062306a36Sopenharmony_ci	{ S5PCSIS_INTSRC_FRAME_END,	"Frame End" },
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_ci#define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events)
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistruct csis_pktbuf {
16562306a36Sopenharmony_ci	u32 *data;
16662306a36Sopenharmony_ci	unsigned int len;
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistruct csis_drvdata {
17062306a36Sopenharmony_ci	/* Mask of all used interrupts in S5PCSIS_INTMSK register */
17162306a36Sopenharmony_ci	u32 interrupt_mask;
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci/**
17562306a36Sopenharmony_ci * struct csis_state - the driver's internal state data structure
17662306a36Sopenharmony_ci * @lock: mutex serializing the subdev and power management operations,
17762306a36Sopenharmony_ci *        protecting @format and @flags members
17862306a36Sopenharmony_ci * @pads: CSIS pads array
17962306a36Sopenharmony_ci * @sd: v4l2_subdev associated with CSIS device instance
18062306a36Sopenharmony_ci * @index: the hardware instance index
18162306a36Sopenharmony_ci * @pdev: CSIS platform device
18262306a36Sopenharmony_ci * @phy: pointer to the CSIS generic PHY
18362306a36Sopenharmony_ci * @regs: mmapped I/O registers memory
18462306a36Sopenharmony_ci * @supplies: CSIS regulator supplies
18562306a36Sopenharmony_ci * @clock: CSIS clocks
18662306a36Sopenharmony_ci * @irq: requested s5p-mipi-csis irq number
18762306a36Sopenharmony_ci * @interrupt_mask: interrupt mask of the all used interrupts
18862306a36Sopenharmony_ci * @flags: the state variable for power and streaming control
18962306a36Sopenharmony_ci * @clk_frequency: device bus clock frequency
19062306a36Sopenharmony_ci * @hs_settle: HS-RX settle time
19162306a36Sopenharmony_ci * @num_lanes: number of MIPI-CSI data lanes used
19262306a36Sopenharmony_ci * @max_num_lanes: maximum number of MIPI-CSI data lanes supported
19362306a36Sopenharmony_ci * @wclk_ext: CSI wrapper clock: 0 - bus clock, 1 - external SCLK_CAM
19462306a36Sopenharmony_ci * @csis_fmt: current CSIS pixel format
19562306a36Sopenharmony_ci * @format: common media bus format for the source and sink pad
19662306a36Sopenharmony_ci * @slock: spinlock protecting structure members below
19762306a36Sopenharmony_ci * @pkt_buf: the frame embedded (non-image) data buffer
19862306a36Sopenharmony_ci * @events: MIPI-CSIS event (error) counters
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_cistruct csis_state {
20162306a36Sopenharmony_ci	struct mutex lock;
20262306a36Sopenharmony_ci	struct media_pad pads[CSIS_PADS_NUM];
20362306a36Sopenharmony_ci	struct v4l2_subdev sd;
20462306a36Sopenharmony_ci	u8 index;
20562306a36Sopenharmony_ci	struct platform_device *pdev;
20662306a36Sopenharmony_ci	struct phy *phy;
20762306a36Sopenharmony_ci	void __iomem *regs;
20862306a36Sopenharmony_ci	struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
20962306a36Sopenharmony_ci	struct clk *clock[NUM_CSIS_CLOCKS];
21062306a36Sopenharmony_ci	int irq;
21162306a36Sopenharmony_ci	u32 interrupt_mask;
21262306a36Sopenharmony_ci	u32 flags;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	u32 clk_frequency;
21562306a36Sopenharmony_ci	u32 hs_settle;
21662306a36Sopenharmony_ci	u32 num_lanes;
21762306a36Sopenharmony_ci	u32 max_num_lanes;
21862306a36Sopenharmony_ci	u8 wclk_ext;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	const struct csis_pix_format *csis_fmt;
22162306a36Sopenharmony_ci	struct v4l2_mbus_framefmt format;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	spinlock_t slock;
22462306a36Sopenharmony_ci	struct csis_pktbuf pkt_buf;
22562306a36Sopenharmony_ci	struct s5pcsis_event events[S5PCSIS_NUM_EVENTS];
22662306a36Sopenharmony_ci};
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/**
22962306a36Sopenharmony_ci * struct csis_pix_format - CSIS pixel format description
23062306a36Sopenharmony_ci * @pix_width_alignment: horizontal pixel alignment, width will be
23162306a36Sopenharmony_ci *                       multiple of 2^pix_width_alignment
23262306a36Sopenharmony_ci * @code: corresponding media bus code
23362306a36Sopenharmony_ci * @fmt_reg: S5PCSIS_CONFIG register value
23462306a36Sopenharmony_ci * @data_alignment: MIPI-CSI data alignment in bits
23562306a36Sopenharmony_ci */
23662306a36Sopenharmony_cistruct csis_pix_format {
23762306a36Sopenharmony_ci	unsigned int pix_width_alignment;
23862306a36Sopenharmony_ci	u32 code;
23962306a36Sopenharmony_ci	u32 fmt_reg;
24062306a36Sopenharmony_ci	u8 data_alignment;
24162306a36Sopenharmony_ci};
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic const struct csis_pix_format s5pcsis_formats[] = {
24462306a36Sopenharmony_ci	{
24562306a36Sopenharmony_ci		.code = MEDIA_BUS_FMT_VYUY8_2X8,
24662306a36Sopenharmony_ci		.fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT,
24762306a36Sopenharmony_ci		.data_alignment = 32,
24862306a36Sopenharmony_ci	}, {
24962306a36Sopenharmony_ci		.code = MEDIA_BUS_FMT_JPEG_1X8,
25062306a36Sopenharmony_ci		.fmt_reg = S5PCSIS_CFG_FMT_USER(1),
25162306a36Sopenharmony_ci		.data_alignment = 32,
25262306a36Sopenharmony_ci	}, {
25362306a36Sopenharmony_ci		.code = MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8,
25462306a36Sopenharmony_ci		.fmt_reg = S5PCSIS_CFG_FMT_USER(1),
25562306a36Sopenharmony_ci		.data_alignment = 32,
25662306a36Sopenharmony_ci	}, {
25762306a36Sopenharmony_ci		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
25862306a36Sopenharmony_ci		.fmt_reg = S5PCSIS_CFG_FMT_RAW8,
25962306a36Sopenharmony_ci		.data_alignment = 24,
26062306a36Sopenharmony_ci	}, {
26162306a36Sopenharmony_ci		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
26262306a36Sopenharmony_ci		.fmt_reg = S5PCSIS_CFG_FMT_RAW10,
26362306a36Sopenharmony_ci		.data_alignment = 24,
26462306a36Sopenharmony_ci	}, {
26562306a36Sopenharmony_ci		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
26662306a36Sopenharmony_ci		.fmt_reg = S5PCSIS_CFG_FMT_RAW12,
26762306a36Sopenharmony_ci		.data_alignment = 24,
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci};
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci#define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r)
27262306a36Sopenharmony_ci#define s5pcsis_read(__csis, __r) readl(__csis->regs + __r)
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic struct csis_state *sd_to_csis_state(struct v4l2_subdev *sdev)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	return container_of(sdev, struct csis_state, sd);
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic const struct csis_pix_format *find_csis_format(
28062306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *mf)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	int i;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(s5pcsis_formats); i++)
28562306a36Sopenharmony_ci		if (mf->code == s5pcsis_formats[i].code)
28662306a36Sopenharmony_ci			return &s5pcsis_formats[i];
28762306a36Sopenharmony_ci	return NULL;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic void s5pcsis_enable_interrupts(struct csis_state *state, bool on)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	u32 val = s5pcsis_read(state, S5PCSIS_INTMSK);
29362306a36Sopenharmony_ci	if (on)
29462306a36Sopenharmony_ci		val |= state->interrupt_mask;
29562306a36Sopenharmony_ci	else
29662306a36Sopenharmony_ci		val &= ~state->interrupt_mask;
29762306a36Sopenharmony_ci	s5pcsis_write(state, S5PCSIS_INTMSK, val);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic void s5pcsis_reset(struct csis_state *state)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	u32 val = s5pcsis_read(state, S5PCSIS_CTRL);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_RESET);
30562306a36Sopenharmony_ci	udelay(10);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void s5pcsis_system_enable(struct csis_state *state, int on)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	u32 val, mask;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	val = s5pcsis_read(state, S5PCSIS_CTRL);
31362306a36Sopenharmony_ci	if (on)
31462306a36Sopenharmony_ci		val |= S5PCSIS_CTRL_ENABLE;
31562306a36Sopenharmony_ci	else
31662306a36Sopenharmony_ci		val &= ~S5PCSIS_CTRL_ENABLE;
31762306a36Sopenharmony_ci	s5pcsis_write(state, S5PCSIS_CTRL, val);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
32062306a36Sopenharmony_ci	val &= ~S5PCSIS_DPHYCTRL_ENABLE;
32162306a36Sopenharmony_ci	if (on) {
32262306a36Sopenharmony_ci		mask = (1 << (state->num_lanes + 1)) - 1;
32362306a36Sopenharmony_ci		val |= (mask & S5PCSIS_DPHYCTRL_ENABLE);
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci	s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci/* Called with the state.lock mutex held */
32962306a36Sopenharmony_cistatic void __s5pcsis_set_format(struct csis_state *state)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *mf = &state->format;
33262306a36Sopenharmony_ci	u32 val;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	v4l2_dbg(1, debug, &state->sd, "fmt: %#x, %d x %d\n",
33562306a36Sopenharmony_ci		 mf->code, mf->width, mf->height);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Color format */
33862306a36Sopenharmony_ci	val = s5pcsis_read(state, S5PCSIS_CONFIG);
33962306a36Sopenharmony_ci	val = (val & ~S5PCSIS_CFG_FMT_MASK) | state->csis_fmt->fmt_reg;
34062306a36Sopenharmony_ci	s5pcsis_write(state, S5PCSIS_CONFIG, val);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* Pixel resolution */
34362306a36Sopenharmony_ci	val = (mf->width << 16) | mf->height;
34462306a36Sopenharmony_ci	s5pcsis_write(state, S5PCSIS_RESOL, val);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic void s5pcsis_set_hsync_settle(struct csis_state *state, int settle)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	u32 val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	val = (val & ~S5PCSIS_DPHYCTRL_HSS_MASK) | (settle << 27);
35262306a36Sopenharmony_ci	s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic void s5pcsis_set_params(struct csis_state *state)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	u32 val;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	val = s5pcsis_read(state, S5PCSIS_CONFIG);
36062306a36Sopenharmony_ci	val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (state->num_lanes - 1);
36162306a36Sopenharmony_ci	s5pcsis_write(state, S5PCSIS_CONFIG, val);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	__s5pcsis_set_format(state);
36462306a36Sopenharmony_ci	s5pcsis_set_hsync_settle(state, state->hs_settle);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	val = s5pcsis_read(state, S5PCSIS_CTRL);
36762306a36Sopenharmony_ci	if (state->csis_fmt->data_alignment == 32)
36862306a36Sopenharmony_ci		val |= S5PCSIS_CTRL_ALIGN_32BIT;
36962306a36Sopenharmony_ci	else /* 24-bits */
37062306a36Sopenharmony_ci		val &= ~S5PCSIS_CTRL_ALIGN_32BIT;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	val &= ~S5PCSIS_CTRL_WCLK_EXTCLK;
37362306a36Sopenharmony_ci	if (state->wclk_ext)
37462306a36Sopenharmony_ci		val |= S5PCSIS_CTRL_WCLK_EXTCLK;
37562306a36Sopenharmony_ci	s5pcsis_write(state, S5PCSIS_CTRL, val);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/* Update the shadow register. */
37862306a36Sopenharmony_ci	val = s5pcsis_read(state, S5PCSIS_CTRL);
37962306a36Sopenharmony_ci	s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_UPDATE_SHADOW);
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic void s5pcsis_clk_put(struct csis_state *state)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	int i;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
38762306a36Sopenharmony_ci		if (IS_ERR(state->clock[i]))
38862306a36Sopenharmony_ci			continue;
38962306a36Sopenharmony_ci		clk_unprepare(state->clock[i]);
39062306a36Sopenharmony_ci		clk_put(state->clock[i]);
39162306a36Sopenharmony_ci		state->clock[i] = ERR_PTR(-EINVAL);
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic int s5pcsis_clk_get(struct csis_state *state)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct device *dev = &state->pdev->dev;
39862306a36Sopenharmony_ci	int i, ret;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	for (i = 0; i < NUM_CSIS_CLOCKS; i++)
40162306a36Sopenharmony_ci		state->clock[i] = ERR_PTR(-EINVAL);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
40462306a36Sopenharmony_ci		state->clock[i] = clk_get(dev, csi_clock_name[i]);
40562306a36Sopenharmony_ci		if (IS_ERR(state->clock[i])) {
40662306a36Sopenharmony_ci			ret = PTR_ERR(state->clock[i]);
40762306a36Sopenharmony_ci			goto err;
40862306a36Sopenharmony_ci		}
40962306a36Sopenharmony_ci		ret = clk_prepare(state->clock[i]);
41062306a36Sopenharmony_ci		if (ret < 0) {
41162306a36Sopenharmony_ci			clk_put(state->clock[i]);
41262306a36Sopenharmony_ci			state->clock[i] = ERR_PTR(-EINVAL);
41362306a36Sopenharmony_ci			goto err;
41462306a36Sopenharmony_ci		}
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci	return 0;
41762306a36Sopenharmony_cierr:
41862306a36Sopenharmony_ci	s5pcsis_clk_put(state);
41962306a36Sopenharmony_ci	dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]);
42062306a36Sopenharmony_ci	return ret;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic void dump_regs(struct csis_state *state, const char *label)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	struct {
42662306a36Sopenharmony_ci		u32 offset;
42762306a36Sopenharmony_ci		const char * const name;
42862306a36Sopenharmony_ci	} registers[] = {
42962306a36Sopenharmony_ci		{ 0x00, "CTRL" },
43062306a36Sopenharmony_ci		{ 0x04, "DPHYCTRL" },
43162306a36Sopenharmony_ci		{ 0x08, "CONFIG" },
43262306a36Sopenharmony_ci		{ 0x0c, "DPHYSTS" },
43362306a36Sopenharmony_ci		{ 0x10, "INTMSK" },
43462306a36Sopenharmony_ci		{ 0x2c, "RESOL" },
43562306a36Sopenharmony_ci		{ 0x38, "SDW_CONFIG" },
43662306a36Sopenharmony_ci	};
43762306a36Sopenharmony_ci	u32 i;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	v4l2_info(&state->sd, "--- %s ---\n", label);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(registers); i++) {
44262306a36Sopenharmony_ci		u32 cfg = s5pcsis_read(state, registers[i].offset);
44362306a36Sopenharmony_ci		v4l2_info(&state->sd, "%10s: 0x%08x\n", registers[i].name, cfg);
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic void s5pcsis_start_stream(struct csis_state *state)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	s5pcsis_reset(state);
45062306a36Sopenharmony_ci	s5pcsis_set_params(state);
45162306a36Sopenharmony_ci	s5pcsis_system_enable(state, true);
45262306a36Sopenharmony_ci	s5pcsis_enable_interrupts(state, true);
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic void s5pcsis_stop_stream(struct csis_state *state)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	s5pcsis_enable_interrupts(state, false);
45862306a36Sopenharmony_ci	s5pcsis_system_enable(state, false);
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic void s5pcsis_clear_counters(struct csis_state *state)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	unsigned long flags;
46462306a36Sopenharmony_ci	int i;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	spin_lock_irqsave(&state->slock, flags);
46762306a36Sopenharmony_ci	for (i = 0; i < S5PCSIS_NUM_EVENTS; i++)
46862306a36Sopenharmony_ci		state->events[i].counter = 0;
46962306a36Sopenharmony_ci	spin_unlock_irqrestore(&state->slock, flags);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic void s5pcsis_log_counters(struct csis_state *state, bool non_errors)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	int i = non_errors ? S5PCSIS_NUM_EVENTS : S5PCSIS_NUM_EVENTS - 4;
47562306a36Sopenharmony_ci	unsigned long flags;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	spin_lock_irqsave(&state->slock, flags);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	for (i--; i >= 0; i--) {
48062306a36Sopenharmony_ci		if (state->events[i].counter > 0 || debug)
48162306a36Sopenharmony_ci			v4l2_info(&state->sd, "%s events: %d\n",
48262306a36Sopenharmony_ci				  state->events[i].name,
48362306a36Sopenharmony_ci				  state->events[i].counter);
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci	spin_unlock_irqrestore(&state->slock, flags);
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci/*
48962306a36Sopenharmony_ci * V4L2 subdev operations
49062306a36Sopenharmony_ci */
49162306a36Sopenharmony_cistatic int s5pcsis_s_power(struct v4l2_subdev *sd, int on)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	struct csis_state *state = sd_to_csis_state(sd);
49462306a36Sopenharmony_ci	struct device *dev = &state->pdev->dev;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (on)
49762306a36Sopenharmony_ci		return pm_runtime_resume_and_get(dev);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return pm_runtime_put_sync(dev);
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct csis_state *state = sd_to_csis_state(sd);
50562306a36Sopenharmony_ci	int ret = 0;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	v4l2_dbg(1, debug, sd, "%s: %d, state: 0x%x\n",
50862306a36Sopenharmony_ci		 __func__, enable, state->flags);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (enable) {
51162306a36Sopenharmony_ci		s5pcsis_clear_counters(state);
51262306a36Sopenharmony_ci		ret = pm_runtime_resume_and_get(&state->pdev->dev);
51362306a36Sopenharmony_ci		if (ret < 0)
51462306a36Sopenharmony_ci			return ret;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	mutex_lock(&state->lock);
51862306a36Sopenharmony_ci	if (enable) {
51962306a36Sopenharmony_ci		if (state->flags & ST_SUSPENDED) {
52062306a36Sopenharmony_ci			ret = -EBUSY;
52162306a36Sopenharmony_ci			goto unlock;
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci		s5pcsis_start_stream(state);
52462306a36Sopenharmony_ci		state->flags |= ST_STREAMING;
52562306a36Sopenharmony_ci	} else {
52662306a36Sopenharmony_ci		s5pcsis_stop_stream(state);
52762306a36Sopenharmony_ci		state->flags &= ~ST_STREAMING;
52862306a36Sopenharmony_ci		if (debug > 0)
52962306a36Sopenharmony_ci			s5pcsis_log_counters(state, true);
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ciunlock:
53262306a36Sopenharmony_ci	mutex_unlock(&state->lock);
53362306a36Sopenharmony_ci	if (!enable)
53462306a36Sopenharmony_ci		pm_runtime_put(&state->pdev->dev);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return ret;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd,
54062306a36Sopenharmony_ci				  struct v4l2_subdev_state *sd_state,
54162306a36Sopenharmony_ci				  struct v4l2_subdev_mbus_code_enum *code)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	if (code->index >= ARRAY_SIZE(s5pcsis_formats))
54462306a36Sopenharmony_ci		return -EINVAL;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	code->code = s5pcsis_formats[code->index].code;
54762306a36Sopenharmony_ci	return 0;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic struct csis_pix_format const *s5pcsis_try_format(
55162306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *mf)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	struct csis_pix_format const *csis_fmt;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	csis_fmt = find_csis_format(mf);
55662306a36Sopenharmony_ci	if (csis_fmt == NULL)
55762306a36Sopenharmony_ci		csis_fmt = &s5pcsis_formats[0];
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	mf->code = csis_fmt->code;
56062306a36Sopenharmony_ci	v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH,
56162306a36Sopenharmony_ci			      csis_fmt->pix_width_alignment,
56262306a36Sopenharmony_ci			      &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1,
56362306a36Sopenharmony_ci			      0);
56462306a36Sopenharmony_ci	return csis_fmt;
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic struct v4l2_mbus_framefmt *__s5pcsis_get_format(
56862306a36Sopenharmony_ci		struct csis_state *state, struct v4l2_subdev_state *sd_state,
56962306a36Sopenharmony_ci		enum v4l2_subdev_format_whence which)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	if (which == V4L2_SUBDEV_FORMAT_TRY)
57262306a36Sopenharmony_ci		return sd_state ? v4l2_subdev_get_try_format(&state->sd,
57362306a36Sopenharmony_ci							     sd_state, 0) : NULL;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return &state->format;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic int s5pcsis_set_fmt(struct v4l2_subdev *sd,
57962306a36Sopenharmony_ci			   struct v4l2_subdev_state *sd_state,
58062306a36Sopenharmony_ci			   struct v4l2_subdev_format *fmt)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct csis_state *state = sd_to_csis_state(sd);
58362306a36Sopenharmony_ci	struct csis_pix_format const *csis_fmt;
58462306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *mf;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	mf = __s5pcsis_get_format(state, sd_state, fmt->which);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (fmt->pad == CSIS_PAD_SOURCE) {
58962306a36Sopenharmony_ci		if (mf) {
59062306a36Sopenharmony_ci			mutex_lock(&state->lock);
59162306a36Sopenharmony_ci			fmt->format = *mf;
59262306a36Sopenharmony_ci			mutex_unlock(&state->lock);
59362306a36Sopenharmony_ci		}
59462306a36Sopenharmony_ci		return 0;
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci	csis_fmt = s5pcsis_try_format(&fmt->format);
59762306a36Sopenharmony_ci	if (mf) {
59862306a36Sopenharmony_ci		mutex_lock(&state->lock);
59962306a36Sopenharmony_ci		*mf = fmt->format;
60062306a36Sopenharmony_ci		if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
60162306a36Sopenharmony_ci			state->csis_fmt = csis_fmt;
60262306a36Sopenharmony_ci		mutex_unlock(&state->lock);
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci	return 0;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic int s5pcsis_get_fmt(struct v4l2_subdev *sd,
60862306a36Sopenharmony_ci			   struct v4l2_subdev_state *sd_state,
60962306a36Sopenharmony_ci			   struct v4l2_subdev_format *fmt)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	struct csis_state *state = sd_to_csis_state(sd);
61262306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *mf;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	mf = __s5pcsis_get_format(state, sd_state, fmt->which);
61562306a36Sopenharmony_ci	if (!mf)
61662306a36Sopenharmony_ci		return -EINVAL;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	mutex_lock(&state->lock);
61962306a36Sopenharmony_ci	fmt->format = *mf;
62062306a36Sopenharmony_ci	mutex_unlock(&state->lock);
62162306a36Sopenharmony_ci	return 0;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic int s5pcsis_s_rx_buffer(struct v4l2_subdev *sd, void *buf,
62562306a36Sopenharmony_ci			       unsigned int *size)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	struct csis_state *state = sd_to_csis_state(sd);
62862306a36Sopenharmony_ci	unsigned long flags;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	*size = min_t(unsigned int, *size, S5PCSIS_PKTDATA_SIZE);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	spin_lock_irqsave(&state->slock, flags);
63362306a36Sopenharmony_ci	state->pkt_buf.data = buf;
63462306a36Sopenharmony_ci	state->pkt_buf.len = *size;
63562306a36Sopenharmony_ci	spin_unlock_irqrestore(&state->slock, flags);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	return 0;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic int s5pcsis_log_status(struct v4l2_subdev *sd)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	struct csis_state *state = sd_to_csis_state(sd);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	mutex_lock(&state->lock);
64562306a36Sopenharmony_ci	s5pcsis_log_counters(state, true);
64662306a36Sopenharmony_ci	if (debug && (state->flags & ST_POWERED))
64762306a36Sopenharmony_ci		dump_regs(state, __func__);
64862306a36Sopenharmony_ci	mutex_unlock(&state->lock);
64962306a36Sopenharmony_ci	return 0;
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic const struct v4l2_subdev_core_ops s5pcsis_core_ops = {
65362306a36Sopenharmony_ci	.s_power = s5pcsis_s_power,
65462306a36Sopenharmony_ci	.log_status = s5pcsis_log_status,
65562306a36Sopenharmony_ci};
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops s5pcsis_pad_ops = {
65862306a36Sopenharmony_ci	.enum_mbus_code = s5pcsis_enum_mbus_code,
65962306a36Sopenharmony_ci	.get_fmt = s5pcsis_get_fmt,
66062306a36Sopenharmony_ci	.set_fmt = s5pcsis_set_fmt,
66162306a36Sopenharmony_ci};
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops s5pcsis_video_ops = {
66462306a36Sopenharmony_ci	.s_rx_buffer = s5pcsis_s_rx_buffer,
66562306a36Sopenharmony_ci	.s_stream = s5pcsis_s_stream,
66662306a36Sopenharmony_ci};
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic const struct v4l2_subdev_ops s5pcsis_subdev_ops = {
66962306a36Sopenharmony_ci	.core = &s5pcsis_core_ops,
67062306a36Sopenharmony_ci	.pad = &s5pcsis_pad_ops,
67162306a36Sopenharmony_ci	.video = &s5pcsis_video_ops,
67262306a36Sopenharmony_ci};
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	struct csis_state *state = dev_id;
67762306a36Sopenharmony_ci	struct csis_pktbuf *pktbuf = &state->pkt_buf;
67862306a36Sopenharmony_ci	unsigned long flags;
67962306a36Sopenharmony_ci	u32 status;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	status = s5pcsis_read(state, S5PCSIS_INTSRC);
68262306a36Sopenharmony_ci	spin_lock_irqsave(&state->slock, flags);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if ((status & S5PCSIS_INTSRC_NON_IMAGE_DATA) && pktbuf->data) {
68562306a36Sopenharmony_ci		u32 offset;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci		if (status & S5PCSIS_INTSRC_EVEN)
68862306a36Sopenharmony_ci			offset = S5PCSIS_PKTDATA_EVEN;
68962306a36Sopenharmony_ci		else
69062306a36Sopenharmony_ci			offset = S5PCSIS_PKTDATA_ODD;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		memcpy(pktbuf->data, (u8 __force *)state->regs + offset,
69362306a36Sopenharmony_ci		       pktbuf->len);
69462306a36Sopenharmony_ci		pktbuf->data = NULL;
69562306a36Sopenharmony_ci		rmb();
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	/* Update the event/error counters */
69962306a36Sopenharmony_ci	if ((status & S5PCSIS_INTSRC_ERRORS) || debug) {
70062306a36Sopenharmony_ci		int i;
70162306a36Sopenharmony_ci		for (i = 0; i < S5PCSIS_NUM_EVENTS; i++) {
70262306a36Sopenharmony_ci			if (!(status & state->events[i].mask))
70362306a36Sopenharmony_ci				continue;
70462306a36Sopenharmony_ci			state->events[i].counter++;
70562306a36Sopenharmony_ci			v4l2_dbg(2, debug, &state->sd, "%s: %d\n",
70662306a36Sopenharmony_ci				 state->events[i].name,
70762306a36Sopenharmony_ci				 state->events[i].counter);
70862306a36Sopenharmony_ci		}
70962306a36Sopenharmony_ci		v4l2_dbg(2, debug, &state->sd, "status: %08x\n", status);
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci	spin_unlock_irqrestore(&state->slock, flags);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	s5pcsis_write(state, S5PCSIS_INTSRC, status);
71462306a36Sopenharmony_ci	return IRQ_HANDLED;
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic int s5pcsis_parse_dt(struct platform_device *pdev,
71862306a36Sopenharmony_ci			    struct csis_state *state)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
72162306a36Sopenharmony_ci	struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 };
72262306a36Sopenharmony_ci	int ret;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (of_property_read_u32(node, "clock-frequency",
72562306a36Sopenharmony_ci				 &state->clk_frequency))
72662306a36Sopenharmony_ci		state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ;
72762306a36Sopenharmony_ci	if (of_property_read_u32(node, "bus-width",
72862306a36Sopenharmony_ci				 &state->max_num_lanes))
72962306a36Sopenharmony_ci		return -EINVAL;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	node = of_graph_get_next_endpoint(node, NULL);
73262306a36Sopenharmony_ci	if (!node) {
73362306a36Sopenharmony_ci		dev_err(&pdev->dev, "No port node at %pOF\n",
73462306a36Sopenharmony_ci				pdev->dev.of_node);
73562306a36Sopenharmony_ci		return -EINVAL;
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci	/* Get port node and validate MIPI-CSI channel id. */
73862306a36Sopenharmony_ci	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &endpoint);
73962306a36Sopenharmony_ci	if (ret)
74062306a36Sopenharmony_ci		goto err;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0;
74362306a36Sopenharmony_ci	if (state->index >= CSIS_MAX_ENTITIES) {
74462306a36Sopenharmony_ci		ret = -ENXIO;
74562306a36Sopenharmony_ci		goto err;
74662306a36Sopenharmony_ci	}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	/* Get MIPI CSI-2 bus configuration from the endpoint node. */
74962306a36Sopenharmony_ci	of_property_read_u32(node, "samsung,csis-hs-settle",
75062306a36Sopenharmony_ci					&state->hs_settle);
75162306a36Sopenharmony_ci	state->wclk_ext = of_property_read_bool(node,
75262306a36Sopenharmony_ci					"samsung,csis-wclk");
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cierr:
75762306a36Sopenharmony_ci	of_node_put(node);
75862306a36Sopenharmony_ci	return ret;
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic int s5pcsis_pm_resume(struct device *dev, bool runtime);
76262306a36Sopenharmony_cistatic const struct of_device_id s5pcsis_of_match[];
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_cistatic int s5pcsis_probe(struct platform_device *pdev)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	const struct of_device_id *of_id;
76762306a36Sopenharmony_ci	const struct csis_drvdata *drv_data;
76862306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
76962306a36Sopenharmony_ci	struct csis_state *state;
77062306a36Sopenharmony_ci	int ret = -ENOMEM;
77162306a36Sopenharmony_ci	int i;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
77462306a36Sopenharmony_ci	if (!state)
77562306a36Sopenharmony_ci		return -ENOMEM;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	mutex_init(&state->lock);
77862306a36Sopenharmony_ci	spin_lock_init(&state->slock);
77962306a36Sopenharmony_ci	state->pdev = pdev;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	of_id = of_match_node(s5pcsis_of_match, dev->of_node);
78262306a36Sopenharmony_ci	if (WARN_ON(of_id == NULL))
78362306a36Sopenharmony_ci		return -EINVAL;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	drv_data = of_id->data;
78662306a36Sopenharmony_ci	state->interrupt_mask = drv_data->interrupt_mask;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	ret = s5pcsis_parse_dt(pdev, state);
78962306a36Sopenharmony_ci	if (ret < 0)
79062306a36Sopenharmony_ci		return ret;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	if (state->num_lanes == 0 || state->num_lanes > state->max_num_lanes) {
79362306a36Sopenharmony_ci		dev_err(dev, "Unsupported number of data lanes: %d (max. %d)\n",
79462306a36Sopenharmony_ci			state->num_lanes, state->max_num_lanes);
79562306a36Sopenharmony_ci		return -EINVAL;
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	state->phy = devm_phy_get(dev, "csis");
79962306a36Sopenharmony_ci	if (IS_ERR(state->phy))
80062306a36Sopenharmony_ci		return PTR_ERR(state->phy);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	state->regs = devm_platform_ioremap_resource(pdev, 0);
80362306a36Sopenharmony_ci	if (IS_ERR(state->regs))
80462306a36Sopenharmony_ci		return PTR_ERR(state->regs);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	state->irq = platform_get_irq(pdev, 0);
80762306a36Sopenharmony_ci	if (state->irq < 0)
80862306a36Sopenharmony_ci		return state->irq;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
81162306a36Sopenharmony_ci		state->supplies[i].supply = csis_supply_name[i];
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	ret = devm_regulator_bulk_get(dev, CSIS_NUM_SUPPLIES,
81462306a36Sopenharmony_ci				 state->supplies);
81562306a36Sopenharmony_ci	if (ret)
81662306a36Sopenharmony_ci		return ret;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	ret = s5pcsis_clk_get(state);
81962306a36Sopenharmony_ci	if (ret < 0)
82062306a36Sopenharmony_ci		return ret;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	if (state->clk_frequency)
82362306a36Sopenharmony_ci		ret = clk_set_rate(state->clock[CSIS_CLK_MUX],
82462306a36Sopenharmony_ci				   state->clk_frequency);
82562306a36Sopenharmony_ci	else
82662306a36Sopenharmony_ci		dev_WARN(dev, "No clock frequency specified!\n");
82762306a36Sopenharmony_ci	if (ret < 0)
82862306a36Sopenharmony_ci		goto e_clkput;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	ret = clk_enable(state->clock[CSIS_CLK_MUX]);
83162306a36Sopenharmony_ci	if (ret < 0)
83262306a36Sopenharmony_ci		goto e_clkput;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	ret = devm_request_irq(dev, state->irq, s5pcsis_irq_handler,
83562306a36Sopenharmony_ci			       0, dev_name(dev), state);
83662306a36Sopenharmony_ci	if (ret) {
83762306a36Sopenharmony_ci		dev_err(dev, "Interrupt request failed\n");
83862306a36Sopenharmony_ci		goto e_clkdis;
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops);
84262306a36Sopenharmony_ci	state->sd.owner = THIS_MODULE;
84362306a36Sopenharmony_ci	snprintf(state->sd.name, sizeof(state->sd.name), "%s.%d",
84462306a36Sopenharmony_ci		 CSIS_SUBDEV_NAME, state->index);
84562306a36Sopenharmony_ci	state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
84662306a36Sopenharmony_ci	state->csis_fmt = &s5pcsis_formats[0];
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	state->format.code = s5pcsis_formats[0].code;
84962306a36Sopenharmony_ci	state->format.width = S5PCSIS_DEF_PIX_WIDTH;
85062306a36Sopenharmony_ci	state->format.height = S5PCSIS_DEF_PIX_HEIGHT;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	state->sd.entity.function = MEDIA_ENT_F_IO_V4L;
85362306a36Sopenharmony_ci	state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
85462306a36Sopenharmony_ci	state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
85562306a36Sopenharmony_ci	ret = media_entity_pads_init(&state->sd.entity,
85662306a36Sopenharmony_ci				CSIS_PADS_NUM, state->pads);
85762306a36Sopenharmony_ci	if (ret < 0)
85862306a36Sopenharmony_ci		goto e_clkdis;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	/* This allows to retrieve the platform device id by the host driver */
86162306a36Sopenharmony_ci	v4l2_set_subdevdata(&state->sd, pdev);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	/* .. and a pointer to the subdev. */
86462306a36Sopenharmony_ci	platform_set_drvdata(pdev, &state->sd);
86562306a36Sopenharmony_ci	memcpy(state->events, s5pcsis_events, sizeof(state->events));
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	pm_runtime_enable(dev);
86862306a36Sopenharmony_ci	if (!pm_runtime_enabled(dev)) {
86962306a36Sopenharmony_ci		ret = s5pcsis_pm_resume(dev, true);
87062306a36Sopenharmony_ci		if (ret < 0)
87162306a36Sopenharmony_ci			goto e_m_ent;
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n",
87562306a36Sopenharmony_ci		 state->num_lanes, state->hs_settle, state->wclk_ext,
87662306a36Sopenharmony_ci		 state->clk_frequency);
87762306a36Sopenharmony_ci	return 0;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cie_m_ent:
88062306a36Sopenharmony_ci	media_entity_cleanup(&state->sd.entity);
88162306a36Sopenharmony_cie_clkdis:
88262306a36Sopenharmony_ci	clk_disable(state->clock[CSIS_CLK_MUX]);
88362306a36Sopenharmony_cie_clkput:
88462306a36Sopenharmony_ci	s5pcsis_clk_put(state);
88562306a36Sopenharmony_ci	return ret;
88662306a36Sopenharmony_ci}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cistatic int s5pcsis_pm_suspend(struct device *dev, bool runtime)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	struct v4l2_subdev *sd = dev_get_drvdata(dev);
89162306a36Sopenharmony_ci	struct csis_state *state = sd_to_csis_state(sd);
89262306a36Sopenharmony_ci	int ret = 0;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
89562306a36Sopenharmony_ci		 __func__, state->flags);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	mutex_lock(&state->lock);
89862306a36Sopenharmony_ci	if (state->flags & ST_POWERED) {
89962306a36Sopenharmony_ci		s5pcsis_stop_stream(state);
90062306a36Sopenharmony_ci		ret = phy_power_off(state->phy);
90162306a36Sopenharmony_ci		if (ret)
90262306a36Sopenharmony_ci			goto unlock;
90362306a36Sopenharmony_ci		ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
90462306a36Sopenharmony_ci					     state->supplies);
90562306a36Sopenharmony_ci		if (ret)
90662306a36Sopenharmony_ci			goto unlock;
90762306a36Sopenharmony_ci		clk_disable(state->clock[CSIS_CLK_GATE]);
90862306a36Sopenharmony_ci		state->flags &= ~ST_POWERED;
90962306a36Sopenharmony_ci		if (!runtime)
91062306a36Sopenharmony_ci			state->flags |= ST_SUSPENDED;
91162306a36Sopenharmony_ci	}
91262306a36Sopenharmony_ci unlock:
91362306a36Sopenharmony_ci	mutex_unlock(&state->lock);
91462306a36Sopenharmony_ci	return ret ? -EAGAIN : 0;
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_cistatic int s5pcsis_pm_resume(struct device *dev, bool runtime)
91862306a36Sopenharmony_ci{
91962306a36Sopenharmony_ci	struct v4l2_subdev *sd = dev_get_drvdata(dev);
92062306a36Sopenharmony_ci	struct csis_state *state = sd_to_csis_state(sd);
92162306a36Sopenharmony_ci	int ret = 0;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
92462306a36Sopenharmony_ci		 __func__, state->flags);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	mutex_lock(&state->lock);
92762306a36Sopenharmony_ci	if (!runtime && !(state->flags & ST_SUSPENDED))
92862306a36Sopenharmony_ci		goto unlock;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (!(state->flags & ST_POWERED)) {
93162306a36Sopenharmony_ci		ret = regulator_bulk_enable(CSIS_NUM_SUPPLIES,
93262306a36Sopenharmony_ci					    state->supplies);
93362306a36Sopenharmony_ci		if (ret)
93462306a36Sopenharmony_ci			goto unlock;
93562306a36Sopenharmony_ci		ret = phy_power_on(state->phy);
93662306a36Sopenharmony_ci		if (!ret) {
93762306a36Sopenharmony_ci			state->flags |= ST_POWERED;
93862306a36Sopenharmony_ci		} else {
93962306a36Sopenharmony_ci			regulator_bulk_disable(CSIS_NUM_SUPPLIES,
94062306a36Sopenharmony_ci					       state->supplies);
94162306a36Sopenharmony_ci			goto unlock;
94262306a36Sopenharmony_ci		}
94362306a36Sopenharmony_ci		clk_enable(state->clock[CSIS_CLK_GATE]);
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci	if (state->flags & ST_STREAMING)
94662306a36Sopenharmony_ci		s5pcsis_start_stream(state);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	state->flags &= ~ST_SUSPENDED;
94962306a36Sopenharmony_ci unlock:
95062306a36Sopenharmony_ci	mutex_unlock(&state->lock);
95162306a36Sopenharmony_ci	return ret ? -EAGAIN : 0;
95262306a36Sopenharmony_ci}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
95562306a36Sopenharmony_cistatic int s5pcsis_suspend(struct device *dev)
95662306a36Sopenharmony_ci{
95762306a36Sopenharmony_ci	return s5pcsis_pm_suspend(dev, false);
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic int s5pcsis_resume(struct device *dev)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	return s5pcsis_pm_resume(dev, false);
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ci#endif
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci#ifdef CONFIG_PM
96762306a36Sopenharmony_cistatic int s5pcsis_runtime_suspend(struct device *dev)
96862306a36Sopenharmony_ci{
96962306a36Sopenharmony_ci	return s5pcsis_pm_suspend(dev, true);
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic int s5pcsis_runtime_resume(struct device *dev)
97362306a36Sopenharmony_ci{
97462306a36Sopenharmony_ci	return s5pcsis_pm_resume(dev, true);
97562306a36Sopenharmony_ci}
97662306a36Sopenharmony_ci#endif
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic void s5pcsis_remove(struct platform_device *pdev)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	struct v4l2_subdev *sd = platform_get_drvdata(pdev);
98162306a36Sopenharmony_ci	struct csis_state *state = sd_to_csis_state(sd);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
98462306a36Sopenharmony_ci	s5pcsis_pm_suspend(&pdev->dev, true);
98562306a36Sopenharmony_ci	clk_disable(state->clock[CSIS_CLK_MUX]);
98662306a36Sopenharmony_ci	pm_runtime_set_suspended(&pdev->dev);
98762306a36Sopenharmony_ci	s5pcsis_clk_put(state);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	media_entity_cleanup(&state->sd.entity);
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_cistatic const struct dev_pm_ops s5pcsis_pm_ops = {
99362306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(s5pcsis_runtime_suspend, s5pcsis_runtime_resume,
99462306a36Sopenharmony_ci			   NULL)
99562306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
99662306a36Sopenharmony_ci};
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_cistatic const struct csis_drvdata exynos4_csis_drvdata = {
99962306a36Sopenharmony_ci	.interrupt_mask = S5PCSIS_INTMSK_EXYNOS4_EN_ALL,
100062306a36Sopenharmony_ci};
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_cistatic const struct csis_drvdata exynos5_csis_drvdata = {
100362306a36Sopenharmony_ci	.interrupt_mask = S5PCSIS_INTMSK_EXYNOS5_EN_ALL,
100462306a36Sopenharmony_ci};
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_cistatic const struct of_device_id s5pcsis_of_match[] = {
100762306a36Sopenharmony_ci	{
100862306a36Sopenharmony_ci		.compatible = "samsung,s5pv210-csis",
100962306a36Sopenharmony_ci		.data = &exynos4_csis_drvdata,
101062306a36Sopenharmony_ci	}, {
101162306a36Sopenharmony_ci		.compatible = "samsung,exynos4210-csis",
101262306a36Sopenharmony_ci		.data = &exynos4_csis_drvdata,
101362306a36Sopenharmony_ci	}, {
101462306a36Sopenharmony_ci		.compatible = "samsung,exynos5250-csis",
101562306a36Sopenharmony_ci		.data = &exynos5_csis_drvdata,
101662306a36Sopenharmony_ci	},
101762306a36Sopenharmony_ci	{ /* sentinel */ },
101862306a36Sopenharmony_ci};
101962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, s5pcsis_of_match);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_cistatic struct platform_driver s5pcsis_driver = {
102262306a36Sopenharmony_ci	.probe		= s5pcsis_probe,
102362306a36Sopenharmony_ci	.remove_new	= s5pcsis_remove,
102462306a36Sopenharmony_ci	.driver		= {
102562306a36Sopenharmony_ci		.of_match_table = s5pcsis_of_match,
102662306a36Sopenharmony_ci		.name		= CSIS_DRIVER_NAME,
102762306a36Sopenharmony_ci		.pm		= &s5pcsis_pm_ops,
102862306a36Sopenharmony_ci	},
102962306a36Sopenharmony_ci};
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cimodule_platform_driver(s5pcsis_driver);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ciMODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
103462306a36Sopenharmony_ciMODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver");
103562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1036