xref: /openbmc/linux/drivers/media/i2c/tw9910.c (revision aaeb31c0)
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>
19e0d76c38SJacopo Mondi #include <linux/delay.h>
207470230aSJacopo Mondi #include <linux/gpio/consumer.h>
217470230aSJacopo Mondi #include <linux/i2c.h>
227470230aSJacopo Mondi #include <linux/init.h>
237470230aSJacopo Mondi #include <linux/kernel.h>
247470230aSJacopo Mondi #include <linux/module.h>
257470230aSJacopo Mondi #include <linux/slab.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  */
to_tw9910(const struct i2c_client * client)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 
tw9910_mask_set(struct i2c_client * client,u8 command,u8 mask,u8 set)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);
342876e32e5SMauro Carvalho Chehab 
343e0d76c38SJacopo Mondi 	if (val < 0)
344e0d76c38SJacopo Mondi 		return val;
345e0d76c38SJacopo Mondi 
346e0d76c38SJacopo Mondi 	val &= ~mask;
347e0d76c38SJacopo Mondi 	val |= set & mask;
348e0d76c38SJacopo Mondi 
349e0d76c38SJacopo Mondi 	return i2c_smbus_write_byte_data(client, command, val);
350e0d76c38SJacopo Mondi }
351e0d76c38SJacopo Mondi 
tw9910_set_scale(struct i2c_client * client,const struct tw9910_scale_ctrl * scale)352e0d76c38SJacopo Mondi static int tw9910_set_scale(struct i2c_client *client,
353e0d76c38SJacopo Mondi 			    const struct tw9910_scale_ctrl *scale)
354e0d76c38SJacopo Mondi {
355e0d76c38SJacopo Mondi 	int ret;
356e0d76c38SJacopo Mondi 
357e0d76c38SJacopo Mondi 	ret = i2c_smbus_write_byte_data(client, SCALE_HI,
358e0d76c38SJacopo Mondi 					(scale->vscale & 0x0F00) >> 4 |
359e0d76c38SJacopo Mondi 					(scale->hscale & 0x0F00) >> 8);
360e0d76c38SJacopo Mondi 	if (ret < 0)
361e0d76c38SJacopo Mondi 		return ret;
362e0d76c38SJacopo Mondi 
363e0d76c38SJacopo Mondi 	ret = i2c_smbus_write_byte_data(client, HSCALE_LO,
364e0d76c38SJacopo Mondi 					scale->hscale & 0x00FF);
365e0d76c38SJacopo Mondi 	if (ret < 0)
366e0d76c38SJacopo Mondi 		return ret;
367e0d76c38SJacopo Mondi 
368e0d76c38SJacopo Mondi 	ret = i2c_smbus_write_byte_data(client, VSCALE_LO,
369e0d76c38SJacopo Mondi 					scale->vscale & 0x00FF);
370e0d76c38SJacopo Mondi 
371e0d76c38SJacopo Mondi 	return ret;
372e0d76c38SJacopo Mondi }
373e0d76c38SJacopo Mondi 
tw9910_set_hsync(struct i2c_client * client)374e0d76c38SJacopo Mondi static int tw9910_set_hsync(struct i2c_client *client)
375e0d76c38SJacopo Mondi {
376e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
377e0d76c38SJacopo Mondi 	int ret;
378e0d76c38SJacopo Mondi 
379e0d76c38SJacopo Mondi 	/* bit 10 - 3 */
380e0d76c38SJacopo Mondi 	ret = i2c_smbus_write_byte_data(client, HSBEGIN,
381e0d76c38SJacopo Mondi 					(HSYNC_START & 0x07F8) >> 3);
382e0d76c38SJacopo Mondi 	if (ret < 0)
383e0d76c38SJacopo Mondi 		return ret;
384e0d76c38SJacopo Mondi 
385e0d76c38SJacopo Mondi 	/* bit 10 - 3 */
386e0d76c38SJacopo Mondi 	ret = i2c_smbus_write_byte_data(client, HSEND,
387e0d76c38SJacopo Mondi 					(HSYNC_END & 0x07F8) >> 3);
388e0d76c38SJacopo Mondi 	if (ret < 0)
389e0d76c38SJacopo Mondi 		return ret;
390e0d76c38SJacopo Mondi 
39199b010b6SJacopo Mondi 	/* So far only revisions 0 and 1 have been seen. */
392e0d76c38SJacopo Mondi 	/* bit 2 - 0 */
393876e32e5SMauro Carvalho Chehab 	if (priv->revision == 1)
394e0d76c38SJacopo Mondi 		ret = tw9910_mask_set(client, HSLOWCTL, 0x77,
395e0d76c38SJacopo Mondi 				      (HSYNC_START & 0x0007) << 4 |
396e0d76c38SJacopo Mondi 				      (HSYNC_END   & 0x0007));
397e0d76c38SJacopo Mondi 
398e0d76c38SJacopo Mondi 	return ret;
399e0d76c38SJacopo Mondi }
400e0d76c38SJacopo Mondi 
tw9910_reset(struct i2c_client * client)401e0d76c38SJacopo Mondi static void tw9910_reset(struct i2c_client *client)
402e0d76c38SJacopo Mondi {
403e0d76c38SJacopo Mondi 	tw9910_mask_set(client, ACNTL1, SRESET, SRESET);
4047bd8c4f2SJacopo Mondi 	usleep_range(1000, 5000);
405e0d76c38SJacopo Mondi }
406e0d76c38SJacopo Mondi 
tw9910_power(struct i2c_client * client,int enable)407e0d76c38SJacopo Mondi static int tw9910_power(struct i2c_client *client, int enable)
408e0d76c38SJacopo Mondi {
409e0d76c38SJacopo Mondi 	int ret;
410e0d76c38SJacopo Mondi 	u8 acntl1;
411e0d76c38SJacopo Mondi 	u8 acntl2;
412e0d76c38SJacopo Mondi 
413e0d76c38SJacopo Mondi 	if (enable) {
414e0d76c38SJacopo Mondi 		acntl1 = 0;
415e0d76c38SJacopo Mondi 		acntl2 = 0;
416e0d76c38SJacopo Mondi 	} else {
417e0d76c38SJacopo Mondi 		acntl1 = CLK_PDN | Y_PDN | C_PDN;
418e0d76c38SJacopo Mondi 		acntl2 = PLL_PDN;
419e0d76c38SJacopo Mondi 	}
420e0d76c38SJacopo Mondi 
421e0d76c38SJacopo Mondi 	ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1);
422e0d76c38SJacopo Mondi 	if (ret < 0)
423e0d76c38SJacopo Mondi 		return ret;
424e0d76c38SJacopo Mondi 
425e0d76c38SJacopo Mondi 	return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2);
426e0d76c38SJacopo Mondi }
427e0d76c38SJacopo Mondi 
tw9910_select_norm(v4l2_std_id norm,u32 width,u32 height)428e0d76c38SJacopo Mondi static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm,
429e0d76c38SJacopo Mondi 							  u32 width, u32 height)
430e0d76c38SJacopo Mondi {
431e0d76c38SJacopo Mondi 	const struct tw9910_scale_ctrl *scale;
432e0d76c38SJacopo Mondi 	const struct tw9910_scale_ctrl *ret = NULL;
433e0d76c38SJacopo Mondi 	__u32 diff = 0xffffffff, tmp;
434e0d76c38SJacopo Mondi 	int size, i;
435e0d76c38SJacopo Mondi 
436e0d76c38SJacopo Mondi 	if (norm & V4L2_STD_NTSC) {
437e0d76c38SJacopo Mondi 		scale = tw9910_ntsc_scales;
438e0d76c38SJacopo Mondi 		size = ARRAY_SIZE(tw9910_ntsc_scales);
439e0d76c38SJacopo Mondi 	} else if (norm & V4L2_STD_PAL) {
440e0d76c38SJacopo Mondi 		scale = tw9910_pal_scales;
441e0d76c38SJacopo Mondi 		size = ARRAY_SIZE(tw9910_pal_scales);
442e0d76c38SJacopo Mondi 	} else {
443e0d76c38SJacopo Mondi 		return NULL;
444e0d76c38SJacopo Mondi 	}
445e0d76c38SJacopo Mondi 
446e0d76c38SJacopo Mondi 	for (i = 0; i < size; i++) {
447e0d76c38SJacopo Mondi 		tmp = abs(width - scale[i].width) +
448e0d76c38SJacopo Mondi 		      abs(height - scale[i].height);
449e0d76c38SJacopo Mondi 		if (tmp < diff) {
450e0d76c38SJacopo Mondi 			diff = tmp;
451e0d76c38SJacopo Mondi 			ret = scale + i;
452e0d76c38SJacopo Mondi 		}
453e0d76c38SJacopo Mondi 	}
454e0d76c38SJacopo Mondi 
455e0d76c38SJacopo Mondi 	return ret;
456e0d76c38SJacopo Mondi }
457e0d76c38SJacopo Mondi 
458e0d76c38SJacopo Mondi /*
459e0d76c38SJacopo Mondi  * subdevice operations
460e0d76c38SJacopo Mondi  */
tw9910_s_stream(struct v4l2_subdev * sd,int enable)461e0d76c38SJacopo Mondi static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
462e0d76c38SJacopo Mondi {
463e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
464e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
465e0d76c38SJacopo Mondi 	u8 val;
466e0d76c38SJacopo Mondi 	int ret;
467e0d76c38SJacopo Mondi 
468e0d76c38SJacopo Mondi 	if (!enable) {
469e0d76c38SJacopo Mondi 		switch (priv->revision) {
470e0d76c38SJacopo Mondi 		case 0:
471e0d76c38SJacopo Mondi 			val = OEN_TRI_SEL_ALL_OFF_r0;
472e0d76c38SJacopo Mondi 			break;
473e0d76c38SJacopo Mondi 		case 1:
474e0d76c38SJacopo Mondi 			val = OEN_TRI_SEL_ALL_OFF_r1;
475e0d76c38SJacopo Mondi 			break;
476e0d76c38SJacopo Mondi 		default:
477e0d76c38SJacopo Mondi 			dev_err(&client->dev, "un-supported revision\n");
478e0d76c38SJacopo Mondi 			return -EINVAL;
479e0d76c38SJacopo Mondi 		}
480e0d76c38SJacopo Mondi 	} else {
481e0d76c38SJacopo Mondi 		val = OEN_TRI_SEL_ALL_ON;
482e0d76c38SJacopo Mondi 
483e0d76c38SJacopo Mondi 		if (!priv->scale) {
484e0d76c38SJacopo Mondi 			dev_err(&client->dev, "norm select error\n");
485e0d76c38SJacopo Mondi 			return -EPERM;
486e0d76c38SJacopo Mondi 		}
487e0d76c38SJacopo Mondi 
488e0d76c38SJacopo Mondi 		dev_dbg(&client->dev, "%s %dx%d\n",
489e0d76c38SJacopo Mondi 			priv->scale->name,
490e0d76c38SJacopo Mondi 			priv->scale->width,
491e0d76c38SJacopo Mondi 			priv->scale->height);
492e0d76c38SJacopo Mondi 	}
493e0d76c38SJacopo Mondi 
494e0d76c38SJacopo Mondi 	ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val);
495e0d76c38SJacopo Mondi 	if (ret < 0)
496e0d76c38SJacopo Mondi 		return ret;
497e0d76c38SJacopo Mondi 
498e0d76c38SJacopo Mondi 	return tw9910_power(client, enable);
499e0d76c38SJacopo Mondi }
500e0d76c38SJacopo Mondi 
tw9910_g_std(struct v4l2_subdev * sd,v4l2_std_id * norm)501e0d76c38SJacopo Mondi static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
502e0d76c38SJacopo Mondi {
503e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
504e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
505e0d76c38SJacopo Mondi 
506e0d76c38SJacopo Mondi 	*norm = priv->norm;
507e0d76c38SJacopo Mondi 
508e0d76c38SJacopo Mondi 	return 0;
509e0d76c38SJacopo Mondi }
510e0d76c38SJacopo Mondi 
tw9910_s_std(struct v4l2_subdev * sd,v4l2_std_id norm)511e0d76c38SJacopo Mondi static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
512e0d76c38SJacopo Mondi {
513e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
514e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
515876e32e5SMauro Carvalho Chehab 	const unsigned int hact = 720;
516876e32e5SMauro Carvalho Chehab 	const unsigned int hdelay = 15;
517876e32e5SMauro Carvalho Chehab 	unsigned int vact;
518876e32e5SMauro Carvalho Chehab 	unsigned int vdelay;
519e0d76c38SJacopo Mondi 	int ret;
520e0d76c38SJacopo Mondi 
521e0d76c38SJacopo Mondi 	if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL)))
522e0d76c38SJacopo Mondi 		return -EINVAL;
523e0d76c38SJacopo Mondi 
524e0d76c38SJacopo Mondi 	priv->norm = norm;
525e0d76c38SJacopo Mondi 	if (norm & V4L2_STD_525_60) {
526e0d76c38SJacopo Mondi 		vact = 240;
527e0d76c38SJacopo Mondi 		vdelay = 18;
528e0d76c38SJacopo Mondi 		ret = tw9910_mask_set(client, VVBI, 0x10, 0x10);
529e0d76c38SJacopo Mondi 	} else {
530e0d76c38SJacopo Mondi 		vact = 288;
531e0d76c38SJacopo Mondi 		vdelay = 24;
532e0d76c38SJacopo Mondi 		ret = tw9910_mask_set(client, VVBI, 0x10, 0x00);
533e0d76c38SJacopo Mondi 	}
534e0d76c38SJacopo Mondi 	if (!ret)
535e0d76c38SJacopo Mondi 		ret = i2c_smbus_write_byte_data(client, CROP_HI,
536e0d76c38SJacopo Mondi 						((vdelay >> 2) & 0xc0)	|
537e0d76c38SJacopo Mondi 						((vact >> 4) & 0x30)	|
538e0d76c38SJacopo Mondi 						((hdelay >> 6) & 0x0c)	|
539e0d76c38SJacopo Mondi 						((hact >> 8) & 0x03));
540e0d76c38SJacopo Mondi 	if (!ret)
541e0d76c38SJacopo Mondi 		ret = i2c_smbus_write_byte_data(client, VDELAY_LO,
542e0d76c38SJacopo Mondi 						vdelay & 0xff);
543e0d76c38SJacopo Mondi 	if (!ret)
544e0d76c38SJacopo Mondi 		ret = i2c_smbus_write_byte_data(client, VACTIVE_LO,
545e0d76c38SJacopo Mondi 						vact & 0xff);
546e0d76c38SJacopo Mondi 
547e0d76c38SJacopo Mondi 	return ret;
548e0d76c38SJacopo Mondi }
549e0d76c38SJacopo Mondi 
550e0d76c38SJacopo Mondi #ifdef CONFIG_VIDEO_ADV_DEBUG
tw9910_g_register(struct v4l2_subdev * sd,struct v4l2_dbg_register * reg)551e0d76c38SJacopo Mondi static int tw9910_g_register(struct v4l2_subdev *sd,
552e0d76c38SJacopo Mondi 			     struct v4l2_dbg_register *reg)
553e0d76c38SJacopo Mondi {
554e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
555e0d76c38SJacopo Mondi 	int ret;
556e0d76c38SJacopo Mondi 
557e0d76c38SJacopo Mondi 	if (reg->reg > 0xff)
558e0d76c38SJacopo Mondi 		return -EINVAL;
559e0d76c38SJacopo Mondi 
560e0d76c38SJacopo Mondi 	reg->size = 1;
561e0d76c38SJacopo Mondi 	ret = i2c_smbus_read_byte_data(client, reg->reg);
562e0d76c38SJacopo Mondi 	if (ret < 0)
563e0d76c38SJacopo Mondi 		return ret;
564e0d76c38SJacopo Mondi 
565e0d76c38SJacopo Mondi 	/*
566e0d76c38SJacopo Mondi 	 * ret      = int
567e0d76c38SJacopo Mondi 	 * reg->val = __u64
568e0d76c38SJacopo Mondi 	 */
569e0d76c38SJacopo Mondi 	reg->val = (__u64)ret;
570e0d76c38SJacopo Mondi 
571e0d76c38SJacopo Mondi 	return 0;
572e0d76c38SJacopo Mondi }
573e0d76c38SJacopo Mondi 
tw9910_s_register(struct v4l2_subdev * sd,const struct v4l2_dbg_register * reg)574e0d76c38SJacopo Mondi static int tw9910_s_register(struct v4l2_subdev *sd,
575e0d76c38SJacopo Mondi 			     const struct v4l2_dbg_register *reg)
576e0d76c38SJacopo Mondi {
577e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
578e0d76c38SJacopo Mondi 
579e0d76c38SJacopo Mondi 	if (reg->reg > 0xff ||
580e0d76c38SJacopo Mondi 	    reg->val > 0xff)
581e0d76c38SJacopo Mondi 		return -EINVAL;
582e0d76c38SJacopo Mondi 
583e0d76c38SJacopo Mondi 	return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
584e0d76c38SJacopo Mondi }
585e0d76c38SJacopo Mondi #endif
586e0d76c38SJacopo Mondi 
tw9910_set_gpio_value(struct gpio_desc * desc,int value)587cf6a9896SAlexey Khoroshilov static void tw9910_set_gpio_value(struct gpio_desc *desc, int value)
588cf6a9896SAlexey Khoroshilov {
589cf6a9896SAlexey Khoroshilov 	if (desc) {
590cf6a9896SAlexey Khoroshilov 		gpiod_set_value(desc, value);
591cf6a9896SAlexey Khoroshilov 		usleep_range(500, 1000);
592cf6a9896SAlexey Khoroshilov 	}
593cf6a9896SAlexey Khoroshilov }
594cf6a9896SAlexey Khoroshilov 
tw9910_power_on(struct tw9910_priv * priv)5957b20f325SJacopo Mondi static int tw9910_power_on(struct tw9910_priv *priv)
5967b20f325SJacopo Mondi {
5977b20f325SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
5987b20f325SJacopo Mondi 	int ret;
5997b20f325SJacopo Mondi 
6007b20f325SJacopo Mondi 	if (priv->clk) {
6017b20f325SJacopo Mondi 		ret = clk_prepare_enable(priv->clk);
6027b20f325SJacopo Mondi 		if (ret)
6037b20f325SJacopo Mondi 			return ret;
6047b20f325SJacopo Mondi 	}
6057b20f325SJacopo Mondi 
606cf6a9896SAlexey Khoroshilov 	tw9910_set_gpio_value(priv->pdn_gpio, 0);
6077b20f325SJacopo Mondi 
6087b20f325SJacopo Mondi 	/*
6097b20f325SJacopo Mondi 	 * FIXME: The reset signal is connected to a shared GPIO on some
6107b20f325SJacopo Mondi 	 * platforms (namely the SuperH Migo-R). Until a framework becomes
6117b20f325SJacopo Mondi 	 * available to handle this cleanly, request the GPIO temporarily
6127b20f325SJacopo Mondi 	 * to avoid conflicts.
6137b20f325SJacopo Mondi 	 */
6147b20f325SJacopo Mondi 	priv->rstb_gpio = gpiod_get_optional(&client->dev, "rstb",
6157b20f325SJacopo Mondi 					     GPIOD_OUT_LOW);
6167b20f325SJacopo Mondi 	if (IS_ERR(priv->rstb_gpio)) {
6177b20f325SJacopo Mondi 		dev_info(&client->dev, "Unable to get GPIO \"rstb\"");
618d6b10dd0SAlexey Khoroshilov 		clk_disable_unprepare(priv->clk);
619cf6a9896SAlexey Khoroshilov 		tw9910_set_gpio_value(priv->pdn_gpio, 1);
6207b20f325SJacopo Mondi 		return PTR_ERR(priv->rstb_gpio);
6217b20f325SJacopo Mondi 	}
6227b20f325SJacopo Mondi 
6237b20f325SJacopo Mondi 	if (priv->rstb_gpio) {
624cf6a9896SAlexey Khoroshilov 		tw9910_set_gpio_value(priv->rstb_gpio, 1);
625cf6a9896SAlexey Khoroshilov 		tw9910_set_gpio_value(priv->rstb_gpio, 0);
6267b20f325SJacopo Mondi 
6277b20f325SJacopo Mondi 		gpiod_put(priv->rstb_gpio);
6287b20f325SJacopo Mondi 	}
6297b20f325SJacopo Mondi 
6307b20f325SJacopo Mondi 	return 0;
6317b20f325SJacopo Mondi }
6327b20f325SJacopo Mondi 
tw9910_power_off(struct tw9910_priv * priv)6337b20f325SJacopo Mondi static int tw9910_power_off(struct tw9910_priv *priv)
6347b20f325SJacopo Mondi {
6357b20f325SJacopo Mondi 	clk_disable_unprepare(priv->clk);
636cf6a9896SAlexey Khoroshilov 	tw9910_set_gpio_value(priv->pdn_gpio, 1);
6377b20f325SJacopo Mondi 
6387b20f325SJacopo Mondi 	return 0;
6397b20f325SJacopo Mondi }
6407b20f325SJacopo Mondi 
tw9910_s_power(struct v4l2_subdev * sd,int on)641e0d76c38SJacopo Mondi static int tw9910_s_power(struct v4l2_subdev *sd, int on)
642e0d76c38SJacopo Mondi {
643e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
644e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
645e0d76c38SJacopo Mondi 
646d6fdad55SJoe Perches 	return on ? tw9910_power_on(priv) : tw9910_power_off(priv);
647e0d76c38SJacopo Mondi }
648e0d76c38SJacopo Mondi 
tw9910_set_frame(struct v4l2_subdev * sd,u32 * width,u32 * height)649e0d76c38SJacopo Mondi static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
650e0d76c38SJacopo Mondi {
651e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
652e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
653e0d76c38SJacopo Mondi 	int ret = -EINVAL;
654e0d76c38SJacopo Mondi 	u8 val;
655e0d76c38SJacopo Mondi 
65699b010b6SJacopo Mondi 	/* Select suitable norm. */
657e0d76c38SJacopo Mondi 	priv->scale = tw9910_select_norm(priv->norm, *width, *height);
658e0d76c38SJacopo Mondi 	if (!priv->scale)
659e0d76c38SJacopo Mondi 		goto tw9910_set_fmt_error;
660e0d76c38SJacopo Mondi 
66199b010b6SJacopo Mondi 	/* Reset hardware. */
662e0d76c38SJacopo Mondi 	tw9910_reset(client);
663e0d76c38SJacopo Mondi 
66499b010b6SJacopo Mondi 	/* Set bus width. */
665e0d76c38SJacopo Mondi 	val = 0x00;
6667b20f325SJacopo Mondi 	if (priv->info->buswidth == 16)
667e0d76c38SJacopo Mondi 		val = LEN;
668e0d76c38SJacopo Mondi 
669e0d76c38SJacopo Mondi 	ret = tw9910_mask_set(client, OPFORM, LEN, val);
670e0d76c38SJacopo Mondi 	if (ret < 0)
671e0d76c38SJacopo Mondi 		goto tw9910_set_fmt_error;
672e0d76c38SJacopo Mondi 
67399b010b6SJacopo Mondi 	/* Select MPOUT behavior. */
674e0d76c38SJacopo Mondi 	switch (priv->info->mpout) {
675e0d76c38SJacopo Mondi 	case TW9910_MPO_VLOSS:
676e0d76c38SJacopo Mondi 		val = RTSEL_VLOSS; break;
677e0d76c38SJacopo Mondi 	case TW9910_MPO_HLOCK:
678e0d76c38SJacopo Mondi 		val = RTSEL_HLOCK; break;
679e0d76c38SJacopo Mondi 	case TW9910_MPO_SLOCK:
680e0d76c38SJacopo Mondi 		val = RTSEL_SLOCK; break;
681e0d76c38SJacopo Mondi 	case TW9910_MPO_VLOCK:
682e0d76c38SJacopo Mondi 		val = RTSEL_VLOCK; break;
683e0d76c38SJacopo Mondi 	case TW9910_MPO_MONO:
684e0d76c38SJacopo Mondi 		val = RTSEL_MONO;  break;
685e0d76c38SJacopo Mondi 	case TW9910_MPO_DET50:
686e0d76c38SJacopo Mondi 		val = RTSEL_DET50; break;
687e0d76c38SJacopo Mondi 	case TW9910_MPO_FIELD:
688e0d76c38SJacopo Mondi 		val = RTSEL_FIELD; break;
689e0d76c38SJacopo Mondi 	case TW9910_MPO_RTCO:
690e0d76c38SJacopo Mondi 		val = RTSEL_RTCO;  break;
691e0d76c38SJacopo Mondi 	default:
692e0d76c38SJacopo Mondi 		val = 0;
693e0d76c38SJacopo Mondi 	}
694e0d76c38SJacopo Mondi 
695e0d76c38SJacopo Mondi 	ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val);
696e0d76c38SJacopo Mondi 	if (ret < 0)
697e0d76c38SJacopo Mondi 		goto tw9910_set_fmt_error;
698e0d76c38SJacopo Mondi 
69999b010b6SJacopo Mondi 	/* Set scale. */
700e0d76c38SJacopo Mondi 	ret = tw9910_set_scale(client, priv->scale);
701e0d76c38SJacopo Mondi 	if (ret < 0)
702e0d76c38SJacopo Mondi 		goto tw9910_set_fmt_error;
703e0d76c38SJacopo Mondi 
70499b010b6SJacopo Mondi 	/* Set hsync. */
705e0d76c38SJacopo Mondi 	ret = tw9910_set_hsync(client);
706e0d76c38SJacopo Mondi 	if (ret < 0)
707e0d76c38SJacopo Mondi 		goto tw9910_set_fmt_error;
708e0d76c38SJacopo Mondi 
709e0d76c38SJacopo Mondi 	*width = priv->scale->width;
710e0d76c38SJacopo Mondi 	*height = priv->scale->height;
711e0d76c38SJacopo Mondi 
712e0d76c38SJacopo Mondi 	return ret;
713e0d76c38SJacopo Mondi 
714e0d76c38SJacopo Mondi tw9910_set_fmt_error:
715e0d76c38SJacopo Mondi 
716e0d76c38SJacopo Mondi 	tw9910_reset(client);
717e0d76c38SJacopo Mondi 	priv->scale = NULL;
718e0d76c38SJacopo Mondi 
719e0d76c38SJacopo Mondi 	return ret;
720e0d76c38SJacopo Mondi }
721e0d76c38SJacopo Mondi 
tw9910_get_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)722e0d76c38SJacopo Mondi static int tw9910_get_selection(struct v4l2_subdev *sd,
7230d346d2aSTomi Valkeinen 				struct v4l2_subdev_state *sd_state,
724e0d76c38SJacopo Mondi 				struct v4l2_subdev_selection *sel)
725e0d76c38SJacopo Mondi {
726e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
727e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
728e0d76c38SJacopo Mondi 
729e0d76c38SJacopo Mondi 	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
730e0d76c38SJacopo Mondi 		return -EINVAL;
73199b010b6SJacopo Mondi 	/* Only CROP, CROP_DEFAULT and CROP_BOUNDS are supported. */
732e0d76c38SJacopo Mondi 	if (sel->target > V4L2_SEL_TGT_CROP_BOUNDS)
733e0d76c38SJacopo Mondi 		return -EINVAL;
734e0d76c38SJacopo Mondi 
735e0d76c38SJacopo Mondi 	sel->r.left	= 0;
736e0d76c38SJacopo Mondi 	sel->r.top	= 0;
737e0d76c38SJacopo Mondi 	if (priv->norm & V4L2_STD_NTSC) {
738e0d76c38SJacopo Mondi 		sel->r.width	= 640;
739e0d76c38SJacopo Mondi 		sel->r.height	= 480;
740e0d76c38SJacopo Mondi 	} else {
741e0d76c38SJacopo Mondi 		sel->r.width	= 768;
742e0d76c38SJacopo Mondi 		sel->r.height	= 576;
743e0d76c38SJacopo Mondi 	}
744cb5fd12aSJoe Perches 
745e0d76c38SJacopo Mondi 	return 0;
746e0d76c38SJacopo Mondi }
747e0d76c38SJacopo Mondi 
tw9910_get_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)748e0d76c38SJacopo Mondi static int tw9910_get_fmt(struct v4l2_subdev *sd,
7490d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
750e0d76c38SJacopo Mondi 			  struct v4l2_subdev_format *format)
751e0d76c38SJacopo Mondi {
752e0d76c38SJacopo Mondi 	struct v4l2_mbus_framefmt *mf = &format->format;
753e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
754e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
755e0d76c38SJacopo Mondi 
756e0d76c38SJacopo Mondi 	if (format->pad)
757e0d76c38SJacopo Mondi 		return -EINVAL;
758e0d76c38SJacopo Mondi 
759e0d76c38SJacopo Mondi 	if (!priv->scale) {
760e0d76c38SJacopo Mondi 		priv->scale = tw9910_select_norm(priv->norm, 640, 480);
761e0d76c38SJacopo Mondi 		if (!priv->scale)
762e0d76c38SJacopo Mondi 			return -EINVAL;
763e0d76c38SJacopo Mondi 	}
764e0d76c38SJacopo Mondi 
765e0d76c38SJacopo Mondi 	mf->width	= priv->scale->width;
766e0d76c38SJacopo Mondi 	mf->height	= priv->scale->height;
767e0d76c38SJacopo Mondi 	mf->code	= MEDIA_BUS_FMT_UYVY8_2X8;
768e0d76c38SJacopo Mondi 	mf->colorspace	= V4L2_COLORSPACE_SMPTE170M;
769e0d76c38SJacopo Mondi 	mf->field	= V4L2_FIELD_INTERLACED_BT;
770e0d76c38SJacopo Mondi 
771e0d76c38SJacopo Mondi 	return 0;
772e0d76c38SJacopo Mondi }
773e0d76c38SJacopo Mondi 
tw9910_s_fmt(struct v4l2_subdev * sd,struct v4l2_mbus_framefmt * mf)774e0d76c38SJacopo Mondi static int tw9910_s_fmt(struct v4l2_subdev *sd,
775e0d76c38SJacopo Mondi 			struct v4l2_mbus_framefmt *mf)
776e0d76c38SJacopo Mondi {
777e0d76c38SJacopo Mondi 	u32 width = mf->width, height = mf->height;
778e0d76c38SJacopo Mondi 	int ret;
779e0d76c38SJacopo Mondi 
780e0d76c38SJacopo Mondi 	WARN_ON(mf->field != V4L2_FIELD_ANY &&
781e0d76c38SJacopo Mondi 		mf->field != V4L2_FIELD_INTERLACED_BT);
782e0d76c38SJacopo Mondi 
78399b010b6SJacopo Mondi 	/* Check color format. */
784e0d76c38SJacopo Mondi 	if (mf->code != MEDIA_BUS_FMT_UYVY8_2X8)
785e0d76c38SJacopo Mondi 		return -EINVAL;
786e0d76c38SJacopo Mondi 
787e0d76c38SJacopo Mondi 	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
788e0d76c38SJacopo Mondi 
789e0d76c38SJacopo Mondi 	ret = tw9910_set_frame(sd, &width, &height);
790cb5fd12aSJoe Perches 	if (ret)
791cb5fd12aSJoe Perches 		return ret;
792cb5fd12aSJoe Perches 
793e0d76c38SJacopo Mondi 	mf->width	= width;
794e0d76c38SJacopo Mondi 	mf->height	= height;
795cb5fd12aSJoe Perches 
796cb5fd12aSJoe Perches 	return 0;
797e0d76c38SJacopo Mondi }
798e0d76c38SJacopo Mondi 
tw9910_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)799e0d76c38SJacopo Mondi static int tw9910_set_fmt(struct v4l2_subdev *sd,
8000d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
801e0d76c38SJacopo Mondi 			  struct v4l2_subdev_format *format)
802e0d76c38SJacopo Mondi {
803e0d76c38SJacopo Mondi 	struct v4l2_mbus_framefmt *mf = &format->format;
804e0d76c38SJacopo Mondi 	struct i2c_client *client = v4l2_get_subdevdata(sd);
805e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
806e0d76c38SJacopo Mondi 	const struct tw9910_scale_ctrl *scale;
807e0d76c38SJacopo Mondi 
808e0d76c38SJacopo Mondi 	if (format->pad)
809e0d76c38SJacopo Mondi 		return -EINVAL;
810e0d76c38SJacopo Mondi 
811876e32e5SMauro Carvalho Chehab 	if (mf->field == V4L2_FIELD_ANY) {
812e0d76c38SJacopo Mondi 		mf->field = V4L2_FIELD_INTERLACED_BT;
813876e32e5SMauro Carvalho Chehab 	} else if (mf->field != V4L2_FIELD_INTERLACED_BT) {
814cb5fd12aSJoe Perches 		dev_err(&client->dev, "Field type %d invalid\n", mf->field);
815e0d76c38SJacopo Mondi 		return -EINVAL;
816e0d76c38SJacopo Mondi 	}
817e0d76c38SJacopo Mondi 
818e0d76c38SJacopo Mondi 	mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
819e0d76c38SJacopo Mondi 	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
820e0d76c38SJacopo Mondi 
82199b010b6SJacopo Mondi 	/* Select suitable norm. */
822e0d76c38SJacopo Mondi 	scale = tw9910_select_norm(priv->norm, mf->width, mf->height);
823e0d76c38SJacopo Mondi 	if (!scale)
824e0d76c38SJacopo Mondi 		return -EINVAL;
825e0d76c38SJacopo Mondi 
826e0d76c38SJacopo Mondi 	mf->width	= scale->width;
827e0d76c38SJacopo Mondi 	mf->height	= scale->height;
828e0d76c38SJacopo Mondi 
829e0d76c38SJacopo Mondi 	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
830e0d76c38SJacopo Mondi 		return tw9910_s_fmt(sd, mf);
831cb5fd12aSJoe Perches 
8320d346d2aSTomi Valkeinen 	sd_state->pads->try_fmt = *mf;
833cb5fd12aSJoe Perches 
834e0d76c38SJacopo Mondi 	return 0;
835e0d76c38SJacopo Mondi }
836e0d76c38SJacopo Mondi 
tw9910_video_probe(struct i2c_client * client)837e0d76c38SJacopo Mondi static int tw9910_video_probe(struct i2c_client *client)
838e0d76c38SJacopo Mondi {
839e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
840e0d76c38SJacopo Mondi 	s32 id;
841e0d76c38SJacopo Mondi 	int ret;
842e0d76c38SJacopo Mondi 
84399b010b6SJacopo Mondi 	/* TW9910 only use 8 or 16 bit bus width. */
8447b20f325SJacopo Mondi 	if (priv->info->buswidth != 16 && priv->info->buswidth != 8) {
845e0d76c38SJacopo Mondi 		dev_err(&client->dev, "bus width error\n");
846e0d76c38SJacopo Mondi 		return -ENODEV;
847e0d76c38SJacopo Mondi 	}
848e0d76c38SJacopo Mondi 
849e0d76c38SJacopo Mondi 	ret = tw9910_s_power(&priv->subdev, 1);
850e0d76c38SJacopo Mondi 	if (ret < 0)
851e0d76c38SJacopo Mondi 		return ret;
852e0d76c38SJacopo Mondi 
853e0d76c38SJacopo Mondi 	/*
85499b010b6SJacopo Mondi 	 * Check and show Product ID.
85599b010b6SJacopo Mondi 	 * So far only revisions 0 and 1 have been seen.
856e0d76c38SJacopo Mondi 	 */
857e0d76c38SJacopo Mondi 	id = i2c_smbus_read_byte_data(client, ID);
858e0d76c38SJacopo Mondi 	priv->revision = GET_REV(id);
859e0d76c38SJacopo Mondi 	id = GET_ID(id);
860e0d76c38SJacopo Mondi 
861876e32e5SMauro Carvalho Chehab 	if (id != 0x0b || priv->revision > 0x01) {
862cb5fd12aSJoe Perches 		dev_err(&client->dev, "Product ID error %x:%x\n",
863e0d76c38SJacopo Mondi 			id, priv->revision);
864e0d76c38SJacopo Mondi 		ret = -ENODEV;
865e0d76c38SJacopo Mondi 		goto done;
866e0d76c38SJacopo Mondi 	}
867e0d76c38SJacopo Mondi 
868cb5fd12aSJoe Perches 	dev_info(&client->dev, "tw9910 Product ID %0x:%0x\n",
869cb5fd12aSJoe Perches 		 id, priv->revision);
870e0d76c38SJacopo Mondi 
871e0d76c38SJacopo Mondi 	priv->norm = V4L2_STD_NTSC;
872e0d76c38SJacopo Mondi 	priv->scale = &tw9910_ntsc_scales[0];
873e0d76c38SJacopo Mondi 
874e0d76c38SJacopo Mondi done:
875e0d76c38SJacopo Mondi 	tw9910_s_power(&priv->subdev, 0);
876cb5fd12aSJoe Perches 
877e0d76c38SJacopo Mondi 	return ret;
878e0d76c38SJacopo Mondi }
879e0d76c38SJacopo Mondi 
880e0d76c38SJacopo Mondi static const struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
881e0d76c38SJacopo Mondi #ifdef CONFIG_VIDEO_ADV_DEBUG
882e0d76c38SJacopo Mondi 	.g_register	= tw9910_g_register,
883e0d76c38SJacopo Mondi 	.s_register	= tw9910_s_register,
884e0d76c38SJacopo Mondi #endif
885e0d76c38SJacopo Mondi 	.s_power	= tw9910_s_power,
886e0d76c38SJacopo Mondi };
887e0d76c38SJacopo Mondi 
tw9910_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)888e0d76c38SJacopo Mondi static int tw9910_enum_mbus_code(struct v4l2_subdev *sd,
8890d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
890e0d76c38SJacopo Mondi 				 struct v4l2_subdev_mbus_code_enum *code)
891e0d76c38SJacopo Mondi {
892e0d76c38SJacopo Mondi 	if (code->pad || code->index)
893e0d76c38SJacopo Mondi 		return -EINVAL;
894e0d76c38SJacopo Mondi 
895e0d76c38SJacopo Mondi 	code->code = MEDIA_BUS_FMT_UYVY8_2X8;
896cb5fd12aSJoe Perches 
897e0d76c38SJacopo Mondi 	return 0;
898e0d76c38SJacopo Mondi }
899e0d76c38SJacopo Mondi 
tw9910_g_tvnorms(struct v4l2_subdev * sd,v4l2_std_id * norm)900e0d76c38SJacopo Mondi static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
901e0d76c38SJacopo Mondi {
902e0d76c38SJacopo Mondi 	*norm = V4L2_STD_NTSC | V4L2_STD_PAL;
903cb5fd12aSJoe Perches 
904e0d76c38SJacopo Mondi 	return 0;
905e0d76c38SJacopo Mondi }
906e0d76c38SJacopo Mondi 
907e0d76c38SJacopo Mondi static const struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
908e0d76c38SJacopo Mondi 	.s_std		= tw9910_s_std,
909e0d76c38SJacopo Mondi 	.g_std		= tw9910_g_std,
910e0d76c38SJacopo Mondi 	.s_stream	= tw9910_s_stream,
911e0d76c38SJacopo Mondi 	.g_tvnorms	= tw9910_g_tvnorms,
912e0d76c38SJacopo Mondi };
913e0d76c38SJacopo Mondi 
914e0d76c38SJacopo Mondi static const struct v4l2_subdev_pad_ops tw9910_subdev_pad_ops = {
915e0d76c38SJacopo Mondi 	.enum_mbus_code = tw9910_enum_mbus_code,
916e0d76c38SJacopo Mondi 	.get_selection	= tw9910_get_selection,
917e0d76c38SJacopo Mondi 	.get_fmt	= tw9910_get_fmt,
918e0d76c38SJacopo Mondi 	.set_fmt	= tw9910_set_fmt,
919e0d76c38SJacopo Mondi };
920e0d76c38SJacopo Mondi 
921e0d76c38SJacopo Mondi static const struct v4l2_subdev_ops tw9910_subdev_ops = {
922e0d76c38SJacopo Mondi 	.core	= &tw9910_subdev_core_ops,
923e0d76c38SJacopo Mondi 	.video	= &tw9910_subdev_video_ops,
924e0d76c38SJacopo Mondi 	.pad	= &tw9910_subdev_pad_ops,
925e0d76c38SJacopo Mondi };
926e0d76c38SJacopo Mondi 
927e0d76c38SJacopo Mondi /*
928e0d76c38SJacopo Mondi  * i2c_driver function
929e0d76c38SJacopo Mondi  */
930e0d76c38SJacopo Mondi 
tw9910_probe(struct i2c_client * client)931dc90c426SUwe Kleine-König static int tw9910_probe(struct i2c_client *client)
932e0d76c38SJacopo Mondi 
933e0d76c38SJacopo Mondi {
934e0d76c38SJacopo Mondi 	struct tw9910_priv		*priv;
935e0d76c38SJacopo Mondi 	struct tw9910_video_info	*info;
9362a50c83bSWolfram Sang 	struct i2c_adapter		*adapter = client->adapter;
937e0d76c38SJacopo Mondi 	int ret;
938e0d76c38SJacopo Mondi 
9397b20f325SJacopo Mondi 	if (!client->dev.platform_data) {
940e0d76c38SJacopo Mondi 		dev_err(&client->dev, "TW9910: missing platform data!\n");
941e0d76c38SJacopo Mondi 		return -EINVAL;
942e0d76c38SJacopo Mondi 	}
943e0d76c38SJacopo Mondi 
9447b20f325SJacopo Mondi 	info = client->dev.platform_data;
945e0d76c38SJacopo Mondi 
946e0d76c38SJacopo Mondi 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
947e0d76c38SJacopo Mondi 		dev_err(&client->dev,
948e0d76c38SJacopo Mondi 			"I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE_DATA\n");
949e0d76c38SJacopo Mondi 		return -EIO;
950e0d76c38SJacopo Mondi 	}
951e0d76c38SJacopo Mondi 
952e0d76c38SJacopo Mondi 	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
953e0d76c38SJacopo Mondi 	if (!priv)
954e0d76c38SJacopo Mondi 		return -ENOMEM;
955e0d76c38SJacopo Mondi 
956e0d76c38SJacopo Mondi 	priv->info = info;
957e0d76c38SJacopo Mondi 
958e0d76c38SJacopo Mondi 	v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
959e0d76c38SJacopo Mondi 
9607b20f325SJacopo Mondi 	priv->clk = clk_get(&client->dev, "xti");
9617b20f325SJacopo Mondi 	if (PTR_ERR(priv->clk) == -ENOENT) {
9627b20f325SJacopo Mondi 		priv->clk = NULL;
9637b20f325SJacopo Mondi 	} else if (IS_ERR(priv->clk)) {
9647b20f325SJacopo Mondi 		dev_err(&client->dev, "Unable to get xti clock\n");
965e0d76c38SJacopo Mondi 		return PTR_ERR(priv->clk);
9667b20f325SJacopo Mondi 	}
9677b20f325SJacopo Mondi 
9687b20f325SJacopo Mondi 	priv->pdn_gpio = gpiod_get_optional(&client->dev, "pdn",
9697b20f325SJacopo Mondi 					    GPIOD_OUT_HIGH);
9707b20f325SJacopo Mondi 	if (IS_ERR(priv->pdn_gpio)) {
9717b20f325SJacopo Mondi 		dev_info(&client->dev, "Unable to get GPIO \"pdn\"");
9727b20f325SJacopo Mondi 		ret = PTR_ERR(priv->pdn_gpio);
9737b20f325SJacopo Mondi 		goto error_clk_put;
9747b20f325SJacopo Mondi 	}
975e0d76c38SJacopo Mondi 
976e0d76c38SJacopo Mondi 	ret = tw9910_video_probe(client);
977e0d76c38SJacopo Mondi 	if (ret < 0)
9787b20f325SJacopo Mondi 		goto error_gpio_put;
9797b20f325SJacopo Mondi 
9807b20f325SJacopo Mondi 	ret = v4l2_async_register_subdev(&priv->subdev);
9817b20f325SJacopo Mondi 	if (ret)
9827b20f325SJacopo Mondi 		goto error_gpio_put;
9837b20f325SJacopo Mondi 
9847b20f325SJacopo Mondi 	return ret;
9857b20f325SJacopo Mondi 
9867b20f325SJacopo Mondi error_gpio_put:
9877b20f325SJacopo Mondi 	if (priv->pdn_gpio)
9887b20f325SJacopo Mondi 		gpiod_put(priv->pdn_gpio);
9897b20f325SJacopo Mondi error_clk_put:
9907b20f325SJacopo Mondi 	clk_put(priv->clk);
991e0d76c38SJacopo Mondi 
992e0d76c38SJacopo Mondi 	return ret;
993e0d76c38SJacopo Mondi }
994e0d76c38SJacopo Mondi 
tw9910_remove(struct i2c_client * client)995ed5c2f5fSUwe Kleine-König static void tw9910_remove(struct i2c_client *client)
996e0d76c38SJacopo Mondi {
997e0d76c38SJacopo Mondi 	struct tw9910_priv *priv = to_tw9910(client);
9987b20f325SJacopo Mondi 
9997b20f325SJacopo Mondi 	if (priv->pdn_gpio)
10007b20f325SJacopo Mondi 		gpiod_put(priv->pdn_gpio);
10017b20f325SJacopo Mondi 	clk_put(priv->clk);
1002341fe1d3SJacopo Mondi 	v4l2_async_unregister_subdev(&priv->subdev);
1003e0d76c38SJacopo Mondi }
1004e0d76c38SJacopo Mondi 
1005e0d76c38SJacopo Mondi static const struct i2c_device_id tw9910_id[] = {
1006e0d76c38SJacopo Mondi 	{ "tw9910", 0 },
1007e0d76c38SJacopo Mondi 	{ }
1008e0d76c38SJacopo Mondi };
1009e0d76c38SJacopo Mondi MODULE_DEVICE_TABLE(i2c, tw9910_id);
1010e0d76c38SJacopo Mondi 
1011e0d76c38SJacopo Mondi static struct i2c_driver tw9910_i2c_driver = {
1012e0d76c38SJacopo Mondi 	.driver = {
1013e0d76c38SJacopo Mondi 		.name = "tw9910",
1014e0d76c38SJacopo Mondi 	},
1015*aaeb31c0SUwe Kleine-König 	.probe    = tw9910_probe,
1016e0d76c38SJacopo Mondi 	.remove   = tw9910_remove,
1017e0d76c38SJacopo Mondi 	.id_table = tw9910_id,
1018e0d76c38SJacopo Mondi };
1019e0d76c38SJacopo Mondi 
1020e0d76c38SJacopo Mondi module_i2c_driver(tw9910_i2c_driver);
1021e0d76c38SJacopo Mondi 
10227b20f325SJacopo Mondi MODULE_DESCRIPTION("V4L2 driver for TW9910 video decoder");
1023e0d76c38SJacopo Mondi MODULE_AUTHOR("Kuninori Morimoto");
1024e0d76c38SJacopo Mondi MODULE_LICENSE("GPL v2");
1025