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