1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * bt856 - BT856A 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 | * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net> |
15 | * - moved over to linux>=2.4.x i2c protocol (9/9/2002) |
16 | */ |
17 | |
18 | #include <linux/module.h> |
19 | #include <linux/types.h> |
20 | #include <linux/slab.h> |
21 | #include <linux/ioctl.h> |
22 | #include <linux/uaccess.h> |
23 | #include <linux/i2c.h> |
24 | #include <linux/videodev2.h> |
25 | #include <media/v4l2-device.h> |
26 | |
27 | MODULE_DESCRIPTION("Brooktree-856A video encoder driver" ); |
28 | MODULE_AUTHOR("Mike Bernson & Dave Perks" ); |
29 | MODULE_LICENSE("GPL" ); |
30 | |
31 | static int debug; |
32 | module_param(debug, int, 0); |
33 | MODULE_PARM_DESC(debug, "Debug level (0-1)" ); |
34 | |
35 | |
36 | /* ----------------------------------------------------------------------- */ |
37 | |
38 | #define BT856_REG_OFFSET 0xDA |
39 | #define BT856_NR_REG 6 |
40 | |
41 | struct bt856 { |
42 | struct v4l2_subdev sd; |
43 | unsigned char reg[BT856_NR_REG]; |
44 | |
45 | v4l2_std_id norm; |
46 | }; |
47 | |
48 | static inline struct bt856 *to_bt856(struct v4l2_subdev *sd) |
49 | { |
50 | return container_of(sd, struct bt856, sd); |
51 | } |
52 | |
53 | /* ----------------------------------------------------------------------- */ |
54 | |
55 | static inline int bt856_write(struct bt856 *encoder, u8 reg, u8 value) |
56 | { |
57 | struct i2c_client *client = v4l2_get_subdevdata(sd: &encoder->sd); |
58 | |
59 | encoder->reg[reg - BT856_REG_OFFSET] = value; |
60 | return i2c_smbus_write_byte_data(client, command: reg, value); |
61 | } |
62 | |
63 | static inline int bt856_setbit(struct bt856 *encoder, u8 reg, u8 bit, u8 value) |
64 | { |
65 | return bt856_write(encoder, reg, |
66 | value: (encoder->reg[reg - BT856_REG_OFFSET] & ~(1 << bit)) | |
67 | (value ? (1 << bit) : 0)); |
68 | } |
69 | |
70 | static void bt856_dump(struct bt856 *encoder) |
71 | { |
72 | int i; |
73 | |
74 | v4l2_info(&encoder->sd, "register dump:\n" ); |
75 | for (i = 0; i < BT856_NR_REG; i += 2) |
76 | printk(KERN_CONT " %02x" , encoder->reg[i]); |
77 | printk(KERN_CONT "\n" ); |
78 | } |
79 | |
80 | /* ----------------------------------------------------------------------- */ |
81 | |
82 | static int bt856_init(struct v4l2_subdev *sd, u32 arg) |
83 | { |
84 | struct bt856 *encoder = to_bt856(sd); |
85 | |
86 | /* This is just for testing!!! */ |
87 | v4l2_dbg(1, debug, sd, "init\n" ); |
88 | bt856_write(encoder, reg: 0xdc, value: 0x18); |
89 | bt856_write(encoder, reg: 0xda, value: 0); |
90 | bt856_write(encoder, reg: 0xde, value: 0); |
91 | |
92 | bt856_setbit(encoder, reg: 0xdc, bit: 3, value: 1); |
93 | /*bt856_setbit(encoder, 0xdc, 6, 0);*/ |
94 | bt856_setbit(encoder, reg: 0xdc, bit: 4, value: 1); |
95 | |
96 | if (encoder->norm & V4L2_STD_NTSC) |
97 | bt856_setbit(encoder, reg: 0xdc, bit: 2, value: 0); |
98 | else |
99 | bt856_setbit(encoder, reg: 0xdc, bit: 2, value: 1); |
100 | |
101 | bt856_setbit(encoder, reg: 0xdc, bit: 1, value: 1); |
102 | bt856_setbit(encoder, reg: 0xde, bit: 4, value: 0); |
103 | bt856_setbit(encoder, reg: 0xde, bit: 3, value: 1); |
104 | if (debug != 0) |
105 | bt856_dump(encoder); |
106 | return 0; |
107 | } |
108 | |
109 | static int bt856_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) |
110 | { |
111 | struct bt856 *encoder = to_bt856(sd); |
112 | |
113 | v4l2_dbg(1, debug, sd, "set norm %llx\n" , (unsigned long long)std); |
114 | |
115 | if (std & V4L2_STD_NTSC) { |
116 | bt856_setbit(encoder, reg: 0xdc, bit: 2, value: 0); |
117 | } else if (std & V4L2_STD_PAL) { |
118 | bt856_setbit(encoder, reg: 0xdc, bit: 2, value: 1); |
119 | bt856_setbit(encoder, reg: 0xda, bit: 0, value: 0); |
120 | /*bt856_setbit(encoder, 0xda, 0, 1);*/ |
121 | } else { |
122 | return -EINVAL; |
123 | } |
124 | encoder->norm = std; |
125 | if (debug != 0) |
126 | bt856_dump(encoder); |
127 | return 0; |
128 | } |
129 | |
130 | static int bt856_s_routing(struct v4l2_subdev *sd, |
131 | u32 input, u32 output, u32 config) |
132 | { |
133 | struct bt856 *encoder = to_bt856(sd); |
134 | |
135 | v4l2_dbg(1, debug, sd, "set input %d\n" , input); |
136 | |
137 | /* We only have video bus. |
138 | * input= 0: input is from bt819 |
139 | * input= 1: input is from ZR36060 */ |
140 | switch (input) { |
141 | case 0: |
142 | bt856_setbit(encoder, reg: 0xde, bit: 4, value: 0); |
143 | bt856_setbit(encoder, reg: 0xde, bit: 3, value: 1); |
144 | bt856_setbit(encoder, reg: 0xdc, bit: 3, value: 1); |
145 | bt856_setbit(encoder, reg: 0xdc, bit: 6, value: 0); |
146 | break; |
147 | case 1: |
148 | bt856_setbit(encoder, reg: 0xde, bit: 4, value: 0); |
149 | bt856_setbit(encoder, reg: 0xde, bit: 3, value: 1); |
150 | bt856_setbit(encoder, reg: 0xdc, bit: 3, value: 1); |
151 | bt856_setbit(encoder, reg: 0xdc, bit: 6, value: 1); |
152 | break; |
153 | case 2: /* Color bar */ |
154 | bt856_setbit(encoder, reg: 0xdc, bit: 3, value: 0); |
155 | bt856_setbit(encoder, reg: 0xde, bit: 4, value: 1); |
156 | break; |
157 | default: |
158 | return -EINVAL; |
159 | } |
160 | |
161 | if (debug != 0) |
162 | bt856_dump(encoder); |
163 | return 0; |
164 | } |
165 | |
166 | /* ----------------------------------------------------------------------- */ |
167 | |
168 | static const struct v4l2_subdev_core_ops bt856_core_ops = { |
169 | .init = bt856_init, |
170 | }; |
171 | |
172 | static const struct v4l2_subdev_video_ops bt856_video_ops = { |
173 | .s_std_output = bt856_s_std_output, |
174 | .s_routing = bt856_s_routing, |
175 | }; |
176 | |
177 | static const struct v4l2_subdev_ops bt856_ops = { |
178 | .core = &bt856_core_ops, |
179 | .video = &bt856_video_ops, |
180 | }; |
181 | |
182 | /* ----------------------------------------------------------------------- */ |
183 | |
184 | static int bt856_probe(struct i2c_client *client) |
185 | { |
186 | struct bt856 *encoder; |
187 | struct v4l2_subdev *sd; |
188 | |
189 | /* Check if the adapter supports the needed features */ |
190 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
191 | return -ENODEV; |
192 | |
193 | v4l_info(client, "chip found @ 0x%x (%s)\n" , |
194 | client->addr << 1, client->adapter->name); |
195 | |
196 | encoder = devm_kzalloc(dev: &client->dev, size: sizeof(*encoder), GFP_KERNEL); |
197 | if (encoder == NULL) |
198 | return -ENOMEM; |
199 | sd = &encoder->sd; |
200 | v4l2_i2c_subdev_init(sd, client, ops: &bt856_ops); |
201 | encoder->norm = V4L2_STD_NTSC; |
202 | |
203 | bt856_write(encoder, reg: 0xdc, value: 0x18); |
204 | bt856_write(encoder, reg: 0xda, value: 0); |
205 | bt856_write(encoder, reg: 0xde, value: 0); |
206 | |
207 | bt856_setbit(encoder, reg: 0xdc, bit: 3, value: 1); |
208 | /*bt856_setbit(encoder, 0xdc, 6, 0);*/ |
209 | bt856_setbit(encoder, reg: 0xdc, bit: 4, value: 1); |
210 | |
211 | if (encoder->norm & V4L2_STD_NTSC) |
212 | bt856_setbit(encoder, reg: 0xdc, bit: 2, value: 0); |
213 | else |
214 | bt856_setbit(encoder, reg: 0xdc, bit: 2, value: 1); |
215 | |
216 | bt856_setbit(encoder, reg: 0xdc, bit: 1, value: 1); |
217 | bt856_setbit(encoder, reg: 0xde, bit: 4, value: 0); |
218 | bt856_setbit(encoder, reg: 0xde, bit: 3, value: 1); |
219 | |
220 | if (debug != 0) |
221 | bt856_dump(encoder); |
222 | return 0; |
223 | } |
224 | |
225 | static void bt856_remove(struct i2c_client *client) |
226 | { |
227 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
228 | |
229 | v4l2_device_unregister_subdev(sd); |
230 | } |
231 | |
232 | static const struct i2c_device_id bt856_id[] = { |
233 | { "bt856" , 0 }, |
234 | { } |
235 | }; |
236 | MODULE_DEVICE_TABLE(i2c, bt856_id); |
237 | |
238 | static struct i2c_driver bt856_driver = { |
239 | .driver = { |
240 | .name = "bt856" , |
241 | }, |
242 | .probe = bt856_probe, |
243 | .remove = bt856_remove, |
244 | .id_table = bt856_id, |
245 | }; |
246 | |
247 | module_i2c_driver(bt856_driver); |
248 | |