xref: /OK3568_Linux_fs/kernel/drivers/media/platform/exynos4-is/mipi-csis.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Samsung S5P/EXYNOS SoC series MIPI-CSI receiver driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
6*4882a593Smuzhiyun  * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <linux/clk.h>
10*4882a593Smuzhiyun #include <linux/delay.h>
11*4882a593Smuzhiyun #include <linux/device.h>
12*4882a593Smuzhiyun #include <linux/errno.h>
13*4882a593Smuzhiyun #include <linux/interrupt.h>
14*4882a593Smuzhiyun #include <linux/io.h>
15*4882a593Smuzhiyun #include <linux/irq.h>
16*4882a593Smuzhiyun #include <linux/kernel.h>
17*4882a593Smuzhiyun #include <linux/memory.h>
18*4882a593Smuzhiyun #include <linux/module.h>
19*4882a593Smuzhiyun #include <linux/of.h>
20*4882a593Smuzhiyun #include <linux/of_graph.h>
21*4882a593Smuzhiyun #include <linux/phy/phy.h>
22*4882a593Smuzhiyun #include <linux/platform_device.h>
23*4882a593Smuzhiyun #include <linux/pm_runtime.h>
24*4882a593Smuzhiyun #include <linux/regulator/consumer.h>
25*4882a593Smuzhiyun #include <linux/sizes.h>
26*4882a593Smuzhiyun #include <linux/slab.h>
27*4882a593Smuzhiyun #include <linux/spinlock.h>
28*4882a593Smuzhiyun #include <linux/videodev2.h>
29*4882a593Smuzhiyun #include <media/drv-intf/exynos-fimc.h>
30*4882a593Smuzhiyun #include <media/v4l2-fwnode.h>
31*4882a593Smuzhiyun #include <media/v4l2-subdev.h>
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun #include "mipi-csis.h"
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun static int debug;
36*4882a593Smuzhiyun module_param(debug, int, 0644);
37*4882a593Smuzhiyun MODULE_PARM_DESC(debug, "Debug level (0-2)");
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun /* Register map definition */
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun /* CSIS global control */
42*4882a593Smuzhiyun #define S5PCSIS_CTRL			0x00
43*4882a593Smuzhiyun #define S5PCSIS_CTRL_DPDN_DEFAULT	(0 << 31)
44*4882a593Smuzhiyun #define S5PCSIS_CTRL_DPDN_SWAP		(1UL << 31)
45*4882a593Smuzhiyun #define S5PCSIS_CTRL_ALIGN_32BIT	(1 << 20)
46*4882a593Smuzhiyun #define S5PCSIS_CTRL_UPDATE_SHADOW	(1 << 16)
47*4882a593Smuzhiyun #define S5PCSIS_CTRL_WCLK_EXTCLK	(1 << 8)
48*4882a593Smuzhiyun #define S5PCSIS_CTRL_RESET		(1 << 4)
49*4882a593Smuzhiyun #define S5PCSIS_CTRL_ENABLE		(1 << 0)
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun /* D-PHY control */
52*4882a593Smuzhiyun #define S5PCSIS_DPHYCTRL		0x04
53*4882a593Smuzhiyun #define S5PCSIS_DPHYCTRL_HSS_MASK	(0x1f << 27)
54*4882a593Smuzhiyun #define S5PCSIS_DPHYCTRL_ENABLE		(0x1f << 0)
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun #define S5PCSIS_CONFIG			0x08
57*4882a593Smuzhiyun #define S5PCSIS_CFG_FMT_YCBCR422_8BIT	(0x1e << 2)
58*4882a593Smuzhiyun #define S5PCSIS_CFG_FMT_RAW8		(0x2a << 2)
59*4882a593Smuzhiyun #define S5PCSIS_CFG_FMT_RAW10		(0x2b << 2)
60*4882a593Smuzhiyun #define S5PCSIS_CFG_FMT_RAW12		(0x2c << 2)
61*4882a593Smuzhiyun /* User defined formats, x = 1...4 */
62*4882a593Smuzhiyun #define S5PCSIS_CFG_FMT_USER(x)		((0x30 + x - 1) << 2)
63*4882a593Smuzhiyun #define S5PCSIS_CFG_FMT_MASK		(0x3f << 2)
64*4882a593Smuzhiyun #define S5PCSIS_CFG_NR_LANE_MASK	3
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun /* Interrupt mask */
67*4882a593Smuzhiyun #define S5PCSIS_INTMSK			0x10
68*4882a593Smuzhiyun #define S5PCSIS_INTMSK_EVEN_BEFORE	(1UL << 31)
69*4882a593Smuzhiyun #define S5PCSIS_INTMSK_EVEN_AFTER	(1 << 30)
70*4882a593Smuzhiyun #define S5PCSIS_INTMSK_ODD_BEFORE	(1 << 29)
71*4882a593Smuzhiyun #define S5PCSIS_INTMSK_ODD_AFTER	(1 << 28)
72*4882a593Smuzhiyun #define S5PCSIS_INTMSK_FRAME_START	(1 << 27)
73*4882a593Smuzhiyun #define S5PCSIS_INTMSK_FRAME_END	(1 << 26)
74*4882a593Smuzhiyun #define S5PCSIS_INTMSK_ERR_SOT_HS	(1 << 12)
75*4882a593Smuzhiyun #define S5PCSIS_INTMSK_ERR_LOST_FS	(1 << 5)
76*4882a593Smuzhiyun #define S5PCSIS_INTMSK_ERR_LOST_FE	(1 << 4)
77*4882a593Smuzhiyun #define S5PCSIS_INTMSK_ERR_OVER		(1 << 3)
78*4882a593Smuzhiyun #define S5PCSIS_INTMSK_ERR_ECC		(1 << 2)
79*4882a593Smuzhiyun #define S5PCSIS_INTMSK_ERR_CRC		(1 << 1)
80*4882a593Smuzhiyun #define S5PCSIS_INTMSK_ERR_UNKNOWN	(1 << 0)
81*4882a593Smuzhiyun #define S5PCSIS_INTMSK_EXYNOS4_EN_ALL	0xf000103f
82*4882a593Smuzhiyun #define S5PCSIS_INTMSK_EXYNOS5_EN_ALL	0xfc00103f
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun /* Interrupt source */
85*4882a593Smuzhiyun #define S5PCSIS_INTSRC			0x14
86*4882a593Smuzhiyun #define S5PCSIS_INTSRC_EVEN_BEFORE	(1UL << 31)
87*4882a593Smuzhiyun #define S5PCSIS_INTSRC_EVEN_AFTER	(1 << 30)
88*4882a593Smuzhiyun #define S5PCSIS_INTSRC_EVEN		(0x3 << 30)
89*4882a593Smuzhiyun #define S5PCSIS_INTSRC_ODD_BEFORE	(1 << 29)
90*4882a593Smuzhiyun #define S5PCSIS_INTSRC_ODD_AFTER	(1 << 28)
91*4882a593Smuzhiyun #define S5PCSIS_INTSRC_ODD		(0x3 << 28)
92*4882a593Smuzhiyun #define S5PCSIS_INTSRC_NON_IMAGE_DATA	(0xf << 28)
93*4882a593Smuzhiyun #define S5PCSIS_INTSRC_FRAME_START	(1 << 27)
94*4882a593Smuzhiyun #define S5PCSIS_INTSRC_FRAME_END	(1 << 26)
95*4882a593Smuzhiyun #define S5PCSIS_INTSRC_ERR_SOT_HS	(0xf << 12)
96*4882a593Smuzhiyun #define S5PCSIS_INTSRC_ERR_LOST_FS	(1 << 5)
97*4882a593Smuzhiyun #define S5PCSIS_INTSRC_ERR_LOST_FE	(1 << 4)
98*4882a593Smuzhiyun #define S5PCSIS_INTSRC_ERR_OVER		(1 << 3)
99*4882a593Smuzhiyun #define S5PCSIS_INTSRC_ERR_ECC		(1 << 2)
100*4882a593Smuzhiyun #define S5PCSIS_INTSRC_ERR_CRC		(1 << 1)
101*4882a593Smuzhiyun #define S5PCSIS_INTSRC_ERR_UNKNOWN	(1 << 0)
102*4882a593Smuzhiyun #define S5PCSIS_INTSRC_ERRORS		0xf03f
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun /* Pixel resolution */
105*4882a593Smuzhiyun #define S5PCSIS_RESOL			0x2c
106*4882a593Smuzhiyun #define CSIS_MAX_PIX_WIDTH		0xffff
107*4882a593Smuzhiyun #define CSIS_MAX_PIX_HEIGHT		0xffff
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun /* Non-image packet data buffers */
110*4882a593Smuzhiyun #define S5PCSIS_PKTDATA_ODD		0x2000
111*4882a593Smuzhiyun #define S5PCSIS_PKTDATA_EVEN		0x3000
112*4882a593Smuzhiyun #define S5PCSIS_PKTDATA_SIZE		SZ_4K
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun enum {
115*4882a593Smuzhiyun 	CSIS_CLK_MUX,
116*4882a593Smuzhiyun 	CSIS_CLK_GATE,
117*4882a593Smuzhiyun };
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun static char *csi_clock_name[] = {
120*4882a593Smuzhiyun 	[CSIS_CLK_MUX]  = "sclk_csis",
121*4882a593Smuzhiyun 	[CSIS_CLK_GATE] = "csis",
122*4882a593Smuzhiyun };
123*4882a593Smuzhiyun #define NUM_CSIS_CLOCKS	ARRAY_SIZE(csi_clock_name)
124*4882a593Smuzhiyun #define DEFAULT_SCLK_CSIS_FREQ	166000000UL
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun static const char * const csis_supply_name[] = {
127*4882a593Smuzhiyun 	"vddcore",  /* CSIS Core (1.0V, 1.1V or 1.2V) suppply */
128*4882a593Smuzhiyun 	"vddio",    /* CSIS I/O and PLL (1.8V) supply */
129*4882a593Smuzhiyun };
130*4882a593Smuzhiyun #define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name)
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun enum {
133*4882a593Smuzhiyun 	ST_POWERED	= 1,
134*4882a593Smuzhiyun 	ST_STREAMING	= 2,
135*4882a593Smuzhiyun 	ST_SUSPENDED	= 4,
136*4882a593Smuzhiyun };
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun struct s5pcsis_event {
139*4882a593Smuzhiyun 	u32 mask;
140*4882a593Smuzhiyun 	const char * const name;
141*4882a593Smuzhiyun 	unsigned int counter;
142*4882a593Smuzhiyun };
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun static const struct s5pcsis_event s5pcsis_events[] = {
145*4882a593Smuzhiyun 	/* Errors */
146*4882a593Smuzhiyun 	{ S5PCSIS_INTSRC_ERR_SOT_HS,	"SOT Error" },
147*4882a593Smuzhiyun 	{ S5PCSIS_INTSRC_ERR_LOST_FS,	"Lost Frame Start Error" },
148*4882a593Smuzhiyun 	{ S5PCSIS_INTSRC_ERR_LOST_FE,	"Lost Frame End Error" },
149*4882a593Smuzhiyun 	{ S5PCSIS_INTSRC_ERR_OVER,	"FIFO Overflow Error" },
150*4882a593Smuzhiyun 	{ S5PCSIS_INTSRC_ERR_ECC,	"ECC Error" },
151*4882a593Smuzhiyun 	{ S5PCSIS_INTSRC_ERR_CRC,	"CRC Error" },
152*4882a593Smuzhiyun 	{ S5PCSIS_INTSRC_ERR_UNKNOWN,	"Unknown Error" },
153*4882a593Smuzhiyun 	/* Non-image data receive events */
154*4882a593Smuzhiyun 	{ S5PCSIS_INTSRC_EVEN_BEFORE,	"Non-image data before even frame" },
155*4882a593Smuzhiyun 	{ S5PCSIS_INTSRC_EVEN_AFTER,	"Non-image data after even frame" },
156*4882a593Smuzhiyun 	{ S5PCSIS_INTSRC_ODD_BEFORE,	"Non-image data before odd frame" },
157*4882a593Smuzhiyun 	{ S5PCSIS_INTSRC_ODD_AFTER,	"Non-image data after odd frame" },
158*4882a593Smuzhiyun 	/* Frame start/end */
159*4882a593Smuzhiyun 	{ S5PCSIS_INTSRC_FRAME_START,	"Frame Start" },
160*4882a593Smuzhiyun 	{ S5PCSIS_INTSRC_FRAME_END,	"Frame End" },
161*4882a593Smuzhiyun };
162*4882a593Smuzhiyun #define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events)
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun struct csis_pktbuf {
165*4882a593Smuzhiyun 	u32 *data;
166*4882a593Smuzhiyun 	unsigned int len;
167*4882a593Smuzhiyun };
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun struct csis_drvdata {
170*4882a593Smuzhiyun 	/* Mask of all used interrupts in S5PCSIS_INTMSK register */
171*4882a593Smuzhiyun 	u32 interrupt_mask;
172*4882a593Smuzhiyun };
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun /**
175*4882a593Smuzhiyun  * struct csis_state - the driver's internal state data structure
176*4882a593Smuzhiyun  * @lock: mutex serializing the subdev and power management operations,
177*4882a593Smuzhiyun  *        protecting @format and @flags members
178*4882a593Smuzhiyun  * @pads: CSIS pads array
179*4882a593Smuzhiyun  * @sd: v4l2_subdev associated with CSIS device instance
180*4882a593Smuzhiyun  * @index: the hardware instance index
181*4882a593Smuzhiyun  * @pdev: CSIS platform device
182*4882a593Smuzhiyun  * @phy: pointer to the CSIS generic PHY
183*4882a593Smuzhiyun  * @regs: mmapped I/O registers memory
184*4882a593Smuzhiyun  * @supplies: CSIS regulator supplies
185*4882a593Smuzhiyun  * @clock: CSIS clocks
186*4882a593Smuzhiyun  * @irq: requested s5p-mipi-csis irq number
187*4882a593Smuzhiyun  * @interrupt_mask: interrupt mask of the all used interrupts
188*4882a593Smuzhiyun  * @flags: the state variable for power and streaming control
189*4882a593Smuzhiyun  * @clk_frequency: device bus clock frequency
190*4882a593Smuzhiyun  * @hs_settle: HS-RX settle time
191*4882a593Smuzhiyun  * @num_lanes: number of MIPI-CSI data lanes used
192*4882a593Smuzhiyun  * @max_num_lanes: maximum number of MIPI-CSI data lanes supported
193*4882a593Smuzhiyun  * @wclk_ext: CSI wrapper clock: 0 - bus clock, 1 - external SCLK_CAM
194*4882a593Smuzhiyun  * @csis_fmt: current CSIS pixel format
195*4882a593Smuzhiyun  * @format: common media bus format for the source and sink pad
196*4882a593Smuzhiyun  * @slock: spinlock protecting structure members below
197*4882a593Smuzhiyun  * @pkt_buf: the frame embedded (non-image) data buffer
198*4882a593Smuzhiyun  * @events: MIPI-CSIS event (error) counters
199*4882a593Smuzhiyun  */
200*4882a593Smuzhiyun struct csis_state {
201*4882a593Smuzhiyun 	struct mutex lock;
202*4882a593Smuzhiyun 	struct media_pad pads[CSIS_PADS_NUM];
203*4882a593Smuzhiyun 	struct v4l2_subdev sd;
204*4882a593Smuzhiyun 	u8 index;
205*4882a593Smuzhiyun 	struct platform_device *pdev;
206*4882a593Smuzhiyun 	struct phy *phy;
207*4882a593Smuzhiyun 	void __iomem *regs;
208*4882a593Smuzhiyun 	struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
209*4882a593Smuzhiyun 	struct clk *clock[NUM_CSIS_CLOCKS];
210*4882a593Smuzhiyun 	int irq;
211*4882a593Smuzhiyun 	u32 interrupt_mask;
212*4882a593Smuzhiyun 	u32 flags;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	u32 clk_frequency;
215*4882a593Smuzhiyun 	u32 hs_settle;
216*4882a593Smuzhiyun 	u32 num_lanes;
217*4882a593Smuzhiyun 	u32 max_num_lanes;
218*4882a593Smuzhiyun 	u8 wclk_ext;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	const struct csis_pix_format *csis_fmt;
221*4882a593Smuzhiyun 	struct v4l2_mbus_framefmt format;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	spinlock_t slock;
224*4882a593Smuzhiyun 	struct csis_pktbuf pkt_buf;
225*4882a593Smuzhiyun 	struct s5pcsis_event events[S5PCSIS_NUM_EVENTS];
226*4882a593Smuzhiyun };
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun /**
229*4882a593Smuzhiyun  * struct csis_pix_format - CSIS pixel format description
230*4882a593Smuzhiyun  * @pix_width_alignment: horizontal pixel alignment, width will be
231*4882a593Smuzhiyun  *                       multiple of 2^pix_width_alignment
232*4882a593Smuzhiyun  * @code: corresponding media bus code
233*4882a593Smuzhiyun  * @fmt_reg: S5PCSIS_CONFIG register value
234*4882a593Smuzhiyun  * @data_alignment: MIPI-CSI data alignment in bits
235*4882a593Smuzhiyun  */
236*4882a593Smuzhiyun struct csis_pix_format {
237*4882a593Smuzhiyun 	unsigned int pix_width_alignment;
238*4882a593Smuzhiyun 	u32 code;
239*4882a593Smuzhiyun 	u32 fmt_reg;
240*4882a593Smuzhiyun 	u8 data_alignment;
241*4882a593Smuzhiyun };
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun static const struct csis_pix_format s5pcsis_formats[] = {
244*4882a593Smuzhiyun 	{
245*4882a593Smuzhiyun 		.code = MEDIA_BUS_FMT_VYUY8_2X8,
246*4882a593Smuzhiyun 		.fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT,
247*4882a593Smuzhiyun 		.data_alignment = 32,
248*4882a593Smuzhiyun 	}, {
249*4882a593Smuzhiyun 		.code = MEDIA_BUS_FMT_JPEG_1X8,
250*4882a593Smuzhiyun 		.fmt_reg = S5PCSIS_CFG_FMT_USER(1),
251*4882a593Smuzhiyun 		.data_alignment = 32,
252*4882a593Smuzhiyun 	}, {
253*4882a593Smuzhiyun 		.code = MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8,
254*4882a593Smuzhiyun 		.fmt_reg = S5PCSIS_CFG_FMT_USER(1),
255*4882a593Smuzhiyun 		.data_alignment = 32,
256*4882a593Smuzhiyun 	}, {
257*4882a593Smuzhiyun 		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
258*4882a593Smuzhiyun 		.fmt_reg = S5PCSIS_CFG_FMT_RAW8,
259*4882a593Smuzhiyun 		.data_alignment = 24,
260*4882a593Smuzhiyun 	}, {
261*4882a593Smuzhiyun 		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
262*4882a593Smuzhiyun 		.fmt_reg = S5PCSIS_CFG_FMT_RAW10,
263*4882a593Smuzhiyun 		.data_alignment = 24,
264*4882a593Smuzhiyun 	}, {
265*4882a593Smuzhiyun 		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
266*4882a593Smuzhiyun 		.fmt_reg = S5PCSIS_CFG_FMT_RAW12,
267*4882a593Smuzhiyun 		.data_alignment = 24,
268*4882a593Smuzhiyun 	}
269*4882a593Smuzhiyun };
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun #define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r)
272*4882a593Smuzhiyun #define s5pcsis_read(__csis, __r) readl(__csis->regs + __r)
273*4882a593Smuzhiyun 
sd_to_csis_state(struct v4l2_subdev * sdev)274*4882a593Smuzhiyun static struct csis_state *sd_to_csis_state(struct v4l2_subdev *sdev)
275*4882a593Smuzhiyun {
276*4882a593Smuzhiyun 	return container_of(sdev, struct csis_state, sd);
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun 
find_csis_format(struct v4l2_mbus_framefmt * mf)279*4882a593Smuzhiyun static const struct csis_pix_format *find_csis_format(
280*4882a593Smuzhiyun 	struct v4l2_mbus_framefmt *mf)
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun 	int i;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(s5pcsis_formats); i++)
285*4882a593Smuzhiyun 		if (mf->code == s5pcsis_formats[i].code)
286*4882a593Smuzhiyun 			return &s5pcsis_formats[i];
287*4882a593Smuzhiyun 	return NULL;
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun 
s5pcsis_enable_interrupts(struct csis_state * state,bool on)290*4882a593Smuzhiyun static void s5pcsis_enable_interrupts(struct csis_state *state, bool on)
291*4882a593Smuzhiyun {
292*4882a593Smuzhiyun 	u32 val = s5pcsis_read(state, S5PCSIS_INTMSK);
293*4882a593Smuzhiyun 	if (on)
294*4882a593Smuzhiyun 		val |= state->interrupt_mask;
295*4882a593Smuzhiyun 	else
296*4882a593Smuzhiyun 		val &= ~state->interrupt_mask;
297*4882a593Smuzhiyun 	s5pcsis_write(state, S5PCSIS_INTMSK, val);
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun 
s5pcsis_reset(struct csis_state * state)300*4882a593Smuzhiyun static void s5pcsis_reset(struct csis_state *state)
301*4882a593Smuzhiyun {
302*4882a593Smuzhiyun 	u32 val = s5pcsis_read(state, S5PCSIS_CTRL);
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_RESET);
305*4882a593Smuzhiyun 	udelay(10);
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun 
s5pcsis_system_enable(struct csis_state * state,int on)308*4882a593Smuzhiyun static void s5pcsis_system_enable(struct csis_state *state, int on)
309*4882a593Smuzhiyun {
310*4882a593Smuzhiyun 	u32 val, mask;
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	val = s5pcsis_read(state, S5PCSIS_CTRL);
313*4882a593Smuzhiyun 	if (on)
314*4882a593Smuzhiyun 		val |= S5PCSIS_CTRL_ENABLE;
315*4882a593Smuzhiyun 	else
316*4882a593Smuzhiyun 		val &= ~S5PCSIS_CTRL_ENABLE;
317*4882a593Smuzhiyun 	s5pcsis_write(state, S5PCSIS_CTRL, val);
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
320*4882a593Smuzhiyun 	val &= ~S5PCSIS_DPHYCTRL_ENABLE;
321*4882a593Smuzhiyun 	if (on) {
322*4882a593Smuzhiyun 		mask = (1 << (state->num_lanes + 1)) - 1;
323*4882a593Smuzhiyun 		val |= (mask & S5PCSIS_DPHYCTRL_ENABLE);
324*4882a593Smuzhiyun 	}
325*4882a593Smuzhiyun 	s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun /* Called with the state.lock mutex held */
__s5pcsis_set_format(struct csis_state * state)329*4882a593Smuzhiyun static void __s5pcsis_set_format(struct csis_state *state)
330*4882a593Smuzhiyun {
331*4882a593Smuzhiyun 	struct v4l2_mbus_framefmt *mf = &state->format;
332*4882a593Smuzhiyun 	u32 val;
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	v4l2_dbg(1, debug, &state->sd, "fmt: %#x, %d x %d\n",
335*4882a593Smuzhiyun 		 mf->code, mf->width, mf->height);
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	/* Color format */
338*4882a593Smuzhiyun 	val = s5pcsis_read(state, S5PCSIS_CONFIG);
339*4882a593Smuzhiyun 	val = (val & ~S5PCSIS_CFG_FMT_MASK) | state->csis_fmt->fmt_reg;
340*4882a593Smuzhiyun 	s5pcsis_write(state, S5PCSIS_CONFIG, val);
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	/* Pixel resolution */
343*4882a593Smuzhiyun 	val = (mf->width << 16) | mf->height;
344*4882a593Smuzhiyun 	s5pcsis_write(state, S5PCSIS_RESOL, val);
345*4882a593Smuzhiyun }
346*4882a593Smuzhiyun 
s5pcsis_set_hsync_settle(struct csis_state * state,int settle)347*4882a593Smuzhiyun static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle)
348*4882a593Smuzhiyun {
349*4882a593Smuzhiyun 	u32 val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 	val = (val & ~S5PCSIS_DPHYCTRL_HSS_MASK) | (settle << 27);
352*4882a593Smuzhiyun 	s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun 
s5pcsis_set_params(struct csis_state * state)355*4882a593Smuzhiyun static void s5pcsis_set_params(struct csis_state *state)
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun 	u32 val;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	val = s5pcsis_read(state, S5PCSIS_CONFIG);
360*4882a593Smuzhiyun 	val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (state->num_lanes - 1);
361*4882a593Smuzhiyun 	s5pcsis_write(state, S5PCSIS_CONFIG, val);
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	__s5pcsis_set_format(state);
364*4882a593Smuzhiyun 	s5pcsis_set_hsync_settle(state, state->hs_settle);
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	val = s5pcsis_read(state, S5PCSIS_CTRL);
367*4882a593Smuzhiyun 	if (state->csis_fmt->data_alignment == 32)
368*4882a593Smuzhiyun 		val |= S5PCSIS_CTRL_ALIGN_32BIT;
369*4882a593Smuzhiyun 	else /* 24-bits */
370*4882a593Smuzhiyun 		val &= ~S5PCSIS_CTRL_ALIGN_32BIT;
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	val &= ~S5PCSIS_CTRL_WCLK_EXTCLK;
373*4882a593Smuzhiyun 	if (state->wclk_ext)
374*4882a593Smuzhiyun 		val |= S5PCSIS_CTRL_WCLK_EXTCLK;
375*4882a593Smuzhiyun 	s5pcsis_write(state, S5PCSIS_CTRL, val);
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	/* Update the shadow register. */
378*4882a593Smuzhiyun 	val = s5pcsis_read(state, S5PCSIS_CTRL);
379*4882a593Smuzhiyun 	s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_UPDATE_SHADOW);
380*4882a593Smuzhiyun }
381*4882a593Smuzhiyun 
s5pcsis_clk_put(struct csis_state * state)382*4882a593Smuzhiyun static void s5pcsis_clk_put(struct csis_state *state)
383*4882a593Smuzhiyun {
384*4882a593Smuzhiyun 	int i;
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
387*4882a593Smuzhiyun 		if (IS_ERR(state->clock[i]))
388*4882a593Smuzhiyun 			continue;
389*4882a593Smuzhiyun 		clk_unprepare(state->clock[i]);
390*4882a593Smuzhiyun 		clk_put(state->clock[i]);
391*4882a593Smuzhiyun 		state->clock[i] = ERR_PTR(-EINVAL);
392*4882a593Smuzhiyun 	}
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun 
s5pcsis_clk_get(struct csis_state * state)395*4882a593Smuzhiyun static int s5pcsis_clk_get(struct csis_state *state)
396*4882a593Smuzhiyun {
397*4882a593Smuzhiyun 	struct device *dev = &state->pdev->dev;
398*4882a593Smuzhiyun 	int i, ret;
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	for (i = 0; i < NUM_CSIS_CLOCKS; i++)
401*4882a593Smuzhiyun 		state->clock[i] = ERR_PTR(-EINVAL);
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
404*4882a593Smuzhiyun 		state->clock[i] = clk_get(dev, csi_clock_name[i]);
405*4882a593Smuzhiyun 		if (IS_ERR(state->clock[i])) {
406*4882a593Smuzhiyun 			ret = PTR_ERR(state->clock[i]);
407*4882a593Smuzhiyun 			goto err;
408*4882a593Smuzhiyun 		}
409*4882a593Smuzhiyun 		ret = clk_prepare(state->clock[i]);
410*4882a593Smuzhiyun 		if (ret < 0) {
411*4882a593Smuzhiyun 			clk_put(state->clock[i]);
412*4882a593Smuzhiyun 			state->clock[i] = ERR_PTR(-EINVAL);
413*4882a593Smuzhiyun 			goto err;
414*4882a593Smuzhiyun 		}
415*4882a593Smuzhiyun 	}
416*4882a593Smuzhiyun 	return 0;
417*4882a593Smuzhiyun err:
418*4882a593Smuzhiyun 	s5pcsis_clk_put(state);
419*4882a593Smuzhiyun 	dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]);
420*4882a593Smuzhiyun 	return ret;
421*4882a593Smuzhiyun }
422*4882a593Smuzhiyun 
dump_regs(struct csis_state * state,const char * label)423*4882a593Smuzhiyun static void dump_regs(struct csis_state *state, const char *label)
424*4882a593Smuzhiyun {
425*4882a593Smuzhiyun 	struct {
426*4882a593Smuzhiyun 		u32 offset;
427*4882a593Smuzhiyun 		const char * const name;
428*4882a593Smuzhiyun 	} registers[] = {
429*4882a593Smuzhiyun 		{ 0x00, "CTRL" },
430*4882a593Smuzhiyun 		{ 0x04, "DPHYCTRL" },
431*4882a593Smuzhiyun 		{ 0x08, "CONFIG" },
432*4882a593Smuzhiyun 		{ 0x0c, "DPHYSTS" },
433*4882a593Smuzhiyun 		{ 0x10, "INTMSK" },
434*4882a593Smuzhiyun 		{ 0x2c, "RESOL" },
435*4882a593Smuzhiyun 		{ 0x38, "SDW_CONFIG" },
436*4882a593Smuzhiyun 	};
437*4882a593Smuzhiyun 	u32 i;
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	v4l2_info(&state->sd, "--- %s ---\n", label);
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(registers); i++) {
442*4882a593Smuzhiyun 		u32 cfg = s5pcsis_read(state, registers[i].offset);
443*4882a593Smuzhiyun 		v4l2_info(&state->sd, "%10s: 0x%08x\n", registers[i].name, cfg);
444*4882a593Smuzhiyun 	}
445*4882a593Smuzhiyun }
446*4882a593Smuzhiyun 
s5pcsis_start_stream(struct csis_state * state)447*4882a593Smuzhiyun static void s5pcsis_start_stream(struct csis_state *state)
448*4882a593Smuzhiyun {
449*4882a593Smuzhiyun 	s5pcsis_reset(state);
450*4882a593Smuzhiyun 	s5pcsis_set_params(state);
451*4882a593Smuzhiyun 	s5pcsis_system_enable(state, true);
452*4882a593Smuzhiyun 	s5pcsis_enable_interrupts(state, true);
453*4882a593Smuzhiyun }
454*4882a593Smuzhiyun 
s5pcsis_stop_stream(struct csis_state * state)455*4882a593Smuzhiyun static void s5pcsis_stop_stream(struct csis_state *state)
456*4882a593Smuzhiyun {
457*4882a593Smuzhiyun 	s5pcsis_enable_interrupts(state, false);
458*4882a593Smuzhiyun 	s5pcsis_system_enable(state, false);
459*4882a593Smuzhiyun }
460*4882a593Smuzhiyun 
s5pcsis_clear_counters(struct csis_state * state)461*4882a593Smuzhiyun static void s5pcsis_clear_counters(struct csis_state *state)
462*4882a593Smuzhiyun {
463*4882a593Smuzhiyun 	unsigned long flags;
464*4882a593Smuzhiyun 	int i;
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	spin_lock_irqsave(&state->slock, flags);
467*4882a593Smuzhiyun 	for (i = 0; i < S5PCSIS_NUM_EVENTS; i++)
468*4882a593Smuzhiyun 		state->events[i].counter = 0;
469*4882a593Smuzhiyun 	spin_unlock_irqrestore(&state->slock, flags);
470*4882a593Smuzhiyun }
471*4882a593Smuzhiyun 
s5pcsis_log_counters(struct csis_state * state,bool non_errors)472*4882a593Smuzhiyun static void s5pcsis_log_counters(struct csis_state *state, bool non_errors)
473*4882a593Smuzhiyun {
474*4882a593Smuzhiyun 	int i = non_errors ? S5PCSIS_NUM_EVENTS : S5PCSIS_NUM_EVENTS - 4;
475*4882a593Smuzhiyun 	unsigned long flags;
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun 	spin_lock_irqsave(&state->slock, flags);
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 	for (i--; i >= 0; i--) {
480*4882a593Smuzhiyun 		if (state->events[i].counter > 0 || debug)
481*4882a593Smuzhiyun 			v4l2_info(&state->sd, "%s events: %d\n",
482*4882a593Smuzhiyun 				  state->events[i].name,
483*4882a593Smuzhiyun 				  state->events[i].counter);
484*4882a593Smuzhiyun 	}
485*4882a593Smuzhiyun 	spin_unlock_irqrestore(&state->slock, flags);
486*4882a593Smuzhiyun }
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun /*
489*4882a593Smuzhiyun  * V4L2 subdev operations
490*4882a593Smuzhiyun  */
s5pcsis_s_power(struct v4l2_subdev * sd,int on)491*4882a593Smuzhiyun static int s5pcsis_s_power(struct v4l2_subdev *sd, int on)
492*4882a593Smuzhiyun {
493*4882a593Smuzhiyun 	struct csis_state *state = sd_to_csis_state(sd);
494*4882a593Smuzhiyun 	struct device *dev = &state->pdev->dev;
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 	if (on)
497*4882a593Smuzhiyun 		return pm_runtime_resume_and_get(dev);
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	return pm_runtime_put_sync(dev);
500*4882a593Smuzhiyun }
501*4882a593Smuzhiyun 
s5pcsis_s_stream(struct v4l2_subdev * sd,int enable)502*4882a593Smuzhiyun static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable)
503*4882a593Smuzhiyun {
504*4882a593Smuzhiyun 	struct csis_state *state = sd_to_csis_state(sd);
505*4882a593Smuzhiyun 	int ret = 0;
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 	v4l2_dbg(1, debug, sd, "%s: %d, state: 0x%x\n",
508*4882a593Smuzhiyun 		 __func__, enable, state->flags);
509*4882a593Smuzhiyun 
510*4882a593Smuzhiyun 	if (enable) {
511*4882a593Smuzhiyun 		s5pcsis_clear_counters(state);
512*4882a593Smuzhiyun 		ret = pm_runtime_resume_and_get(&state->pdev->dev);
513*4882a593Smuzhiyun 		if (ret < 0)
514*4882a593Smuzhiyun 			return ret;
515*4882a593Smuzhiyun 	}
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun 	mutex_lock(&state->lock);
518*4882a593Smuzhiyun 	if (enable) {
519*4882a593Smuzhiyun 		if (state->flags & ST_SUSPENDED) {
520*4882a593Smuzhiyun 			ret = -EBUSY;
521*4882a593Smuzhiyun 			goto unlock;
522*4882a593Smuzhiyun 		}
523*4882a593Smuzhiyun 		s5pcsis_start_stream(state);
524*4882a593Smuzhiyun 		state->flags |= ST_STREAMING;
525*4882a593Smuzhiyun 	} else {
526*4882a593Smuzhiyun 		s5pcsis_stop_stream(state);
527*4882a593Smuzhiyun 		state->flags &= ~ST_STREAMING;
528*4882a593Smuzhiyun 		if (debug > 0)
529*4882a593Smuzhiyun 			s5pcsis_log_counters(state, true);
530*4882a593Smuzhiyun 	}
531*4882a593Smuzhiyun unlock:
532*4882a593Smuzhiyun 	mutex_unlock(&state->lock);
533*4882a593Smuzhiyun 	if (!enable)
534*4882a593Smuzhiyun 		pm_runtime_put(&state->pdev->dev);
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	return ret;
537*4882a593Smuzhiyun }
538*4882a593Smuzhiyun 
s5pcsis_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_mbus_code_enum * code)539*4882a593Smuzhiyun static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd,
540*4882a593Smuzhiyun 				  struct v4l2_subdev_pad_config *cfg,
541*4882a593Smuzhiyun 				  struct v4l2_subdev_mbus_code_enum *code)
542*4882a593Smuzhiyun {
543*4882a593Smuzhiyun 	if (code->index >= ARRAY_SIZE(s5pcsis_formats))
544*4882a593Smuzhiyun 		return -EINVAL;
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun 	code->code = s5pcsis_formats[code->index].code;
547*4882a593Smuzhiyun 	return 0;
548*4882a593Smuzhiyun }
549*4882a593Smuzhiyun 
s5pcsis_try_format(struct v4l2_mbus_framefmt * mf)550*4882a593Smuzhiyun static struct csis_pix_format const *s5pcsis_try_format(
551*4882a593Smuzhiyun 	struct v4l2_mbus_framefmt *mf)
552*4882a593Smuzhiyun {
553*4882a593Smuzhiyun 	struct csis_pix_format const *csis_fmt;
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	csis_fmt = find_csis_format(mf);
556*4882a593Smuzhiyun 	if (csis_fmt == NULL)
557*4882a593Smuzhiyun 		csis_fmt = &s5pcsis_formats[0];
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 	mf->code = csis_fmt->code;
560*4882a593Smuzhiyun 	v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH,
561*4882a593Smuzhiyun 			      csis_fmt->pix_width_alignment,
562*4882a593Smuzhiyun 			      &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1,
563*4882a593Smuzhiyun 			      0);
564*4882a593Smuzhiyun 	return csis_fmt;
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun 
__s5pcsis_get_format(struct csis_state * state,struct v4l2_subdev_pad_config * cfg,enum v4l2_subdev_format_whence which)567*4882a593Smuzhiyun static struct v4l2_mbus_framefmt *__s5pcsis_get_format(
568*4882a593Smuzhiyun 		struct csis_state *state, struct v4l2_subdev_pad_config *cfg,
569*4882a593Smuzhiyun 		enum v4l2_subdev_format_whence which)
570*4882a593Smuzhiyun {
571*4882a593Smuzhiyun 	if (which == V4L2_SUBDEV_FORMAT_TRY)
572*4882a593Smuzhiyun 		return cfg ? v4l2_subdev_get_try_format(&state->sd, cfg, 0) : NULL;
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	return &state->format;
575*4882a593Smuzhiyun }
576*4882a593Smuzhiyun 
s5pcsis_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_format * fmt)577*4882a593Smuzhiyun static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
578*4882a593Smuzhiyun 			   struct v4l2_subdev_format *fmt)
579*4882a593Smuzhiyun {
580*4882a593Smuzhiyun 	struct csis_state *state = sd_to_csis_state(sd);
581*4882a593Smuzhiyun 	struct csis_pix_format const *csis_fmt;
582*4882a593Smuzhiyun 	struct v4l2_mbus_framefmt *mf;
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 	mf = __s5pcsis_get_format(state, cfg, fmt->which);
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 	if (fmt->pad == CSIS_PAD_SOURCE) {
587*4882a593Smuzhiyun 		if (mf) {
588*4882a593Smuzhiyun 			mutex_lock(&state->lock);
589*4882a593Smuzhiyun 			fmt->format = *mf;
590*4882a593Smuzhiyun 			mutex_unlock(&state->lock);
591*4882a593Smuzhiyun 		}
592*4882a593Smuzhiyun 		return 0;
593*4882a593Smuzhiyun 	}
594*4882a593Smuzhiyun 	csis_fmt = s5pcsis_try_format(&fmt->format);
595*4882a593Smuzhiyun 	if (mf) {
596*4882a593Smuzhiyun 		mutex_lock(&state->lock);
597*4882a593Smuzhiyun 		*mf = fmt->format;
598*4882a593Smuzhiyun 		if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
599*4882a593Smuzhiyun 			state->csis_fmt = csis_fmt;
600*4882a593Smuzhiyun 		mutex_unlock(&state->lock);
601*4882a593Smuzhiyun 	}
602*4882a593Smuzhiyun 	return 0;
603*4882a593Smuzhiyun }
604*4882a593Smuzhiyun 
s5pcsis_get_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_format * fmt)605*4882a593Smuzhiyun static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
606*4882a593Smuzhiyun 			   struct v4l2_subdev_format *fmt)
607*4882a593Smuzhiyun {
608*4882a593Smuzhiyun 	struct csis_state *state = sd_to_csis_state(sd);
609*4882a593Smuzhiyun 	struct v4l2_mbus_framefmt *mf;
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 	mf = __s5pcsis_get_format(state, cfg, fmt->which);
612*4882a593Smuzhiyun 	if (!mf)
613*4882a593Smuzhiyun 		return -EINVAL;
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	mutex_lock(&state->lock);
616*4882a593Smuzhiyun 	fmt->format = *mf;
617*4882a593Smuzhiyun 	mutex_unlock(&state->lock);
618*4882a593Smuzhiyun 	return 0;
619*4882a593Smuzhiyun }
620*4882a593Smuzhiyun 
s5pcsis_s_rx_buffer(struct v4l2_subdev * sd,void * buf,unsigned int * size)621*4882a593Smuzhiyun static int s5pcsis_s_rx_buffer(struct v4l2_subdev *sd, void *buf,
622*4882a593Smuzhiyun 			       unsigned int *size)
623*4882a593Smuzhiyun {
624*4882a593Smuzhiyun 	struct csis_state *state = sd_to_csis_state(sd);
625*4882a593Smuzhiyun 	unsigned long flags;
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun 	*size = min_t(unsigned int, *size, S5PCSIS_PKTDATA_SIZE);
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun 	spin_lock_irqsave(&state->slock, flags);
630*4882a593Smuzhiyun 	state->pkt_buf.data = buf;
631*4882a593Smuzhiyun 	state->pkt_buf.len = *size;
632*4882a593Smuzhiyun 	spin_unlock_irqrestore(&state->slock, flags);
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun 	return 0;
635*4882a593Smuzhiyun }
636*4882a593Smuzhiyun 
s5pcsis_log_status(struct v4l2_subdev * sd)637*4882a593Smuzhiyun static int s5pcsis_log_status(struct v4l2_subdev *sd)
638*4882a593Smuzhiyun {
639*4882a593Smuzhiyun 	struct csis_state *state = sd_to_csis_state(sd);
640*4882a593Smuzhiyun 
641*4882a593Smuzhiyun 	mutex_lock(&state->lock);
642*4882a593Smuzhiyun 	s5pcsis_log_counters(state, true);
643*4882a593Smuzhiyun 	if (debug && (state->flags & ST_POWERED))
644*4882a593Smuzhiyun 		dump_regs(state, __func__);
645*4882a593Smuzhiyun 	mutex_unlock(&state->lock);
646*4882a593Smuzhiyun 	return 0;
647*4882a593Smuzhiyun }
648*4882a593Smuzhiyun 
649*4882a593Smuzhiyun static const struct v4l2_subdev_core_ops s5pcsis_core_ops = {
650*4882a593Smuzhiyun 	.s_power = s5pcsis_s_power,
651*4882a593Smuzhiyun 	.log_status = s5pcsis_log_status,
652*4882a593Smuzhiyun };
653*4882a593Smuzhiyun 
654*4882a593Smuzhiyun static const struct v4l2_subdev_pad_ops s5pcsis_pad_ops = {
655*4882a593Smuzhiyun 	.enum_mbus_code = s5pcsis_enum_mbus_code,
656*4882a593Smuzhiyun 	.get_fmt = s5pcsis_get_fmt,
657*4882a593Smuzhiyun 	.set_fmt = s5pcsis_set_fmt,
658*4882a593Smuzhiyun };
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun static const struct v4l2_subdev_video_ops s5pcsis_video_ops = {
661*4882a593Smuzhiyun 	.s_rx_buffer = s5pcsis_s_rx_buffer,
662*4882a593Smuzhiyun 	.s_stream = s5pcsis_s_stream,
663*4882a593Smuzhiyun };
664*4882a593Smuzhiyun 
665*4882a593Smuzhiyun static const struct v4l2_subdev_ops s5pcsis_subdev_ops = {
666*4882a593Smuzhiyun 	.core = &s5pcsis_core_ops,
667*4882a593Smuzhiyun 	.pad = &s5pcsis_pad_ops,
668*4882a593Smuzhiyun 	.video = &s5pcsis_video_ops,
669*4882a593Smuzhiyun };
670*4882a593Smuzhiyun 
s5pcsis_irq_handler(int irq,void * dev_id)671*4882a593Smuzhiyun static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id)
672*4882a593Smuzhiyun {
673*4882a593Smuzhiyun 	struct csis_state *state = dev_id;
674*4882a593Smuzhiyun 	struct csis_pktbuf *pktbuf = &state->pkt_buf;
675*4882a593Smuzhiyun 	unsigned long flags;
676*4882a593Smuzhiyun 	u32 status;
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun 	status = s5pcsis_read(state, S5PCSIS_INTSRC);
679*4882a593Smuzhiyun 	spin_lock_irqsave(&state->slock, flags);
680*4882a593Smuzhiyun 
681*4882a593Smuzhiyun 	if ((status & S5PCSIS_INTSRC_NON_IMAGE_DATA) && pktbuf->data) {
682*4882a593Smuzhiyun 		u32 offset;
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 		if (status & S5PCSIS_INTSRC_EVEN)
685*4882a593Smuzhiyun 			offset = S5PCSIS_PKTDATA_EVEN;
686*4882a593Smuzhiyun 		else
687*4882a593Smuzhiyun 			offset = S5PCSIS_PKTDATA_ODD;
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun 		memcpy(pktbuf->data, (u8 __force *)state->regs + offset,
690*4882a593Smuzhiyun 		       pktbuf->len);
691*4882a593Smuzhiyun 		pktbuf->data = NULL;
692*4882a593Smuzhiyun 		rmb();
693*4882a593Smuzhiyun 	}
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 	/* Update the event/error counters */
696*4882a593Smuzhiyun 	if ((status & S5PCSIS_INTSRC_ERRORS) || debug) {
697*4882a593Smuzhiyun 		int i;
698*4882a593Smuzhiyun 		for (i = 0; i < S5PCSIS_NUM_EVENTS; i++) {
699*4882a593Smuzhiyun 			if (!(status & state->events[i].mask))
700*4882a593Smuzhiyun 				continue;
701*4882a593Smuzhiyun 			state->events[i].counter++;
702*4882a593Smuzhiyun 			v4l2_dbg(2, debug, &state->sd, "%s: %d\n",
703*4882a593Smuzhiyun 				 state->events[i].name,
704*4882a593Smuzhiyun 				 state->events[i].counter);
705*4882a593Smuzhiyun 		}
706*4882a593Smuzhiyun 		v4l2_dbg(2, debug, &state->sd, "status: %08x\n", status);
707*4882a593Smuzhiyun 	}
708*4882a593Smuzhiyun 	spin_unlock_irqrestore(&state->slock, flags);
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun 	s5pcsis_write(state, S5PCSIS_INTSRC, status);
711*4882a593Smuzhiyun 	return IRQ_HANDLED;
712*4882a593Smuzhiyun }
713*4882a593Smuzhiyun 
s5pcsis_parse_dt(struct platform_device * pdev,struct csis_state * state)714*4882a593Smuzhiyun static int s5pcsis_parse_dt(struct platform_device *pdev,
715*4882a593Smuzhiyun 			    struct csis_state *state)
716*4882a593Smuzhiyun {
717*4882a593Smuzhiyun 	struct device_node *node = pdev->dev.of_node;
718*4882a593Smuzhiyun 	struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 };
719*4882a593Smuzhiyun 	int ret;
720*4882a593Smuzhiyun 
721*4882a593Smuzhiyun 	if (of_property_read_u32(node, "clock-frequency",
722*4882a593Smuzhiyun 				 &state->clk_frequency))
723*4882a593Smuzhiyun 		state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ;
724*4882a593Smuzhiyun 	if (of_property_read_u32(node, "bus-width",
725*4882a593Smuzhiyun 				 &state->max_num_lanes))
726*4882a593Smuzhiyun 		return -EINVAL;
727*4882a593Smuzhiyun 
728*4882a593Smuzhiyun 	node = of_graph_get_next_endpoint(node, NULL);
729*4882a593Smuzhiyun 	if (!node) {
730*4882a593Smuzhiyun 		dev_err(&pdev->dev, "No port node at %pOF\n",
731*4882a593Smuzhiyun 				pdev->dev.of_node);
732*4882a593Smuzhiyun 		return -EINVAL;
733*4882a593Smuzhiyun 	}
734*4882a593Smuzhiyun 	/* Get port node and validate MIPI-CSI channel id. */
735*4882a593Smuzhiyun 	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &endpoint);
736*4882a593Smuzhiyun 	if (ret)
737*4882a593Smuzhiyun 		goto err;
738*4882a593Smuzhiyun 
739*4882a593Smuzhiyun 	state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0;
740*4882a593Smuzhiyun 	if (state->index >= CSIS_MAX_ENTITIES) {
741*4882a593Smuzhiyun 		ret = -ENXIO;
742*4882a593Smuzhiyun 		goto err;
743*4882a593Smuzhiyun 	}
744*4882a593Smuzhiyun 
745*4882a593Smuzhiyun 	/* Get MIPI CSI-2 bus configuration from the endpoint node. */
746*4882a593Smuzhiyun 	of_property_read_u32(node, "samsung,csis-hs-settle",
747*4882a593Smuzhiyun 					&state->hs_settle);
748*4882a593Smuzhiyun 	state->wclk_ext = of_property_read_bool(node,
749*4882a593Smuzhiyun 					"samsung,csis-wclk");
750*4882a593Smuzhiyun 
751*4882a593Smuzhiyun 	state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
752*4882a593Smuzhiyun 
753*4882a593Smuzhiyun err:
754*4882a593Smuzhiyun 	of_node_put(node);
755*4882a593Smuzhiyun 	return ret;
756*4882a593Smuzhiyun }
757*4882a593Smuzhiyun 
758*4882a593Smuzhiyun static int s5pcsis_pm_resume(struct device *dev, bool runtime);
759*4882a593Smuzhiyun static const struct of_device_id s5pcsis_of_match[];
760*4882a593Smuzhiyun 
s5pcsis_probe(struct platform_device * pdev)761*4882a593Smuzhiyun static int s5pcsis_probe(struct platform_device *pdev)
762*4882a593Smuzhiyun {
763*4882a593Smuzhiyun 	const struct of_device_id *of_id;
764*4882a593Smuzhiyun 	const struct csis_drvdata *drv_data;
765*4882a593Smuzhiyun 	struct device *dev = &pdev->dev;
766*4882a593Smuzhiyun 	struct resource *mem_res;
767*4882a593Smuzhiyun 	struct csis_state *state;
768*4882a593Smuzhiyun 	int ret = -ENOMEM;
769*4882a593Smuzhiyun 	int i;
770*4882a593Smuzhiyun 
771*4882a593Smuzhiyun 	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
772*4882a593Smuzhiyun 	if (!state)
773*4882a593Smuzhiyun 		return -ENOMEM;
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun 	mutex_init(&state->lock);
776*4882a593Smuzhiyun 	spin_lock_init(&state->slock);
777*4882a593Smuzhiyun 	state->pdev = pdev;
778*4882a593Smuzhiyun 
779*4882a593Smuzhiyun 	of_id = of_match_node(s5pcsis_of_match, dev->of_node);
780*4882a593Smuzhiyun 	if (WARN_ON(of_id == NULL))
781*4882a593Smuzhiyun 		return -EINVAL;
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	drv_data = of_id->data;
784*4882a593Smuzhiyun 	state->interrupt_mask = drv_data->interrupt_mask;
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun 	ret = s5pcsis_parse_dt(pdev, state);
787*4882a593Smuzhiyun 	if (ret < 0)
788*4882a593Smuzhiyun 		return ret;
789*4882a593Smuzhiyun 
790*4882a593Smuzhiyun 	if (state->num_lanes == 0 || state->num_lanes > state->max_num_lanes) {
791*4882a593Smuzhiyun 		dev_err(dev, "Unsupported number of data lanes: %d (max. %d)\n",
792*4882a593Smuzhiyun 			state->num_lanes, state->max_num_lanes);
793*4882a593Smuzhiyun 		return -EINVAL;
794*4882a593Smuzhiyun 	}
795*4882a593Smuzhiyun 
796*4882a593Smuzhiyun 	state->phy = devm_phy_get(dev, "csis");
797*4882a593Smuzhiyun 	if (IS_ERR(state->phy))
798*4882a593Smuzhiyun 		return PTR_ERR(state->phy);
799*4882a593Smuzhiyun 
800*4882a593Smuzhiyun 	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
801*4882a593Smuzhiyun 	state->regs = devm_ioremap_resource(dev, mem_res);
802*4882a593Smuzhiyun 	if (IS_ERR(state->regs))
803*4882a593Smuzhiyun 		return PTR_ERR(state->regs);
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 	state->irq = platform_get_irq(pdev, 0);
806*4882a593Smuzhiyun 	if (state->irq < 0)
807*4882a593Smuzhiyun 		return state->irq;
808*4882a593Smuzhiyun 
809*4882a593Smuzhiyun 	for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
810*4882a593Smuzhiyun 		state->supplies[i].supply = csis_supply_name[i];
811*4882a593Smuzhiyun 
812*4882a593Smuzhiyun 	ret = devm_regulator_bulk_get(dev, CSIS_NUM_SUPPLIES,
813*4882a593Smuzhiyun 				 state->supplies);
814*4882a593Smuzhiyun 	if (ret)
815*4882a593Smuzhiyun 		return ret;
816*4882a593Smuzhiyun 
817*4882a593Smuzhiyun 	ret = s5pcsis_clk_get(state);
818*4882a593Smuzhiyun 	if (ret < 0)
819*4882a593Smuzhiyun 		return ret;
820*4882a593Smuzhiyun 
821*4882a593Smuzhiyun 	if (state->clk_frequency)
822*4882a593Smuzhiyun 		ret = clk_set_rate(state->clock[CSIS_CLK_MUX],
823*4882a593Smuzhiyun 				   state->clk_frequency);
824*4882a593Smuzhiyun 	else
825*4882a593Smuzhiyun 		dev_WARN(dev, "No clock frequency specified!\n");
826*4882a593Smuzhiyun 	if (ret < 0)
827*4882a593Smuzhiyun 		goto e_clkput;
828*4882a593Smuzhiyun 
829*4882a593Smuzhiyun 	ret = clk_enable(state->clock[CSIS_CLK_MUX]);
830*4882a593Smuzhiyun 	if (ret < 0)
831*4882a593Smuzhiyun 		goto e_clkput;
832*4882a593Smuzhiyun 
833*4882a593Smuzhiyun 	ret = devm_request_irq(dev, state->irq, s5pcsis_irq_handler,
834*4882a593Smuzhiyun 			       0, dev_name(dev), state);
835*4882a593Smuzhiyun 	if (ret) {
836*4882a593Smuzhiyun 		dev_err(dev, "Interrupt request failed\n");
837*4882a593Smuzhiyun 		goto e_clkdis;
838*4882a593Smuzhiyun 	}
839*4882a593Smuzhiyun 
840*4882a593Smuzhiyun 	v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops);
841*4882a593Smuzhiyun 	state->sd.owner = THIS_MODULE;
842*4882a593Smuzhiyun 	snprintf(state->sd.name, sizeof(state->sd.name), "%s.%d",
843*4882a593Smuzhiyun 		 CSIS_SUBDEV_NAME, state->index);
844*4882a593Smuzhiyun 	state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
845*4882a593Smuzhiyun 	state->csis_fmt = &s5pcsis_formats[0];
846*4882a593Smuzhiyun 
847*4882a593Smuzhiyun 	state->format.code = s5pcsis_formats[0].code;
848*4882a593Smuzhiyun 	state->format.width = S5PCSIS_DEF_PIX_WIDTH;
849*4882a593Smuzhiyun 	state->format.height = S5PCSIS_DEF_PIX_HEIGHT;
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun 	state->sd.entity.function = MEDIA_ENT_F_IO_V4L;
852*4882a593Smuzhiyun 	state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
853*4882a593Smuzhiyun 	state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
854*4882a593Smuzhiyun 	ret = media_entity_pads_init(&state->sd.entity,
855*4882a593Smuzhiyun 				CSIS_PADS_NUM, state->pads);
856*4882a593Smuzhiyun 	if (ret < 0)
857*4882a593Smuzhiyun 		goto e_clkdis;
858*4882a593Smuzhiyun 
859*4882a593Smuzhiyun 	/* This allows to retrieve the platform device id by the host driver */
860*4882a593Smuzhiyun 	v4l2_set_subdevdata(&state->sd, pdev);
861*4882a593Smuzhiyun 
862*4882a593Smuzhiyun 	/* .. and a pointer to the subdev. */
863*4882a593Smuzhiyun 	platform_set_drvdata(pdev, &state->sd);
864*4882a593Smuzhiyun 	memcpy(state->events, s5pcsis_events, sizeof(state->events));
865*4882a593Smuzhiyun 
866*4882a593Smuzhiyun 	pm_runtime_enable(dev);
867*4882a593Smuzhiyun 	if (!pm_runtime_enabled(dev)) {
868*4882a593Smuzhiyun 		ret = s5pcsis_pm_resume(dev, true);
869*4882a593Smuzhiyun 		if (ret < 0)
870*4882a593Smuzhiyun 			goto e_m_ent;
871*4882a593Smuzhiyun 	}
872*4882a593Smuzhiyun 
873*4882a593Smuzhiyun 	dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n",
874*4882a593Smuzhiyun 		 state->num_lanes, state->hs_settle, state->wclk_ext,
875*4882a593Smuzhiyun 		 state->clk_frequency);
876*4882a593Smuzhiyun 	return 0;
877*4882a593Smuzhiyun 
878*4882a593Smuzhiyun e_m_ent:
879*4882a593Smuzhiyun 	media_entity_cleanup(&state->sd.entity);
880*4882a593Smuzhiyun e_clkdis:
881*4882a593Smuzhiyun 	clk_disable(state->clock[CSIS_CLK_MUX]);
882*4882a593Smuzhiyun e_clkput:
883*4882a593Smuzhiyun 	s5pcsis_clk_put(state);
884*4882a593Smuzhiyun 	return ret;
885*4882a593Smuzhiyun }
886*4882a593Smuzhiyun 
s5pcsis_pm_suspend(struct device * dev,bool runtime)887*4882a593Smuzhiyun static int s5pcsis_pm_suspend(struct device *dev, bool runtime)
888*4882a593Smuzhiyun {
889*4882a593Smuzhiyun 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
890*4882a593Smuzhiyun 	struct csis_state *state = sd_to_csis_state(sd);
891*4882a593Smuzhiyun 	int ret = 0;
892*4882a593Smuzhiyun 
893*4882a593Smuzhiyun 	v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
894*4882a593Smuzhiyun 		 __func__, state->flags);
895*4882a593Smuzhiyun 
896*4882a593Smuzhiyun 	mutex_lock(&state->lock);
897*4882a593Smuzhiyun 	if (state->flags & ST_POWERED) {
898*4882a593Smuzhiyun 		s5pcsis_stop_stream(state);
899*4882a593Smuzhiyun 		ret = phy_power_off(state->phy);
900*4882a593Smuzhiyun 		if (ret)
901*4882a593Smuzhiyun 			goto unlock;
902*4882a593Smuzhiyun 		ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
903*4882a593Smuzhiyun 					     state->supplies);
904*4882a593Smuzhiyun 		if (ret)
905*4882a593Smuzhiyun 			goto unlock;
906*4882a593Smuzhiyun 		clk_disable(state->clock[CSIS_CLK_GATE]);
907*4882a593Smuzhiyun 		state->flags &= ~ST_POWERED;
908*4882a593Smuzhiyun 		if (!runtime)
909*4882a593Smuzhiyun 			state->flags |= ST_SUSPENDED;
910*4882a593Smuzhiyun 	}
911*4882a593Smuzhiyun  unlock:
912*4882a593Smuzhiyun 	mutex_unlock(&state->lock);
913*4882a593Smuzhiyun 	return ret ? -EAGAIN : 0;
914*4882a593Smuzhiyun }
915*4882a593Smuzhiyun 
s5pcsis_pm_resume(struct device * dev,bool runtime)916*4882a593Smuzhiyun static int s5pcsis_pm_resume(struct device *dev, bool runtime)
917*4882a593Smuzhiyun {
918*4882a593Smuzhiyun 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
919*4882a593Smuzhiyun 	struct csis_state *state = sd_to_csis_state(sd);
920*4882a593Smuzhiyun 	int ret = 0;
921*4882a593Smuzhiyun 
922*4882a593Smuzhiyun 	v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
923*4882a593Smuzhiyun 		 __func__, state->flags);
924*4882a593Smuzhiyun 
925*4882a593Smuzhiyun 	mutex_lock(&state->lock);
926*4882a593Smuzhiyun 	if (!runtime && !(state->flags & ST_SUSPENDED))
927*4882a593Smuzhiyun 		goto unlock;
928*4882a593Smuzhiyun 
929*4882a593Smuzhiyun 	if (!(state->flags & ST_POWERED)) {
930*4882a593Smuzhiyun 		ret = regulator_bulk_enable(CSIS_NUM_SUPPLIES,
931*4882a593Smuzhiyun 					    state->supplies);
932*4882a593Smuzhiyun 		if (ret)
933*4882a593Smuzhiyun 			goto unlock;
934*4882a593Smuzhiyun 		ret = phy_power_on(state->phy);
935*4882a593Smuzhiyun 		if (!ret) {
936*4882a593Smuzhiyun 			state->flags |= ST_POWERED;
937*4882a593Smuzhiyun 		} else {
938*4882a593Smuzhiyun 			regulator_bulk_disable(CSIS_NUM_SUPPLIES,
939*4882a593Smuzhiyun 					       state->supplies);
940*4882a593Smuzhiyun 			goto unlock;
941*4882a593Smuzhiyun 		}
942*4882a593Smuzhiyun 		clk_enable(state->clock[CSIS_CLK_GATE]);
943*4882a593Smuzhiyun 	}
944*4882a593Smuzhiyun 	if (state->flags & ST_STREAMING)
945*4882a593Smuzhiyun 		s5pcsis_start_stream(state);
946*4882a593Smuzhiyun 
947*4882a593Smuzhiyun 	state->flags &= ~ST_SUSPENDED;
948*4882a593Smuzhiyun  unlock:
949*4882a593Smuzhiyun 	mutex_unlock(&state->lock);
950*4882a593Smuzhiyun 	return ret ? -EAGAIN : 0;
951*4882a593Smuzhiyun }
952*4882a593Smuzhiyun 
953*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
s5pcsis_suspend(struct device * dev)954*4882a593Smuzhiyun static int s5pcsis_suspend(struct device *dev)
955*4882a593Smuzhiyun {
956*4882a593Smuzhiyun 	return s5pcsis_pm_suspend(dev, false);
957*4882a593Smuzhiyun }
958*4882a593Smuzhiyun 
s5pcsis_resume(struct device * dev)959*4882a593Smuzhiyun static int s5pcsis_resume(struct device *dev)
960*4882a593Smuzhiyun {
961*4882a593Smuzhiyun 	return s5pcsis_pm_resume(dev, false);
962*4882a593Smuzhiyun }
963*4882a593Smuzhiyun #endif
964*4882a593Smuzhiyun 
965*4882a593Smuzhiyun #ifdef CONFIG_PM
s5pcsis_runtime_suspend(struct device * dev)966*4882a593Smuzhiyun static int s5pcsis_runtime_suspend(struct device *dev)
967*4882a593Smuzhiyun {
968*4882a593Smuzhiyun 	return s5pcsis_pm_suspend(dev, true);
969*4882a593Smuzhiyun }
970*4882a593Smuzhiyun 
s5pcsis_runtime_resume(struct device * dev)971*4882a593Smuzhiyun static int s5pcsis_runtime_resume(struct device *dev)
972*4882a593Smuzhiyun {
973*4882a593Smuzhiyun 	return s5pcsis_pm_resume(dev, true);
974*4882a593Smuzhiyun }
975*4882a593Smuzhiyun #endif
976*4882a593Smuzhiyun 
s5pcsis_remove(struct platform_device * pdev)977*4882a593Smuzhiyun static int s5pcsis_remove(struct platform_device *pdev)
978*4882a593Smuzhiyun {
979*4882a593Smuzhiyun 	struct v4l2_subdev *sd = platform_get_drvdata(pdev);
980*4882a593Smuzhiyun 	struct csis_state *state = sd_to_csis_state(sd);
981*4882a593Smuzhiyun 
982*4882a593Smuzhiyun 	pm_runtime_disable(&pdev->dev);
983*4882a593Smuzhiyun 	s5pcsis_pm_suspend(&pdev->dev, true);
984*4882a593Smuzhiyun 	clk_disable(state->clock[CSIS_CLK_MUX]);
985*4882a593Smuzhiyun 	pm_runtime_set_suspended(&pdev->dev);
986*4882a593Smuzhiyun 	s5pcsis_clk_put(state);
987*4882a593Smuzhiyun 
988*4882a593Smuzhiyun 	media_entity_cleanup(&state->sd.entity);
989*4882a593Smuzhiyun 
990*4882a593Smuzhiyun 	return 0;
991*4882a593Smuzhiyun }
992*4882a593Smuzhiyun 
993*4882a593Smuzhiyun static const struct dev_pm_ops s5pcsis_pm_ops = {
994*4882a593Smuzhiyun 	SET_RUNTIME_PM_OPS(s5pcsis_runtime_suspend, s5pcsis_runtime_resume,
995*4882a593Smuzhiyun 			   NULL)
996*4882a593Smuzhiyun 	SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
997*4882a593Smuzhiyun };
998*4882a593Smuzhiyun 
999*4882a593Smuzhiyun static const struct csis_drvdata exynos4_csis_drvdata = {
1000*4882a593Smuzhiyun 	.interrupt_mask = S5PCSIS_INTMSK_EXYNOS4_EN_ALL,
1001*4882a593Smuzhiyun };
1002*4882a593Smuzhiyun 
1003*4882a593Smuzhiyun static const struct csis_drvdata exynos5_csis_drvdata = {
1004*4882a593Smuzhiyun 	.interrupt_mask = S5PCSIS_INTMSK_EXYNOS5_EN_ALL,
1005*4882a593Smuzhiyun };
1006*4882a593Smuzhiyun 
1007*4882a593Smuzhiyun static const struct of_device_id s5pcsis_of_match[] = {
1008*4882a593Smuzhiyun 	{
1009*4882a593Smuzhiyun 		.compatible = "samsung,s5pv210-csis",
1010*4882a593Smuzhiyun 		.data = &exynos4_csis_drvdata,
1011*4882a593Smuzhiyun 	}, {
1012*4882a593Smuzhiyun 		.compatible = "samsung,exynos4210-csis",
1013*4882a593Smuzhiyun 		.data = &exynos4_csis_drvdata,
1014*4882a593Smuzhiyun 	}, {
1015*4882a593Smuzhiyun 		.compatible = "samsung,exynos5250-csis",
1016*4882a593Smuzhiyun 		.data = &exynos5_csis_drvdata,
1017*4882a593Smuzhiyun 	},
1018*4882a593Smuzhiyun 	{ /* sentinel */ },
1019*4882a593Smuzhiyun };
1020*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, s5pcsis_of_match);
1021*4882a593Smuzhiyun 
1022*4882a593Smuzhiyun static struct platform_driver s5pcsis_driver = {
1023*4882a593Smuzhiyun 	.probe		= s5pcsis_probe,
1024*4882a593Smuzhiyun 	.remove		= s5pcsis_remove,
1025*4882a593Smuzhiyun 	.driver		= {
1026*4882a593Smuzhiyun 		.of_match_table = s5pcsis_of_match,
1027*4882a593Smuzhiyun 		.name		= CSIS_DRIVER_NAME,
1028*4882a593Smuzhiyun 		.pm		= &s5pcsis_pm_ops,
1029*4882a593Smuzhiyun 	},
1030*4882a593Smuzhiyun };
1031*4882a593Smuzhiyun 
1032*4882a593Smuzhiyun module_platform_driver(s5pcsis_driver);
1033*4882a593Smuzhiyun 
1034*4882a593Smuzhiyun MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
1035*4882a593Smuzhiyun MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver");
1036*4882a593Smuzhiyun MODULE_LICENSE("GPL");
1037