wis-tw2804.c revision 866b8695d67e83f47194731d3a7ba55826a7ec70
1866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman/*
2866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman * Copyright (C) 2005-2006 Micronas USA Inc.
3866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman *
4866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman * This program is free software; you can redistribute it and/or modify
5866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman * it under the terms of the GNU General Public License (Version 2) as
6866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman * published by the Free Software Foundation.
7866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman *
8866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman * This program is distributed in the hope that it will be useful,
9866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman * but WITHOUT ANY WARRANTY; without even the implied warranty of
10866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman * GNU General Public License for more details.
12866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman *
13866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman * You should have received a copy of the GNU General Public License
14866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman * along with this program; if not, write to the Free Software Foundation,
15866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
16866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman */
17866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
18866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman#include <linux/module.h>
19866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman#include <linux/init.h>
20866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman#include <linux/version.h>
21866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman#include <linux/i2c.h>
22866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman#include <linux/videodev.h>
23866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman#include <linux/video_decoder.h>
24866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman#include <linux/ioctl.h>
25866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
26866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman#include "wis-i2c.h"
27866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
28866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanstruct wis_tw2804 {
29866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	int channel;
30866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	int norm;
31866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	int brightness;
32866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	int contrast;
33866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	int saturation;
34866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	int hue;
35866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman};
36866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
37866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanstatic u8 global_registers[] =
38866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman{
39866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x39, 0x00,
40866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x3a, 0xff,
41866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x3b, 0x84,
42866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x3c, 0x80,
43866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x3d, 0x80,
44866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x3e, 0x82,
45866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x3f, 0x82,
46866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0xff, 0xff, /* Terminator (reg 0xff does not exist) */
47866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman};
48866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
49866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanstatic u8 channel_registers[] =
50866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman{
51866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x01, 0xc4,
52866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x02, 0xa5,
53866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x03, 0x20,
54866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x04, 0xd0,
55866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x05, 0x20,
56866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x06, 0xd0,
57866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x07, 0x88,
58866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x08, 0x20,
59866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x09, 0x07,
60866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x0a, 0xf0,
61866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x0b, 0x07,
62866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x0c, 0xf0,
63866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x0d, 0x40,
64866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x0e, 0xd2,
65866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x0f, 0x80,
66866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x10, 0x80,
67866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x11, 0x80,
68866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x12, 0x80,
69866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x13, 0x1f,
70866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x14, 0x00,
71866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x15, 0x00,
72866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x16, 0x00,
73866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x17, 0x00,
74866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x18, 0xff,
75866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x19, 0xff,
76866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x1a, 0xff,
77866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x1b, 0xff,
78866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x1c, 0xff,
79866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x1d, 0xff,
80866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x1e, 0xff,
81866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x1f, 0xff,
82866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x20, 0x07,
83866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x21, 0x07,
84866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x22, 0x00,
85866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x23, 0x91,
86866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x24, 0x51,
87866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x25, 0x03,
88866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x26, 0x00,
89866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x27, 0x00,
90866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x28, 0x00,
91866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x29, 0x00,
92866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x2a, 0x00,
93866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x2b, 0x00,
94866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x2c, 0x00,
95866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x2d, 0x00,
96866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x2e, 0x00,
97866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x2f, 0x00,
98866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x30, 0x00,
99866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x31, 0x00,
100866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x32, 0x00,
101866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x33, 0x00,
102866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x34, 0x00,
103866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x35, 0x00,
104866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x36, 0x00,
105866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0x37, 0x00,
106866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	0xff, 0xff, /* Terminator (reg 0xff does not exist) */
107866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman};
108866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
109866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanstatic int write_reg(struct i2c_client *client, u8 reg, u8 value, int channel)
110866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman{
111866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	return i2c_smbus_write_byte_data(client, reg | (channel << 6), value);
112866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman}
113866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
114866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanstatic int write_regs(struct i2c_client *client, u8 *regs, int channel)
115866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman{
116866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	int i;
117866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
118866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	for (i = 0; regs[i] != 0xff; i += 2)
119866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		if (i2c_smbus_write_byte_data(client,
120866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				regs[i] | (channel << 6), regs[i + 1]) < 0)
121866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			return -1;
122866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	return 0;
123866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman}
124866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
125866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanstatic int wis_tw2804_command(struct i2c_client *client,
126866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				unsigned int cmd, void *arg)
127866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman{
128866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	struct wis_tw2804 *dec = i2c_get_clientdata(client);
129866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
130866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	if (cmd == DECODER_SET_CHANNEL) {
131866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		int *input = arg;
132866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
133866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		if (*input < 0 || *input > 3) {
134866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			printk(KERN_ERR "wis-tw2804: channel %d is not "
135866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman					"between 0 and 3!\n", *input);
136866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			return 0;
137866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		}
138866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		dec->channel = *input;
139866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		printk(KERN_DEBUG "wis-tw2804: initializing TW2804 "
140866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				"channel %d\n", dec->channel);
141866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		if (dec->channel == 0 &&
142866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				write_regs(client, global_registers, 0) < 0) {
143866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			printk(KERN_ERR "wis-tw2804: error initializing "
144866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman					"TW2804 global registers\n");
145866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			return 0;
146866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		}
147866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		if (write_regs(client, channel_registers, dec->channel) < 0) {
148866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			printk(KERN_ERR "wis-tw2804: error initializing "
149866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman					"TW2804 channel %d\n", dec->channel);
150866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			return 0;
151866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		}
152866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		return 0;
153866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	}
154866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
155866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	if (dec->channel < 0) {
156866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		printk(KERN_DEBUG "wis-tw2804: ignoring command %08x until "
157866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				"channel number is set\n", cmd);
158866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		return 0;
159866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	}
160866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
161866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	switch (cmd) {
162866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	case DECODER_SET_NORM:
163866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	{
164866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		int *input = arg;
165866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		u8 regs[] = {
166866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			0x01, *input == VIDEO_MODE_NTSC ? 0xc4 : 0x84,
167866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			0x09, *input == VIDEO_MODE_NTSC ? 0x07 : 0x04,
168866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			0x0a, *input == VIDEO_MODE_NTSC ? 0xf0 : 0x20,
169866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			0x0b, *input == VIDEO_MODE_NTSC ? 0x07 : 0x04,
170866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			0x0c, *input == VIDEO_MODE_NTSC ? 0xf0 : 0x20,
171866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			0x0d, *input == VIDEO_MODE_NTSC ? 0x40 : 0x4a,
172866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			0x16, *input == VIDEO_MODE_NTSC ? 0x00 : 0x40,
173866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			0x17, *input == VIDEO_MODE_NTSC ? 0x00 : 0x40,
174866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			0x20, *input == VIDEO_MODE_NTSC ? 0x07 : 0x0f,
175866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			0x21, *input == VIDEO_MODE_NTSC ? 0x07 : 0x0f,
176866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			0xff,	0xff,
177866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		};
178866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		write_regs(client, regs, dec->channel);
179866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		dec->norm = *input;
180866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		break;
181866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	}
182866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	case VIDIOC_QUERYCTRL:
183866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	{
184866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		struct v4l2_queryctrl *ctrl = arg;
185866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
186866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		switch (ctrl->id) {
187866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		case V4L2_CID_BRIGHTNESS:
188866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
189866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
190866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->minimum = 0;
191866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->maximum = 255;
192866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->step = 1;
193866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->default_value = 128;
194866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->flags = 0;
195866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			break;
196866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		case V4L2_CID_CONTRAST:
197866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
198866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
199866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->minimum = 0;
200866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->maximum = 255;
201866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->step = 1;
202866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->default_value = 128;
203866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->flags = 0;
204866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			break;
205866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		case V4L2_CID_SATURATION:
206866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
207866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
208866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->minimum = 0;
209866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->maximum = 255;
210866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->step = 1;
211866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->default_value = 128;
212866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->flags = 0;
213866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			break;
214866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		case V4L2_CID_HUE:
215866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
216866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
217866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->minimum = 0;
218866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->maximum = 255;
219866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->step = 1;
220866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->default_value = 128;
221866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->flags = 0;
222866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			break;
223866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		}
224866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		break;
225866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	}
226866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	case VIDIOC_S_CTRL:
227866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	{
228866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		struct v4l2_control *ctrl = arg;
229866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
230866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		switch (ctrl->id) {
231866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		case V4L2_CID_BRIGHTNESS:
232866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			if (ctrl->value > 255)
233866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				dec->brightness = 255;
234866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			else if (ctrl->value < 0)
235866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				dec->brightness = 0;
236866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			else
237866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				dec->brightness = ctrl->value;
238866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			write_reg(client, 0x12, dec->brightness, dec->channel);
239866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			break;
240866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		case V4L2_CID_CONTRAST:
241866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			if (ctrl->value > 255)
242866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				dec->contrast = 255;
243866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			else if (ctrl->value < 0)
244866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				dec->contrast = 0;
245866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			else
246866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				dec->contrast = ctrl->value;
247866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			write_reg(client, 0x11, dec->contrast, dec->channel);
248866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			break;
249866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		case V4L2_CID_SATURATION:
250866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			if (ctrl->value > 255)
251866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				dec->saturation = 255;
252866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			else if (ctrl->value < 0)
253866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				dec->saturation = 0;
254866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			else
255866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				dec->saturation = ctrl->value;
256866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			write_reg(client, 0x10, dec->saturation, dec->channel);
257866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			break;
258866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		case V4L2_CID_HUE:
259866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			if (ctrl->value > 255)
260866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				dec->hue = 255;
261866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			else if (ctrl->value < 0)
262866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				dec->hue = 0;
263866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			else
264866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman				dec->hue = ctrl->value;
265866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			write_reg(client, 0x0f, dec->hue, dec->channel);
266866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			break;
267866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		}
268866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		break;
269866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	}
270866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	case VIDIOC_G_CTRL:
271866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	{
272866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		struct v4l2_control *ctrl = arg;
273866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
274866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		switch (ctrl->id) {
275866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		case V4L2_CID_BRIGHTNESS:
276866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->value = dec->brightness;
277866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			break;
278866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		case V4L2_CID_CONTRAST:
279866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->value = dec->contrast;
280866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			break;
281866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		case V4L2_CID_SATURATION:
282866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->value = dec->saturation;
283866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			break;
284866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		case V4L2_CID_HUE:
285866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			ctrl->value = dec->hue;
286866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			break;
287866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		}
288866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		break;
289866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	}
290866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	default:
291866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		break;
292866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	}
293866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	return 0;
294866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman}
295866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
296866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanstatic struct i2c_driver wis_tw2804_driver;
297866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
298866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanstatic struct i2c_client wis_tw2804_client_templ = {
299866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	.name		= "TW2804 (WIS)",
300866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	.driver		= &wis_tw2804_driver,
301866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman};
302866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
303866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanstatic int wis_tw2804_detect(struct i2c_adapter *adapter, int addr, int kind)
304866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman{
305866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	struct i2c_client *client;
306866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	struct wis_tw2804 *dec;
307866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
308866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
309866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		return 0;
310866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
311866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
312866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	if (client == NULL)
313866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		return -ENOMEM;
314866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	memcpy(client, &wis_tw2804_client_templ,
315866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman			sizeof(wis_tw2804_client_templ));
316866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	client->adapter = adapter;
317866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	client->addr = addr;
318866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
319866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	dec = kmalloc(sizeof(struct wis_tw2804), GFP_KERNEL);
320866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	if (dec == NULL) {
321866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		kfree(client);
322866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		return -ENOMEM;
323866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	}
324866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	dec->channel = -1;
325866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	dec->norm = VIDEO_MODE_NTSC;
326866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	dec->brightness = 128;
327866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	dec->contrast = 128;
328866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	dec->saturation = 128;
329866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	dec->hue = 128;
330866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	i2c_set_clientdata(client, dec);
331866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
332866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n",
333866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		addr, adapter->name);
334866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
335866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	i2c_attach_client(client);
336866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	return 0;
337866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman}
338866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
339866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanstatic int wis_tw2804_detach(struct i2c_client *client)
340866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman{
341866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	struct wis_tw2804 *dec = i2c_get_clientdata(client);
342866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	int r;
343866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
344866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	r = i2c_detach_client(client);
345866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	if (r < 0)
346866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		return r;
347866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
348866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	kfree(client);
349866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	kfree(dec);
350866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	return 0;
351866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman}
352866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
353866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanstatic struct i2c_driver wis_tw2804_driver = {
354866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	.driver = {
355866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		.name	= "WIS TW2804 I2C driver",
356866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	},
357866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	.id		= I2C_DRIVERID_WIS_TW2804,
358866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	.detach_client	= wis_tw2804_detach,
359866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	.command	= wis_tw2804_command,
360866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman};
361866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
362866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanstatic int __init wis_tw2804_init(void)
363866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman{
364866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	int r;
365866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
366866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	r = i2c_add_driver(&wis_tw2804_driver);
367866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	if (r < 0)
368866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman		return r;
369866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	return wis_i2c_add_driver(wis_tw2804_driver.id, wis_tw2804_detect);
370866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman}
371866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
372866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanstatic void __exit wis_tw2804_cleanup(void)
373866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman{
374866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	wis_i2c_del_driver(wis_tw2804_detect);
375866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman	i2c_del_driver(&wis_tw2804_driver);
376866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman}
377866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
378866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanmodule_init(wis_tw2804_init);
379866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartmanmodule_exit(wis_tw2804_cleanup);
380866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-Hartman
381866b8695d67e83f47194731d3a7ba55826a7ec70Greg Kroah-HartmanMODULE_LICENSE("GPL v2");
382