blob: dca20a3d98e2592bed7fcdc70fce38eb01d86343 [file] [log] [blame]
Thomas Gleixner74ba9202019-05-20 09:19:02 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards
Mauro Carvalho Chehaba8733ca2006-03-17 10:37:02 -03004
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 Visit http://www.mihu.de/linux/saa7146/ and follow the link
6 to "hexium" for further details about this card.
Mauro Carvalho Chehaba8733ca2006-03-17 10:37:02 -03007
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 Copyright (C) 2003 Michael Hunold <michael@mihu.de>
9
Linus Torvalds1da177e2005-04-16 15:20:36 -070010*/
11
Joe Perches44d0b802011-08-21 19:56:44 -030012#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#define DEBUG_VARIABLE debug
15
Mauro Carvalho Chehabd647f0b2015-11-13 19:40:07 -020016#include <media/drv-intf/saa7146_vv.h>
Paul Gortmaker7a707b82011-07-03 14:03:12 -040017#include <linux/module.h>
Jérémy Lefauree40d14a2017-10-01 15:30:41 -040018#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -030020static int debug;
Linus Torvalds1da177e2005-04-16 15:20:36 -070021module_param(debug, int, 0);
22MODULE_PARM_DESC(debug, "debug verbosity");
23
24/* global variables */
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -030025static int hexium_num;
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
27#define HEXIUM_GEMINI 4
28#define HEXIUM_GEMINI_DUAL 5
29
30#define HEXIUM_INPUTS 9
31static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
Hans Verkuil332e7992012-05-01 14:49:28 -030032 { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
33 { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
34 { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
35 { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
36 { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
37 { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
38 { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
39 { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
40 { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
Linus Torvalds1da177e2005-04-16 15:20:36 -070041};
42
43#define HEXIUM_AUDIOS 0
44
45struct hexium_data
46{
47 s8 adr;
48 u8 byte;
49};
50
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#define HEXIUM_GEMINI_V_1_0 1
52#define HEXIUM_GEMINI_DUAL_V_1_0 2
53
54struct hexium
55{
56 int type;
57
Hans Verkuil3ae863e2015-03-09 13:33:57 -030058 struct video_device video_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 struct i2c_adapter i2c_adapter;
Mauro Carvalho Chehaba8733ca2006-03-17 10:37:02 -030060
Mauro Carvalho Chehab6e6a8b52018-01-04 13:08:56 -050061 int cur_input; /* current input */
62 v4l2_std_id cur_std; /* current standard */
Linus Torvalds1da177e2005-04-16 15:20:36 -070063};
64
65/* Samsung KS0127B decoder default registers */
66static u8 hexium_ks0127b[0x100]={
67/*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10,
68/*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06,
69/*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00,
70/*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22,
71/*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
72/*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00,
73/*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80,
74/*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00,
75/*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
76/*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
77/*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
78/*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
79/*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
80/*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
81/*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
82/*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
83/*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
84/*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
85/*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
86/*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
87/*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
88/*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
89/*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
90/*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
91/*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
92/*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
93/*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
94/*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
95/*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
96/*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
97/*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
98/*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
99};
100
101static struct hexium_data hexium_pal[] = {
102 { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
103};
104
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105static struct hexium_data hexium_ntsc[] = {
106 { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
107};
108
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109static struct hexium_data hexium_secam[] = {
110 { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
111};
112
113static struct hexium_data hexium_input_select[] = {
114 { 0x02, 0x60 },
115 { 0x02, 0x64 },
116 { 0x02, 0x61 },
117 { 0x02, 0x65 },
118 { 0x02, 0x62 },
119 { 0x02, 0x66 },
120 { 0x02, 0x68 },
121 { 0x02, 0x69 },
122 { 0x02, 0x6A },
123};
124
125/* fixme: h_offset = 0 for Hexium Gemini *Dual*, which
126 are currently *not* supported*/
127static struct saa7146_standard hexium_standards[] = {
128 {
Mauro Carvalho Chehab6e6a8b52018-01-04 13:08:56 -0500129 .name = "PAL", .id = V4L2_STD_PAL,
130 .v_offset = 28, .v_field = 288,
131 .h_offset = 1, .h_pixels = 680,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 .v_max_out = 576, .h_max_out = 768,
133 }, {
Mauro Carvalho Chehab6e6a8b52018-01-04 13:08:56 -0500134 .name = "NTSC", .id = V4L2_STD_NTSC,
135 .v_offset = 28, .v_field = 240,
136 .h_offset = 1, .h_pixels = 640,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 .v_max_out = 480, .h_max_out = 640,
138 }, {
Mauro Carvalho Chehab6e6a8b52018-01-04 13:08:56 -0500139 .name = "SECAM", .id = V4L2_STD_SECAM,
140 .v_offset = 28, .v_field = 288,
141 .h_offset = 1, .h_pixels = 720,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 .v_max_out = 576, .h_max_out = 768,
143 }
Mauro Carvalho Chehaba8733ca2006-03-17 10:37:02 -0300144};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145
146/* bring hardware to a sane state. this has to be done, just in case someone
147 wants to capture from this device before it has been properly initialized.
148 the capture engine would badly fail, because no valid signal arrives on the
149 saa7146, thus leading to timeouts and stuff. */
150static int hexium_init_done(struct saa7146_dev *dev)
151{
152 struct hexium *hexium = (struct hexium *) dev->ext_priv;
153 union i2c_smbus_data data;
154 int i = 0;
155
Joe Perches44d0b802011-08-21 19:56:44 -0300156 DEB_D("hexium_init_done called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
158 /* initialize the helper ics to useful values */
159 for (i = 0; i < sizeof(hexium_ks0127b); i++) {
160 data.byte = hexium_ks0127b[i];
161 if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
Joe Perches44d0b802011-08-21 19:56:44 -0300162 pr_err("hexium_init_done() failed for address 0x%02x\n",
163 i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 }
165 }
166
167 return 0;
168}
169
170static int hexium_set_input(struct hexium *hexium, int input)
171{
172 union i2c_smbus_data data;
173
Joe Perches44d0b802011-08-21 19:56:44 -0300174 DEB_D("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
176 data.byte = hexium_input_select[input].byte;
177 if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) {
178 return -1;
179 }
180
181 return 0;
182}
183
184static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
185{
186 union i2c_smbus_data data;
187 int i = 0;
188
Joe Perches44d0b802011-08-21 19:56:44 -0300189 DEB_D("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
191 while (vdec[i].adr != -1) {
192 data.byte = vdec[i].byte;
193 if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) {
Joe Perches44d0b802011-08-21 19:56:44 -0300194 pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n",
195 i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 return -1;
197 }
198 i++;
199 }
200 return 0;
201}
202
Hans Verkuilb9600742009-01-18 19:59:11 -0300203static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
204{
Joe Perches44d0b802011-08-21 19:56:44 -0300205 DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
Hans Verkuilb9600742009-01-18 19:59:11 -0300206
Roel Kluin223ffe52009-05-02 16:38:47 -0300207 if (i->index >= HEXIUM_INPUTS)
Hans Verkuilb9600742009-01-18 19:59:11 -0300208 return -EINVAL;
209
210 memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
211
Joe Perches44d0b802011-08-21 19:56:44 -0300212 DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
Hans Verkuilb9600742009-01-18 19:59:11 -0300213 return 0;
214}
215
216static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
217{
218 struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
219 struct hexium *hexium = (struct hexium *) dev->ext_priv;
220
221 *input = hexium->cur_input;
222
Joe Perches44d0b802011-08-21 19:56:44 -0300223 DEB_D("VIDIOC_G_INPUT: %d\n", *input);
Hans Verkuilb9600742009-01-18 19:59:11 -0300224 return 0;
225}
226
227static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
228{
229 struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
230 struct hexium *hexium = (struct hexium *) dev->ext_priv;
231
Joe Perches44d0b802011-08-21 19:56:44 -0300232 DEB_EE("VIDIOC_S_INPUT %d\n", input);
Hans Verkuilb9600742009-01-18 19:59:11 -0300233
Roel Kluinf14a2972009-10-23 07:59:42 -0300234 if (input >= HEXIUM_INPUTS)
Hans Verkuilb9600742009-01-18 19:59:11 -0300235 return -EINVAL;
236
237 hexium->cur_input = input;
238 hexium_set_input(hexium, input);
239 return 0;
240}
241
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242static struct saa7146_ext_vv vv_data;
243
244/* this function only gets called when the probing was successful */
245static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
246{
Julia Lawallf00fd912011-08-04 07:29:33 -0300247 struct hexium *hexium;
Hans Verkuilcd7d9beb2010-02-20 07:56:25 -0300248 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
Joe Perches44d0b802011-08-21 19:56:44 -0300250 DEB_EE("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
Markus Elfring2d3da592017-08-28 05:55:16 -0400252 hexium = kzalloc(sizeof(*hexium), GFP_KERNEL);
Markus Elfringc38e8652017-08-28 05:46:57 -0400253 if (!hexium)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 return -ENOMEM;
Markus Elfringc38e8652017-08-28 05:46:57 -0400255
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 dev->ext_priv = hexium;
257
258 /* enable i2c-port pins */
259 saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
260
Arnd Bergmann03aa4f12019-02-19 12:01:56 -0500261 strscpy(hexium->i2c_adapter.name, "hexium gemini",
262 sizeof(hexium->i2c_adapter.name));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
264 if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
Joe Perches44d0b802011-08-21 19:56:44 -0300265 DEB_S("cannot register i2c-device. skipping.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 kfree(hexium);
267 return -EFAULT;
268 }
269
270 /* set HWControl GPIO number 2 */
271 saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
272
273 saa7146_write(dev, DD1_INIT, 0x07000700);
274 saa7146_write(dev, DD1_STREAM_B, 0x00000000);
275 saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
276
277 /* the rest */
278 hexium->cur_input = 0;
279 hexium_init_done(dev);
280
281 hexium_set_standard(hexium, hexium_pal);
282 hexium->cur_std = V4L2_STD_PAL;
283
284 hexium_set_input(hexium, 0);
285 hexium->cur_input = 0;
286
287 saa7146_vv_init(dev, &vv_data);
Hans Verkuil332e7992012-05-01 14:49:28 -0300288
Hans Verkuilab49ae02012-05-01 12:57:57 -0300289 vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
290 vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
291 vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
Hans Verkuilcd7d9beb2010-02-20 07:56:25 -0300292 ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER);
293 if (ret < 0) {
Joe Perches44d0b802011-08-21 19:56:44 -0300294 pr_err("cannot register capture v4l2 device. skipping.\n");
Hans Verkuilcd7d9beb2010-02-20 07:56:25 -0300295 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 }
297
Joe Perches44d0b802011-08-21 19:56:44 -0300298 pr_info("found 'hexium gemini' frame grabber-%d\n", hexium_num);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 hexium_num++;
300
301 return 0;
302}
303
304static int hexium_detach(struct saa7146_dev *dev)
305{
306 struct hexium *hexium = (struct hexium *) dev->ext_priv;
307
Joe Perches44d0b802011-08-21 19:56:44 -0300308 DEB_EE("dev:%p\n", dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
310 saa7146_unregister_device(&hexium->video_dev, dev);
311 saa7146_vv_release(dev);
312
313 hexium_num--;
314
315 i2c_del_adapter(&hexium->i2c_adapter);
316 kfree(hexium);
317 return 0;
318}
319
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
321{
322 struct hexium *hexium = (struct hexium *) dev->ext_priv;
323
324 if (V4L2_STD_PAL == std->id) {
325 hexium_set_standard(hexium, hexium_pal);
326 hexium->cur_std = V4L2_STD_PAL;
327 return 0;
328 } else if (V4L2_STD_NTSC == std->id) {
329 hexium_set_standard(hexium, hexium_ntsc);
330 hexium->cur_std = V4L2_STD_NTSC;
331 return 0;
332 } else if (V4L2_STD_SECAM == std->id) {
333 hexium_set_standard(hexium, hexium_secam);
334 hexium->cur_std = V4L2_STD_SECAM;
335 return 0;
336 }
337
338 return -1;
339}
340
341static struct saa7146_extension hexium_extension;
342
343static struct saa7146_pci_extension_data hexium_gemini_4bnc = {
344 .ext_priv = "Hexium Gemini (4 BNC)",
345 .ext = &hexium_extension,
346};
347
348static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = {
349 .ext_priv = "Hexium Gemini Dual (4 BNC)",
350 .ext = &hexium_extension,
351};
352
Arvind Yadavc6e3bdb2017-08-02 13:14:57 -0400353static const struct pci_device_id pci_tbl[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 {
355 .vendor = PCI_VENDOR_ID_PHILIPS,
356 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
357 .subvendor = 0x17c8,
358 .subdevice = 0x2401,
359 .driver_data = (unsigned long) &hexium_gemini_4bnc,
360 },
361 {
362 .vendor = PCI_VENDOR_ID_PHILIPS,
363 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
364 .subvendor = 0x17c8,
365 .subdevice = 0x2402,
366 .driver_data = (unsigned long) &hexium_gemini_dual_4bnc,
367 },
368 {
369 .vendor = 0,
370 }
371};
372
373MODULE_DEVICE_TABLE(pci, pci_tbl);
374
375static struct saa7146_ext_vv vv_data = {
376 .inputs = HEXIUM_INPUTS,
377 .capabilities = 0,
378 .stds = &hexium_standards[0],
Jérémy Lefauree40d14a2017-10-01 15:30:41 -0400379 .num_stds = ARRAY_SIZE(hexium_standards),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 .std_callback = &std_callback,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381};
382
383static struct saa7146_extension hexium_extension = {
384 .name = "hexium gemini",
385 .flags = SAA7146_USE_I2C_IRQ,
386
387 .pci_tbl = &pci_tbl[0],
388 .module = THIS_MODULE,
389
390 .attach = hexium_attach,
391 .detach = hexium_detach,
392
393 .irq_mask = 0,
394 .irq_func = NULL,
395};
396
397static int __init hexium_init_module(void)
398{
399 if (0 != saa7146_register_extension(&hexium_extension)) {
Joe Perches44d0b802011-08-21 19:56:44 -0300400 DEB_S("failed to register extension\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 return -ENODEV;
402 }
403
404 return 0;
405}
406
407static void __exit hexium_cleanup_module(void)
408{
409 saa7146_unregister_extension(&hexium_extension);
410}
411
412module_init(hexium_init_module);
413module_exit(hexium_cleanup_module);
414
415MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards");
416MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
417MODULE_LICENSE("GPL");