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