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