Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.9.4.
  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
 25#include "wis-i2c.h"
 26
 27struct wis_tw2804 {
 28	int channel;
 29	int norm;
 30	int brightness;
 31	int contrast;
 32	int saturation;
 33	int hue;
 34};
 35
 36static u8 global_registers[] =
 37{
 38	0x39, 0x00,
 39	0x3a, 0xff,
 40	0x3b, 0x84,
 41	0x3c, 0x80,
 42	0x3d, 0x80,
 43	0x3e, 0x82,
 44	0x3f, 0x82,
 45	0xff, 0xff, /* Terminator (reg 0xff does not exist) */
 46};
 47
 48static u8 channel_registers[] =
 49{
 50	0x01, 0xc4,
 51	0x02, 0xa5,
 52	0x03, 0x20,
 53	0x04, 0xd0,
 54	0x05, 0x20,
 55	0x06, 0xd0,
 56	0x07, 0x88,
 57	0x08, 0x20,
 58	0x09, 0x07,
 59	0x0a, 0xf0,
 60	0x0b, 0x07,
 61	0x0c, 0xf0,
 62	0x0d, 0x40,
 63	0x0e, 0xd2,
 64	0x0f, 0x80,
 65	0x10, 0x80,
 66	0x11, 0x80,
 67	0x12, 0x80,
 68	0x13, 0x1f,
 69	0x14, 0x00,
 70	0x15, 0x00,
 71	0x16, 0x00,
 72	0x17, 0x00,
 73	0x18, 0xff,
 74	0x19, 0xff,
 75	0x1a, 0xff,
 76	0x1b, 0xff,
 77	0x1c, 0xff,
 78	0x1d, 0xff,
 79	0x1e, 0xff,
 80	0x1f, 0xff,
 81	0x20, 0x07,
 82	0x21, 0x07,
 83	0x22, 0x00,
 84	0x23, 0x91,
 85	0x24, 0x51,
 86	0x25, 0x03,
 87	0x26, 0x00,
 88	0x27, 0x00,
 89	0x28, 0x00,
 90	0x29, 0x00,
 91	0x2a, 0x00,
 92	0x2b, 0x00,
 93	0x2c, 0x00,
 94	0x2d, 0x00,
 95	0x2e, 0x00,
 96	0x2f, 0x00,
 97	0x30, 0x00,
 98	0x31, 0x00,
 99	0x32, 0x00,
100	0x33, 0x00,
101	0x34, 0x00,
102	0x35, 0x00,
103	0x36, 0x00,
104	0x37, 0x00,
105	0xff, 0xff, /* Terminator (reg 0xff does not exist) */
106};
107
108static int write_reg(struct i2c_client *client, u8 reg, u8 value, int channel)
109{
110	return i2c_smbus_write_byte_data(client, reg | (channel << 6), value);
111}
112
113static int write_regs(struct i2c_client *client, u8 *regs, int channel)
114{
115	int i;
116
117	for (i = 0; regs[i] != 0xff; i += 2)
118		if (i2c_smbus_write_byte_data(client,
119				regs[i] | (channel << 6), regs[i + 1]) < 0)
120			return -1;
121	return 0;
122}
123
124static int wis_tw2804_command(struct i2c_client *client,
125				unsigned int cmd, void *arg)
126{
127	struct wis_tw2804 *dec = i2c_get_clientdata(client);
128
129	if (cmd == DECODER_SET_CHANNEL) {
130		int *input = arg;
131
132		if (*input < 0 || *input > 3) {
133			printk(KERN_ERR "wis-tw2804: channel %d is not "
134					"between 0 and 3!\n", *input);
135			return 0;
136		}
137		dec->channel = *input;
138		printk(KERN_DEBUG "wis-tw2804: initializing TW2804 "
139				"channel %d\n", dec->channel);
140		if (dec->channel == 0 &&
141				write_regs(client, global_registers, 0) < 0) {
142			printk(KERN_ERR "wis-tw2804: error initializing "
143					"TW2804 global registers\n");
144			return 0;
145		}
146		if (write_regs(client, channel_registers, dec->channel) < 0) {
147			printk(KERN_ERR "wis-tw2804: error initializing "
148					"TW2804 channel %d\n", dec->channel);
149			return 0;
150		}
151		return 0;
152	}
153
154	if (dec->channel < 0) {
155		printk(KERN_DEBUG "wis-tw2804: ignoring command %08x until "
156				"channel number is set\n", cmd);
157		return 0;
158	}
159
160	switch (cmd) {
161	case VIDIOC_S_STD:
162	{
163		v4l2_std_id *input = arg;
164		u8 regs[] = {
165			0x01, *input & V4L2_STD_NTSC ? 0xc4 : 0x84,
166			0x09, *input & V4L2_STD_NTSC ? 0x07 : 0x04,
167			0x0a, *input & V4L2_STD_NTSC ? 0xf0 : 0x20,
168			0x0b, *input & V4L2_STD_NTSC ? 0x07 : 0x04,
169			0x0c, *input & V4L2_STD_NTSC ? 0xf0 : 0x20,
170			0x0d, *input & V4L2_STD_NTSC ? 0x40 : 0x4a,
171			0x16, *input & V4L2_STD_NTSC ? 0x00 : 0x40,
172			0x17, *input & V4L2_STD_NTSC ? 0x00 : 0x40,
173			0x20, *input & V4L2_STD_NTSC ? 0x07 : 0x0f,
174			0x21, *input & V4L2_STD_NTSC ? 0x07 : 0x0f,
175			0xff,	0xff,
176		};
177		write_regs(client, regs, dec->channel);
178		dec->norm = *input;
179		break;
180	}
181	case VIDIOC_QUERYCTRL:
182	{
183		struct v4l2_queryctrl *ctrl = arg;
184
185		switch (ctrl->id) {
186		case V4L2_CID_BRIGHTNESS:
187			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
188			strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
189			ctrl->minimum = 0;
190			ctrl->maximum = 255;
191			ctrl->step = 1;
192			ctrl->default_value = 128;
193			ctrl->flags = 0;
194			break;
195		case V4L2_CID_CONTRAST:
196			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
197			strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
198			ctrl->minimum = 0;
199			ctrl->maximum = 255;
200			ctrl->step = 1;
201			ctrl->default_value = 128;
202			ctrl->flags = 0;
203			break;
204		case V4L2_CID_SATURATION:
205			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
206			strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
207			ctrl->minimum = 0;
208			ctrl->maximum = 255;
209			ctrl->step = 1;
210			ctrl->default_value = 128;
211			ctrl->flags = 0;
212			break;
213		case V4L2_CID_HUE:
214			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
215			strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
216			ctrl->minimum = 0;
217			ctrl->maximum = 255;
218			ctrl->step = 1;
219			ctrl->default_value = 128;
220			ctrl->flags = 0;
221			break;
222		}
223		break;
224	}
225	case VIDIOC_S_CTRL:
226	{
227		struct v4l2_control *ctrl = arg;
228
229		switch (ctrl->id) {
230		case V4L2_CID_BRIGHTNESS:
231			if (ctrl->value > 255)
232				dec->brightness = 255;
233			else if (ctrl->value < 0)
234				dec->brightness = 0;
235			else
236				dec->brightness = ctrl->value;
237			write_reg(client, 0x12, dec->brightness, dec->channel);
238			break;
239		case V4L2_CID_CONTRAST:
240			if (ctrl->value > 255)
241				dec->contrast = 255;
242			else if (ctrl->value < 0)
243				dec->contrast = 0;
244			else
245				dec->contrast = ctrl->value;
246			write_reg(client, 0x11, dec->contrast, dec->channel);
247			break;
248		case V4L2_CID_SATURATION:
249			if (ctrl->value > 255)
250				dec->saturation = 255;
251			else if (ctrl->value < 0)
252				dec->saturation = 0;
253			else
254				dec->saturation = ctrl->value;
255			write_reg(client, 0x10, dec->saturation, dec->channel);
256			break;
257		case V4L2_CID_HUE:
258			if (ctrl->value > 255)
259				dec->hue = 255;
260			else if (ctrl->value < 0)
261				dec->hue = 0;
262			else
263				dec->hue = ctrl->value;
264			write_reg(client, 0x0f, dec->hue, dec->channel);
265			break;
266		}
267		break;
268	}
269	case VIDIOC_G_CTRL:
270	{
271		struct v4l2_control *ctrl = arg;
272
273		switch (ctrl->id) {
274		case V4L2_CID_BRIGHTNESS:
275			ctrl->value = dec->brightness;
276			break;
277		case V4L2_CID_CONTRAST:
278			ctrl->value = dec->contrast;
279			break;
280		case V4L2_CID_SATURATION:
281			ctrl->value = dec->saturation;
282			break;
283		case V4L2_CID_HUE:
284			ctrl->value = dec->hue;
285			break;
286		}
287		break;
288	}
289	default:
290		break;
291	}
292	return 0;
293}
294
295static int wis_tw2804_probe(struct i2c_client *client,
296			    const struct i2c_device_id *id)
297{
298	struct i2c_adapter *adapter = client->adapter;
299	struct wis_tw2804 *dec;
300
301	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
302		return -ENODEV;
303
304	dec = kmalloc(sizeof(struct wis_tw2804), GFP_KERNEL);
305	if (dec == NULL)
306		return -ENOMEM;
307
308	dec->channel = -1;
309	dec->norm = V4L2_STD_NTSC;
310	dec->brightness = 128;
311	dec->contrast = 128;
312	dec->saturation = 128;
313	dec->hue = 128;
314	i2c_set_clientdata(client, dec);
315
316	printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n",
317		client->addr, adapter->name);
318
319	return 0;
320}
321
322static int wis_tw2804_remove(struct i2c_client *client)
323{
324	struct wis_tw2804 *dec = i2c_get_clientdata(client);
325
326	kfree(dec);
327	return 0;
328}
329
330static const struct i2c_device_id wis_tw2804_id[] = {
331	{ "wis_tw2804", 0 },
332	{ }
333};
334MODULE_DEVICE_TABLE(i2c, wis_tw2804_id);
335
336static struct i2c_driver wis_tw2804_driver = {
337	.driver = {
338		.name	= "WIS TW2804 I2C driver",
339	},
340	.probe		= wis_tw2804_probe,
341	.remove		= wis_tw2804_remove,
342	.command	= wis_tw2804_command,
343	.id_table	= wis_tw2804_id,
344};
345
346static int __init wis_tw2804_init(void)
347{
348	return i2c_add_driver(&wis_tw2804_driver);
349}
350
351static void __exit wis_tw2804_cleanup(void)
352{
353	i2c_del_driver(&wis_tw2804_driver);
354}
355
356module_init(wis_tw2804_init);
357module_exit(wis_tw2804_cleanup);
358
359MODULE_LICENSE("GPL v2");