Linux 4.16.11
[linux/fpc-iii.git] / drivers / media / i2c / tw2804.c
blobbc8a3ecebffbe4cc24ceaabbbad5dcc7101254c5
1 /*
2 * Copyright (C) 2005-2006 Micronas USA Inc.
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.
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.
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>
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
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;
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) */
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) */
114 static int write_reg(struct i2c_client *client, u8 reg, u8 value, u8 channel)
116 return i2c_smbus_write_byte_data(client, reg | (channel << 6), value);
119 static int write_regs(struct i2c_client *client, const u8 *regs, u8 channel)
121 int ret;
122 int i;
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;
130 return 0;
133 static int read_reg(struct i2c_client *client, u8 reg, u8 channel)
135 return i2c_smbus_read_byte_data(client, (reg) | (channel << 6));
138 static inline struct tw2804 *to_state(struct v4l2_subdev *sd)
140 return container_of(sd, struct tw2804, sd);
143 static inline struct tw2804 *to_state_from_ctrl(struct v4l2_ctrl *ctrl)
145 return container_of(ctrl->handler, struct tw2804, hdl);
148 static int tw2804_log_status(struct v4l2_subdev *sd)
150 struct tw2804 *state = to_state(sd);
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);
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.
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.
170 * So implementing these global registers as volatile is the best we can do.
172 static int tw2804_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
174 struct tw2804 *state = to_state_from_ctrl(ctrl);
175 struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
177 switch (ctrl->id) {
178 case V4L2_CID_GAIN:
179 ctrl->val = read_reg(client, TW2804_REG_GAIN, 0);
180 return 0;
182 case V4L2_CID_CHROMA_GAIN:
183 ctrl->val = read_reg(client, TW2804_REG_CHROMA_GAIN, 0);
184 return 0;
186 case V4L2_CID_BLUE_BALANCE:
187 ctrl->val = read_reg(client, TW2804_REG_BLUE_BALANCE, 0);
188 return 0;
190 case V4L2_CID_RED_BALANCE:
191 ctrl->val = read_reg(client, TW2804_REG_RED_BALANCE, 0);
192 return 0;
194 return 0;
197 static int tw2804_s_ctrl(struct v4l2_ctrl *ctrl)
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;
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);
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);
224 case V4L2_CID_GAIN:
225 return write_reg(client, TW2804_REG_GAIN, ctrl->val, 0);
227 case V4L2_CID_CHROMA_GAIN:
228 return write_reg(client, TW2804_REG_CHROMA_GAIN, ctrl->val, 0);
230 case V4L2_CID_BLUE_BALANCE:
231 return write_reg(client, TW2804_REG_BLUE_BALANCE, ctrl->val, 0);
233 case V4L2_CID_RED_BALANCE:
234 return write_reg(client, TW2804_REG_RED_BALANCE, ctrl->val, 0);
236 case V4L2_CID_BRIGHTNESS:
237 return write_reg(client, TW2804_REG_BRIGHTNESS,
238 ctrl->val, state->channel);
240 case V4L2_CID_CONTRAST:
241 return write_reg(client, TW2804_REG_CONTRAST,
242 ctrl->val, state->channel);
244 case V4L2_CID_SATURATION:
245 return write_reg(client, TW2804_REG_SATURATION,
246 ctrl->val, state->channel);
248 case V4L2_CID_HUE:
249 return write_reg(client, TW2804_REG_HUE,
250 ctrl->val, state->channel);
252 default:
253 break;
255 return -EINVAL;
258 static int tw2804_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
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,
277 write_regs(client, regs, dec->channel);
278 dec->norm = norm;
279 return 0;
282 static int tw2804_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output,
283 u32 config)
285 struct tw2804 *dec = to_state(sd);
286 struct i2c_client *client = v4l2_get_subdevdata(sd);
287 int reg;
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;
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;
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;
312 if (input > 1)
313 return -EINVAL;
315 if (input == dec->input)
316 return 0;
318 reg = read_reg(client, 0x22, dec->channel);
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);
328 if (reg >= 0)
329 dec->input = input;
330 else
331 return reg;
332 return 0;
335 static const struct v4l2_ctrl_ops tw2804_ctrl_ops = {
336 .g_volatile_ctrl = tw2804_g_volatile_ctrl,
337 .s_ctrl = tw2804_s_ctrl,
340 static const struct v4l2_subdev_video_ops tw2804_video_ops = {
341 .s_std = tw2804_s_std,
342 .s_routing = tw2804_s_video_routing,
345 static const struct v4l2_subdev_core_ops tw2804_core_ops = {
346 .log_status = tw2804_log_status,
349 static const struct v4l2_subdev_ops tw2804_ops = {
350 .core = &tw2804_core_ops,
351 .video = &tw2804_video_ops,
354 static int tw2804_probe(struct i2c_client *client,
355 const struct i2c_device_id *id)
357 struct i2c_adapter *adapter = client->adapter;
358 struct tw2804 *state;
359 struct v4l2_subdev *sd;
360 struct v4l2_ctrl *ctrl;
361 int err;
363 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
364 return -ENODEV;
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;
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;
410 v4l_info(client, "chip found @ 0x%02x (%s)\n",
411 client->addr << 1, client->adapter->name);
413 return 0;
416 static int tw2804_remove(struct i2c_client *client)
418 struct v4l2_subdev *sd = i2c_get_clientdata(client);
419 struct tw2804 *state = to_state(sd);
421 v4l2_device_unregister_subdev(sd);
422 v4l2_ctrl_handler_free(&state->hdl);
423 return 0;
426 static const struct i2c_device_id tw2804_id[] = {
427 { "tw2804", 0 },
430 MODULE_DEVICE_TABLE(i2c, tw2804_id);
432 static struct i2c_driver tw2804_driver = {
433 .driver = {
434 .name = "tw2804",
436 .probe = tw2804_probe,
437 .remove = tw2804_remove,
438 .id_table = tw2804_id,
441 module_i2c_driver(tw2804_driver);
443 MODULE_LICENSE("GPL v2");
444 MODULE_DESCRIPTION("TW2804/TW2802 V4L2 i2c driver");
445 MODULE_AUTHOR("Micronas USA Inc");