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
20static int debug;
21module_param(debug, int, 0);
22MODULE_PARM_DESC(debug, "debug verbosity");
23
24/* global variables */
25static 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
32static 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
46struct 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
55struct 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 */
67static 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
102static struct hexium_data hexium_pal[] = {
103 { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
104};
105
106static struct hexium_data hexium_ntsc[] = {
107 { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
108};
109
110static struct hexium_data hexium_secam[] = {
111 { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
112};
113
114static 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*/
128static 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. */
151static 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
171static 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
185static 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
204static 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
217static 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
228static 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
243static struct saa7146_ext_vv vv_data;
244
245/* this function only gets called when the probing was successful */
246static 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
313static 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
329static 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
350static struct saa7146_extension hexium_extension;
351
352static struct saa7146_pci_extension_data hexium_gemini_4bnc = {
353 .ext_priv = "Hexium Gemini (4 BNC)",
354 .ext = &hexium_extension,
355};
356
357static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = {
358 .ext_priv = "Hexium Gemini Dual (4 BNC)",
359 .ext = &hexium_extension,
360};
361
362static 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
382MODULE_DEVICE_TABLE(pci, pci_tbl);
383
384static 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
392static 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
406static 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
416static void __exit hexium_cleanup_module(void)
417{
418 saa7146_unregister_extension(&hexium_extension);
419}
420
421module_init(hexium_init_module);
422module_exit(hexium_cleanup_module);
423
424MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards");
425MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
426MODULE_LICENSE("GPL");
427

source code of linux/drivers/media/pci/saa7146/hexium_gemini.c