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