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