1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards |
4 | |
5 | Visit http://www.mihu.de/linux/saa7146/ and follow the link |
6 | to "hexium" for further details about this card. |
7 | |
8 | Copyright (C) 2003 Michael Hunold <michael@mihu.de> |
9 | |
10 | */ |
11 | |
12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
13 | |
14 | #define DEBUG_VARIABLE debug |
15 | |
16 | #include <media/drv-intf/saa7146_vv.h> |
17 | #include <linux/module.h> |
18 | #include <linux/kernel.h> |
19 | |
20 | static int debug; |
21 | module_param(debug, int, 0); |
22 | MODULE_PARM_DESC(debug, "debug verbosity" ); |
23 | |
24 | /* global variables */ |
25 | static int hexium_num; |
26 | |
27 | #define HEXIUM_GEMINI 4 |
28 | #define HEXIUM_GEMINI_DUAL 5 |
29 | |
30 | #define HEXIUM_STD (V4L2_STD_PAL | V4L2_STD_SECAM | V4L2_STD_NTSC) |
31 | #define HEXIUM_INPUTS 9 |
32 | static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { |
33 | { 0, "CVBS 1" , V4L2_INPUT_TYPE_CAMERA, 0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD }, |
34 | { 1, "CVBS 2" , V4L2_INPUT_TYPE_CAMERA, 0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD }, |
35 | { 2, "CVBS 3" , V4L2_INPUT_TYPE_CAMERA, 0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD }, |
36 | { 3, "CVBS 4" , V4L2_INPUT_TYPE_CAMERA, 0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD }, |
37 | { 4, "CVBS 5" , V4L2_INPUT_TYPE_CAMERA, 0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD }, |
38 | { 5, "CVBS 6" , V4L2_INPUT_TYPE_CAMERA, 0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD }, |
39 | { 6, "Y/C 1" , V4L2_INPUT_TYPE_CAMERA, 0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD }, |
40 | { 7, "Y/C 2" , V4L2_INPUT_TYPE_CAMERA, 0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD }, |
41 | { 8, "Y/C 3" , V4L2_INPUT_TYPE_CAMERA, 0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD }, |
42 | }; |
43 | |
44 | #define HEXIUM_AUDIOS 0 |
45 | |
46 | struct hexium_data |
47 | { |
48 | s8 adr; |
49 | u8 byte; |
50 | }; |
51 | |
52 | #define HEXIUM_GEMINI_V_1_0 1 |
53 | #define HEXIUM_GEMINI_DUAL_V_1_0 2 |
54 | |
55 | struct hexium |
56 | { |
57 | int type; |
58 | |
59 | struct video_device video_dev; |
60 | struct i2c_adapter i2c_adapter; |
61 | |
62 | int cur_input; /* current input */ |
63 | v4l2_std_id cur_std; /* current standard */ |
64 | }; |
65 | |
66 | /* Samsung KS0127B decoder default registers */ |
67 | static u8 hexium_ks0127b[0x100]={ |
68 | /*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10, |
69 | /*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06, |
70 | /*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00, |
71 | /*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22, |
72 | /*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
73 | /*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00, |
74 | /*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80, |
75 | /*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00, |
76 | /*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
77 | /*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
78 | /*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
79 | /*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
80 | /*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
81 | /*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
82 | /*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
83 | /*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
84 | /*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
85 | /*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
86 | /*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
87 | /*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
88 | /*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
89 | /*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
90 | /*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
91 | /*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
92 | /*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
93 | /*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
94 | /*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
95 | /*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
96 | /*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
97 | /*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
98 | /*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
99 | /*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 |
100 | }; |
101 | |
102 | static struct hexium_data hexium_pal[] = { |
103 | { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } |
104 | }; |
105 | |
106 | static struct hexium_data hexium_ntsc[] = { |
107 | { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF } |
108 | }; |
109 | |
110 | static struct hexium_data hexium_secam[] = { |
111 | { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } |
112 | }; |
113 | |
114 | static struct hexium_data hexium_input_select[] = { |
115 | { 0x02, 0x60 }, |
116 | { 0x02, 0x64 }, |
117 | { 0x02, 0x61 }, |
118 | { 0x02, 0x65 }, |
119 | { 0x02, 0x62 }, |
120 | { 0x02, 0x66 }, |
121 | { 0x02, 0x68 }, |
122 | { 0x02, 0x69 }, |
123 | { 0x02, 0x6A }, |
124 | }; |
125 | |
126 | /* fixme: h_offset = 0 for Hexium Gemini *Dual*, which |
127 | are currently *not* supported*/ |
128 | static struct saa7146_standard hexium_standards[] = { |
129 | { |
130 | .name = "PAL" , .id = V4L2_STD_PAL, |
131 | .v_offset = 28, .v_field = 288, |
132 | .h_offset = 1, .h_pixels = 680, |
133 | .v_max_out = 576, .h_max_out = 768, |
134 | }, { |
135 | .name = "NTSC" , .id = V4L2_STD_NTSC, |
136 | .v_offset = 28, .v_field = 240, |
137 | .h_offset = 1, .h_pixels = 640, |
138 | .v_max_out = 480, .h_max_out = 640, |
139 | }, { |
140 | .name = "SECAM" , .id = V4L2_STD_SECAM, |
141 | .v_offset = 28, .v_field = 288, |
142 | .h_offset = 1, .h_pixels = 720, |
143 | .v_max_out = 576, .h_max_out = 768, |
144 | } |
145 | }; |
146 | |
147 | /* bring hardware to a sane state. this has to be done, just in case someone |
148 | wants to capture from this device before it has been properly initialized. |
149 | the capture engine would badly fail, because no valid signal arrives on the |
150 | saa7146, thus leading to timeouts and stuff. */ |
151 | static int hexium_init_done(struct saa7146_dev *dev) |
152 | { |
153 | struct hexium *hexium = (struct hexium *) dev->ext_priv; |
154 | union i2c_smbus_data data; |
155 | int i = 0; |
156 | |
157 | DEB_D("hexium_init_done called\n" ); |
158 | |
159 | /* initialize the helper ics to useful values */ |
160 | for (i = 0; i < sizeof(hexium_ks0127b); i++) { |
161 | data.byte = hexium_ks0127b[i]; |
162 | if (0 != i2c_smbus_xfer(adapter: &hexium->i2c_adapter, addr: 0x6c, flags: 0, I2C_SMBUS_WRITE, command: i, I2C_SMBUS_BYTE_DATA, data: &data)) { |
163 | pr_err("hexium_init_done() failed for address 0x%02x\n" , |
164 | i); |
165 | } |
166 | } |
167 | |
168 | return 0; |
169 | } |
170 | |
171 | static int hexium_set_input(struct hexium *hexium, int input) |
172 | { |
173 | union i2c_smbus_data data; |
174 | |
175 | DEB_D("\n" ); |
176 | |
177 | data.byte = hexium_input_select[input].byte; |
178 | if (0 != i2c_smbus_xfer(adapter: &hexium->i2c_adapter, addr: 0x6c, flags: 0, I2C_SMBUS_WRITE, command: hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, data: &data)) { |
179 | return -1; |
180 | } |
181 | |
182 | return 0; |
183 | } |
184 | |
185 | static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec) |
186 | { |
187 | union i2c_smbus_data data; |
188 | int i = 0; |
189 | |
190 | DEB_D("\n" ); |
191 | |
192 | while (vdec[i].adr != -1) { |
193 | data.byte = vdec[i].byte; |
194 | if (0 != i2c_smbus_xfer(adapter: &hexium->i2c_adapter, addr: 0x6c, flags: 0, I2C_SMBUS_WRITE, command: vdec[i].adr, I2C_SMBUS_BYTE_DATA, data: &data)) { |
195 | pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n" , |
196 | i); |
197 | return -1; |
198 | } |
199 | i++; |
200 | } |
201 | return 0; |
202 | } |
203 | |
204 | static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) |
205 | { |
206 | DEB_EE("VIDIOC_ENUMINPUT %d\n" , i->index); |
207 | |
208 | if (i->index >= HEXIUM_INPUTS) |
209 | return -EINVAL; |
210 | |
211 | memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); |
212 | |
213 | DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n" , i->index); |
214 | return 0; |
215 | } |
216 | |
217 | static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) |
218 | { |
219 | struct saa7146_dev *dev = video_drvdata(file); |
220 | struct hexium *hexium = (struct hexium *) dev->ext_priv; |
221 | |
222 | *input = hexium->cur_input; |
223 | |
224 | DEB_D("VIDIOC_G_INPUT: %d\n" , *input); |
225 | return 0; |
226 | } |
227 | |
228 | static int vidioc_s_input(struct file *file, void *fh, unsigned int input) |
229 | { |
230 | struct saa7146_dev *dev = video_drvdata(file); |
231 | struct hexium *hexium = (struct hexium *) dev->ext_priv; |
232 | |
233 | DEB_EE("VIDIOC_S_INPUT %d\n" , input); |
234 | |
235 | if (input >= HEXIUM_INPUTS) |
236 | return -EINVAL; |
237 | |
238 | hexium->cur_input = input; |
239 | hexium_set_input(hexium, input); |
240 | return 0; |
241 | } |
242 | |
243 | static struct saa7146_ext_vv vv_data; |
244 | |
245 | /* this function only gets called when the probing was successful */ |
246 | static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) |
247 | { |
248 | struct hexium *hexium; |
249 | int ret; |
250 | |
251 | DEB_EE("\n" ); |
252 | |
253 | hexium = kzalloc(size: sizeof(*hexium), GFP_KERNEL); |
254 | if (!hexium) |
255 | return -ENOMEM; |
256 | |
257 | dev->ext_priv = hexium; |
258 | |
259 | /* enable i2c-port pins */ |
260 | saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); |
261 | |
262 | strscpy(hexium->i2c_adapter.name, "hexium gemini" , |
263 | sizeof(hexium->i2c_adapter.name)); |
264 | saa7146_i2c_adapter_prepare(dev, i2c_adapter: &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); |
265 | if (i2c_add_adapter(adap: &hexium->i2c_adapter) < 0) { |
266 | DEB_S("cannot register i2c-device. skipping.\n" ); |
267 | kfree(objp: hexium); |
268 | return -EFAULT; |
269 | } |
270 | |
271 | /* set HWControl GPIO number 2 */ |
272 | saa7146_setgpio(dev, port: 2, SAA7146_GPIO_OUTHI); |
273 | |
274 | saa7146_write(dev, DD1_INIT, 0x07000700); |
275 | saa7146_write(dev, DD1_STREAM_B, 0x00000000); |
276 | saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); |
277 | |
278 | /* the rest */ |
279 | hexium->cur_input = 0; |
280 | hexium_init_done(dev); |
281 | |
282 | hexium_set_standard(hexium, vdec: hexium_pal); |
283 | hexium->cur_std = V4L2_STD_PAL; |
284 | |
285 | hexium_set_input(hexium, input: 0); |
286 | hexium->cur_input = 0; |
287 | |
288 | ret = saa7146_vv_init(dev, ext_vv: &vv_data); |
289 | if (ret) { |
290 | i2c_del_adapter(adap: &hexium->i2c_adapter); |
291 | kfree(objp: hexium); |
292 | return ret; |
293 | } |
294 | |
295 | vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; |
296 | vv_data.vid_ops.vidioc_g_input = vidioc_g_input; |
297 | vv_data.vid_ops.vidioc_s_input = vidioc_s_input; |
298 | ret = saa7146_register_device(vid: &hexium->video_dev, dev, name: "hexium gemini" , type: VFL_TYPE_VIDEO); |
299 | if (ret < 0) { |
300 | pr_err("cannot register capture v4l2 device. skipping.\n" ); |
301 | saa7146_vv_release(dev); |
302 | i2c_del_adapter(adap: &hexium->i2c_adapter); |
303 | kfree(objp: hexium); |
304 | return ret; |
305 | } |
306 | |
307 | pr_info("found 'hexium gemini' frame grabber-%d\n" , hexium_num); |
308 | hexium_num++; |
309 | |
310 | return 0; |
311 | } |
312 | |
313 | static int hexium_detach(struct saa7146_dev *dev) |
314 | { |
315 | struct hexium *hexium = (struct hexium *) dev->ext_priv; |
316 | |
317 | DEB_EE("dev:%p\n" , dev); |
318 | |
319 | saa7146_unregister_device(vid: &hexium->video_dev, dev); |
320 | saa7146_vv_release(dev); |
321 | |
322 | hexium_num--; |
323 | |
324 | i2c_del_adapter(adap: &hexium->i2c_adapter); |
325 | kfree(objp: hexium); |
326 | return 0; |
327 | } |
328 | |
329 | static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) |
330 | { |
331 | struct hexium *hexium = (struct hexium *) dev->ext_priv; |
332 | |
333 | if (V4L2_STD_PAL == std->id) { |
334 | hexium_set_standard(hexium, vdec: hexium_pal); |
335 | hexium->cur_std = V4L2_STD_PAL; |
336 | return 0; |
337 | } else if (V4L2_STD_NTSC == std->id) { |
338 | hexium_set_standard(hexium, vdec: hexium_ntsc); |
339 | hexium->cur_std = V4L2_STD_NTSC; |
340 | return 0; |
341 | } else if (V4L2_STD_SECAM == std->id) { |
342 | hexium_set_standard(hexium, vdec: hexium_secam); |
343 | hexium->cur_std = V4L2_STD_SECAM; |
344 | return 0; |
345 | } |
346 | |
347 | return -1; |
348 | } |
349 | |
350 | static struct saa7146_extension hexium_extension; |
351 | |
352 | static struct saa7146_pci_extension_data hexium_gemini_4bnc = { |
353 | .ext_priv = "Hexium Gemini (4 BNC)" , |
354 | .ext = &hexium_extension, |
355 | }; |
356 | |
357 | static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = { |
358 | .ext_priv = "Hexium Gemini Dual (4 BNC)" , |
359 | .ext = &hexium_extension, |
360 | }; |
361 | |
362 | static const struct pci_device_id pci_tbl[] = { |
363 | { |
364 | .vendor = PCI_VENDOR_ID_PHILIPS, |
365 | .device = PCI_DEVICE_ID_PHILIPS_SAA7146, |
366 | .subvendor = 0x17c8, |
367 | .subdevice = 0x2401, |
368 | .driver_data = (unsigned long) &hexium_gemini_4bnc, |
369 | }, |
370 | { |
371 | .vendor = PCI_VENDOR_ID_PHILIPS, |
372 | .device = PCI_DEVICE_ID_PHILIPS_SAA7146, |
373 | .subvendor = 0x17c8, |
374 | .subdevice = 0x2402, |
375 | .driver_data = (unsigned long) &hexium_gemini_dual_4bnc, |
376 | }, |
377 | { |
378 | .vendor = 0, |
379 | } |
380 | }; |
381 | |
382 | MODULE_DEVICE_TABLE(pci, pci_tbl); |
383 | |
384 | static struct saa7146_ext_vv vv_data = { |
385 | .inputs = HEXIUM_INPUTS, |
386 | .capabilities = 0, |
387 | .stds = &hexium_standards[0], |
388 | .num_stds = ARRAY_SIZE(hexium_standards), |
389 | .std_callback = &std_callback, |
390 | }; |
391 | |
392 | static struct saa7146_extension hexium_extension = { |
393 | .name = "hexium gemini" , |
394 | .flags = SAA7146_USE_I2C_IRQ, |
395 | |
396 | .pci_tbl = &pci_tbl[0], |
397 | .module = THIS_MODULE, |
398 | |
399 | .attach = hexium_attach, |
400 | .detach = hexium_detach, |
401 | |
402 | .irq_mask = 0, |
403 | .irq_func = NULL, |
404 | }; |
405 | |
406 | static int __init hexium_init_module(void) |
407 | { |
408 | if (0 != saa7146_register_extension(&hexium_extension)) { |
409 | DEB_S("failed to register extension\n" ); |
410 | return -ENODEV; |
411 | } |
412 | |
413 | return 0; |
414 | } |
415 | |
416 | static void __exit hexium_cleanup_module(void) |
417 | { |
418 | saa7146_unregister_extension(&hexium_extension); |
419 | } |
420 | |
421 | module_init(hexium_init_module); |
422 | module_exit(hexium_cleanup_module); |
423 | |
424 | MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards" ); |
425 | MODULE_AUTHOR("Michael Hunold <michael@mihu.de>" ); |
426 | MODULE_LICENSE("GPL" ); |
427 | |