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