xref: /openbmc/linux/drivers/media/i2c/bt819.c (revision aaeb31c0)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2cb7a01acSMauro Carvalho Chehab /*
3cb7a01acSMauro Carvalho Chehab  *  bt819 - BT819A VideoStream Decoder (Rockwell Part)
4cb7a01acSMauro Carvalho Chehab  *
5cb7a01acSMauro Carvalho Chehab  * Copyright (C) 1999 Mike Bernson <mike@mlb.org>
6cb7a01acSMauro Carvalho Chehab  * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
7cb7a01acSMauro Carvalho Chehab  *
8cb7a01acSMauro Carvalho Chehab  * Modifications for LML33/DC10plus unified driver
9cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
10cb7a01acSMauro Carvalho Chehab  *
11cb7a01acSMauro Carvalho Chehab  * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
12cb7a01acSMauro Carvalho Chehab  *    - moved over to linux>=2.4.x i2c protocol (9/9/2002)
13cb7a01acSMauro Carvalho Chehab  *
14cb7a01acSMauro Carvalho Chehab  * This code was modify/ported from the saa7111 driver written
15cb7a01acSMauro Carvalho Chehab  * by Dave Perks.
16cb7a01acSMauro Carvalho Chehab  */
17cb7a01acSMauro Carvalho Chehab 
18cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
19cb7a01acSMauro Carvalho Chehab #include <linux/types.h>
20cb7a01acSMauro Carvalho Chehab #include <linux/ioctl.h>
21cb7a01acSMauro Carvalho Chehab #include <linux/delay.h>
22cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
23cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
24cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
25cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
26cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
27b5dcee22SMauro Carvalho Chehab #include <media/i2c/bt819.h>
28cb7a01acSMauro Carvalho Chehab 
29cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Brooktree-819 video decoder driver");
30cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Mike Bernson & Dave Perks");
31cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
32cb7a01acSMauro Carvalho Chehab 
33cb7a01acSMauro Carvalho Chehab static int debug;
34cb7a01acSMauro Carvalho Chehab module_param(debug, int, 0);
35cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)");
36cb7a01acSMauro Carvalho Chehab 
37cb7a01acSMauro Carvalho Chehab 
38cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
39cb7a01acSMauro Carvalho Chehab 
40cb7a01acSMauro Carvalho Chehab struct bt819 {
41cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
42cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler hdl;
43cb7a01acSMauro Carvalho Chehab 	unsigned char reg[32];
44cb7a01acSMauro Carvalho Chehab 
45cb7a01acSMauro Carvalho Chehab 	v4l2_std_id norm;
46cb7a01acSMauro Carvalho Chehab 	int input;
47cb7a01acSMauro Carvalho Chehab 	int enable;
48cb7a01acSMauro Carvalho Chehab };
49cb7a01acSMauro Carvalho Chehab 
to_bt819(struct v4l2_subdev * sd)50cb7a01acSMauro Carvalho Chehab static inline struct bt819 *to_bt819(struct v4l2_subdev *sd)
51cb7a01acSMauro Carvalho Chehab {
52cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct bt819, sd);
53cb7a01acSMauro Carvalho Chehab }
54cb7a01acSMauro Carvalho Chehab 
to_sd(struct v4l2_ctrl * ctrl)55cb7a01acSMauro Carvalho Chehab static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
56cb7a01acSMauro Carvalho Chehab {
57cb7a01acSMauro Carvalho Chehab 	return &container_of(ctrl->handler, struct bt819, hdl)->sd;
58cb7a01acSMauro Carvalho Chehab }
59cb7a01acSMauro Carvalho Chehab 
60cb7a01acSMauro Carvalho Chehab struct timing {
61cb7a01acSMauro Carvalho Chehab 	int hactive;
62cb7a01acSMauro Carvalho Chehab 	int hdelay;
63cb7a01acSMauro Carvalho Chehab 	int vactive;
64cb7a01acSMauro Carvalho Chehab 	int vdelay;
65cb7a01acSMauro Carvalho Chehab 	int hscale;
66cb7a01acSMauro Carvalho Chehab 	int vscale;
67cb7a01acSMauro Carvalho Chehab };
68cb7a01acSMauro Carvalho Chehab 
69cb7a01acSMauro Carvalho Chehab /* for values, see the bt819 datasheet */
70cb7a01acSMauro Carvalho Chehab static struct timing timing_data[] = {
71cb7a01acSMauro Carvalho Chehab 	{864 - 24, 20, 625 - 2, 1, 0x0504, 0x0000},
72cb7a01acSMauro Carvalho Chehab 	{858 - 24, 20, 525 - 2, 1, 0x00f8, 0x0000},
73cb7a01acSMauro Carvalho Chehab };
74cb7a01acSMauro Carvalho Chehab 
75cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
76cb7a01acSMauro Carvalho Chehab 
bt819_write(struct bt819 * decoder,u8 reg,u8 value)77cb7a01acSMauro Carvalho Chehab static inline int bt819_write(struct bt819 *decoder, u8 reg, u8 value)
78cb7a01acSMauro Carvalho Chehab {
79cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd);
80cb7a01acSMauro Carvalho Chehab 
81cb7a01acSMauro Carvalho Chehab 	decoder->reg[reg] = value;
82cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_write_byte_data(client, reg, value);
83cb7a01acSMauro Carvalho Chehab }
84cb7a01acSMauro Carvalho Chehab 
bt819_setbit(struct bt819 * decoder,u8 reg,u8 bit,u8 value)85cb7a01acSMauro Carvalho Chehab static inline int bt819_setbit(struct bt819 *decoder, u8 reg, u8 bit, u8 value)
86cb7a01acSMauro Carvalho Chehab {
87cb7a01acSMauro Carvalho Chehab 	return bt819_write(decoder, reg,
88cb7a01acSMauro Carvalho Chehab 		(decoder->reg[reg] & ~(1 << bit)) | (value ? (1 << bit) : 0));
89cb7a01acSMauro Carvalho Chehab }
90cb7a01acSMauro Carvalho Chehab 
bt819_write_block(struct bt819 * decoder,const u8 * data,unsigned int len)91cb7a01acSMauro Carvalho Chehab static int bt819_write_block(struct bt819 *decoder, const u8 *data, unsigned int len)
92cb7a01acSMauro Carvalho Chehab {
93cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd);
94cb7a01acSMauro Carvalho Chehab 	int ret = -1;
95cb7a01acSMauro Carvalho Chehab 	u8 reg;
96cb7a01acSMauro Carvalho Chehab 
97cb7a01acSMauro Carvalho Chehab 	/* the bt819 has an autoincrement function, use it if
98cb7a01acSMauro Carvalho Chehab 	 * the adapter understands raw I2C */
99cb7a01acSMauro Carvalho Chehab 	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
100cb7a01acSMauro Carvalho Chehab 		/* do raw I2C, not smbus compatible */
101cb7a01acSMauro Carvalho Chehab 		u8 block_data[32];
102cb7a01acSMauro Carvalho Chehab 		int block_len;
103cb7a01acSMauro Carvalho Chehab 
104cb7a01acSMauro Carvalho Chehab 		while (len >= 2) {
105cb7a01acSMauro Carvalho Chehab 			block_len = 0;
106cb7a01acSMauro Carvalho Chehab 			block_data[block_len++] = reg = data[0];
107cb7a01acSMauro Carvalho Chehab 			do {
108cb7a01acSMauro Carvalho Chehab 				block_data[block_len++] =
109cb7a01acSMauro Carvalho Chehab 				    decoder->reg[reg++] = data[1];
110cb7a01acSMauro Carvalho Chehab 				len -= 2;
111cb7a01acSMauro Carvalho Chehab 				data += 2;
112cb7a01acSMauro Carvalho Chehab 			} while (len >= 2 && data[0] == reg && block_len < 32);
113cb7a01acSMauro Carvalho Chehab 			ret = i2c_master_send(client, block_data, block_len);
114cb7a01acSMauro Carvalho Chehab 			if (ret < 0)
115cb7a01acSMauro Carvalho Chehab 				break;
116cb7a01acSMauro Carvalho Chehab 		}
117cb7a01acSMauro Carvalho Chehab 	} else {
118cb7a01acSMauro Carvalho Chehab 		/* do some slow I2C emulation kind of thing */
119cb7a01acSMauro Carvalho Chehab 		while (len >= 2) {
120cb7a01acSMauro Carvalho Chehab 			reg = *data++;
121cb7a01acSMauro Carvalho Chehab 			ret = bt819_write(decoder, reg, *data++);
122cb7a01acSMauro Carvalho Chehab 			if (ret < 0)
123cb7a01acSMauro Carvalho Chehab 				break;
124cb7a01acSMauro Carvalho Chehab 			len -= 2;
125cb7a01acSMauro Carvalho Chehab 		}
126cb7a01acSMauro Carvalho Chehab 	}
127cb7a01acSMauro Carvalho Chehab 
128cb7a01acSMauro Carvalho Chehab 	return ret;
129cb7a01acSMauro Carvalho Chehab }
130cb7a01acSMauro Carvalho Chehab 
bt819_read(struct bt819 * decoder,u8 reg)131cb7a01acSMauro Carvalho Chehab static inline int bt819_read(struct bt819 *decoder, u8 reg)
132cb7a01acSMauro Carvalho Chehab {
133cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd);
134cb7a01acSMauro Carvalho Chehab 
135cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_read_byte_data(client, reg);
136cb7a01acSMauro Carvalho Chehab }
137cb7a01acSMauro Carvalho Chehab 
bt819_init(struct v4l2_subdev * sd)138cb7a01acSMauro Carvalho Chehab static int bt819_init(struct v4l2_subdev *sd)
139cb7a01acSMauro Carvalho Chehab {
140cb7a01acSMauro Carvalho Chehab 	static unsigned char init[] = {
141cb7a01acSMauro Carvalho Chehab 		/*0x1f, 0x00,*/     /* Reset */
142cb7a01acSMauro Carvalho Chehab 		0x01, 0x59,	/* 0x01 input format */
143cb7a01acSMauro Carvalho Chehab 		0x02, 0x00,	/* 0x02 temporal decimation */
144cb7a01acSMauro Carvalho Chehab 		0x03, 0x12,	/* 0x03 Cropping msb */
145cb7a01acSMauro Carvalho Chehab 		0x04, 0x16,	/* 0x04 Vertical Delay, lsb */
146cb7a01acSMauro Carvalho Chehab 		0x05, 0xe0,	/* 0x05 Vertical Active lsb */
147cb7a01acSMauro Carvalho Chehab 		0x06, 0x80,	/* 0x06 Horizontal Delay lsb */
148cb7a01acSMauro Carvalho Chehab 		0x07, 0xd0,	/* 0x07 Horizontal Active lsb */
149cb7a01acSMauro Carvalho Chehab 		0x08, 0x00,	/* 0x08 Horizontal Scaling msb */
150cb7a01acSMauro Carvalho Chehab 		0x09, 0xf8,	/* 0x09 Horizontal Scaling lsb */
151cb7a01acSMauro Carvalho Chehab 		0x0a, 0x00,	/* 0x0a Brightness control */
152cb7a01acSMauro Carvalho Chehab 		0x0b, 0x30,	/* 0x0b Miscellaneous control */
153cb7a01acSMauro Carvalho Chehab 		0x0c, 0xd8,	/* 0x0c Luma Gain lsb */
154cb7a01acSMauro Carvalho Chehab 		0x0d, 0xfe,	/* 0x0d Chroma Gain (U) lsb */
155cb7a01acSMauro Carvalho Chehab 		0x0e, 0xb4,	/* 0x0e Chroma Gain (V) msb */
156cb7a01acSMauro Carvalho Chehab 		0x0f, 0x00,	/* 0x0f Hue control */
157cb7a01acSMauro Carvalho Chehab 		0x12, 0x04,	/* 0x12 Output Format */
158f8a7647dSMauro Carvalho Chehab 		0x13, 0x20,	/* 0x13 Vertical Scaling msb 0x00
159cb7a01acSMauro Carvalho Chehab 					   chroma comb OFF, line drop scaling, interlace scaling
160fa7662aaSAndy Shevchenko 					   BUG? Why does turning the chroma comb on screw up color?
161cb7a01acSMauro Carvalho Chehab 					   Bug in the bt819 stepping on my board?
162cb7a01acSMauro Carvalho Chehab 					*/
163f8a7647dSMauro Carvalho Chehab 		0x14, 0x00,	/* 0x14 Vertical Scaling lsb */
164cb7a01acSMauro Carvalho Chehab 		0x16, 0x07,	/* 0x16 Video Timing Polarity
165cb7a01acSMauro Carvalho Chehab 					   ACTIVE=active low
166cb7a01acSMauro Carvalho Chehab 					   FIELD: high=odd,
167cb7a01acSMauro Carvalho Chehab 					   vreset=active high,
168cb7a01acSMauro Carvalho Chehab 					   hreset=active high */
169cb7a01acSMauro Carvalho Chehab 		0x18, 0x68,	/* 0x18 AGC Delay */
170cb7a01acSMauro Carvalho Chehab 		0x19, 0x5d,	/* 0x19 Burst Gate Delay */
171cb7a01acSMauro Carvalho Chehab 		0x1a, 0x80,	/* 0x1a ADC Interface */
172cb7a01acSMauro Carvalho Chehab 	};
173cb7a01acSMauro Carvalho Chehab 
174cb7a01acSMauro Carvalho Chehab 	struct bt819 *decoder = to_bt819(sd);
175cb7a01acSMauro Carvalho Chehab 	struct timing *timing = &timing_data[(decoder->norm & V4L2_STD_525_60) ? 1 : 0];
176cb7a01acSMauro Carvalho Chehab 
177cb7a01acSMauro Carvalho Chehab 	init[0x03 * 2 - 1] =
178cb7a01acSMauro Carvalho Chehab 	    (((timing->vdelay >> 8) & 0x03) << 6) |
179cb7a01acSMauro Carvalho Chehab 	    (((timing->vactive >> 8) & 0x03) << 4) |
180cb7a01acSMauro Carvalho Chehab 	    (((timing->hdelay >> 8) & 0x03) << 2) |
181cb7a01acSMauro Carvalho Chehab 	    ((timing->hactive >> 8) & 0x03);
182cb7a01acSMauro Carvalho Chehab 	init[0x04 * 2 - 1] = timing->vdelay & 0xff;
183cb7a01acSMauro Carvalho Chehab 	init[0x05 * 2 - 1] = timing->vactive & 0xff;
184cb7a01acSMauro Carvalho Chehab 	init[0x06 * 2 - 1] = timing->hdelay & 0xff;
185cb7a01acSMauro Carvalho Chehab 	init[0x07 * 2 - 1] = timing->hactive & 0xff;
186cb7a01acSMauro Carvalho Chehab 	init[0x08 * 2 - 1] = timing->hscale >> 8;
187cb7a01acSMauro Carvalho Chehab 	init[0x09 * 2 - 1] = timing->hscale & 0xff;
188cb7a01acSMauro Carvalho Chehab 	/* 0x15 in array is address 0x19 */
189cb7a01acSMauro Carvalho Chehab 	init[0x15 * 2 - 1] = (decoder->norm & V4L2_STD_625_50) ? 115 : 93;	/* Chroma burst delay */
190cb7a01acSMauro Carvalho Chehab 	/* reset */
191cb7a01acSMauro Carvalho Chehab 	bt819_write(decoder, 0x1f, 0x00);
192cb7a01acSMauro Carvalho Chehab 	mdelay(1);
193cb7a01acSMauro Carvalho Chehab 
194cb7a01acSMauro Carvalho Chehab 	/* init */
195cb7a01acSMauro Carvalho Chehab 	return bt819_write_block(decoder, init, sizeof(init));
196cb7a01acSMauro Carvalho Chehab }
197cb7a01acSMauro Carvalho Chehab 
198cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
199cb7a01acSMauro Carvalho Chehab 
bt819_status(struct v4l2_subdev * sd,u32 * pstatus,v4l2_std_id * pstd)200cb7a01acSMauro Carvalho Chehab static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
201cb7a01acSMauro Carvalho Chehab {
202cb7a01acSMauro Carvalho Chehab 	struct bt819 *decoder = to_bt819(sd);
203cb7a01acSMauro Carvalho Chehab 	int status = bt819_read(decoder, 0x00);
204cb7a01acSMauro Carvalho Chehab 	int res = V4L2_IN_ST_NO_SIGNAL;
205ddc7f72aSHans Verkuil 	v4l2_std_id std = pstd ? *pstd : V4L2_STD_ALL;
206cb7a01acSMauro Carvalho Chehab 
207cb7a01acSMauro Carvalho Chehab 	if ((status & 0x80))
208cb7a01acSMauro Carvalho Chehab 		res = 0;
209ddc7f72aSHans Verkuil 	else
210ddc7f72aSHans Verkuil 		std = V4L2_STD_UNKNOWN;
211cb7a01acSMauro Carvalho Chehab 
212cb7a01acSMauro Carvalho Chehab 	if ((status & 0x10))
213ddc7f72aSHans Verkuil 		std &= V4L2_STD_PAL;
214cb7a01acSMauro Carvalho Chehab 	else
215ddc7f72aSHans Verkuil 		std &= V4L2_STD_NTSC;
216cb7a01acSMauro Carvalho Chehab 	if (pstd)
217cb7a01acSMauro Carvalho Chehab 		*pstd = std;
218cb7a01acSMauro Carvalho Chehab 	if (pstatus)
219cb7a01acSMauro Carvalho Chehab 		*pstatus = res;
220cb7a01acSMauro Carvalho Chehab 
221cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "get status %x\n", status);
222cb7a01acSMauro Carvalho Chehab 	return 0;
223cb7a01acSMauro Carvalho Chehab }
224cb7a01acSMauro Carvalho Chehab 
bt819_querystd(struct v4l2_subdev * sd,v4l2_std_id * std)225cb7a01acSMauro Carvalho Chehab static int bt819_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
226cb7a01acSMauro Carvalho Chehab {
227cb7a01acSMauro Carvalho Chehab 	return bt819_status(sd, NULL, std);
228cb7a01acSMauro Carvalho Chehab }
229cb7a01acSMauro Carvalho Chehab 
bt819_g_input_status(struct v4l2_subdev * sd,u32 * status)230cb7a01acSMauro Carvalho Chehab static int bt819_g_input_status(struct v4l2_subdev *sd, u32 *status)
231cb7a01acSMauro Carvalho Chehab {
232cb7a01acSMauro Carvalho Chehab 	return bt819_status(sd, status, NULL);
233cb7a01acSMauro Carvalho Chehab }
234cb7a01acSMauro Carvalho Chehab 
bt819_s_std(struct v4l2_subdev * sd,v4l2_std_id std)235cb7a01acSMauro Carvalho Chehab static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
236cb7a01acSMauro Carvalho Chehab {
237cb7a01acSMauro Carvalho Chehab 	struct bt819 *decoder = to_bt819(sd);
238cb7a01acSMauro Carvalho Chehab 	struct timing *timing = NULL;
239cb7a01acSMauro Carvalho Chehab 
240cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
241cb7a01acSMauro Carvalho Chehab 
242cb7a01acSMauro Carvalho Chehab 	if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL)
243cb7a01acSMauro Carvalho Chehab 		v4l2_err(sd, "no notify found!\n");
244cb7a01acSMauro Carvalho Chehab 
245cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_NTSC) {
246cb7a01acSMauro Carvalho Chehab 		v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL);
247cb7a01acSMauro Carvalho Chehab 		bt819_setbit(decoder, 0x01, 0, 1);
248cb7a01acSMauro Carvalho Chehab 		bt819_setbit(decoder, 0x01, 1, 0);
249cb7a01acSMauro Carvalho Chehab 		bt819_setbit(decoder, 0x01, 5, 0);
250cb7a01acSMauro Carvalho Chehab 		bt819_write(decoder, 0x18, 0x68);
251cb7a01acSMauro Carvalho Chehab 		bt819_write(decoder, 0x19, 0x5d);
252cb7a01acSMauro Carvalho Chehab 		/* bt819_setbit(decoder, 0x1a,  5, 1); */
253cb7a01acSMauro Carvalho Chehab 		timing = &timing_data[1];
254cb7a01acSMauro Carvalho Chehab 	} else if (std & V4L2_STD_PAL) {
255cb7a01acSMauro Carvalho Chehab 		v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL);
256cb7a01acSMauro Carvalho Chehab 		bt819_setbit(decoder, 0x01, 0, 1);
257cb7a01acSMauro Carvalho Chehab 		bt819_setbit(decoder, 0x01, 1, 1);
258cb7a01acSMauro Carvalho Chehab 		bt819_setbit(decoder, 0x01, 5, 1);
259cb7a01acSMauro Carvalho Chehab 		bt819_write(decoder, 0x18, 0x7f);
260cb7a01acSMauro Carvalho Chehab 		bt819_write(decoder, 0x19, 0x72);
261cb7a01acSMauro Carvalho Chehab 		/* bt819_setbit(decoder, 0x1a,  5, 0); */
262cb7a01acSMauro Carvalho Chehab 		timing = &timing_data[0];
263cb7a01acSMauro Carvalho Chehab 	} else {
264cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "unsupported norm %llx\n",
265cb7a01acSMauro Carvalho Chehab 				(unsigned long long)std);
266cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
267cb7a01acSMauro Carvalho Chehab 	}
268cb7a01acSMauro Carvalho Chehab 	bt819_write(decoder, 0x03,
269cb7a01acSMauro Carvalho Chehab 			(((timing->vdelay >> 8) & 0x03) << 6) |
270cb7a01acSMauro Carvalho Chehab 			(((timing->vactive >> 8) & 0x03) << 4) |
271cb7a01acSMauro Carvalho Chehab 			(((timing->hdelay >> 8) & 0x03) << 2) |
272cb7a01acSMauro Carvalho Chehab 			((timing->hactive >> 8) & 0x03));
273cb7a01acSMauro Carvalho Chehab 	bt819_write(decoder, 0x04, timing->vdelay & 0xff);
274cb7a01acSMauro Carvalho Chehab 	bt819_write(decoder, 0x05, timing->vactive & 0xff);
275cb7a01acSMauro Carvalho Chehab 	bt819_write(decoder, 0x06, timing->hdelay & 0xff);
276cb7a01acSMauro Carvalho Chehab 	bt819_write(decoder, 0x07, timing->hactive & 0xff);
277cb7a01acSMauro Carvalho Chehab 	bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff);
278cb7a01acSMauro Carvalho Chehab 	bt819_write(decoder, 0x09, timing->hscale & 0xff);
279cb7a01acSMauro Carvalho Chehab 	decoder->norm = std;
280cb7a01acSMauro Carvalho Chehab 	v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL);
281cb7a01acSMauro Carvalho Chehab 	return 0;
282cb7a01acSMauro Carvalho Chehab }
283cb7a01acSMauro Carvalho Chehab 
bt819_s_routing(struct v4l2_subdev * sd,u32 input,u32 output,u32 config)284cb7a01acSMauro Carvalho Chehab static int bt819_s_routing(struct v4l2_subdev *sd,
285cb7a01acSMauro Carvalho Chehab 			   u32 input, u32 output, u32 config)
286cb7a01acSMauro Carvalho Chehab {
287cb7a01acSMauro Carvalho Chehab 	struct bt819 *decoder = to_bt819(sd);
288cb7a01acSMauro Carvalho Chehab 
289cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "set input %x\n", input);
290cb7a01acSMauro Carvalho Chehab 
291cb7a01acSMauro Carvalho Chehab 	if (input > 7)
292cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
293cb7a01acSMauro Carvalho Chehab 
294cb7a01acSMauro Carvalho Chehab 	if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL)
295cb7a01acSMauro Carvalho Chehab 		v4l2_err(sd, "no notify found!\n");
296cb7a01acSMauro Carvalho Chehab 
297cb7a01acSMauro Carvalho Chehab 	if (decoder->input != input) {
298cb7a01acSMauro Carvalho Chehab 		v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL);
299cb7a01acSMauro Carvalho Chehab 		decoder->input = input;
300cb7a01acSMauro Carvalho Chehab 		/* select mode */
301cb7a01acSMauro Carvalho Chehab 		if (decoder->input == 0) {
302cb7a01acSMauro Carvalho Chehab 			bt819_setbit(decoder, 0x0b, 6, 0);
303cb7a01acSMauro Carvalho Chehab 			bt819_setbit(decoder, 0x1a, 1, 1);
304cb7a01acSMauro Carvalho Chehab 		} else {
305cb7a01acSMauro Carvalho Chehab 			bt819_setbit(decoder, 0x0b, 6, 1);
306cb7a01acSMauro Carvalho Chehab 			bt819_setbit(decoder, 0x1a, 1, 0);
307cb7a01acSMauro Carvalho Chehab 		}
308cb7a01acSMauro Carvalho Chehab 		v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL);
309cb7a01acSMauro Carvalho Chehab 	}
310cb7a01acSMauro Carvalho Chehab 	return 0;
311cb7a01acSMauro Carvalho Chehab }
312cb7a01acSMauro Carvalho Chehab 
bt819_s_stream(struct v4l2_subdev * sd,int enable)313cb7a01acSMauro Carvalho Chehab static int bt819_s_stream(struct v4l2_subdev *sd, int enable)
314cb7a01acSMauro Carvalho Chehab {
315cb7a01acSMauro Carvalho Chehab 	struct bt819 *decoder = to_bt819(sd);
316cb7a01acSMauro Carvalho Chehab 
317cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "enable output %x\n", enable);
318cb7a01acSMauro Carvalho Chehab 
319cb7a01acSMauro Carvalho Chehab 	if (decoder->enable != enable) {
320cb7a01acSMauro Carvalho Chehab 		decoder->enable = enable;
321cb7a01acSMauro Carvalho Chehab 		bt819_setbit(decoder, 0x16, 7, !enable);
322cb7a01acSMauro Carvalho Chehab 	}
323cb7a01acSMauro Carvalho Chehab 	return 0;
324cb7a01acSMauro Carvalho Chehab }
325cb7a01acSMauro Carvalho Chehab 
bt819_s_ctrl(struct v4l2_ctrl * ctrl)326cb7a01acSMauro Carvalho Chehab static int bt819_s_ctrl(struct v4l2_ctrl *ctrl)
327cb7a01acSMauro Carvalho Chehab {
328cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_sd(ctrl);
329cb7a01acSMauro Carvalho Chehab 	struct bt819 *decoder = to_bt819(sd);
330cb7a01acSMauro Carvalho Chehab 	int temp;
331cb7a01acSMauro Carvalho Chehab 
332cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
333cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
334cb7a01acSMauro Carvalho Chehab 		bt819_write(decoder, 0x0a, ctrl->val);
335cb7a01acSMauro Carvalho Chehab 		break;
336cb7a01acSMauro Carvalho Chehab 
337cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
338cb7a01acSMauro Carvalho Chehab 		bt819_write(decoder, 0x0c, ctrl->val & 0xff);
339cb7a01acSMauro Carvalho Chehab 		bt819_setbit(decoder, 0x0b, 2, ((ctrl->val >> 8) & 0x01));
340cb7a01acSMauro Carvalho Chehab 		break;
341cb7a01acSMauro Carvalho Chehab 
342cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
343cb7a01acSMauro Carvalho Chehab 		bt819_write(decoder, 0x0d, (ctrl->val >> 7) & 0xff);
344cb7a01acSMauro Carvalho Chehab 		bt819_setbit(decoder, 0x0b, 1, ((ctrl->val >> 15) & 0x01));
345cb7a01acSMauro Carvalho Chehab 
346cb7a01acSMauro Carvalho Chehab 		/* Ratio between U gain and V gain must stay the same as
347cb7a01acSMauro Carvalho Chehab 		   the ratio between the default U and V gain values. */
348cb7a01acSMauro Carvalho Chehab 		temp = (ctrl->val * 180) / 254;
349cb7a01acSMauro Carvalho Chehab 		bt819_write(decoder, 0x0e, (temp >> 7) & 0xff);
350cb7a01acSMauro Carvalho Chehab 		bt819_setbit(decoder, 0x0b, 0, (temp >> 15) & 0x01);
351cb7a01acSMauro Carvalho Chehab 		break;
352cb7a01acSMauro Carvalho Chehab 
353cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
354cb7a01acSMauro Carvalho Chehab 		bt819_write(decoder, 0x0f, ctrl->val);
355cb7a01acSMauro Carvalho Chehab 		break;
356cb7a01acSMauro Carvalho Chehab 
357cb7a01acSMauro Carvalho Chehab 	default:
358cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
359cb7a01acSMauro Carvalho Chehab 	}
360cb7a01acSMauro Carvalho Chehab 	return 0;
361cb7a01acSMauro Carvalho Chehab }
362cb7a01acSMauro Carvalho Chehab 
363cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
364cb7a01acSMauro Carvalho Chehab 
365cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops bt819_ctrl_ops = {
366cb7a01acSMauro Carvalho Chehab 	.s_ctrl = bt819_s_ctrl,
367cb7a01acSMauro Carvalho Chehab };
368cb7a01acSMauro Carvalho Chehab 
369cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops bt819_video_ops = {
3708774bed9SLaurent Pinchart 	.s_std = bt819_s_std,
371cb7a01acSMauro Carvalho Chehab 	.s_routing = bt819_s_routing,
372cb7a01acSMauro Carvalho Chehab 	.s_stream = bt819_s_stream,
373cb7a01acSMauro Carvalho Chehab 	.querystd = bt819_querystd,
374cb7a01acSMauro Carvalho Chehab 	.g_input_status = bt819_g_input_status,
375cb7a01acSMauro Carvalho Chehab };
376cb7a01acSMauro Carvalho Chehab 
377cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops bt819_ops = {
378cb7a01acSMauro Carvalho Chehab 	.video = &bt819_video_ops,
379cb7a01acSMauro Carvalho Chehab };
380cb7a01acSMauro Carvalho Chehab 
381cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
382cb7a01acSMauro Carvalho Chehab 
bt819_probe(struct i2c_client * client)3830a1533e3SUwe Kleine-König static int bt819_probe(struct i2c_client *client)
384cb7a01acSMauro Carvalho Chehab {
385cb7a01acSMauro Carvalho Chehab 	int i, ver;
386cb7a01acSMauro Carvalho Chehab 	struct bt819 *decoder;
387cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
388cb7a01acSMauro Carvalho Chehab 	const char *name;
389cb7a01acSMauro Carvalho Chehab 
390cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
391cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
392cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
393cb7a01acSMauro Carvalho Chehab 
394c02b211dSLaurent Pinchart 	decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
395cb7a01acSMauro Carvalho Chehab 	if (decoder == NULL)
396cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
397cb7a01acSMauro Carvalho Chehab 	sd = &decoder->sd;
398cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &bt819_ops);
399cb7a01acSMauro Carvalho Chehab 
400cb7a01acSMauro Carvalho Chehab 	ver = bt819_read(decoder, 0x17);
401cb7a01acSMauro Carvalho Chehab 	switch (ver & 0xf0) {
402cb7a01acSMauro Carvalho Chehab 	case 0x70:
403cb7a01acSMauro Carvalho Chehab 		name = "bt819a";
404cb7a01acSMauro Carvalho Chehab 		break;
405cb7a01acSMauro Carvalho Chehab 	case 0x60:
406cb7a01acSMauro Carvalho Chehab 		name = "bt817a";
407cb7a01acSMauro Carvalho Chehab 		break;
408cb7a01acSMauro Carvalho Chehab 	case 0x20:
409cb7a01acSMauro Carvalho Chehab 		name = "bt815a";
410cb7a01acSMauro Carvalho Chehab 		break;
411cb7a01acSMauro Carvalho Chehab 	default:
412cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd,
413cb7a01acSMauro Carvalho Chehab 			"unknown chip version 0x%02x\n", ver);
414cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
415cb7a01acSMauro Carvalho Chehab 	}
416cb7a01acSMauro Carvalho Chehab 
417cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "%s found @ 0x%x (%s)\n", name,
418cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
419cb7a01acSMauro Carvalho Chehab 
420cb7a01acSMauro Carvalho Chehab 	decoder->norm = V4L2_STD_NTSC;
421cb7a01acSMauro Carvalho Chehab 	decoder->input = 0;
422cb7a01acSMauro Carvalho Chehab 	decoder->enable = 1;
423cb7a01acSMauro Carvalho Chehab 
424cb7a01acSMauro Carvalho Chehab 	i = bt819_init(sd);
425cb7a01acSMauro Carvalho Chehab 	if (i < 0)
426cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "init status %d\n", i);
427cb7a01acSMauro Carvalho Chehab 
428cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&decoder->hdl, 4);
429cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
430cb7a01acSMauro Carvalho Chehab 			V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
431cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
432cb7a01acSMauro Carvalho Chehab 			V4L2_CID_CONTRAST, 0, 511, 1, 0xd8);
433cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
434cb7a01acSMauro Carvalho Chehab 			V4L2_CID_SATURATION, 0, 511, 1, 0xfe);
435cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
436cb7a01acSMauro Carvalho Chehab 			V4L2_CID_HUE, -128, 127, 1, 0);
437cb7a01acSMauro Carvalho Chehab 	sd->ctrl_handler = &decoder->hdl;
438cb7a01acSMauro Carvalho Chehab 	if (decoder->hdl.error) {
439cb7a01acSMauro Carvalho Chehab 		int err = decoder->hdl.error;
440cb7a01acSMauro Carvalho Chehab 
441cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&decoder->hdl);
442cb7a01acSMauro Carvalho Chehab 		return err;
443cb7a01acSMauro Carvalho Chehab 	}
444cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_setup(&decoder->hdl);
445cb7a01acSMauro Carvalho Chehab 	return 0;
446cb7a01acSMauro Carvalho Chehab }
447cb7a01acSMauro Carvalho Chehab 
bt819_remove(struct i2c_client * client)448ed5c2f5fSUwe Kleine-König static void bt819_remove(struct i2c_client *client)
449cb7a01acSMauro Carvalho Chehab {
450cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
451cb7a01acSMauro Carvalho Chehab 	struct bt819 *decoder = to_bt819(sd);
452cb7a01acSMauro Carvalho Chehab 
453cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
454cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&decoder->hdl);
455cb7a01acSMauro Carvalho Chehab }
456cb7a01acSMauro Carvalho Chehab 
457cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
458cb7a01acSMauro Carvalho Chehab 
459cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id bt819_id[] = {
460cb7a01acSMauro Carvalho Chehab 	{ "bt819a", 0 },
461cb7a01acSMauro Carvalho Chehab 	{ "bt817a", 0 },
462cb7a01acSMauro Carvalho Chehab 	{ "bt815a", 0 },
463cb7a01acSMauro Carvalho Chehab 	{ }
464cb7a01acSMauro Carvalho Chehab };
465cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, bt819_id);
466cb7a01acSMauro Carvalho Chehab 
467cb7a01acSMauro Carvalho Chehab static struct i2c_driver bt819_driver = {
468cb7a01acSMauro Carvalho Chehab 	.driver = {
469cb7a01acSMauro Carvalho Chehab 		.name	= "bt819",
470cb7a01acSMauro Carvalho Chehab 	},
471*aaeb31c0SUwe Kleine-König 	.probe		= bt819_probe,
472cb7a01acSMauro Carvalho Chehab 	.remove		= bt819_remove,
473cb7a01acSMauro Carvalho Chehab 	.id_table	= bt819_id,
474cb7a01acSMauro Carvalho Chehab };
475cb7a01acSMauro Carvalho Chehab 
476cb7a01acSMauro Carvalho Chehab module_i2c_driver(bt819_driver);
477