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