xref: /openbmc/linux/drivers/media/i2c/bt866.c (revision 7c0f6ba682b9c7632072ffbedf8d328c8f3c42ba)
1cb7a01acSMauro Carvalho Chehab /*
2cb7a01acSMauro Carvalho Chehab     bt866 - BT866 Digital Video Encoder (Rockwell Part)
3cb7a01acSMauro Carvalho Chehab 
4cb7a01acSMauro Carvalho Chehab     Copyright (C) 1999 Mike Bernson <mike@mlb.org>
5cb7a01acSMauro Carvalho Chehab     Copyright (C) 1998 Dave Perks <dperks@ibm.net>
6cb7a01acSMauro Carvalho Chehab 
7cb7a01acSMauro Carvalho Chehab     Modifications for LML33/DC10plus unified driver
8cb7a01acSMauro Carvalho Chehab     Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
9cb7a01acSMauro Carvalho Chehab 
10cb7a01acSMauro Carvalho Chehab     This code was modify/ported from the saa7111 driver written
11cb7a01acSMauro Carvalho Chehab     by Dave Perks.
12cb7a01acSMauro Carvalho Chehab 
13cb7a01acSMauro Carvalho Chehab     This code was adapted for the bt866 by Christer Weinigel and ported
14cb7a01acSMauro Carvalho Chehab     to 2.6 by Martin Samuelsson.
15cb7a01acSMauro Carvalho Chehab 
16cb7a01acSMauro Carvalho Chehab     This program is free software; you can redistribute it and/or modify
17cb7a01acSMauro Carvalho Chehab     it under the terms of the GNU General Public License as published by
18cb7a01acSMauro Carvalho Chehab     the Free Software Foundation; either version 2 of the License, or
19cb7a01acSMauro Carvalho Chehab     (at your option) any later version.
20cb7a01acSMauro Carvalho Chehab 
21cb7a01acSMauro Carvalho Chehab     This program is distributed in the hope that it will be useful,
22cb7a01acSMauro Carvalho Chehab     but WITHOUT ANY WARRANTY; without even the implied warranty of
23cb7a01acSMauro Carvalho Chehab     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24cb7a01acSMauro Carvalho Chehab     GNU General Public License for more details.
25cb7a01acSMauro Carvalho Chehab 
26cb7a01acSMauro Carvalho Chehab     You should have received a copy of the GNU General Public License
27cb7a01acSMauro Carvalho Chehab     along with this program; if not, write to the Free Software
28cb7a01acSMauro Carvalho Chehab     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29cb7a01acSMauro Carvalho Chehab */
30cb7a01acSMauro Carvalho Chehab 
31cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
32cb7a01acSMauro Carvalho Chehab #include <linux/types.h>
33cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
34cb7a01acSMauro Carvalho Chehab #include <linux/ioctl.h>
35*7c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
36cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
37cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
38cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
39cb7a01acSMauro Carvalho Chehab 
40cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Brooktree-866 video encoder driver");
41cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Mike Bernson & Dave Perks");
42cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
43cb7a01acSMauro Carvalho Chehab 
44cb7a01acSMauro Carvalho Chehab static int debug;
45cb7a01acSMauro Carvalho Chehab module_param(debug, int, 0);
46cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)");
47cb7a01acSMauro Carvalho Chehab 
48cb7a01acSMauro Carvalho Chehab 
49cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
50cb7a01acSMauro Carvalho Chehab 
51cb7a01acSMauro Carvalho Chehab struct bt866 {
52cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
53cb7a01acSMauro Carvalho Chehab 	u8 reg[256];
54cb7a01acSMauro Carvalho Chehab };
55cb7a01acSMauro Carvalho Chehab 
56cb7a01acSMauro Carvalho Chehab static inline struct bt866 *to_bt866(struct v4l2_subdev *sd)
57cb7a01acSMauro Carvalho Chehab {
58cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct bt866, sd);
59cb7a01acSMauro Carvalho Chehab }
60cb7a01acSMauro Carvalho Chehab 
61cb7a01acSMauro Carvalho Chehab static int bt866_write(struct bt866 *encoder, u8 subaddr, u8 data)
62cb7a01acSMauro Carvalho Chehab {
63cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd);
64cb7a01acSMauro Carvalho Chehab 	u8 buffer[2];
65cb7a01acSMauro Carvalho Chehab 	int err;
66cb7a01acSMauro Carvalho Chehab 
67cb7a01acSMauro Carvalho Chehab 	buffer[0] = subaddr;
68cb7a01acSMauro Carvalho Chehab 	buffer[1] = data;
69cb7a01acSMauro Carvalho Chehab 
70cb7a01acSMauro Carvalho Chehab 	encoder->reg[subaddr] = data;
71cb7a01acSMauro Carvalho Chehab 
72cb7a01acSMauro Carvalho Chehab 	v4l_dbg(1, debug, client, "write 0x%02x = 0x%02x\n", subaddr, data);
73cb7a01acSMauro Carvalho Chehab 
74cb7a01acSMauro Carvalho Chehab 	for (err = 0; err < 3;) {
75cb7a01acSMauro Carvalho Chehab 		if (i2c_master_send(client, buffer, 2) == 2)
76cb7a01acSMauro Carvalho Chehab 			break;
77cb7a01acSMauro Carvalho Chehab 		err++;
78cb7a01acSMauro Carvalho Chehab 		v4l_warn(client, "error #%d writing to 0x%02x\n",
79cb7a01acSMauro Carvalho Chehab 				err, subaddr);
80cb7a01acSMauro Carvalho Chehab 		schedule_timeout_interruptible(msecs_to_jiffies(100));
81cb7a01acSMauro Carvalho Chehab 	}
82cb7a01acSMauro Carvalho Chehab 	if (err == 3) {
83cb7a01acSMauro Carvalho Chehab 		v4l_warn(client, "giving up\n");
84cb7a01acSMauro Carvalho Chehab 		return -1;
85cb7a01acSMauro Carvalho Chehab 	}
86cb7a01acSMauro Carvalho Chehab 
87cb7a01acSMauro Carvalho Chehab 	return 0;
88cb7a01acSMauro Carvalho Chehab }
89cb7a01acSMauro Carvalho Chehab 
90cb7a01acSMauro Carvalho Chehab static int bt866_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
91cb7a01acSMauro Carvalho Chehab {
92cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
93cb7a01acSMauro Carvalho Chehab 
94cb7a01acSMauro Carvalho Chehab 	/* Only PAL supported by this driver at the moment! */
95cb7a01acSMauro Carvalho Chehab 	if (!(std & V4L2_STD_NTSC))
96cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
97cb7a01acSMauro Carvalho Chehab 	return 0;
98cb7a01acSMauro Carvalho Chehab }
99cb7a01acSMauro Carvalho Chehab 
100cb7a01acSMauro Carvalho Chehab static int bt866_s_routing(struct v4l2_subdev *sd,
101cb7a01acSMauro Carvalho Chehab 			   u32 input, u32 output, u32 config)
102cb7a01acSMauro Carvalho Chehab {
103cb7a01acSMauro Carvalho Chehab 	static const __u8 init[] = {
104cb7a01acSMauro Carvalho Chehab 		0xc8, 0xcc, /* CRSCALE */
105cb7a01acSMauro Carvalho Chehab 		0xca, 0x91, /* CBSCALE */
106cb7a01acSMauro Carvalho Chehab 		0xcc, 0x24, /* YC16 | OSDNUM */
107cb7a01acSMauro Carvalho Chehab 		0xda, 0x00, /*  */
108cb7a01acSMauro Carvalho Chehab 		0xdc, 0x24, /* SETMODE | PAL */
109cb7a01acSMauro Carvalho Chehab 		0xde, 0x02, /* EACTIVE */
110cb7a01acSMauro Carvalho Chehab 
111cb7a01acSMauro Carvalho Chehab 		/* overlay colors */
112cb7a01acSMauro Carvalho Chehab 		0x70, 0xEB, 0x90, 0x80, 0xB0, 0x80, /* white */
113cb7a01acSMauro Carvalho Chehab 		0x72, 0xA2, 0x92, 0x8E, 0xB2, 0x2C, /* yellow */
114cb7a01acSMauro Carvalho Chehab 		0x74, 0x83, 0x94, 0x2C, 0xB4, 0x9C, /* cyan */
115cb7a01acSMauro Carvalho Chehab 		0x76, 0x70, 0x96, 0x3A, 0xB6, 0x48, /* green */
116cb7a01acSMauro Carvalho Chehab 		0x78, 0x54, 0x98, 0xC6, 0xB8, 0xB8, /* magenta */
117cb7a01acSMauro Carvalho Chehab 		0x7A, 0x41, 0x9A, 0xD4, 0xBA, 0x64, /* red */
118cb7a01acSMauro Carvalho Chehab 		0x7C, 0x23, 0x9C, 0x72, 0xBC, 0xD4, /* blue */
119cb7a01acSMauro Carvalho Chehab 		0x7E, 0x10, 0x9E, 0x80, 0xBE, 0x80, /* black */
120cb7a01acSMauro Carvalho Chehab 
121cb7a01acSMauro Carvalho Chehab 		0x60, 0xEB, 0x80, 0x80, 0xc0, 0x80, /* white */
122cb7a01acSMauro Carvalho Chehab 		0x62, 0xA2, 0x82, 0x8E, 0xc2, 0x2C, /* yellow */
123cb7a01acSMauro Carvalho Chehab 		0x64, 0x83, 0x84, 0x2C, 0xc4, 0x9C, /* cyan */
124cb7a01acSMauro Carvalho Chehab 		0x66, 0x70, 0x86, 0x3A, 0xc6, 0x48, /* green */
125cb7a01acSMauro Carvalho Chehab 		0x68, 0x54, 0x88, 0xC6, 0xc8, 0xB8, /* magenta */
126cb7a01acSMauro Carvalho Chehab 		0x6A, 0x41, 0x8A, 0xD4, 0xcA, 0x64, /* red */
127cb7a01acSMauro Carvalho Chehab 		0x6C, 0x23, 0x8C, 0x72, 0xcC, 0xD4, /* blue */
128cb7a01acSMauro Carvalho Chehab 		0x6E, 0x10, 0x8E, 0x80, 0xcE, 0x80, /* black */
129cb7a01acSMauro Carvalho Chehab 	};
130cb7a01acSMauro Carvalho Chehab 	struct bt866 *encoder = to_bt866(sd);
131cb7a01acSMauro Carvalho Chehab 	u8 val;
132cb7a01acSMauro Carvalho Chehab 	int i;
133cb7a01acSMauro Carvalho Chehab 
134cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(init) / 2; i += 2)
135cb7a01acSMauro Carvalho Chehab 		bt866_write(encoder, init[i], init[i+1]);
136cb7a01acSMauro Carvalho Chehab 
137cb7a01acSMauro Carvalho Chehab 	val = encoder->reg[0xdc];
138cb7a01acSMauro Carvalho Chehab 
139cb7a01acSMauro Carvalho Chehab 	if (input == 0)
140cb7a01acSMauro Carvalho Chehab 		val |= 0x40; /* CBSWAP */
141cb7a01acSMauro Carvalho Chehab 	else
142cb7a01acSMauro Carvalho Chehab 		val &= ~0x40; /* !CBSWAP */
143cb7a01acSMauro Carvalho Chehab 
144cb7a01acSMauro Carvalho Chehab 	bt866_write(encoder, 0xdc, val);
145cb7a01acSMauro Carvalho Chehab 
146cb7a01acSMauro Carvalho Chehab 	val = encoder->reg[0xcc];
147cb7a01acSMauro Carvalho Chehab 	if (input == 2)
148cb7a01acSMauro Carvalho Chehab 		val |= 0x01; /* OSDBAR */
149cb7a01acSMauro Carvalho Chehab 	else
150cb7a01acSMauro Carvalho Chehab 		val &= ~0x01; /* !OSDBAR */
151cb7a01acSMauro Carvalho Chehab 	bt866_write(encoder, 0xcc, val);
152cb7a01acSMauro Carvalho Chehab 
153cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "set input %d\n", input);
154cb7a01acSMauro Carvalho Chehab 
155cb7a01acSMauro Carvalho Chehab 	switch (input) {
156cb7a01acSMauro Carvalho Chehab 	case 0:
157cb7a01acSMauro Carvalho Chehab 	case 1:
158cb7a01acSMauro Carvalho Chehab 	case 2:
159cb7a01acSMauro Carvalho Chehab 		break;
160cb7a01acSMauro Carvalho Chehab 	default:
161cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
162cb7a01acSMauro Carvalho Chehab 	}
163cb7a01acSMauro Carvalho Chehab 	return 0;
164cb7a01acSMauro Carvalho Chehab }
165cb7a01acSMauro Carvalho Chehab 
166cb7a01acSMauro Carvalho Chehab #if 0
167cb7a01acSMauro Carvalho Chehab /* Code to setup square pixels, might be of some use in the future,
168cb7a01acSMauro Carvalho Chehab    but is currently unused. */
169cb7a01acSMauro Carvalho Chehab 	val = encoder->reg[0xdc];
170cb7a01acSMauro Carvalho Chehab 	if (*iarg)
171cb7a01acSMauro Carvalho Chehab 		val |= 1; /* SQUARE */
172cb7a01acSMauro Carvalho Chehab 	else
173cb7a01acSMauro Carvalho Chehab 		val &= ~1; /* !SQUARE */
174cb7a01acSMauro Carvalho Chehab 	bt866_write(client, 0xdc, val);
175cb7a01acSMauro Carvalho Chehab #endif
176cb7a01acSMauro Carvalho Chehab 
177cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
178cb7a01acSMauro Carvalho Chehab 
179cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops bt866_video_ops = {
180cb7a01acSMauro Carvalho Chehab 	.s_std_output = bt866_s_std_output,
181cb7a01acSMauro Carvalho Chehab 	.s_routing = bt866_s_routing,
182cb7a01acSMauro Carvalho Chehab };
183cb7a01acSMauro Carvalho Chehab 
184cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops bt866_ops = {
185cb7a01acSMauro Carvalho Chehab 	.video = &bt866_video_ops,
186cb7a01acSMauro Carvalho Chehab };
187cb7a01acSMauro Carvalho Chehab 
188cb7a01acSMauro Carvalho Chehab static int bt866_probe(struct i2c_client *client,
189cb7a01acSMauro Carvalho Chehab 			const struct i2c_device_id *id)
190cb7a01acSMauro Carvalho Chehab {
191cb7a01acSMauro Carvalho Chehab 	struct bt866 *encoder;
192cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
193cb7a01acSMauro Carvalho Chehab 
194cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%x (%s)\n",
195cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
196cb7a01acSMauro Carvalho Chehab 
197c02b211dSLaurent Pinchart 	encoder = devm_kzalloc(&client->dev, sizeof(*encoder), GFP_KERNEL);
198cb7a01acSMauro Carvalho Chehab 	if (encoder == NULL)
199cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
200cb7a01acSMauro Carvalho Chehab 	sd = &encoder->sd;
201cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &bt866_ops);
202cb7a01acSMauro Carvalho Chehab 	return 0;
203cb7a01acSMauro Carvalho Chehab }
204cb7a01acSMauro Carvalho Chehab 
205cb7a01acSMauro Carvalho Chehab static int bt866_remove(struct i2c_client *client)
206cb7a01acSMauro Carvalho Chehab {
207cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
208cb7a01acSMauro Carvalho Chehab 
209cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
210cb7a01acSMauro Carvalho Chehab 	return 0;
211cb7a01acSMauro Carvalho Chehab }
212cb7a01acSMauro Carvalho Chehab 
213cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id bt866_id[] = {
214cb7a01acSMauro Carvalho Chehab 	{ "bt866", 0 },
215cb7a01acSMauro Carvalho Chehab 	{ }
216cb7a01acSMauro Carvalho Chehab };
217cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, bt866_id);
218cb7a01acSMauro Carvalho Chehab 
219cb7a01acSMauro Carvalho Chehab static struct i2c_driver bt866_driver = {
220cb7a01acSMauro Carvalho Chehab 	.driver = {
221cb7a01acSMauro Carvalho Chehab 		.name	= "bt866",
222cb7a01acSMauro Carvalho Chehab 	},
223cb7a01acSMauro Carvalho Chehab 	.probe		= bt866_probe,
224cb7a01acSMauro Carvalho Chehab 	.remove		= bt866_remove,
225cb7a01acSMauro Carvalho Chehab 	.id_table	= bt866_id,
226cb7a01acSMauro Carvalho Chehab };
227cb7a01acSMauro Carvalho Chehab 
228cb7a01acSMauro Carvalho Chehab module_i2c_driver(bt866_driver);
229