xref: /OK3568_Linux_fs/kernel/drivers/media/i2c/tw2804.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2005-2006 Micronas USA Inc.
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <linux/module.h>
7*4882a593Smuzhiyun #include <linux/init.h>
8*4882a593Smuzhiyun #include <linux/i2c.h>
9*4882a593Smuzhiyun #include <linux/videodev2.h>
10*4882a593Smuzhiyun #include <linux/ioctl.h>
11*4882a593Smuzhiyun #include <linux/slab.h>
12*4882a593Smuzhiyun #include <media/v4l2-subdev.h>
13*4882a593Smuzhiyun #include <media/v4l2-device.h>
14*4882a593Smuzhiyun #include <media/v4l2-ctrls.h>
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #define TW2804_REG_AUTOGAIN		0x02
17*4882a593Smuzhiyun #define TW2804_REG_HUE			0x0f
18*4882a593Smuzhiyun #define TW2804_REG_SATURATION		0x10
19*4882a593Smuzhiyun #define TW2804_REG_CONTRAST		0x11
20*4882a593Smuzhiyun #define TW2804_REG_BRIGHTNESS		0x12
21*4882a593Smuzhiyun #define TW2804_REG_COLOR_KILLER		0x14
22*4882a593Smuzhiyun #define TW2804_REG_GAIN			0x3c
23*4882a593Smuzhiyun #define TW2804_REG_CHROMA_GAIN		0x3d
24*4882a593Smuzhiyun #define TW2804_REG_BLUE_BALANCE		0x3e
25*4882a593Smuzhiyun #define TW2804_REG_RED_BALANCE		0x3f
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun struct tw2804 {
28*4882a593Smuzhiyun 	struct v4l2_subdev sd;
29*4882a593Smuzhiyun 	struct v4l2_ctrl_handler hdl;
30*4882a593Smuzhiyun 	u8 channel:2;
31*4882a593Smuzhiyun 	u8 input:1;
32*4882a593Smuzhiyun 	int norm;
33*4882a593Smuzhiyun };
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun static const u8 global_registers[] = {
36*4882a593Smuzhiyun 	0x39, 0x00,
37*4882a593Smuzhiyun 	0x3a, 0xff,
38*4882a593Smuzhiyun 	0x3b, 0x84,
39*4882a593Smuzhiyun 	0x3c, 0x80,
40*4882a593Smuzhiyun 	0x3d, 0x80,
41*4882a593Smuzhiyun 	0x3e, 0x82,
42*4882a593Smuzhiyun 	0x3f, 0x82,
43*4882a593Smuzhiyun 	0x78, 0x00,
44*4882a593Smuzhiyun 	0xff, 0xff, /* Terminator (reg 0xff does not exist) */
45*4882a593Smuzhiyun };
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun static const u8 channel_registers[] = {
48*4882a593Smuzhiyun 	0x01, 0xc4,
49*4882a593Smuzhiyun 	0x02, 0xa5,
50*4882a593Smuzhiyun 	0x03, 0x20,
51*4882a593Smuzhiyun 	0x04, 0xd0,
52*4882a593Smuzhiyun 	0x05, 0x20,
53*4882a593Smuzhiyun 	0x06, 0xd0,
54*4882a593Smuzhiyun 	0x07, 0x88,
55*4882a593Smuzhiyun 	0x08, 0x20,
56*4882a593Smuzhiyun 	0x09, 0x07,
57*4882a593Smuzhiyun 	0x0a, 0xf0,
58*4882a593Smuzhiyun 	0x0b, 0x07,
59*4882a593Smuzhiyun 	0x0c, 0xf0,
60*4882a593Smuzhiyun 	0x0d, 0x40,
61*4882a593Smuzhiyun 	0x0e, 0xd2,
62*4882a593Smuzhiyun 	0x0f, 0x80,
63*4882a593Smuzhiyun 	0x10, 0x80,
64*4882a593Smuzhiyun 	0x11, 0x80,
65*4882a593Smuzhiyun 	0x12, 0x80,
66*4882a593Smuzhiyun 	0x13, 0x1f,
67*4882a593Smuzhiyun 	0x14, 0x00,
68*4882a593Smuzhiyun 	0x15, 0x00,
69*4882a593Smuzhiyun 	0x16, 0x00,
70*4882a593Smuzhiyun 	0x17, 0x00,
71*4882a593Smuzhiyun 	0x18, 0xff,
72*4882a593Smuzhiyun 	0x19, 0xff,
73*4882a593Smuzhiyun 	0x1a, 0xff,
74*4882a593Smuzhiyun 	0x1b, 0xff,
75*4882a593Smuzhiyun 	0x1c, 0xff,
76*4882a593Smuzhiyun 	0x1d, 0xff,
77*4882a593Smuzhiyun 	0x1e, 0xff,
78*4882a593Smuzhiyun 	0x1f, 0xff,
79*4882a593Smuzhiyun 	0x20, 0x07,
80*4882a593Smuzhiyun 	0x21, 0x07,
81*4882a593Smuzhiyun 	0x22, 0x00,
82*4882a593Smuzhiyun 	0x23, 0x91,
83*4882a593Smuzhiyun 	0x24, 0x51,
84*4882a593Smuzhiyun 	0x25, 0x03,
85*4882a593Smuzhiyun 	0x26, 0x00,
86*4882a593Smuzhiyun 	0x27, 0x00,
87*4882a593Smuzhiyun 	0x28, 0x00,
88*4882a593Smuzhiyun 	0x29, 0x00,
89*4882a593Smuzhiyun 	0x2a, 0x00,
90*4882a593Smuzhiyun 	0x2b, 0x00,
91*4882a593Smuzhiyun 	0x2c, 0x00,
92*4882a593Smuzhiyun 	0x2d, 0x00,
93*4882a593Smuzhiyun 	0x2e, 0x00,
94*4882a593Smuzhiyun 	0x2f, 0x00,
95*4882a593Smuzhiyun 	0x30, 0x00,
96*4882a593Smuzhiyun 	0x31, 0x00,
97*4882a593Smuzhiyun 	0x32, 0x00,
98*4882a593Smuzhiyun 	0x33, 0x00,
99*4882a593Smuzhiyun 	0x34, 0x00,
100*4882a593Smuzhiyun 	0x35, 0x00,
101*4882a593Smuzhiyun 	0x36, 0x00,
102*4882a593Smuzhiyun 	0x37, 0x00,
103*4882a593Smuzhiyun 	0xff, 0xff, /* Terminator (reg 0xff does not exist) */
104*4882a593Smuzhiyun };
105*4882a593Smuzhiyun 
write_reg(struct i2c_client * client,u8 reg,u8 value,u8 channel)106*4882a593Smuzhiyun static int write_reg(struct i2c_client *client, u8 reg, u8 value, u8 channel)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun 	return i2c_smbus_write_byte_data(client, reg | (channel << 6), value);
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun 
write_regs(struct i2c_client * client,const u8 * regs,u8 channel)111*4882a593Smuzhiyun static int write_regs(struct i2c_client *client, const u8 *regs, u8 channel)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun 	int ret;
114*4882a593Smuzhiyun 	int i;
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	for (i = 0; regs[i] != 0xff; i += 2) {
117*4882a593Smuzhiyun 		ret = i2c_smbus_write_byte_data(client,
118*4882a593Smuzhiyun 				regs[i] | (channel << 6), regs[i + 1]);
119*4882a593Smuzhiyun 		if (ret < 0)
120*4882a593Smuzhiyun 			return ret;
121*4882a593Smuzhiyun 	}
122*4882a593Smuzhiyun 	return 0;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun 
read_reg(struct i2c_client * client,u8 reg,u8 channel)125*4882a593Smuzhiyun static int read_reg(struct i2c_client *client, u8 reg, u8 channel)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun 	return i2c_smbus_read_byte_data(client, (reg) | (channel << 6));
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
to_state(struct v4l2_subdev * sd)130*4882a593Smuzhiyun static inline struct tw2804 *to_state(struct v4l2_subdev *sd)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun 	return container_of(sd, struct tw2804, sd);
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun 
to_state_from_ctrl(struct v4l2_ctrl * ctrl)135*4882a593Smuzhiyun static inline struct tw2804 *to_state_from_ctrl(struct v4l2_ctrl *ctrl)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun 	return container_of(ctrl->handler, struct tw2804, hdl);
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun 
tw2804_log_status(struct v4l2_subdev * sd)140*4882a593Smuzhiyun static int tw2804_log_status(struct v4l2_subdev *sd)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun 	struct tw2804 *state = to_state(sd);
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	v4l2_info(sd, "Standard: %s\n",
145*4882a593Smuzhiyun 			state->norm & V4L2_STD_525_60 ? "60 Hz" : "50 Hz");
146*4882a593Smuzhiyun 	v4l2_info(sd, "Channel: %d\n", state->channel);
147*4882a593Smuzhiyun 	v4l2_info(sd, "Input: %d\n", state->input);
148*4882a593Smuzhiyun 	return v4l2_ctrl_subdev_log_status(sd);
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun /*
152*4882a593Smuzhiyun  * These volatile controls are needed because all four channels share
153*4882a593Smuzhiyun  * these controls. So a change made to them through one channel would
154*4882a593Smuzhiyun  * require another channel to be updated.
155*4882a593Smuzhiyun  *
156*4882a593Smuzhiyun  * Normally this would have been done in a different way, but since the one
157*4882a593Smuzhiyun  * board that uses this driver sees this single chip as if it was on four
158*4882a593Smuzhiyun  * different i2c adapters (each adapter belonging to a separate instance of
159*4882a593Smuzhiyun  * the same USB driver) there is no reliable method that I have found to let
160*4882a593Smuzhiyun  * the instances know about each other.
161*4882a593Smuzhiyun  *
162*4882a593Smuzhiyun  * So implementing these global registers as volatile is the best we can do.
163*4882a593Smuzhiyun  */
tw2804_g_volatile_ctrl(struct v4l2_ctrl * ctrl)164*4882a593Smuzhiyun static int tw2804_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun 	struct tw2804 *state = to_state_from_ctrl(ctrl);
167*4882a593Smuzhiyun 	struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	switch (ctrl->id) {
170*4882a593Smuzhiyun 	case V4L2_CID_GAIN:
171*4882a593Smuzhiyun 		ctrl->val = read_reg(client, TW2804_REG_GAIN, 0);
172*4882a593Smuzhiyun 		return 0;
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 	case V4L2_CID_CHROMA_GAIN:
175*4882a593Smuzhiyun 		ctrl->val = read_reg(client, TW2804_REG_CHROMA_GAIN, 0);
176*4882a593Smuzhiyun 		return 0;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	case V4L2_CID_BLUE_BALANCE:
179*4882a593Smuzhiyun 		ctrl->val = read_reg(client, TW2804_REG_BLUE_BALANCE, 0);
180*4882a593Smuzhiyun 		return 0;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	case V4L2_CID_RED_BALANCE:
183*4882a593Smuzhiyun 		ctrl->val = read_reg(client, TW2804_REG_RED_BALANCE, 0);
184*4882a593Smuzhiyun 		return 0;
185*4882a593Smuzhiyun 	}
186*4882a593Smuzhiyun 	return 0;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun 
tw2804_s_ctrl(struct v4l2_ctrl * ctrl)189*4882a593Smuzhiyun static int tw2804_s_ctrl(struct v4l2_ctrl *ctrl)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun 	struct tw2804 *state = to_state_from_ctrl(ctrl);
192*4882a593Smuzhiyun 	struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
193*4882a593Smuzhiyun 	int addr;
194*4882a593Smuzhiyun 	int reg;
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	switch (ctrl->id) {
197*4882a593Smuzhiyun 	case V4L2_CID_AUTOGAIN:
198*4882a593Smuzhiyun 		addr = TW2804_REG_AUTOGAIN;
199*4882a593Smuzhiyun 		reg = read_reg(client, addr, state->channel);
200*4882a593Smuzhiyun 		if (reg < 0)
201*4882a593Smuzhiyun 			return reg;
202*4882a593Smuzhiyun 		if (ctrl->val == 0)
203*4882a593Smuzhiyun 			reg &= ~(1 << 7);
204*4882a593Smuzhiyun 		else
205*4882a593Smuzhiyun 			reg |= 1 << 7;
206*4882a593Smuzhiyun 		return write_reg(client, addr, reg, state->channel);
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	case V4L2_CID_COLOR_KILLER:
209*4882a593Smuzhiyun 		addr = TW2804_REG_COLOR_KILLER;
210*4882a593Smuzhiyun 		reg = read_reg(client, addr, state->channel);
211*4882a593Smuzhiyun 		if (reg < 0)
212*4882a593Smuzhiyun 			return reg;
213*4882a593Smuzhiyun 		reg = (reg & ~(0x03)) | (ctrl->val == 0 ? 0x02 : 0x03);
214*4882a593Smuzhiyun 		return write_reg(client, addr, reg, state->channel);
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	case V4L2_CID_GAIN:
217*4882a593Smuzhiyun 		return write_reg(client, TW2804_REG_GAIN, ctrl->val, 0);
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	case V4L2_CID_CHROMA_GAIN:
220*4882a593Smuzhiyun 		return write_reg(client, TW2804_REG_CHROMA_GAIN, ctrl->val, 0);
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	case V4L2_CID_BLUE_BALANCE:
223*4882a593Smuzhiyun 		return write_reg(client, TW2804_REG_BLUE_BALANCE, ctrl->val, 0);
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	case V4L2_CID_RED_BALANCE:
226*4882a593Smuzhiyun 		return write_reg(client, TW2804_REG_RED_BALANCE, ctrl->val, 0);
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	case V4L2_CID_BRIGHTNESS:
229*4882a593Smuzhiyun 		return write_reg(client, TW2804_REG_BRIGHTNESS,
230*4882a593Smuzhiyun 				ctrl->val, state->channel);
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	case V4L2_CID_CONTRAST:
233*4882a593Smuzhiyun 		return write_reg(client, TW2804_REG_CONTRAST,
234*4882a593Smuzhiyun 				ctrl->val, state->channel);
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	case V4L2_CID_SATURATION:
237*4882a593Smuzhiyun 		return write_reg(client, TW2804_REG_SATURATION,
238*4882a593Smuzhiyun 				ctrl->val, state->channel);
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	case V4L2_CID_HUE:
241*4882a593Smuzhiyun 		return write_reg(client, TW2804_REG_HUE,
242*4882a593Smuzhiyun 				ctrl->val, state->channel);
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	default:
245*4882a593Smuzhiyun 		break;
246*4882a593Smuzhiyun 	}
247*4882a593Smuzhiyun 	return -EINVAL;
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun 
tw2804_s_std(struct v4l2_subdev * sd,v4l2_std_id norm)250*4882a593Smuzhiyun static int tw2804_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun 	struct tw2804 *dec = to_state(sd);
253*4882a593Smuzhiyun 	struct i2c_client *client = v4l2_get_subdevdata(sd);
254*4882a593Smuzhiyun 	bool is_60hz = norm & V4L2_STD_525_60;
255*4882a593Smuzhiyun 	u8 regs[] = {
256*4882a593Smuzhiyun 		0x01, is_60hz ? 0xc4 : 0x84,
257*4882a593Smuzhiyun 		0x09, is_60hz ? 0x07 : 0x04,
258*4882a593Smuzhiyun 		0x0a, is_60hz ? 0xf0 : 0x20,
259*4882a593Smuzhiyun 		0x0b, is_60hz ? 0x07 : 0x04,
260*4882a593Smuzhiyun 		0x0c, is_60hz ? 0xf0 : 0x20,
261*4882a593Smuzhiyun 		0x0d, is_60hz ? 0x40 : 0x4a,
262*4882a593Smuzhiyun 		0x16, is_60hz ? 0x00 : 0x40,
263*4882a593Smuzhiyun 		0x17, is_60hz ? 0x00 : 0x40,
264*4882a593Smuzhiyun 		0x20, is_60hz ? 0x07 : 0x0f,
265*4882a593Smuzhiyun 		0x21, is_60hz ? 0x07 : 0x0f,
266*4882a593Smuzhiyun 		0xff, 0xff,
267*4882a593Smuzhiyun 	};
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	write_regs(client, regs, dec->channel);
270*4882a593Smuzhiyun 	dec->norm = norm;
271*4882a593Smuzhiyun 	return 0;
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun 
tw2804_s_video_routing(struct v4l2_subdev * sd,u32 input,u32 output,u32 config)274*4882a593Smuzhiyun static int tw2804_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output,
275*4882a593Smuzhiyun 	u32 config)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	struct tw2804 *dec = to_state(sd);
278*4882a593Smuzhiyun 	struct i2c_client *client = v4l2_get_subdevdata(sd);
279*4882a593Smuzhiyun 	int reg;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	if (config && config - 1 != dec->channel) {
282*4882a593Smuzhiyun 		if (config > 4) {
283*4882a593Smuzhiyun 			dev_err(&client->dev,
284*4882a593Smuzhiyun 				"channel %d is not between 1 and 4!\n", config);
285*4882a593Smuzhiyun 			return -EINVAL;
286*4882a593Smuzhiyun 		}
287*4882a593Smuzhiyun 		dec->channel = config - 1;
288*4882a593Smuzhiyun 		dev_dbg(&client->dev, "initializing TW2804 channel %d\n",
289*4882a593Smuzhiyun 			dec->channel);
290*4882a593Smuzhiyun 		if (dec->channel == 0 &&
291*4882a593Smuzhiyun 				write_regs(client, global_registers, 0) < 0) {
292*4882a593Smuzhiyun 			dev_err(&client->dev,
293*4882a593Smuzhiyun 				"error initializing TW2804 global registers\n");
294*4882a593Smuzhiyun 			return -EIO;
295*4882a593Smuzhiyun 		}
296*4882a593Smuzhiyun 		if (write_regs(client, channel_registers, dec->channel) < 0) {
297*4882a593Smuzhiyun 			dev_err(&client->dev,
298*4882a593Smuzhiyun 				"error initializing TW2804 channel %d\n",
299*4882a593Smuzhiyun 				dec->channel);
300*4882a593Smuzhiyun 			return -EIO;
301*4882a593Smuzhiyun 		}
302*4882a593Smuzhiyun 	}
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	if (input > 1)
305*4882a593Smuzhiyun 		return -EINVAL;
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	if (input == dec->input)
308*4882a593Smuzhiyun 		return 0;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	reg = read_reg(client, 0x22, dec->channel);
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	if (reg >= 0) {
313*4882a593Smuzhiyun 		if (input == 0)
314*4882a593Smuzhiyun 			reg &= ~(1 << 2);
315*4882a593Smuzhiyun 		else
316*4882a593Smuzhiyun 			reg |= 1 << 2;
317*4882a593Smuzhiyun 		reg = write_reg(client, 0x22, reg, dec->channel);
318*4882a593Smuzhiyun 	}
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	if (reg >= 0)
321*4882a593Smuzhiyun 		dec->input = input;
322*4882a593Smuzhiyun 	else
323*4882a593Smuzhiyun 		return reg;
324*4882a593Smuzhiyun 	return 0;
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun static const struct v4l2_ctrl_ops tw2804_ctrl_ops = {
328*4882a593Smuzhiyun 	.g_volatile_ctrl = tw2804_g_volatile_ctrl,
329*4882a593Smuzhiyun 	.s_ctrl = tw2804_s_ctrl,
330*4882a593Smuzhiyun };
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun static const struct v4l2_subdev_video_ops tw2804_video_ops = {
333*4882a593Smuzhiyun 	.s_std = tw2804_s_std,
334*4882a593Smuzhiyun 	.s_routing = tw2804_s_video_routing,
335*4882a593Smuzhiyun };
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun static const struct v4l2_subdev_core_ops tw2804_core_ops = {
338*4882a593Smuzhiyun 	.log_status = tw2804_log_status,
339*4882a593Smuzhiyun };
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun static const struct v4l2_subdev_ops tw2804_ops = {
342*4882a593Smuzhiyun 	.core = &tw2804_core_ops,
343*4882a593Smuzhiyun 	.video = &tw2804_video_ops,
344*4882a593Smuzhiyun };
345*4882a593Smuzhiyun 
tw2804_probe(struct i2c_client * client,const struct i2c_device_id * id)346*4882a593Smuzhiyun static int tw2804_probe(struct i2c_client *client,
347*4882a593Smuzhiyun 			    const struct i2c_device_id *id)
348*4882a593Smuzhiyun {
349*4882a593Smuzhiyun 	struct i2c_adapter *adapter = client->adapter;
350*4882a593Smuzhiyun 	struct tw2804 *state;
351*4882a593Smuzhiyun 	struct v4l2_subdev *sd;
352*4882a593Smuzhiyun 	struct v4l2_ctrl *ctrl;
353*4882a593Smuzhiyun 	int err;
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
356*4882a593Smuzhiyun 		return -ENODEV;
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
359*4882a593Smuzhiyun 	if (state == NULL)
360*4882a593Smuzhiyun 		return -ENOMEM;
361*4882a593Smuzhiyun 	sd = &state->sd;
362*4882a593Smuzhiyun 	v4l2_i2c_subdev_init(sd, client, &tw2804_ops);
363*4882a593Smuzhiyun 	state->channel = -1;
364*4882a593Smuzhiyun 	state->norm = V4L2_STD_NTSC;
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	v4l2_ctrl_handler_init(&state->hdl, 10);
367*4882a593Smuzhiyun 	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
368*4882a593Smuzhiyun 				V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
369*4882a593Smuzhiyun 	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
370*4882a593Smuzhiyun 				V4L2_CID_CONTRAST, 0, 255, 1, 128);
371*4882a593Smuzhiyun 	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
372*4882a593Smuzhiyun 				V4L2_CID_SATURATION, 0, 255, 1, 128);
373*4882a593Smuzhiyun 	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
374*4882a593Smuzhiyun 				V4L2_CID_HUE, 0, 255, 1, 128);
375*4882a593Smuzhiyun 	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
376*4882a593Smuzhiyun 				V4L2_CID_COLOR_KILLER, 0, 1, 1, 0);
377*4882a593Smuzhiyun 	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
378*4882a593Smuzhiyun 				V4L2_CID_AUTOGAIN, 0, 1, 1, 0);
379*4882a593Smuzhiyun 	ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
380*4882a593Smuzhiyun 				V4L2_CID_GAIN, 0, 255, 1, 128);
381*4882a593Smuzhiyun 	if (ctrl)
382*4882a593Smuzhiyun 		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
383*4882a593Smuzhiyun 	ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
384*4882a593Smuzhiyun 				V4L2_CID_CHROMA_GAIN, 0, 255, 1, 128);
385*4882a593Smuzhiyun 	if (ctrl)
386*4882a593Smuzhiyun 		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
387*4882a593Smuzhiyun 	ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
388*4882a593Smuzhiyun 				V4L2_CID_BLUE_BALANCE, 0, 255, 1, 122);
389*4882a593Smuzhiyun 	if (ctrl)
390*4882a593Smuzhiyun 		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
391*4882a593Smuzhiyun 	ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
392*4882a593Smuzhiyun 				V4L2_CID_RED_BALANCE, 0, 255, 1, 122);
393*4882a593Smuzhiyun 	if (ctrl)
394*4882a593Smuzhiyun 		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
395*4882a593Smuzhiyun 	sd->ctrl_handler = &state->hdl;
396*4882a593Smuzhiyun 	err = state->hdl.error;
397*4882a593Smuzhiyun 	if (err) {
398*4882a593Smuzhiyun 		v4l2_ctrl_handler_free(&state->hdl);
399*4882a593Smuzhiyun 		return err;
400*4882a593Smuzhiyun 	}
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
403*4882a593Smuzhiyun 			client->addr << 1, client->adapter->name);
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	return 0;
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun 
tw2804_remove(struct i2c_client * client)408*4882a593Smuzhiyun static int tw2804_remove(struct i2c_client *client)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
411*4882a593Smuzhiyun 	struct tw2804 *state = to_state(sd);
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun 	v4l2_device_unregister_subdev(sd);
414*4882a593Smuzhiyun 	v4l2_ctrl_handler_free(&state->hdl);
415*4882a593Smuzhiyun 	return 0;
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun static const struct i2c_device_id tw2804_id[] = {
419*4882a593Smuzhiyun 	{ "tw2804", 0 },
420*4882a593Smuzhiyun 	{ }
421*4882a593Smuzhiyun };
422*4882a593Smuzhiyun MODULE_DEVICE_TABLE(i2c, tw2804_id);
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun static struct i2c_driver tw2804_driver = {
425*4882a593Smuzhiyun 	.driver = {
426*4882a593Smuzhiyun 		.name	= "tw2804",
427*4882a593Smuzhiyun 	},
428*4882a593Smuzhiyun 	.probe		= tw2804_probe,
429*4882a593Smuzhiyun 	.remove		= tw2804_remove,
430*4882a593Smuzhiyun 	.id_table	= tw2804_id,
431*4882a593Smuzhiyun };
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun module_i2c_driver(tw2804_driver);
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
436*4882a593Smuzhiyun MODULE_DESCRIPTION("TW2804/TW2802 V4L2 i2c driver");
437*4882a593Smuzhiyun MODULE_AUTHOR("Micronas USA Inc");
438