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-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,
55	0x78, 0x00,
56	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
339static 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_std = tw2804_s_std,
346	.s_routing = tw2804_s_video_routing,
347};
348
349static const struct v4l2_subdev_core_ops tw2804_core_ops = {
350	.log_status = tw2804_log_status,
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
370	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
371	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);
411		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);
427	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");
450