xref: /openbmc/linux/drivers/media/i2c/tw9910.c (revision 7b20f325)
17b20f325SJacopo Mondi // SPDX-License-Identifier: GPL-2.0
2e0d76c38SJacopo Mondi /*
3e0d76c38SJacopo Mondi  * tw9910 Video Driver
4e0d76c38SJacopo Mondi  *
57b20f325SJacopo Mondi  * Copyright (C) 2017 Jacopo Mondi <jacopo+renesas@jmondi.org>
67b20f325SJacopo Mondi  *
7e0d76c38SJacopo Mondi  * Copyright (C) 2008 Renesas Solutions Corp.
8e0d76c38SJacopo Mondi  * Kuninori Morimoto <morimoto.kuninori@renesas.com>
9e0d76c38SJacopo Mondi  *
10e0d76c38SJacopo Mondi  * Based on ov772x driver,
11e0d76c38SJacopo Mondi  *
12e0d76c38SJacopo Mondi  * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
13e0d76c38SJacopo Mondi  * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
14e0d76c38SJacopo Mondi  * Copyright (C) 2008 Magnus Damm
15e0d76c38SJacopo Mondi  * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
16e0d76c38SJacopo Mondi  */
17e0d76c38SJacopo Mondi 
187b20f325SJacopo Mondi #include <linux/clk.h>
197b20f325SJacopo Mondi #include <linux/gpio/consumer.h>
20e0d76c38SJacopo Mondi #include <linux/init.h>
21e0d76c38SJacopo Mondi #include <linux/module.h>
22e0d76c38SJacopo Mondi #include <linux/i2c.h>
23e0d76c38SJacopo Mondi #include <linux/slab.h>
24e0d76c38SJacopo Mondi #include <linux/kernel.h>
25e0d76c38SJacopo Mondi #include <linux/delay.h>
26e0d76c38SJacopo Mondi #include <linux/v4l2-mediabus.h>
27e0d76c38SJacopo Mondi #include <linux/videodev2.h>
28e0d76c38SJacopo Mondi 
29e0d76c38SJacopo Mondi #include <media/i2c/tw9910.h>
30e0d76c38SJacopo Mondi #include <media/v4l2-subdev.h>
31e0d76c38SJacopo Mondi 
32e0d76c38SJacopo Mondi #define GET_ID(val)  ((val & 0xF8) >> 3)
33e0d76c38SJacopo Mondi #define GET_REV(val) (val & 0x07)
34e0d76c38SJacopo Mondi 
35e0d76c38SJacopo Mondi /*
36e0d76c38SJacopo Mondi  * register offset
37e0d76c38SJacopo Mondi  */
38e0d76c38SJacopo Mondi #define ID		0x00 /* Product ID Code Register */
39e0d76c38SJacopo Mondi #define STATUS1		0x01 /* Chip Status Register I */
40e0d76c38SJacopo Mondi #define INFORM		0x02 /* Input Format */
41e0d76c38SJacopo Mondi #define OPFORM		0x03 /* Output Format Control Register */
42e0d76c38SJacopo Mondi #define DLYCTR		0x04 /* Hysteresis and HSYNC Delay Control */
43e0d76c38SJacopo Mondi #define OUTCTR1		0x05 /* Output Control I */
44e0d76c38SJacopo Mondi #define ACNTL1		0x06 /* Analog Control Register 1 */
45e0d76c38SJacopo Mondi #define CROP_HI		0x07 /* Cropping Register, High */
46e0d76c38SJacopo Mondi #define VDELAY_LO	0x08 /* Vertical Delay Register, Low */
47e0d76c38SJacopo Mondi #define VACTIVE_LO	0x09 /* Vertical Active Register, Low */
48e0d76c38SJacopo Mondi #define HDELAY_LO	0x0A /* Horizontal Delay Register, Low */
49e0d76c38SJacopo Mondi #define HACTIVE_LO	0x0B /* Horizontal Active Register, Low */
50e0d76c38SJacopo Mondi #define CNTRL1		0x0C /* Control Register I */
51e0d76c38SJacopo Mondi #define VSCALE_LO	0x0D /* Vertical Scaling Register, Low */
52e0d76c38SJacopo Mondi #define SCALE_HI	0x0E /* Scaling Register, High */
53e0d76c38SJacopo Mondi #define HSCALE_LO	0x0F /* Horizontal Scaling Register, Low */
54e0d76c38SJacopo Mondi #define BRIGHT		0x10 /* BRIGHTNESS Control Register */
55e0d76c38SJacopo Mondi #define CONTRAST	0x11 /* CONTRAST Control Register */
56e0d76c38SJacopo Mondi #define SHARPNESS	0x12 /* SHARPNESS Control Register I */
57e0d76c38SJacopo Mondi #define SAT_U		0x13 /* Chroma (U) Gain Register */
58e0d76c38SJacopo Mondi #define SAT_V		0x14 /* Chroma (V) Gain Register */
59e0d76c38SJacopo Mondi #define HUE		0x15 /* Hue Control Register */
60e0d76c38SJacopo Mondi #define CORING1		0x17
61e0d76c38SJacopo Mondi #define CORING2		0x18 /* Coring and IF compensation */
62e0d76c38SJacopo Mondi #define VBICNTL		0x19 /* VBI Control Register */
63e0d76c38SJacopo Mondi #define ACNTL2		0x1A /* Analog Control 2 */
64e0d76c38SJacopo Mondi #define OUTCTR2		0x1B /* Output Control 2 */
65e0d76c38SJacopo Mondi #define SDT		0x1C /* Standard Selection */
66e0d76c38SJacopo Mondi #define SDTR		0x1D /* Standard Recognition */
67e0d76c38SJacopo Mondi #define TEST		0x1F /* Test Control Register */
68e0d76c38SJacopo Mondi #define CLMPG		0x20 /* Clamping Gain */
69e0d76c38SJacopo Mondi #define IAGC		0x21 /* Individual AGC Gain */
70e0d76c38SJacopo Mondi #define AGCGAIN		0x22 /* AGC Gain */
71e0d76c38SJacopo Mondi #define PEAKWT		0x23 /* White Peak Threshold */
72e0d76c38SJacopo Mondi #define CLMPL		0x24 /* Clamp level */
73e0d76c38SJacopo Mondi #define SYNCT		0x25 /* Sync Amplitude */
74e0d76c38SJacopo Mondi #define MISSCNT		0x26 /* Sync Miss Count Register */
75e0d76c38SJacopo Mondi #define PCLAMP		0x27 /* Clamp Position Register */
76e0d76c38SJacopo Mondi #define VCNTL1		0x28 /* Vertical Control I */
77e0d76c38SJacopo Mondi #define VCNTL2		0x29 /* Vertical Control II */
78e0d76c38SJacopo Mondi #define CKILL		0x2A /* Color Killer Level Control */
79e0d76c38SJacopo Mondi #define COMB		0x2B /* Comb Filter Control */
80e0d76c38SJacopo Mondi #define LDLY		0x2C /* Luma Delay and H Filter Control */
81e0d76c38SJacopo Mondi #define MISC1		0x2D /* Miscellaneous Control I */
82e0d76c38SJacopo Mondi #define LOOP		0x2E /* LOOP Control Register */
83e0d76c38SJacopo Mondi #define MISC2		0x2F /* Miscellaneous Control II */
84e0d76c38SJacopo Mondi #define MVSN		0x30 /* Macrovision Detection */
85e0d76c38SJacopo Mondi #define STATUS2		0x31 /* Chip STATUS II */
86e0d76c38SJacopo Mondi #define HFREF		0x32 /* H monitor */
87e0d76c38SJacopo Mondi #define CLMD		0x33 /* CLAMP MODE */
88e0d76c38SJacopo Mondi #define IDCNTL		0x34 /* ID Detection Control */
89e0d76c38SJacopo Mondi #define CLCNTL1		0x35 /* Clamp Control I */
90e0d76c38SJacopo Mondi #define ANAPLLCTL	0x4C
91e0d76c38SJacopo Mondi #define VBIMIN		0x4D
92e0d76c38SJacopo Mondi #define HSLOWCTL	0x4E
93e0d76c38SJacopo Mondi #define WSS3		0x4F
94e0d76c38SJacopo Mondi #define FILLDATA	0x50
95e0d76c38SJacopo Mondi #define SDID		0x51
96e0d76c38SJacopo Mondi #define DID		0x52
97e0d76c38SJacopo Mondi #define WSS1		0x53
98e0d76c38SJacopo Mondi #define WSS2		0x54
99e0d76c38SJacopo Mondi #define VVBI		0x55
100e0d76c38SJacopo Mondi #define LCTL6		0x56
101e0d76c38SJacopo Mondi #define LCTL7		0x57
102e0d76c38SJacopo Mondi #define LCTL8		0x58
103e0d76c38SJacopo Mondi #define LCTL9		0x59
104e0d76c38SJacopo Mondi #define LCTL10		0x5A
105e0d76c38SJacopo Mondi #define LCTL11		0x5B
106e0d76c38SJacopo Mondi #define LCTL12		0x5C
107e0d76c38SJacopo Mondi #define LCTL13		0x5D
108e0d76c38SJacopo Mondi #define LCTL14		0x5E
109e0d76c38SJacopo Mondi #define LCTL15		0x5F
110e0d76c38SJacopo Mondi #define LCTL16		0x60
111e0d76c38SJacopo Mondi #define LCTL17		0x61
112e0d76c38SJacopo Mondi #define LCTL18		0x62
113e0d76c38SJacopo Mondi #define LCTL19		0x63
114e0d76c38SJacopo Mondi #define LCTL20		0x64
115e0d76c38SJacopo Mondi #define LCTL21		0x65
116e0d76c38SJacopo Mondi #define LCTL22		0x66
117e0d76c38SJacopo Mondi #define LCTL23		0x67
118e0d76c38SJacopo Mondi #define LCTL24		0x68
119e0d76c38SJacopo Mondi #define LCTL25		0x69
120e0d76c38SJacopo Mondi #define LCTL26		0x6A
121e0d76c38SJacopo Mondi #define HSBEGIN		0x6B
122e0d76c38SJacopo Mondi #define HSEND		0x6C
123e0d76c38SJacopo Mondi #define OVSDLY		0x6D
124e0d76c38SJacopo Mondi #define OVSEND		0x6E
125e0d76c38SJacopo Mondi #define VBIDELAY	0x6F
126e0d76c38SJacopo Mondi 
127e0d76c38SJacopo Mondi /*
128e0d76c38SJacopo Mondi  * register detail
129e0d76c38SJacopo Mondi  */
130e0d76c38SJacopo Mondi 
131e0d76c38SJacopo Mondi /* INFORM */
132e0d76c38SJacopo Mondi #define FC27_ON     0x40 /* 1 : Input crystal clock frequency is 27MHz */
133e0d76c38SJacopo Mondi #define FC27_FF     0x00 /* 0 : Square pixel mode. */
134e0d76c38SJacopo Mondi 			 /*     Must use 24.54MHz for 60Hz field rate */
135e0d76c38SJacopo Mondi 			 /*     source or 29.5MHz for 50Hz field rate */
136e0d76c38SJacopo Mondi #define IFSEL_S     0x10 /* 01 : S-video decoding */
137e0d76c38SJacopo Mondi #define IFSEL_C     0x00 /* 00 : Composite video decoding */
138e0d76c38SJacopo Mondi 			 /* Y input video selection */
139e0d76c38SJacopo Mondi #define YSEL_M0     0x00 /*  00 : Mux0 selected */
140e0d76c38SJacopo Mondi #define YSEL_M1     0x04 /*  01 : Mux1 selected */
141e0d76c38SJacopo Mondi #define YSEL_M2     0x08 /*  10 : Mux2 selected */
142e0d76c38SJacopo Mondi #define YSEL_M3     0x10 /*  11 : Mux3 selected */
143e0d76c38SJacopo Mondi 
144e0d76c38SJacopo Mondi /* OPFORM */
145e0d76c38SJacopo Mondi #define MODE        0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */
146e0d76c38SJacopo Mondi 			 /* 1 : ITU-R-656 compatible data sequence format */
147e0d76c38SJacopo Mondi #define LEN         0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */
148e0d76c38SJacopo Mondi 			 /* 1 : 16-bit YCrCb 4:2:2 output format.*/
149e0d76c38SJacopo Mondi #define LLCMODE     0x20 /* 1 : LLC output mode. */
150e0d76c38SJacopo Mondi 			 /* 0 : free-run output mode */
151e0d76c38SJacopo Mondi #define AINC        0x10 /* Serial interface auto-indexing control */
152e0d76c38SJacopo Mondi 			 /* 0 : auto-increment */
153e0d76c38SJacopo Mondi 			 /* 1 : non-auto */
154e0d76c38SJacopo Mondi #define VSCTL       0x08 /* 1 : Vertical out ctrl by DVALID */
155e0d76c38SJacopo Mondi 			 /* 0 : Vertical out ctrl by HACTIVE and DVALID */
156e0d76c38SJacopo Mondi #define OEN_TRI_SEL_MASK	0x07
157e0d76c38SJacopo Mondi #define OEN_TRI_SEL_ALL_ON	0x00 /* Enable output for Rev0/Rev1 */
158e0d76c38SJacopo Mondi #define OEN_TRI_SEL_ALL_OFF_r0	0x06 /* All tri-stated for Rev0 */
159e0d76c38SJacopo Mondi #define OEN_TRI_SEL_ALL_OFF_r1	0x07 /* All tri-stated for Rev1 */
160e0d76c38SJacopo Mondi 
161e0d76c38SJacopo Mondi /* OUTCTR1 */
162e0d76c38SJacopo Mondi #define VSP_LO      0x00 /* 0 : VS pin output polarity is active low */
163e0d76c38SJacopo Mondi #define VSP_HI      0x80 /* 1 : VS pin output polarity is active high. */
164e0d76c38SJacopo Mondi 			 /* VS pin output control */
165e0d76c38SJacopo Mondi #define VSSL_VSYNC  0x00 /*   0 : VSYNC  */
166e0d76c38SJacopo Mondi #define VSSL_VACT   0x10 /*   1 : VACT   */
167e0d76c38SJacopo Mondi #define VSSL_FIELD  0x20 /*   2 : FIELD  */
168e0d76c38SJacopo Mondi #define VSSL_VVALID 0x30 /*   3 : VVALID */
169e0d76c38SJacopo Mondi #define VSSL_ZERO   0x70 /*   7 : 0      */
170e0d76c38SJacopo Mondi #define HSP_LOW     0x00 /* 0 : HS pin output polarity is active low */
171e0d76c38SJacopo Mondi #define HSP_HI      0x08 /* 1 : HS pin output polarity is active high.*/
172e0d76c38SJacopo Mondi 			 /* HS pin output control */
173e0d76c38SJacopo Mondi #define HSSL_HACT   0x00 /*   0 : HACT   */
174e0d76c38SJacopo Mondi #define HSSL_HSYNC  0x01 /*   1 : HSYNC  */
175e0d76c38SJacopo Mondi #define HSSL_DVALID 0x02 /*   2 : DVALID */
176e0d76c38SJacopo Mondi #define HSSL_HLOCK  0x03 /*   3 : HLOCK  */
177e0d76c38SJacopo Mondi #define HSSL_ASYNCW 0x04 /*   4 : ASYNCW */
178e0d76c38SJacopo Mondi #define HSSL_ZERO   0x07 /*   7 : 0      */
179e0d76c38SJacopo Mondi 
180e0d76c38SJacopo Mondi /* ACNTL1 */
181e0d76c38SJacopo Mondi #define SRESET      0x80 /* resets the device to its default state
182e0d76c38SJacopo Mondi 			  * but all register content remain unchanged.
183e0d76c38SJacopo Mondi 			  * This bit is self-resetting.
184e0d76c38SJacopo Mondi 			  */
185e0d76c38SJacopo Mondi #define ACNTL1_PDN_MASK	0x0e
186e0d76c38SJacopo Mondi #define CLK_PDN		0x08 /* system clock power down */
187e0d76c38SJacopo Mondi #define Y_PDN		0x04 /* Luma ADC power down */
188e0d76c38SJacopo Mondi #define C_PDN		0x02 /* Chroma ADC power down */
189e0d76c38SJacopo Mondi 
190e0d76c38SJacopo Mondi /* ACNTL2 */
191e0d76c38SJacopo Mondi #define ACNTL2_PDN_MASK	0x40
192e0d76c38SJacopo Mondi #define PLL_PDN		0x40 /* PLL power down */
193e0d76c38SJacopo Mondi 
194e0d76c38SJacopo Mondi /* VBICNTL */
195e0d76c38SJacopo Mondi 
196e0d76c38SJacopo Mondi /* RTSEL : control the real time signal output from the MPOUT pin */
197e0d76c38SJacopo Mondi #define RTSEL_MASK  0x07
198e0d76c38SJacopo Mondi #define RTSEL_VLOSS 0x00 /* 0000 = Video loss */
199e0d76c38SJacopo Mondi #define RTSEL_HLOCK 0x01 /* 0001 = H-lock */
200e0d76c38SJacopo Mondi #define RTSEL_SLOCK 0x02 /* 0010 = S-lock */
201e0d76c38SJacopo Mondi #define RTSEL_VLOCK 0x03 /* 0011 = V-lock */
202e0d76c38SJacopo Mondi #define RTSEL_MONO  0x04 /* 0100 = MONO */
203e0d76c38SJacopo Mondi #define RTSEL_DET50 0x05 /* 0101 = DET50 */
204e0d76c38SJacopo Mondi #define RTSEL_FIELD 0x06 /* 0110 = FIELD */
205e0d76c38SJacopo Mondi #define RTSEL_RTCO  0x07 /* 0111 = RTCO ( Real Time Control ) */
206e0d76c38SJacopo Mondi 
207e0d76c38SJacopo Mondi /* HSYNC start and end are constant for now */
208e0d76c38SJacopo Mondi #define HSYNC_START	0x0260
209e0d76c38SJacopo Mondi #define HSYNC_END	0x0300
210e0d76c38SJacopo Mondi 
211e0d76c38SJacopo Mondi /*
212e0d76c38SJacopo Mondi  * structure
213e0d76c38SJacopo Mondi  */
214e0d76c38SJacopo Mondi 
215e0d76c38SJacopo Mondi struct regval_list {
216e0d76c38SJacopo Mondi 	unsigned char reg_num;
217e0d76c38SJacopo Mondi 	unsigned char value;
218e0d76c38SJacopo Mondi };
219e0d76c38SJacopo Mondi 
220e0d76c38SJacopo Mondi struct tw9910_scale_ctrl {
221e0d76c38SJacopo Mondi 	char           *name;
222e0d76c38SJacopo Mondi 	unsigned short  width;
223e0d76c38SJacopo Mondi 	unsigned short  height;
224e0d76c38SJacopo Mondi 	u16             hscale;
225e0d76c38SJacopo Mondi 	u16             vscale;
226e0d76c38SJacopo Mondi };
227e0d76c38SJacopo Mondi 
228e0d76c38SJacopo Mondi struct tw9910_priv {
229e0d76c38SJacopo Mondi 	struct v4l2_subdev		subdev;
2307b20f325SJacopo Mondi 	struct clk			*clk;
231e0d76c38SJacopo Mondi 	struct tw9910_video_info	*info;
2327b20f325SJacopo Mondi 	struct gpio_desc		*pdn_gpio;
2337b20f325SJacopo Mondi 	struct gpio_desc		*rstb_gpio;
234e0d76c38SJacopo Mondi 	const struct tw9910_scale_ctrl	*scale;
235e0d76c38SJacopo Mondi 	v4l2_std_id			norm;
236e0d76c38SJacopo Mondi 	u32				revision;
237e0d76c38SJacopo Mondi };
238e0d76c38SJacopo Mondi 
239e0d76c38SJacopo Mondi static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = {
240e0d76c38SJacopo Mondi 	{
241e0d76c38SJacopo Mondi 		.name   = "NTSC SQ",
242e0d76c38SJacopo Mondi 		.width  = 640,
243e0d76c38SJacopo Mondi 		.height = 480,
244e0d76c38SJacopo Mondi 		.hscale = 0x0100,
245e0d76c38SJacopo Mondi 		.vscale = 0x0100,
246e0d76c38SJacopo Mondi 	},
247e0d76c38SJacopo Mondi 	{
248e0d76c38SJacopo Mondi 		.name   = "NTSC CCIR601",
249e0d76c38SJacopo Mondi 		.width  = 720,
250e0d76c38SJacopo Mondi 		.height = 480,
251e0d76c38SJacopo Mondi 		.hscale = 0x0100,
252e0d76c38SJacopo Mondi 		.vscale = 0x0100,
253e0d76c38SJacopo Mondi 	},
254e0d76c38SJacopo Mondi 	{
255e0d76c38SJacopo Mondi 		.name   = "NTSC SQ (CIF)",
256e0d76c38SJacopo Mondi 		.width  = 320,
257e0d76c38SJacopo Mondi 		.height = 240,
258e0d76c38SJacopo Mondi 		.hscale = 0x0200,
259e0d76c38SJacopo Mondi 		.vscale = 0x0200,
260e0d76c38SJacopo Mondi 	},
261e0d76c38SJacopo Mondi 	{
262e0d76c38SJacopo Mondi 		.name   = "NTSC CCIR601 (CIF)",
263e0d76c38SJacopo Mondi 		.width  = 360,
264e0d76c38SJacopo Mondi 		.height = 240,
265e0d76c38SJacopo Mondi 		.hscale = 0x0200,
266e0d76c38SJacopo Mondi 		.vscale = 0x0200,
267e0d76c38SJacopo Mondi 	},
268e0d76c38SJacopo Mondi 	{
269e0d76c38SJacopo Mondi 		.name   = "NTSC SQ (QCIF)",
270e0d76c38SJacopo Mondi 		.width  = 160,
271e0d76c38SJacopo Mondi 		.height = 120,
272e0d76c38SJacopo Mondi 		.hscale = 0x0400,
273e0d76c38SJacopo Mondi 		.vscale = 0x0400,
274e0d76c38SJacopo Mondi 	},
275e0d76c38SJacopo Mondi 	{
276e0d76c38SJacopo Mondi 		.name   = "NTSC CCIR601 (QCIF)",
277e0d76c38SJacopo Mondi 		.width  = 180,
278e0d76c38SJacopo Mondi 		.height = 120,
279e0d76c38SJacopo Mondi 		.hscale = 0x0400,
280e0d76c38SJacopo Mondi 		.vscale = 0x0400,
281e0d76c38SJacopo Mondi 	},
282e0d76c38SJacopo Mondi };
283e0d76c38SJacopo Mondi 
284e0d76c38SJacopo Mondi static const struct tw9910_scale_ctrl tw9910_pal_scales[] = {
285e0d76c38SJacopo Mondi 	{
286e0d76c38SJacopo Mondi 		.name   = "PAL SQ",
287e0d76c38SJacopo Mondi 		.width  = 768,
288e0d76c38SJacopo Mondi 		.height = 576,
289e0d76c38SJacopo Mondi 		.hscale = 0x0100,
290e0d76c38SJacopo Mondi 		.vscale = 0x0100,
291e0d76c38SJacopo Mondi 	},
292e0d76c38SJacopo Mondi 	{
293e0d76c38SJacopo Mondi 		.name   = "PAL CCIR601",
294e0d76c38SJacopo Mondi 		.width  = 720,
295e0d76c38SJacopo Mondi 		.height = 576,
296e0d76c38SJacopo Mondi 		.hscale = 0x0100,
297e0d76c38SJacopo Mondi 		.vscale = 0x0100,
298e0d76c38SJacopo Mondi 	},
299e0d76c38SJacopo Mondi 	{
300e0d76c38SJacopo Mondi 		.name   = "PAL SQ (CIF)",
301e0d76c38SJacopo Mondi 		.width  = 384,
302e0d76c38SJacopo Mondi 		.height = 288,
303e0d76c38SJacopo Mondi 		.hscale = 0x0200,
304e0d76c38SJacopo Mondi 		.vscale = 0x0200,
305e0d76c38SJacopo Mondi 	},
306e0d76c38SJacopo Mondi 	{
307e0d76c38SJacopo Mondi 		.name   = "PAL CCIR601 (CIF)",
308e0d76c38SJacopo Mondi 		.width  = 360,
309e0d76c38SJacopo Mondi 		.height = 288,
310e0d76c38SJacopo Mondi 		.hscale = 0x0200,
311e0d76c38SJacopo Mondi 		.vscale = 0x0200,
312e0d76c38SJacopo Mondi 	},
313e0d76c38SJacopo Mondi 	{
314e0d76c38SJacopo Mondi 		.name   = "PAL SQ (QCIF)",
315e0d76c38SJacopo Mondi 		.width  = 192,
316e0d76c38SJacopo Mondi 		.height = 144,
317e0d76c38SJacopo Mondi 		.hscale = 0x0400,
318e0d76c38SJacopo Mondi 		.vscale = 0x0400,
319e0d76c38SJacopo Mondi 	},
320e0d76c38SJacopo Mondi 	{
321e0d76c38SJacopo Mondi 		.name   = "PAL CCIR601 (QCIF)",
322e0d76c38SJacopo Mondi 		.width  = 180,
323e0d76c38SJacopo Mondi 		.height = 144,
324e0d76c38SJacopo Mondi 		.hscale = 0x0400,
325e0d76c38SJacopo Mondi 		.vscale = 0x0400,
326e0d76c38SJacopo Mondi 	},
327e0d76c38SJacopo Mondi };
328e0d76c38SJacopo Mondi 
329e0d76c38SJacopo Mondi /*
330e0d76c38SJacopo Mondi  * general function
331e0d76c38SJacopo Mondi  */
332e0d76c38SJacopo Mondi static struct tw9910_priv *to_tw9910(const struct i2c_client *client)
333e0d76c38SJacopo Mondi {
334e0d76c38SJacopo Mondi 	return container_of(i2c_get_clientdata(client), struct tw9910_priv,
335e0d76c38SJacopo Mondi 			    subdev);
336e0d76c38SJacopo Mondi }
337e0d76c38SJacopo Mondi 
338e0d76c38SJacopo Mondi static int tw9910_mask_set(struct i2c_client *client, u8 command,
339e0d76c38SJacopo Mondi 			   u8 mask, u8 set)
340e0d76c38SJacopo Mondi {
341e0d76c38SJacopo Mondi 	s32 val = i2c_smbus_read_byte_data(client, command);
342e0d76c38SJacopo Mondi 	if (val < 0)
343e0d76c38SJacopo Mondi 		return val;
344e0d76c38SJacopo Mondi 
345e0d76c38SJacopo Mondi 	val &= ~mask;
346e0d76c38SJacopo Mondi 	val |= set & mask;
347e0d76c38SJacopo Mondi 
348e0d76c38SJacopo Mondi 	return i2c_smbus_write_byte_data(client, command, val);
349e0d76c38SJacopo Mondi }
350e0d76c38SJacopo Mondi 
351e0d76c38SJacopo Mondi static int tw9910_set_scale(struct i2c_client *client,
352e0d76c38SJacopo Mondi 			    const struct tw9910_scale_ctrl *scale)
353e0d76c38SJacopo Mondi {
354e0d76c38SJacopo Mondi 	int ret;
355e0d76c38SJacopo Mondi 
356e0d76c38SJacopo Mondi 	ret = i2c_smbus_write_byte_data(client, SCALE_HI,
357e0d76c38SJacopo Mondi 					(scale->vscale & 0x0F00) >> 4 |
358e0d76c38SJacopo Mondi 					(scale->hscale & 0x0F00) >> 8);
359e0d76c38SJacopo Mondi 	if (ret < 0)
360e0d76c38SJacopo Mondi 		return ret;
361e0d76c38SJacopo Mondi 
362e0d76c38SJacopo Mondi 	ret = i2c_smbus_write_byte_data(client, HSCALE_LO,
363e0d76c38SJacopo Mondi 					scale->hscale & 0x00FF);
364e0d76c38SJacopo Mondi 	if (ret < 0)
365e0d76c38SJacopo Mondi 		return ret;
366e0d76c38SJacopo Mondi 
367e0d76c38SJacopo Mondi 	ret = i2c_smbus_write_byte_data(client, VSCALE_LO,
368e0d76c38SJacopo Mondi 					scale->vscale & 0x00FF);
369e0d76c38SJacopo Mondi 
370e0d76c38SJacopo Mondi 	return ret;
371e0d76c38SJacopo Mondi }
372e0d76c38SJacopo Mondi 
373e0d76c38SJacopo Mondi static int tw9910_set_hsync(struct i2c_client *client)
374e0d76c38SJacopo Mondi {
375e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
376e0d76c38SJacopo Mondi 	int ret;
377e0d76c38SJacopo Mondi 
378e0d76c38SJacopo Mondi 	/* bit 10 - 3 */
379e0d76c38SJacopo Mondi 	ret = i2c_smbus_write_byte_data(client, HSBEGIN,
380e0d76c38SJacopo Mondi 					(HSYNC_START & 0x07F8) >> 3);
381e0d76c38SJacopo Mondi 	if (ret < 0)
382e0d76c38SJacopo Mondi 		return ret;
383e0d76c38SJacopo Mondi 
384e0d76c38SJacopo Mondi 	/* bit 10 - 3 */
385e0d76c38SJacopo Mondi 	ret = i2c_smbus_write_byte_data(client, HSEND,
386e0d76c38SJacopo Mondi 					(HSYNC_END & 0x07F8) >> 3);
387e0d76c38SJacopo Mondi 	if (ret < 0)
388e0d76c38SJacopo Mondi 		return ret;
389e0d76c38SJacopo Mondi 
390e0d76c38SJacopo Mondi 	/* So far only revisions 0 and 1 have been seen */
391e0d76c38SJacopo Mondi 	/* bit 2 - 0 */
392e0d76c38SJacopo Mondi 	if (1 == priv->revision)
393e0d76c38SJacopo Mondi 		ret = tw9910_mask_set(client, HSLOWCTL, 0x77,
394e0d76c38SJacopo Mondi 				      (HSYNC_START & 0x0007) << 4 |
395e0d76c38SJacopo Mondi 				      (HSYNC_END   & 0x0007));
396e0d76c38SJacopo Mondi 
397e0d76c38SJacopo Mondi 	return ret;
398e0d76c38SJacopo Mondi }
399e0d76c38SJacopo Mondi 
400e0d76c38SJacopo Mondi static void tw9910_reset(struct i2c_client *client)
401e0d76c38SJacopo Mondi {
402e0d76c38SJacopo Mondi 	tw9910_mask_set(client, ACNTL1, SRESET, SRESET);
403e0d76c38SJacopo Mondi 	msleep(1);
404e0d76c38SJacopo Mondi }
405e0d76c38SJacopo Mondi 
406e0d76c38SJacopo Mondi static int tw9910_power(struct i2c_client *client, int enable)
407e0d76c38SJacopo Mondi {
408e0d76c38SJacopo Mondi 	int ret;
409e0d76c38SJacopo Mondi 	u8 acntl1;
410e0d76c38SJacopo Mondi 	u8 acntl2;
411e0d76c38SJacopo Mondi 
412e0d76c38SJacopo Mondi 	if (enable) {
413e0d76c38SJacopo Mondi 		acntl1 = 0;
414e0d76c38SJacopo Mondi 		acntl2 = 0;
415e0d76c38SJacopo Mondi 	} else {
416e0d76c38SJacopo Mondi 		acntl1 = CLK_PDN | Y_PDN | C_PDN;
417e0d76c38SJacopo Mondi 		acntl2 = PLL_PDN;
418e0d76c38SJacopo Mondi 	}
419e0d76c38SJacopo Mondi 
420e0d76c38SJacopo Mondi 	ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1);
421e0d76c38SJacopo Mondi 	if (ret < 0)
422e0d76c38SJacopo Mondi 		return ret;
423e0d76c38SJacopo Mondi 
424e0d76c38SJacopo Mondi 	return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2);
425e0d76c38SJacopo Mondi }
426e0d76c38SJacopo Mondi 
427e0d76c38SJacopo Mondi static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm,
428e0d76c38SJacopo Mondi 							  u32 width, u32 height)
429e0d76c38SJacopo Mondi {
430e0d76c38SJacopo Mondi 	const struct tw9910_scale_ctrl *scale;
431e0d76c38SJacopo Mondi 	const struct tw9910_scale_ctrl *ret = NULL;
432e0d76c38SJacopo Mondi 	__u32 diff = 0xffffffff, tmp;
433e0d76c38SJacopo Mondi 	int size, i;
434e0d76c38SJacopo Mondi 
435e0d76c38SJacopo Mondi 	if (norm & V4L2_STD_NTSC) {
436e0d76c38SJacopo Mondi 		scale = tw9910_ntsc_scales;
437e0d76c38SJacopo Mondi 		size = ARRAY_SIZE(tw9910_ntsc_scales);
438e0d76c38SJacopo Mondi 	} else if (norm & V4L2_STD_PAL) {
439e0d76c38SJacopo Mondi 		scale = tw9910_pal_scales;
440e0d76c38SJacopo Mondi 		size = ARRAY_SIZE(tw9910_pal_scales);
441e0d76c38SJacopo Mondi 	} else {
442e0d76c38SJacopo Mondi 		return NULL;
443e0d76c38SJacopo Mondi 	}
444e0d76c38SJacopo Mondi 
445e0d76c38SJacopo Mondi 	for (i = 0; i < size; i++) {
446e0d76c38SJacopo Mondi 		tmp = abs(width - scale[i].width) +
447e0d76c38SJacopo Mondi 			abs(height - scale[i].height);
448e0d76c38SJacopo Mondi 		if (tmp < diff) {
449e0d76c38SJacopo Mondi 			diff = tmp;
450e0d76c38SJacopo Mondi 			ret = scale + i;
451e0d76c38SJacopo Mondi 		}
452e0d76c38SJacopo Mondi 	}
453e0d76c38SJacopo Mondi 
454e0d76c38SJacopo Mondi 	return ret;
455e0d76c38SJacopo Mondi }
456e0d76c38SJacopo Mondi 
457e0d76c38SJacopo Mondi /*
458e0d76c38SJacopo Mondi  * subdevice operations
459e0d76c38SJacopo Mondi  */
460e0d76c38SJacopo Mondi static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
461e0d76c38SJacopo Mondi {
462e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
463e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
464e0d76c38SJacopo Mondi 	u8 val;
465e0d76c38SJacopo Mondi 	int ret;
466e0d76c38SJacopo Mondi 
467e0d76c38SJacopo Mondi 	if (!enable) {
468e0d76c38SJacopo Mondi 		switch (priv->revision) {
469e0d76c38SJacopo Mondi 		case 0:
470e0d76c38SJacopo Mondi 			val = OEN_TRI_SEL_ALL_OFF_r0;
471e0d76c38SJacopo Mondi 			break;
472e0d76c38SJacopo Mondi 		case 1:
473e0d76c38SJacopo Mondi 			val = OEN_TRI_SEL_ALL_OFF_r1;
474e0d76c38SJacopo Mondi 			break;
475e0d76c38SJacopo Mondi 		default:
476e0d76c38SJacopo Mondi 			dev_err(&client->dev, "un-supported revision\n");
477e0d76c38SJacopo Mondi 			return -EINVAL;
478e0d76c38SJacopo Mondi 		}
479e0d76c38SJacopo Mondi 	} else {
480e0d76c38SJacopo Mondi 		val = OEN_TRI_SEL_ALL_ON;
481e0d76c38SJacopo Mondi 
482e0d76c38SJacopo Mondi 		if (!priv->scale) {
483e0d76c38SJacopo Mondi 			dev_err(&client->dev, "norm select error\n");
484e0d76c38SJacopo Mondi 			return -EPERM;
485e0d76c38SJacopo Mondi 		}
486e0d76c38SJacopo Mondi 
487e0d76c38SJacopo Mondi 		dev_dbg(&client->dev, "%s %dx%d\n",
488e0d76c38SJacopo Mondi 			priv->scale->name,
489e0d76c38SJacopo Mondi 			priv->scale->width,
490e0d76c38SJacopo Mondi 			priv->scale->height);
491e0d76c38SJacopo Mondi 	}
492e0d76c38SJacopo Mondi 
493e0d76c38SJacopo Mondi 	ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val);
494e0d76c38SJacopo Mondi 	if (ret < 0)
495e0d76c38SJacopo Mondi 		return ret;
496e0d76c38SJacopo Mondi 
497e0d76c38SJacopo Mondi 	return tw9910_power(client, enable);
498e0d76c38SJacopo Mondi }
499e0d76c38SJacopo Mondi 
500e0d76c38SJacopo Mondi static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
501e0d76c38SJacopo Mondi {
502e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
503e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
504e0d76c38SJacopo Mondi 
505e0d76c38SJacopo Mondi 	*norm = priv->norm;
506e0d76c38SJacopo Mondi 
507e0d76c38SJacopo Mondi 	return 0;
508e0d76c38SJacopo Mondi }
509e0d76c38SJacopo Mondi 
510e0d76c38SJacopo Mondi static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
511e0d76c38SJacopo Mondi {
512e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
513e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
514e0d76c38SJacopo Mondi 	const unsigned hact = 720;
515e0d76c38SJacopo Mondi 	const unsigned hdelay = 15;
516e0d76c38SJacopo Mondi 	unsigned vact;
517e0d76c38SJacopo Mondi 	unsigned vdelay;
518e0d76c38SJacopo Mondi 	int ret;
519e0d76c38SJacopo Mondi 
520e0d76c38SJacopo Mondi 	if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL)))
521e0d76c38SJacopo Mondi 		return -EINVAL;
522e0d76c38SJacopo Mondi 
523e0d76c38SJacopo Mondi 	priv->norm = norm;
524e0d76c38SJacopo Mondi 	if (norm & V4L2_STD_525_60) {
525e0d76c38SJacopo Mondi 		vact = 240;
526e0d76c38SJacopo Mondi 		vdelay = 18;
527e0d76c38SJacopo Mondi 		ret = tw9910_mask_set(client, VVBI, 0x10, 0x10);
528e0d76c38SJacopo Mondi 	} else {
529e0d76c38SJacopo Mondi 		vact = 288;
530e0d76c38SJacopo Mondi 		vdelay = 24;
531e0d76c38SJacopo Mondi 		ret = tw9910_mask_set(client, VVBI, 0x10, 0x00);
532e0d76c38SJacopo Mondi 	}
533e0d76c38SJacopo Mondi 	if (!ret)
534e0d76c38SJacopo Mondi 		ret = i2c_smbus_write_byte_data(client, CROP_HI,
535e0d76c38SJacopo Mondi 			((vdelay >> 2) & 0xc0) |
536e0d76c38SJacopo Mondi 			((vact >> 4) & 0x30) |
537e0d76c38SJacopo Mondi 			((hdelay >> 6) & 0x0c) |
538e0d76c38SJacopo Mondi 			((hact >> 8) & 0x03));
539e0d76c38SJacopo Mondi 	if (!ret)
540e0d76c38SJacopo Mondi 		ret = i2c_smbus_write_byte_data(client, VDELAY_LO,
541e0d76c38SJacopo Mondi 			vdelay & 0xff);
542e0d76c38SJacopo Mondi 	if (!ret)
543e0d76c38SJacopo Mondi 		ret = i2c_smbus_write_byte_data(client, VACTIVE_LO,
544e0d76c38SJacopo Mondi 			vact & 0xff);
545e0d76c38SJacopo Mondi 
546e0d76c38SJacopo Mondi 	return ret;
547e0d76c38SJacopo Mondi }
548e0d76c38SJacopo Mondi 
549e0d76c38SJacopo Mondi #ifdef CONFIG_VIDEO_ADV_DEBUG
550e0d76c38SJacopo Mondi static int tw9910_g_register(struct v4l2_subdev *sd,
551e0d76c38SJacopo Mondi 			     struct v4l2_dbg_register *reg)
552e0d76c38SJacopo Mondi {
553e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
554e0d76c38SJacopo Mondi 	int ret;
555e0d76c38SJacopo Mondi 
556e0d76c38SJacopo Mondi 	if (reg->reg > 0xff)
557e0d76c38SJacopo Mondi 		return -EINVAL;
558e0d76c38SJacopo Mondi 
559e0d76c38SJacopo Mondi 	reg->size = 1;
560e0d76c38SJacopo Mondi 	ret = i2c_smbus_read_byte_data(client, reg->reg);
561e0d76c38SJacopo Mondi 	if (ret < 0)
562e0d76c38SJacopo Mondi 		return ret;
563e0d76c38SJacopo Mondi 
564e0d76c38SJacopo Mondi 	/*
565e0d76c38SJacopo Mondi 	 * ret      = int
566e0d76c38SJacopo Mondi 	 * reg->val = __u64
567e0d76c38SJacopo Mondi 	 */
568e0d76c38SJacopo Mondi 	reg->val = (__u64)ret;
569e0d76c38SJacopo Mondi 
570e0d76c38SJacopo Mondi 	return 0;
571e0d76c38SJacopo Mondi }
572e0d76c38SJacopo Mondi 
573e0d76c38SJacopo Mondi static int tw9910_s_register(struct v4l2_subdev *sd,
574e0d76c38SJacopo Mondi 			     const struct v4l2_dbg_register *reg)
575e0d76c38SJacopo Mondi {
576e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
577e0d76c38SJacopo Mondi 
578e0d76c38SJacopo Mondi 	if (reg->reg > 0xff ||
579e0d76c38SJacopo Mondi 	    reg->val > 0xff)
580e0d76c38SJacopo Mondi 		return -EINVAL;
581e0d76c38SJacopo Mondi 
582e0d76c38SJacopo Mondi 	return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
583e0d76c38SJacopo Mondi }
584e0d76c38SJacopo Mondi #endif
585e0d76c38SJacopo Mondi 
5867b20f325SJacopo Mondi static int tw9910_power_on(struct tw9910_priv *priv)
5877b20f325SJacopo Mondi {
5887b20f325SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
5897b20f325SJacopo Mondi 	int ret;
5907b20f325SJacopo Mondi 
5917b20f325SJacopo Mondi 	if (priv->clk) {
5927b20f325SJacopo Mondi 		ret = clk_prepare_enable(priv->clk);
5937b20f325SJacopo Mondi 		if (ret)
5947b20f325SJacopo Mondi 			return ret;
5957b20f325SJacopo Mondi 	}
5967b20f325SJacopo Mondi 
5977b20f325SJacopo Mondi 	if (priv->pdn_gpio) {
5987b20f325SJacopo Mondi 		gpiod_set_value(priv->pdn_gpio, 0);
5997b20f325SJacopo Mondi 		usleep_range(500, 1000);
6007b20f325SJacopo Mondi 	}
6017b20f325SJacopo Mondi 
6027b20f325SJacopo Mondi 	/*
6037b20f325SJacopo Mondi 	 * FIXME: The reset signal is connected to a shared GPIO on some
6047b20f325SJacopo Mondi 	 * platforms (namely the SuperH Migo-R). Until a framework becomes
6057b20f325SJacopo Mondi 	 * available to handle this cleanly, request the GPIO temporarily
6067b20f325SJacopo Mondi 	 * to avoid conflicts.
6077b20f325SJacopo Mondi 	 */
6087b20f325SJacopo Mondi 	priv->rstb_gpio = gpiod_get_optional(&client->dev, "rstb",
6097b20f325SJacopo Mondi 					     GPIOD_OUT_LOW);
6107b20f325SJacopo Mondi 	if (IS_ERR(priv->rstb_gpio)) {
6117b20f325SJacopo Mondi 		dev_info(&client->dev, "Unable to get GPIO \"rstb\"");
6127b20f325SJacopo Mondi 		return PTR_ERR(priv->rstb_gpio);
6137b20f325SJacopo Mondi 	}
6147b20f325SJacopo Mondi 
6157b20f325SJacopo Mondi 	if (priv->rstb_gpio) {
6167b20f325SJacopo Mondi 		gpiod_set_value(priv->rstb_gpio, 1);
6177b20f325SJacopo Mondi 		usleep_range(500, 1000);
6187b20f325SJacopo Mondi 		gpiod_set_value(priv->rstb_gpio, 0);
6197b20f325SJacopo Mondi 		usleep_range(500, 1000);
6207b20f325SJacopo Mondi 
6217b20f325SJacopo Mondi 		gpiod_put(priv->rstb_gpio);
6227b20f325SJacopo Mondi 	}
6237b20f325SJacopo Mondi 
6247b20f325SJacopo Mondi 	return 0;
6257b20f325SJacopo Mondi }
6267b20f325SJacopo Mondi 
6277b20f325SJacopo Mondi static int tw9910_power_off(struct tw9910_priv *priv)
6287b20f325SJacopo Mondi {
6297b20f325SJacopo Mondi 	clk_disable_unprepare(priv->clk);
6307b20f325SJacopo Mondi 
6317b20f325SJacopo Mondi 	if (priv->pdn_gpio) {
6327b20f325SJacopo Mondi 		gpiod_set_value(priv->pdn_gpio, 1);
6337b20f325SJacopo Mondi 		usleep_range(500, 1000);
6347b20f325SJacopo Mondi 	}
6357b20f325SJacopo Mondi 
6367b20f325SJacopo Mondi 	return 0;
6377b20f325SJacopo Mondi }
6387b20f325SJacopo Mondi 
639e0d76c38SJacopo Mondi static int tw9910_s_power(struct v4l2_subdev *sd, int on)
640e0d76c38SJacopo Mondi {
641e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
642e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
643e0d76c38SJacopo Mondi 
6447b20f325SJacopo Mondi 	return on ? tw9910_power_on(priv) :
6457b20f325SJacopo Mondi 		    tw9910_power_off(priv);
646e0d76c38SJacopo Mondi }
647e0d76c38SJacopo Mondi 
648e0d76c38SJacopo Mondi static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
649e0d76c38SJacopo Mondi {
650e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
651e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
652e0d76c38SJacopo Mondi 	int ret = -EINVAL;
653e0d76c38SJacopo Mondi 	u8 val;
654e0d76c38SJacopo Mondi 
655e0d76c38SJacopo Mondi 	/*
656e0d76c38SJacopo Mondi 	 * select suitable norm
657e0d76c38SJacopo Mondi 	 */
658e0d76c38SJacopo Mondi 	priv->scale = tw9910_select_norm(priv->norm, *width, *height);
659e0d76c38SJacopo Mondi 	if (!priv->scale)
660e0d76c38SJacopo Mondi 		goto tw9910_set_fmt_error;
661e0d76c38SJacopo Mondi 
662e0d76c38SJacopo Mondi 	/*
663e0d76c38SJacopo Mondi 	 * reset hardware
664e0d76c38SJacopo Mondi 	 */
665e0d76c38SJacopo Mondi 	tw9910_reset(client);
666e0d76c38SJacopo Mondi 
667e0d76c38SJacopo Mondi 	/*
668e0d76c38SJacopo Mondi 	 * set bus width
669e0d76c38SJacopo Mondi 	 */
670e0d76c38SJacopo Mondi 	val = 0x00;
6717b20f325SJacopo Mondi 	if (priv->info->buswidth == 16)
672e0d76c38SJacopo Mondi 		val = LEN;
673e0d76c38SJacopo Mondi 
674e0d76c38SJacopo Mondi 	ret = tw9910_mask_set(client, OPFORM, LEN, val);
675e0d76c38SJacopo Mondi 	if (ret < 0)
676e0d76c38SJacopo Mondi 		goto tw9910_set_fmt_error;
677e0d76c38SJacopo Mondi 
678e0d76c38SJacopo Mondi 	/*
679e0d76c38SJacopo Mondi 	 * select MPOUT behavior
680e0d76c38SJacopo Mondi 	 */
681e0d76c38SJacopo Mondi 	switch (priv->info->mpout) {
682e0d76c38SJacopo Mondi 	case TW9910_MPO_VLOSS:
683e0d76c38SJacopo Mondi 		val = RTSEL_VLOSS; break;
684e0d76c38SJacopo Mondi 	case TW9910_MPO_HLOCK:
685e0d76c38SJacopo Mondi 		val = RTSEL_HLOCK; break;
686e0d76c38SJacopo Mondi 	case TW9910_MPO_SLOCK:
687e0d76c38SJacopo Mondi 		val = RTSEL_SLOCK; break;
688e0d76c38SJacopo Mondi 	case TW9910_MPO_VLOCK:
689e0d76c38SJacopo Mondi 		val = RTSEL_VLOCK; break;
690e0d76c38SJacopo Mondi 	case TW9910_MPO_MONO:
691e0d76c38SJacopo Mondi 		val = RTSEL_MONO;  break;
692e0d76c38SJacopo Mondi 	case TW9910_MPO_DET50:
693e0d76c38SJacopo Mondi 		val = RTSEL_DET50; break;
694e0d76c38SJacopo Mondi 	case TW9910_MPO_FIELD:
695e0d76c38SJacopo Mondi 		val = RTSEL_FIELD; break;
696e0d76c38SJacopo Mondi 	case TW9910_MPO_RTCO:
697e0d76c38SJacopo Mondi 		val = RTSEL_RTCO;  break;
698e0d76c38SJacopo Mondi 	default:
699e0d76c38SJacopo Mondi 		val = 0;
700e0d76c38SJacopo Mondi 	}
701e0d76c38SJacopo Mondi 
702e0d76c38SJacopo Mondi 	ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val);
703e0d76c38SJacopo Mondi 	if (ret < 0)
704e0d76c38SJacopo Mondi 		goto tw9910_set_fmt_error;
705e0d76c38SJacopo Mondi 
706e0d76c38SJacopo Mondi 	/*
707e0d76c38SJacopo Mondi 	 * set scale
708e0d76c38SJacopo Mondi 	 */
709e0d76c38SJacopo Mondi 	ret = tw9910_set_scale(client, priv->scale);
710e0d76c38SJacopo Mondi 	if (ret < 0)
711e0d76c38SJacopo Mondi 		goto tw9910_set_fmt_error;
712e0d76c38SJacopo Mondi 
713e0d76c38SJacopo Mondi 	/*
714e0d76c38SJacopo Mondi 	 * set hsync
715e0d76c38SJacopo Mondi 	 */
716e0d76c38SJacopo Mondi 	ret = tw9910_set_hsync(client);
717e0d76c38SJacopo Mondi 	if (ret < 0)
718e0d76c38SJacopo Mondi 		goto tw9910_set_fmt_error;
719e0d76c38SJacopo Mondi 
720e0d76c38SJacopo Mondi 	*width = priv->scale->width;
721e0d76c38SJacopo Mondi 	*height = priv->scale->height;
722e0d76c38SJacopo Mondi 
723e0d76c38SJacopo Mondi 	return ret;
724e0d76c38SJacopo Mondi 
725e0d76c38SJacopo Mondi tw9910_set_fmt_error:
726e0d76c38SJacopo Mondi 
727e0d76c38SJacopo Mondi 	tw9910_reset(client);
728e0d76c38SJacopo Mondi 	priv->scale = NULL;
729e0d76c38SJacopo Mondi 
730e0d76c38SJacopo Mondi 	return ret;
731e0d76c38SJacopo Mondi }
732e0d76c38SJacopo Mondi 
733e0d76c38SJacopo Mondi static int tw9910_get_selection(struct v4l2_subdev *sd,
734e0d76c38SJacopo Mondi 		struct v4l2_subdev_pad_config *cfg,
735e0d76c38SJacopo Mondi 		struct v4l2_subdev_selection *sel)
736e0d76c38SJacopo Mondi {
737e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
738e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
739e0d76c38SJacopo Mondi 
740e0d76c38SJacopo Mondi 	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
741e0d76c38SJacopo Mondi 		return -EINVAL;
742e0d76c38SJacopo Mondi 	/* Only CROP, CROP_DEFAULT and CROP_BOUNDS are supported */
743e0d76c38SJacopo Mondi 	if (sel->target > V4L2_SEL_TGT_CROP_BOUNDS)
744e0d76c38SJacopo Mondi 		return -EINVAL;
745e0d76c38SJacopo Mondi 
746e0d76c38SJacopo Mondi 	sel->r.left	= 0;
747e0d76c38SJacopo Mondi 	sel->r.top	= 0;
748e0d76c38SJacopo Mondi 	if (priv->norm & V4L2_STD_NTSC) {
749e0d76c38SJacopo Mondi 		sel->r.width	= 640;
750e0d76c38SJacopo Mondi 		sel->r.height	= 480;
751e0d76c38SJacopo Mondi 	} else {
752e0d76c38SJacopo Mondi 		sel->r.width	= 768;
753e0d76c38SJacopo Mondi 		sel->r.height	= 576;
754e0d76c38SJacopo Mondi 	}
755e0d76c38SJacopo Mondi 	return 0;
756e0d76c38SJacopo Mondi }
757e0d76c38SJacopo Mondi 
758e0d76c38SJacopo Mondi static int tw9910_get_fmt(struct v4l2_subdev *sd,
759e0d76c38SJacopo Mondi 		struct v4l2_subdev_pad_config *cfg,
760e0d76c38SJacopo Mondi 		struct v4l2_subdev_format *format)
761e0d76c38SJacopo Mondi {
762e0d76c38SJacopo Mondi 	struct v4l2_mbus_framefmt *mf = &format->format;
763e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
764e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
765e0d76c38SJacopo Mondi 
766e0d76c38SJacopo Mondi 	if (format->pad)
767e0d76c38SJacopo Mondi 		return -EINVAL;
768e0d76c38SJacopo Mondi 
769e0d76c38SJacopo Mondi 	if (!priv->scale) {
770e0d76c38SJacopo Mondi 		priv->scale = tw9910_select_norm(priv->norm, 640, 480);
771e0d76c38SJacopo Mondi 		if (!priv->scale)
772e0d76c38SJacopo Mondi 			return -EINVAL;
773e0d76c38SJacopo Mondi 	}
774e0d76c38SJacopo Mondi 
775e0d76c38SJacopo Mondi 	mf->width	= priv->scale->width;
776e0d76c38SJacopo Mondi 	mf->height	= priv->scale->height;
777e0d76c38SJacopo Mondi 	mf->code	= MEDIA_BUS_FMT_UYVY8_2X8;
778e0d76c38SJacopo Mondi 	mf->colorspace	= V4L2_COLORSPACE_SMPTE170M;
779e0d76c38SJacopo Mondi 	mf->field	= V4L2_FIELD_INTERLACED_BT;
780e0d76c38SJacopo Mondi 
781e0d76c38SJacopo Mondi 	return 0;
782e0d76c38SJacopo Mondi }
783e0d76c38SJacopo Mondi 
784e0d76c38SJacopo Mondi static int tw9910_s_fmt(struct v4l2_subdev *sd,
785e0d76c38SJacopo Mondi 			struct v4l2_mbus_framefmt *mf)
786e0d76c38SJacopo Mondi {
787e0d76c38SJacopo Mondi 	u32 width = mf->width, height = mf->height;
788e0d76c38SJacopo Mondi 	int ret;
789e0d76c38SJacopo Mondi 
790e0d76c38SJacopo Mondi 	WARN_ON(mf->field != V4L2_FIELD_ANY &&
791e0d76c38SJacopo Mondi 		mf->field != V4L2_FIELD_INTERLACED_BT);
792e0d76c38SJacopo Mondi 
793e0d76c38SJacopo Mondi 	/*
794e0d76c38SJacopo Mondi 	 * check color format
795e0d76c38SJacopo Mondi 	 */
796e0d76c38SJacopo Mondi 	if (mf->code != MEDIA_BUS_FMT_UYVY8_2X8)
797e0d76c38SJacopo Mondi 		return -EINVAL;
798e0d76c38SJacopo Mondi 
799e0d76c38SJacopo Mondi 	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
800e0d76c38SJacopo Mondi 
801e0d76c38SJacopo Mondi 	ret = tw9910_set_frame(sd, &width, &height);
802e0d76c38SJacopo Mondi 	if (!ret) {
803e0d76c38SJacopo Mondi 		mf->width	= width;
804e0d76c38SJacopo Mondi 		mf->height	= height;
805e0d76c38SJacopo Mondi 	}
806e0d76c38SJacopo Mondi 	return ret;
807e0d76c38SJacopo Mondi }
808e0d76c38SJacopo Mondi 
809e0d76c38SJacopo Mondi static int tw9910_set_fmt(struct v4l2_subdev *sd,
810e0d76c38SJacopo Mondi 		struct v4l2_subdev_pad_config *cfg,
811e0d76c38SJacopo Mondi 		struct v4l2_subdev_format *format)
812e0d76c38SJacopo Mondi {
813e0d76c38SJacopo Mondi 	struct v4l2_mbus_framefmt *mf = &format->format;
814e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
815e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
816e0d76c38SJacopo Mondi 	const struct tw9910_scale_ctrl *scale;
817e0d76c38SJacopo Mondi 
818e0d76c38SJacopo Mondi 	if (format->pad)
819e0d76c38SJacopo Mondi 		return -EINVAL;
820e0d76c38SJacopo Mondi 
821e0d76c38SJacopo Mondi 	if (V4L2_FIELD_ANY == mf->field) {
822e0d76c38SJacopo Mondi 		mf->field = V4L2_FIELD_INTERLACED_BT;
823e0d76c38SJacopo Mondi 	} else if (V4L2_FIELD_INTERLACED_BT != mf->field) {
824e0d76c38SJacopo Mondi 		dev_err(&client->dev, "Field type %d invalid.\n", mf->field);
825e0d76c38SJacopo Mondi 		return -EINVAL;
826e0d76c38SJacopo Mondi 	}
827e0d76c38SJacopo Mondi 
828e0d76c38SJacopo Mondi 	mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
829e0d76c38SJacopo Mondi 	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
830e0d76c38SJacopo Mondi 
831e0d76c38SJacopo Mondi 	/*
832e0d76c38SJacopo Mondi 	 * select suitable norm
833e0d76c38SJacopo Mondi 	 */
834e0d76c38SJacopo Mondi 	scale = tw9910_select_norm(priv->norm, mf->width, mf->height);
835e0d76c38SJacopo Mondi 	if (!scale)
836e0d76c38SJacopo Mondi 		return -EINVAL;
837e0d76c38SJacopo Mondi 
838e0d76c38SJacopo Mondi 	mf->width	= scale->width;
839e0d76c38SJacopo Mondi 	mf->height	= scale->height;
840e0d76c38SJacopo Mondi 
841e0d76c38SJacopo Mondi 	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
842e0d76c38SJacopo Mondi 		return tw9910_s_fmt(sd, mf);
843e0d76c38SJacopo Mondi 	cfg->try_fmt = *mf;
844e0d76c38SJacopo Mondi 	return 0;
845e0d76c38SJacopo Mondi }
846e0d76c38SJacopo Mondi 
847e0d76c38SJacopo Mondi static int tw9910_video_probe(struct i2c_client *client)
848e0d76c38SJacopo Mondi {
849e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
850e0d76c38SJacopo Mondi 	s32 id;
851e0d76c38SJacopo Mondi 	int ret;
852e0d76c38SJacopo Mondi 
853e0d76c38SJacopo Mondi 	/*
854e0d76c38SJacopo Mondi 	 * tw9910 only use 8 or 16 bit bus width
855e0d76c38SJacopo Mondi 	 */
8567b20f325SJacopo Mondi 	if (priv->info->buswidth != 16 && priv->info->buswidth != 8) {
857e0d76c38SJacopo Mondi 		dev_err(&client->dev, "bus width error\n");
858e0d76c38SJacopo Mondi 		return -ENODEV;
859e0d76c38SJacopo Mondi 	}
860e0d76c38SJacopo Mondi 
861e0d76c38SJacopo Mondi 	ret = tw9910_s_power(&priv->subdev, 1);
862e0d76c38SJacopo Mondi 	if (ret < 0)
863e0d76c38SJacopo Mondi 		return ret;
864e0d76c38SJacopo Mondi 
865e0d76c38SJacopo Mondi 	/*
866e0d76c38SJacopo Mondi 	 * check and show Product ID
867e0d76c38SJacopo Mondi 	 * So far only revisions 0 and 1 have been seen
868e0d76c38SJacopo Mondi 	 */
869e0d76c38SJacopo Mondi 	id = i2c_smbus_read_byte_data(client, ID);
870e0d76c38SJacopo Mondi 	priv->revision = GET_REV(id);
871e0d76c38SJacopo Mondi 	id = GET_ID(id);
872e0d76c38SJacopo Mondi 
873e0d76c38SJacopo Mondi 	if (0x0B != id ||
874e0d76c38SJacopo Mondi 	    0x01 < priv->revision) {
875e0d76c38SJacopo Mondi 		dev_err(&client->dev,
876e0d76c38SJacopo Mondi 			"Product ID error %x:%x\n",
877e0d76c38SJacopo Mondi 			id, priv->revision);
878e0d76c38SJacopo Mondi 		ret = -ENODEV;
879e0d76c38SJacopo Mondi 		goto done;
880e0d76c38SJacopo Mondi 	}
881e0d76c38SJacopo Mondi 
882e0d76c38SJacopo Mondi 	dev_info(&client->dev,
883e0d76c38SJacopo Mondi 		 "tw9910 Product ID %0x:%0x\n", id, priv->revision);
884e0d76c38SJacopo Mondi 
885e0d76c38SJacopo Mondi 	priv->norm = V4L2_STD_NTSC;
886e0d76c38SJacopo Mondi 	priv->scale = &tw9910_ntsc_scales[0];
887e0d76c38SJacopo Mondi 
888e0d76c38SJacopo Mondi done:
889e0d76c38SJacopo Mondi 	tw9910_s_power(&priv->subdev, 0);
890e0d76c38SJacopo Mondi 	return ret;
891e0d76c38SJacopo Mondi }
892e0d76c38SJacopo Mondi 
893e0d76c38SJacopo Mondi static const struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
894e0d76c38SJacopo Mondi #ifdef CONFIG_VIDEO_ADV_DEBUG
895e0d76c38SJacopo Mondi 	.g_register	= tw9910_g_register,
896e0d76c38SJacopo Mondi 	.s_register	= tw9910_s_register,
897e0d76c38SJacopo Mondi #endif
898e0d76c38SJacopo Mondi 	.s_power	= tw9910_s_power,
899e0d76c38SJacopo Mondi };
900e0d76c38SJacopo Mondi 
901e0d76c38SJacopo Mondi static int tw9910_enum_mbus_code(struct v4l2_subdev *sd,
902e0d76c38SJacopo Mondi 		struct v4l2_subdev_pad_config *cfg,
903e0d76c38SJacopo Mondi 		struct v4l2_subdev_mbus_code_enum *code)
904e0d76c38SJacopo Mondi {
905e0d76c38SJacopo Mondi 	if (code->pad || code->index)
906e0d76c38SJacopo Mondi 		return -EINVAL;
907e0d76c38SJacopo Mondi 
908e0d76c38SJacopo Mondi 	code->code = MEDIA_BUS_FMT_UYVY8_2X8;
909e0d76c38SJacopo Mondi 	return 0;
910e0d76c38SJacopo Mondi }
911e0d76c38SJacopo Mondi 
912e0d76c38SJacopo Mondi static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
913e0d76c38SJacopo Mondi {
914e0d76c38SJacopo Mondi 	*norm = V4L2_STD_NTSC | V4L2_STD_PAL;
915e0d76c38SJacopo Mondi 	return 0;
916e0d76c38SJacopo Mondi }
917e0d76c38SJacopo Mondi 
918e0d76c38SJacopo Mondi static const struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
919e0d76c38SJacopo Mondi 	.s_std		= tw9910_s_std,
920e0d76c38SJacopo Mondi 	.g_std		= tw9910_g_std,
921e0d76c38SJacopo Mondi 	.s_stream	= tw9910_s_stream,
922e0d76c38SJacopo Mondi 	.g_tvnorms	= tw9910_g_tvnorms,
923e0d76c38SJacopo Mondi };
924e0d76c38SJacopo Mondi 
925e0d76c38SJacopo Mondi static const struct v4l2_subdev_pad_ops tw9910_subdev_pad_ops = {
926e0d76c38SJacopo Mondi 	.enum_mbus_code = tw9910_enum_mbus_code,
927e0d76c38SJacopo Mondi 	.get_selection	= tw9910_get_selection,
928e0d76c38SJacopo Mondi 	.get_fmt	= tw9910_get_fmt,
929e0d76c38SJacopo Mondi 	.set_fmt	= tw9910_set_fmt,
930e0d76c38SJacopo Mondi };
931e0d76c38SJacopo Mondi 
932e0d76c38SJacopo Mondi static const struct v4l2_subdev_ops tw9910_subdev_ops = {
933e0d76c38SJacopo Mondi 	.core	= &tw9910_subdev_core_ops,
934e0d76c38SJacopo Mondi 	.video	= &tw9910_subdev_video_ops,
935e0d76c38SJacopo Mondi 	.pad	= &tw9910_subdev_pad_ops,
936e0d76c38SJacopo Mondi };
937e0d76c38SJacopo Mondi 
938e0d76c38SJacopo Mondi /*
939e0d76c38SJacopo Mondi  * i2c_driver function
940e0d76c38SJacopo Mondi  */
941e0d76c38SJacopo Mondi 
942e0d76c38SJacopo Mondi static int tw9910_probe(struct i2c_client *client,
943e0d76c38SJacopo Mondi 			const struct i2c_device_id *did)
944e0d76c38SJacopo Mondi 
945e0d76c38SJacopo Mondi {
946e0d76c38SJacopo Mondi 	struct tw9910_priv		*priv;
947e0d76c38SJacopo Mondi 	struct tw9910_video_info	*info;
948e0d76c38SJacopo Mondi 	struct i2c_adapter		*adapter =
949e0d76c38SJacopo Mondi 		to_i2c_adapter(client->dev.parent);
950e0d76c38SJacopo Mondi 	int ret;
951e0d76c38SJacopo Mondi 
9527b20f325SJacopo Mondi 	if (!client->dev.platform_data) {
953e0d76c38SJacopo Mondi 		dev_err(&client->dev, "TW9910: missing platform data!\n");
954e0d76c38SJacopo Mondi 		return -EINVAL;
955e0d76c38SJacopo Mondi 	}
956e0d76c38SJacopo Mondi 
9577b20f325SJacopo Mondi 	info = client->dev.platform_data;
958e0d76c38SJacopo Mondi 
959e0d76c38SJacopo Mondi 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
960e0d76c38SJacopo Mondi 		dev_err(&client->dev,
961e0d76c38SJacopo Mondi 			"I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE_DATA\n");
962e0d76c38SJacopo Mondi 		return -EIO;
963e0d76c38SJacopo Mondi 	}
964e0d76c38SJacopo Mondi 
965e0d76c38SJacopo Mondi 	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
966e0d76c38SJacopo Mondi 	if (!priv)
967e0d76c38SJacopo Mondi 		return -ENOMEM;
968e0d76c38SJacopo Mondi 
969e0d76c38SJacopo Mondi 	priv->info   = info;
970e0d76c38SJacopo Mondi 
971e0d76c38SJacopo Mondi 	v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
972e0d76c38SJacopo Mondi 
9737b20f325SJacopo Mondi 	priv->clk = clk_get(&client->dev, "xti");
9747b20f325SJacopo Mondi 	if (PTR_ERR(priv->clk) == -ENOENT) {
9757b20f325SJacopo Mondi 		priv->clk = NULL;
9767b20f325SJacopo Mondi 	} else if (IS_ERR(priv->clk)) {
9777b20f325SJacopo Mondi 		dev_err(&client->dev, "Unable to get xti clock\n");
978e0d76c38SJacopo Mondi 		return PTR_ERR(priv->clk);
9797b20f325SJacopo Mondi 	}
9807b20f325SJacopo Mondi 
9817b20f325SJacopo Mondi 	priv->pdn_gpio = gpiod_get_optional(&client->dev, "pdn",
9827b20f325SJacopo Mondi 					    GPIOD_OUT_HIGH);
9837b20f325SJacopo Mondi 	if (IS_ERR(priv->pdn_gpio)) {
9847b20f325SJacopo Mondi 		dev_info(&client->dev, "Unable to get GPIO \"pdn\"");
9857b20f325SJacopo Mondi 		ret = PTR_ERR(priv->pdn_gpio);
9867b20f325SJacopo Mondi 		goto error_clk_put;
9877b20f325SJacopo Mondi 	}
988e0d76c38SJacopo Mondi 
989e0d76c38SJacopo Mondi 	ret = tw9910_video_probe(client);
990e0d76c38SJacopo Mondi 	if (ret < 0)
9917b20f325SJacopo Mondi 		goto error_gpio_put;
9927b20f325SJacopo Mondi 
9937b20f325SJacopo Mondi 	ret = v4l2_async_register_subdev(&priv->subdev);
9947b20f325SJacopo Mondi 	if (ret)
9957b20f325SJacopo Mondi 		goto error_gpio_put;
9967b20f325SJacopo Mondi 
9977b20f325SJacopo Mondi 	return ret;
9987b20f325SJacopo Mondi 
9997b20f325SJacopo Mondi error_gpio_put:
10007b20f325SJacopo Mondi 	if (priv->pdn_gpio)
10017b20f325SJacopo Mondi 		gpiod_put(priv->pdn_gpio);
10027b20f325SJacopo Mondi error_clk_put:
10037b20f325SJacopo Mondi 	clk_put(priv->clk);
1004e0d76c38SJacopo Mondi 
1005e0d76c38SJacopo Mondi 	return ret;
1006e0d76c38SJacopo Mondi }
1007e0d76c38SJacopo Mondi 
1008e0d76c38SJacopo Mondi static int tw9910_remove(struct i2c_client *client)
1009e0d76c38SJacopo Mondi {
1010e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
10117b20f325SJacopo Mondi 
10127b20f325SJacopo Mondi 	if (priv->pdn_gpio)
10137b20f325SJacopo Mondi 		gpiod_put(priv->pdn_gpio);
10147b20f325SJacopo Mondi 	clk_put(priv->clk);
10157b20f325SJacopo Mondi 	v4l2_device_unregister_subdev(&priv->subdev);
10167b20f325SJacopo Mondi 
1017e0d76c38SJacopo Mondi 	return 0;
1018e0d76c38SJacopo Mondi }
1019e0d76c38SJacopo Mondi 
1020e0d76c38SJacopo Mondi static const struct i2c_device_id tw9910_id[] = {
1021e0d76c38SJacopo Mondi 	{ "tw9910", 0 },
1022e0d76c38SJacopo Mondi 	{ }
1023e0d76c38SJacopo Mondi };
1024e0d76c38SJacopo Mondi MODULE_DEVICE_TABLE(i2c, tw9910_id);
1025e0d76c38SJacopo Mondi 
1026e0d76c38SJacopo Mondi static struct i2c_driver tw9910_i2c_driver = {
1027e0d76c38SJacopo Mondi 	.driver = {
1028e0d76c38SJacopo Mondi 		.name = "tw9910",
1029e0d76c38SJacopo Mondi 	},
1030e0d76c38SJacopo Mondi 	.probe    = tw9910_probe,
1031e0d76c38SJacopo Mondi 	.remove   = tw9910_remove,
1032e0d76c38SJacopo Mondi 	.id_table = tw9910_id,
1033e0d76c38SJacopo Mondi };
1034e0d76c38SJacopo Mondi 
1035e0d76c38SJacopo Mondi module_i2c_driver(tw9910_i2c_driver);
1036e0d76c38SJacopo Mondi 
10377b20f325SJacopo Mondi MODULE_DESCRIPTION("V4L2 driver for TW9910 video decoder");
1038e0d76c38SJacopo Mondi MODULE_AUTHOR("Kuninori Morimoto");
1039e0d76c38SJacopo Mondi MODULE_LICENSE("GPL v2");
1040