1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * av7110_v4l.c: av7110 video4linux interface for DVB and Siemens DVB-C analog module |
4 | * |
5 | * Copyright (C) 1999-2002 Ralph Metzler |
6 | * & Marcus Metzler for convergence integrated media GmbH |
7 | * |
8 | * originally based on code by: |
9 | * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.de> |
10 | * |
11 | * the project's page is at https://linuxtv.org |
12 | */ |
13 | |
14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
15 | |
16 | #include <linux/kernel.h> |
17 | #include <linux/types.h> |
18 | #include <linux/delay.h> |
19 | #include <linux/fs.h> |
20 | #include <linux/timer.h> |
21 | #include <linux/poll.h> |
22 | |
23 | #include "av7110.h" |
24 | #include "av7110_hw.h" |
25 | #include "av7110_av.h" |
26 | |
27 | int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val) |
28 | { |
29 | u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff }; |
30 | struct i2c_msg msgs = { .flags = 0, .len = 5, .buf = msg }; |
31 | |
32 | switch (av7110->adac_type) { |
33 | case DVB_ADAC_MSP34x0: |
34 | msgs.addr = 0x40; |
35 | break; |
36 | case DVB_ADAC_MSP34x5: |
37 | msgs.addr = 0x42; |
38 | break; |
39 | default: |
40 | return 0; |
41 | } |
42 | |
43 | if (i2c_transfer(adap: &av7110->i2c_adap, msgs: &msgs, num: 1) != 1) { |
44 | dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n" , |
45 | av7110->dvb_adapter.num, reg, val); |
46 | return -EIO; |
47 | } |
48 | return 0; |
49 | } |
50 | |
51 | static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val) |
52 | { |
53 | u8 msg1[3] = { dev, reg >> 8, reg & 0xff }; |
54 | u8 msg2[2]; |
55 | struct i2c_msg msgs[2] = { |
56 | { .flags = 0 , .len = 3, .buf = msg1 }, |
57 | { .flags = I2C_M_RD, .len = 2, .buf = msg2 } |
58 | }; |
59 | |
60 | switch (av7110->adac_type) { |
61 | case DVB_ADAC_MSP34x0: |
62 | msgs[0].addr = 0x40; |
63 | msgs[1].addr = 0x40; |
64 | break; |
65 | case DVB_ADAC_MSP34x5: |
66 | msgs[0].addr = 0x42; |
67 | msgs[1].addr = 0x42; |
68 | break; |
69 | default: |
70 | return 0; |
71 | } |
72 | |
73 | if (i2c_transfer(adap: &av7110->i2c_adap, msgs: &msgs[0], num: 2) != 2) { |
74 | dprintk(1, "dvb-ttpci: failed @ card %d, %u\n" , |
75 | av7110->dvb_adapter.num, reg); |
76 | return -EIO; |
77 | } |
78 | *val = (msg2[0] << 8) | msg2[1]; |
79 | return 0; |
80 | } |
81 | |
82 | static struct v4l2_input inputs[4] = { |
83 | { |
84 | .index = 0, |
85 | .name = "DVB" , |
86 | .type = V4L2_INPUT_TYPE_CAMERA, |
87 | .audioset = 1, |
88 | .tuner = 0, /* ignored */ |
89 | .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, |
90 | .status = 0, |
91 | .capabilities = V4L2_IN_CAP_STD, |
92 | }, { |
93 | .index = 1, |
94 | .name = "Television" , |
95 | .type = V4L2_INPUT_TYPE_TUNER, |
96 | .audioset = 1, |
97 | .tuner = 0, |
98 | .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, |
99 | .status = 0, |
100 | .capabilities = V4L2_IN_CAP_STD, |
101 | }, { |
102 | .index = 2, |
103 | .name = "Video" , |
104 | .type = V4L2_INPUT_TYPE_CAMERA, |
105 | .audioset = 0, |
106 | .tuner = 0, |
107 | .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, |
108 | .status = 0, |
109 | .capabilities = V4L2_IN_CAP_STD, |
110 | }, { |
111 | .index = 3, |
112 | .name = "Y/C" , |
113 | .type = V4L2_INPUT_TYPE_CAMERA, |
114 | .audioset = 0, |
115 | .tuner = 0, |
116 | .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, |
117 | .status = 0, |
118 | .capabilities = V4L2_IN_CAP_STD, |
119 | } |
120 | }; |
121 | |
122 | static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) |
123 | { |
124 | struct av7110 *av7110 = dev->ext_priv; |
125 | u8 buf[] = { 0x00, reg, data }; |
126 | struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; |
127 | |
128 | dprintk(4, "dev: %p\n" , dev); |
129 | |
130 | if (1 != i2c_transfer(adap: &av7110->i2c_adap, msgs: &msg, num: 1)) |
131 | return -1; |
132 | return 0; |
133 | } |
134 | |
135 | static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) |
136 | { |
137 | struct av7110 *av7110 = dev->ext_priv; |
138 | struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; |
139 | |
140 | dprintk(4, "dev: %p\n" , dev); |
141 | |
142 | if (1 != i2c_transfer(adap: &av7110->i2c_adap, msgs: &msg, num: 1)) |
143 | return -1; |
144 | return 0; |
145 | } |
146 | |
147 | static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq) |
148 | { |
149 | u32 div; |
150 | u8 config; |
151 | u8 buf[4]; |
152 | |
153 | dprintk(4, "freq: 0x%08x\n" , freq); |
154 | |
155 | /* magic number: 614. tuning with the frequency given by v4l2 |
156 | is always off by 614*62.5 = 38375 kHz...*/ |
157 | div = freq + 614; |
158 | |
159 | buf[0] = (div >> 8) & 0x7f; |
160 | buf[1] = div & 0xff; |
161 | buf[2] = 0x8e; |
162 | |
163 | if (freq < 16U * 16825 / 100) |
164 | config = 0xa0; |
165 | else if (freq < 16U * 44725 / 100) |
166 | config = 0x90; |
167 | else |
168 | config = 0x30; |
169 | config &= ~0x02; |
170 | |
171 | buf[3] = config; |
172 | |
173 | return tuner_write(dev, addr: 0x61, data: buf); |
174 | } |
175 | |
176 | static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq) |
177 | { |
178 | struct av7110 *av7110 = (struct av7110*)dev->ext_priv; |
179 | u32 div; |
180 | u8 data[4]; |
181 | |
182 | div = (freq + 38900000 + 31250) / 62500; |
183 | |
184 | data[0] = (div >> 8) & 0x7f; |
185 | data[1] = div & 0xff; |
186 | data[2] = 0xce; |
187 | |
188 | if (freq < 45000000) |
189 | return -EINVAL; |
190 | else if (freq < 137000000) |
191 | data[3] = 0x01; |
192 | else if (freq < 403000000) |
193 | data[3] = 0x02; |
194 | else if (freq < 860000000) |
195 | data[3] = 0x04; |
196 | else |
197 | return -EINVAL; |
198 | |
199 | if (av7110->fe->ops.i2c_gate_ctrl) |
200 | av7110->fe->ops.i2c_gate_ctrl(av7110->fe, 1); |
201 | return tuner_write(dev, addr: 0x63, data); |
202 | } |
203 | |
204 | |
205 | |
206 | static struct saa7146_standard analog_standard[]; |
207 | static struct saa7146_standard dvb_standard[]; |
208 | static struct saa7146_standard standard[]; |
209 | |
210 | static const struct v4l2_audio msp3400_v4l2_audio = { |
211 | .index = 0, |
212 | .name = "Television" , |
213 | .capability = V4L2_AUDCAP_STEREO |
214 | }; |
215 | |
216 | static int av7110_dvb_c_switch(struct saa7146_dev *dev) |
217 | { |
218 | struct av7110 *av7110 = (struct av7110*)dev->ext_priv; |
219 | u16 adswitch; |
220 | int source, sync; |
221 | |
222 | dprintk(4, "%p\n" , av7110); |
223 | |
224 | if (0 != av7110->current_input) { |
225 | dprintk(1, "switching to analog TV:\n" ); |
226 | adswitch = 1; |
227 | source = SAA7146_HPS_SOURCE_PORT_B; |
228 | sync = SAA7146_HPS_SYNC_PORT_B; |
229 | memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2); |
230 | |
231 | switch (av7110->current_input) { |
232 | case 1: |
233 | dprintk(1, "switching SAA7113 to Analog Tuner Input\n" ); |
234 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0008, val: 0x0000); // loudspeaker source |
235 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0009, val: 0x0000); // headphone source |
236 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x000a, val: 0x0000); // SCART 1 source |
237 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x000e, val: 0x3000); // FM matrix, mono |
238 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0000, val: 0x4f00); // loudspeaker + headphone |
239 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0007, val: 0x4f00); // SCART 1 volume |
240 | |
241 | if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { |
242 | if (ves1820_writereg(dev, addr: 0x09, reg: 0x0f, data: 0x60)) |
243 | dprintk(1, "setting band in demodulator failed\n" ); |
244 | } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { |
245 | saa7146_setgpio(dev, port: 1, SAA7146_GPIO_OUTHI); // TDA9819 pin9(STD) |
246 | saa7146_setgpio(dev, port: 3, SAA7146_GPIO_OUTHI); // TDA9819 pin30(VIF) |
247 | } |
248 | if (i2c_writereg(av7110, id: 0x48, reg: 0x02, val: 0xd0) != 1) |
249 | dprintk(1, "saa7113 write failed @ card %d" , av7110->dvb_adapter.num); |
250 | break; |
251 | case 2: |
252 | dprintk(1, "switching SAA7113 to Video AV CVBS Input\n" ); |
253 | if (i2c_writereg(av7110, id: 0x48, reg: 0x02, val: 0xd2) != 1) |
254 | dprintk(1, "saa7113 write failed @ card %d" , av7110->dvb_adapter.num); |
255 | break; |
256 | case 3: |
257 | dprintk(1, "switching SAA7113 to Video AV Y/C Input\n" ); |
258 | if (i2c_writereg(av7110, id: 0x48, reg: 0x02, val: 0xd9) != 1) |
259 | dprintk(1, "saa7113 write failed @ card %d" , av7110->dvb_adapter.num); |
260 | break; |
261 | default: |
262 | dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input\n" ); |
263 | } |
264 | } else { |
265 | adswitch = 0; |
266 | source = SAA7146_HPS_SOURCE_PORT_A; |
267 | sync = SAA7146_HPS_SYNC_PORT_A; |
268 | memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); |
269 | dprintk(1, "switching DVB mode\n" ); |
270 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0008, val: 0x0220); // loudspeaker source |
271 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0009, val: 0x0220); // headphone source |
272 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x000a, val: 0x0220); // SCART 1 source |
273 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x000e, val: 0x3000); // FM matrix, mono |
274 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0000, val: 0x7f00); // loudspeaker + headphone |
275 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0007, val: 0x7f00); // SCART 1 volume |
276 | |
277 | if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { |
278 | if (ves1820_writereg(dev, addr: 0x09, reg: 0x0f, data: 0x20)) |
279 | dprintk(1, "setting band in demodulator failed\n" ); |
280 | } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { |
281 | saa7146_setgpio(dev, port: 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) |
282 | saa7146_setgpio(dev, port: 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) |
283 | } |
284 | } |
285 | |
286 | /* hmm, this does not do anything!? */ |
287 | if (av7110_fw_cmd(av7110, type: COMTYPE_AUDIODAC, com: ADSwitch, num: 1, adswitch)) |
288 | dprintk(1, "ADSwitch error\n" ); |
289 | |
290 | saa7146_set_hps_source_and_sync(saa: dev, source, sync); |
291 | |
292 | return 0; |
293 | } |
294 | |
295 | static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) |
296 | { |
297 | struct saa7146_dev *dev = video_drvdata(file); |
298 | struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; |
299 | u16 stereo_det; |
300 | s8 stereo; |
301 | |
302 | dprintk(2, "VIDIOC_G_TUNER: %d\n" , t->index); |
303 | |
304 | if (!av7110->analog_tuner_flags || t->index != 0) |
305 | return -EINVAL; |
306 | |
307 | memset(t, 0, sizeof(*t)); |
308 | strscpy((char *)t->name, "Television" , sizeof(t->name)); |
309 | |
310 | t->type = V4L2_TUNER_ANALOG_TV; |
311 | t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | |
312 | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; |
313 | t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */ |
314 | t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */ |
315 | /* FIXME: add the real signal strength here */ |
316 | t->signal = 0xffff; |
317 | t->afc = 0; |
318 | |
319 | /* FIXME: standard / stereo detection is still broken */ |
320 | msp_readreg(av7110, MSP_RD_DEM, reg: 0x007e, val: &stereo_det); |
321 | dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n" , stereo_det); |
322 | msp_readreg(av7110, MSP_RD_DSP, reg: 0x0018, val: &stereo_det); |
323 | dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n" , stereo_det); |
324 | stereo = (s8)(stereo_det >> 8); |
325 | if (stereo > 0x10) { |
326 | /* stereo */ |
327 | t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; |
328 | t->audmode = V4L2_TUNER_MODE_STEREO; |
329 | } else if (stereo < -0x10) { |
330 | /* bilingual */ |
331 | t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; |
332 | t->audmode = V4L2_TUNER_MODE_LANG1; |
333 | } else /* mono */ |
334 | t->rxsubchans = V4L2_TUNER_SUB_MONO; |
335 | |
336 | return 0; |
337 | } |
338 | |
339 | static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *t) |
340 | { |
341 | struct saa7146_dev *dev = video_drvdata(file); |
342 | struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; |
343 | u16 fm_matrix, src; |
344 | dprintk(2, "VIDIOC_S_TUNER: %d\n" , t->index); |
345 | |
346 | if (!av7110->analog_tuner_flags || av7110->current_input != 1) |
347 | return -EINVAL; |
348 | |
349 | switch (t->audmode) { |
350 | case V4L2_TUNER_MODE_STEREO: |
351 | dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n" ); |
352 | fm_matrix = 0x3001; /* stereo */ |
353 | src = 0x0020; |
354 | break; |
355 | case V4L2_TUNER_MODE_LANG1_LANG2: |
356 | dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n" ); |
357 | fm_matrix = 0x3000; /* bilingual */ |
358 | src = 0x0020; |
359 | break; |
360 | case V4L2_TUNER_MODE_LANG1: |
361 | dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n" ); |
362 | fm_matrix = 0x3000; /* mono */ |
363 | src = 0x0000; |
364 | break; |
365 | case V4L2_TUNER_MODE_LANG2: |
366 | dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n" ); |
367 | fm_matrix = 0x3000; /* mono */ |
368 | src = 0x0010; |
369 | break; |
370 | default: /* case V4L2_TUNER_MODE_MONO: */ |
371 | dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n" ); |
372 | fm_matrix = 0x3000; /* mono */ |
373 | src = 0x0030; |
374 | break; |
375 | } |
376 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x000e, val: fm_matrix); |
377 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0008, val: src); |
378 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0009, val: src); |
379 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x000a, val: src); |
380 | return 0; |
381 | } |
382 | |
383 | static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) |
384 | { |
385 | struct saa7146_dev *dev = video_drvdata(file); |
386 | struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; |
387 | |
388 | dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x\n" , f->frequency); |
389 | |
390 | if (!av7110->analog_tuner_flags || av7110->current_input != 1) |
391 | return -EINVAL; |
392 | |
393 | memset(f, 0, sizeof(*f)); |
394 | f->type = V4L2_TUNER_ANALOG_TV; |
395 | f->frequency = av7110->current_freq; |
396 | return 0; |
397 | } |
398 | |
399 | static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *f) |
400 | { |
401 | struct saa7146_dev *dev = video_drvdata(file); |
402 | struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; |
403 | |
404 | dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x\n" , f->frequency); |
405 | |
406 | if (!av7110->analog_tuner_flags || av7110->current_input != 1) |
407 | return -EINVAL; |
408 | |
409 | if (V4L2_TUNER_ANALOG_TV != f->type) |
410 | return -EINVAL; |
411 | |
412 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0000, val: 0xffe0); /* fast mute */ |
413 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0007, val: 0xffe0); |
414 | |
415 | /* tune in desired frequency */ |
416 | if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) |
417 | ves1820_set_tv_freq(dev, freq: f->frequency); |
418 | else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) |
419 | stv0297_set_tv_freq(dev, freq: f->frequency); |
420 | av7110->current_freq = f->frequency; |
421 | |
422 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0015, val: 0x003f); /* start stereo detection */ |
423 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0015, val: 0x0000); |
424 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0000, val: 0x4f00); /* loudspeaker + headphone */ |
425 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0007, val: 0x4f00); /* SCART 1 volume */ |
426 | return 0; |
427 | } |
428 | |
429 | static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) |
430 | { |
431 | struct saa7146_dev *dev = video_drvdata(file); |
432 | struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; |
433 | |
434 | dprintk(2, "VIDIOC_ENUMINPUT: %d\n" , i->index); |
435 | |
436 | if (av7110->analog_tuner_flags) { |
437 | if (i->index >= 4) |
438 | return -EINVAL; |
439 | } else { |
440 | if (i->index != 0) |
441 | return -EINVAL; |
442 | } |
443 | |
444 | memcpy(i, &inputs[i->index], sizeof(struct v4l2_input)); |
445 | |
446 | return 0; |
447 | } |
448 | |
449 | static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) |
450 | { |
451 | struct saa7146_dev *dev = video_drvdata(file); |
452 | struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; |
453 | |
454 | *input = av7110->current_input; |
455 | dprintk(2, "VIDIOC_G_INPUT: %d\n" , *input); |
456 | return 0; |
457 | } |
458 | |
459 | static int vidioc_s_input(struct file *file, void *fh, unsigned int input) |
460 | { |
461 | struct saa7146_dev *dev = video_drvdata(file); |
462 | struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; |
463 | |
464 | dprintk(2, "VIDIOC_S_INPUT: %d\n" , input); |
465 | |
466 | if (!av7110->analog_tuner_flags) |
467 | return input ? -EINVAL : 0; |
468 | |
469 | if (input >= 4) |
470 | return -EINVAL; |
471 | |
472 | av7110->current_input = input; |
473 | return av7110_dvb_c_switch(dev); |
474 | } |
475 | |
476 | static int vidioc_enum_output(struct file *file, void *fh, struct v4l2_output *o) |
477 | { |
478 | if (o->index) |
479 | return -EINVAL; |
480 | strscpy(o->name, "Video Output" , sizeof(o->name)); |
481 | o->type = V4L2_OUTPUT_TYPE_ANALOG; |
482 | o->std = V4L2_STD_NTSC_M | V4L2_STD_PAL_BG; |
483 | o->capabilities = V4L2_OUT_CAP_STD; |
484 | return 0; |
485 | } |
486 | |
487 | static int vidioc_g_output(struct file *file, void *fh, unsigned int *output) |
488 | { |
489 | *output = 0; |
490 | return 0; |
491 | } |
492 | |
493 | static int vidioc_s_output(struct file *file, void *fh, unsigned int output) |
494 | { |
495 | return output ? -EINVAL : 0; |
496 | } |
497 | |
498 | static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) |
499 | { |
500 | dprintk(2, "VIDIOC_G_AUDIO: %d\n" , a->index); |
501 | if (a->index != 0) |
502 | return -EINVAL; |
503 | *a = msp3400_v4l2_audio; |
504 | return 0; |
505 | } |
506 | |
507 | static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) |
508 | { |
509 | struct saa7146_dev *dev = video_drvdata(file); |
510 | struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; |
511 | |
512 | dprintk(2, "VIDIOC_G_AUDIO: %d\n" , a->index); |
513 | if (a->index != 0) |
514 | return -EINVAL; |
515 | if (av7110->current_input >= 2) |
516 | return -EINVAL; |
517 | *a = msp3400_v4l2_audio; |
518 | return 0; |
519 | } |
520 | |
521 | static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) |
522 | { |
523 | struct saa7146_dev *dev = video_drvdata(file); |
524 | struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; |
525 | |
526 | dprintk(2, "VIDIOC_S_AUDIO: %d\n" , a->index); |
527 | if (av7110->current_input >= 2) |
528 | return -EINVAL; |
529 | return a->index ? -EINVAL : 0; |
530 | } |
531 | |
532 | static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, |
533 | struct v4l2_sliced_vbi_cap *cap) |
534 | { |
535 | struct saa7146_dev *dev = video_drvdata(file); |
536 | struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; |
537 | |
538 | dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n" ); |
539 | if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) |
540 | return -EINVAL; |
541 | if (FW_VERSION(av7110->arm_app) >= 0x2623) { |
542 | cap->service_set = V4L2_SLICED_WSS_625; |
543 | cap->service_lines[0][23] = V4L2_SLICED_WSS_625; |
544 | } |
545 | return 0; |
546 | } |
547 | |
548 | static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, |
549 | struct v4l2_format *f) |
550 | { |
551 | struct saa7146_dev *dev = video_drvdata(file); |
552 | struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; |
553 | |
554 | dprintk(2, "VIDIOC_G_FMT:\n" ); |
555 | if (FW_VERSION(av7110->arm_app) < 0x2623) |
556 | return -EINVAL; |
557 | memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced); |
558 | if (av7110->wssMode) { |
559 | f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; |
560 | f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; |
561 | } |
562 | f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); |
563 | return 0; |
564 | } |
565 | |
566 | static int vidioc_try_fmt_sliced_vbi_out(struct file *file, void *fh, |
567 | struct v4l2_format *f) |
568 | { |
569 | struct saa7146_dev *dev = video_drvdata(file); |
570 | struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; |
571 | bool want_wss = (f->fmt.sliced.service_set & V4L2_SLICED_WSS_625) || |
572 | (!f->fmt.sliced.service_set && |
573 | f->fmt.sliced.service_lines[0][23] == V4L2_SLICED_WSS_625); |
574 | |
575 | dprintk(2, "VIDIOC_G_FMT:\n" ); |
576 | if (FW_VERSION(av7110->arm_app) < 0x2623) |
577 | return -EINVAL; |
578 | memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); |
579 | if (want_wss) { |
580 | f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; |
581 | f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; |
582 | } |
583 | f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); |
584 | return 0; |
585 | } |
586 | |
587 | static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, |
588 | struct v4l2_format *f) |
589 | { |
590 | struct saa7146_dev *dev = video_drvdata(file); |
591 | struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; |
592 | |
593 | dprintk(2, "VIDIOC_S_FMT\n" ); |
594 | if (vidioc_try_fmt_sliced_vbi_out(file, fh, f)) |
595 | return -EINVAL; |
596 | if (f->fmt.sliced.service_set & V4L2_SLICED_WSS_625) { |
597 | /* WSS controlled by userspace */ |
598 | av7110->wssMode = 1; |
599 | av7110->wssData = 0; |
600 | } else { |
601 | /* WSS controlled by firmware */ |
602 | av7110->wssMode = 0; |
603 | av7110->wssData = 0; |
604 | return av7110_fw_cmd(av7110, type: COMTYPE_ENCODER, |
605 | com: SetWSSConfig, num: 1, 0); |
606 | } |
607 | return 0; |
608 | } |
609 | |
610 | static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) |
611 | { |
612 | struct saa7146_dev *dev = video_drvdata(file); |
613 | struct av7110 *av7110 = (struct av7110*) dev->ext_priv; |
614 | struct v4l2_sliced_vbi_data d; |
615 | int rc; |
616 | |
617 | dprintk(2, "%s\n" , __func__); |
618 | if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d) |
619 | return -EINVAL; |
620 | if (copy_from_user(to: &d, from: data, n: count)) |
621 | return -EFAULT; |
622 | if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23) |
623 | return -EINVAL; |
624 | if (d.id) |
625 | av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0]; |
626 | else |
627 | av7110->wssData = 0x8000; |
628 | rc = av7110_fw_cmd(av7110, type: COMTYPE_ENCODER, com: SetWSSConfig, num: 2, 1, av7110->wssData); |
629 | return (rc < 0) ? rc : count; |
630 | } |
631 | |
632 | /**************************************************************************** |
633 | * INITIALIZATION |
634 | ****************************************************************************/ |
635 | |
636 | static u8 saa7113_init_regs[] = { |
637 | 0x02, 0xd0, |
638 | 0x03, 0x23, |
639 | 0x04, 0x00, |
640 | 0x05, 0x00, |
641 | 0x06, 0xe9, |
642 | 0x07, 0x0d, |
643 | 0x08, 0x98, |
644 | 0x09, 0x02, |
645 | 0x0a, 0x80, |
646 | 0x0b, 0x40, |
647 | 0x0c, 0x40, |
648 | 0x0d, 0x00, |
649 | 0x0e, 0x01, |
650 | 0x0f, 0x7c, |
651 | 0x10, 0x48, |
652 | 0x11, 0x0c, |
653 | 0x12, 0x8b, |
654 | 0x13, 0x1a, |
655 | 0x14, 0x00, |
656 | 0x15, 0x00, |
657 | 0x16, 0x00, |
658 | 0x17, 0x00, |
659 | 0x18, 0x00, |
660 | 0x19, 0x00, |
661 | 0x1a, 0x00, |
662 | 0x1b, 0x00, |
663 | 0x1c, 0x00, |
664 | 0x1d, 0x00, |
665 | 0x1e, 0x00, |
666 | |
667 | 0x41, 0x77, |
668 | 0x42, 0x77, |
669 | 0x43, 0x77, |
670 | 0x44, 0x77, |
671 | 0x45, 0x77, |
672 | 0x46, 0x77, |
673 | 0x47, 0x77, |
674 | 0x48, 0x77, |
675 | 0x49, 0x77, |
676 | 0x4a, 0x77, |
677 | 0x4b, 0x77, |
678 | 0x4c, 0x77, |
679 | 0x4d, 0x77, |
680 | 0x4e, 0x77, |
681 | 0x4f, 0x77, |
682 | 0x50, 0x77, |
683 | 0x51, 0x77, |
684 | 0x52, 0x77, |
685 | 0x53, 0x77, |
686 | 0x54, 0x77, |
687 | 0x55, 0x77, |
688 | 0x56, 0x77, |
689 | 0x57, 0xff, |
690 | |
691 | 0xff |
692 | }; |
693 | |
694 | |
695 | static struct saa7146_ext_vv av7110_vv_data_st; |
696 | static struct saa7146_ext_vv av7110_vv_data_c; |
697 | |
698 | int av7110_init_analog_module(struct av7110 *av7110) |
699 | { |
700 | u16 version1, version2; |
701 | |
702 | if (i2c_writereg(av7110, id: 0x80, reg: 0x0, val: 0x80) == 1 && |
703 | i2c_writereg(av7110, id: 0x80, reg: 0x0, val: 0) == 1) { |
704 | pr_info("DVB-C analog module @ card %d detected, initializing MSP3400\n" , |
705 | av7110->dvb_adapter.num); |
706 | av7110->adac_type = DVB_ADAC_MSP34x0; |
707 | } else if (i2c_writereg(av7110, id: 0x84, reg: 0x0, val: 0x80) == 1 && |
708 | i2c_writereg(av7110, id: 0x84, reg: 0x0, val: 0) == 1) { |
709 | pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n" , |
710 | av7110->dvb_adapter.num); |
711 | av7110->adac_type = DVB_ADAC_MSP34x5; |
712 | } else |
713 | return -ENODEV; |
714 | |
715 | msleep(msecs: 100); // the probing above resets the msp... |
716 | msp_readreg(av7110, MSP_RD_DSP, reg: 0x001e, val: &version1); |
717 | msp_readreg(av7110, MSP_RD_DSP, reg: 0x001f, val: &version2); |
718 | dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n" , |
719 | av7110->dvb_adapter.num, version1, version2); |
720 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0013, val: 0x0c00); |
721 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0000, val: 0x7f00); // loudspeaker + headphone |
722 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0008, val: 0x0220); // loudspeaker source |
723 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0009, val: 0x0220); // headphone source |
724 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0004, val: 0x7f00); // loudspeaker volume |
725 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x000a, val: 0x0220); // SCART 1 source |
726 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x0007, val: 0x7f00); // SCART 1 volume |
727 | msp_writereg(av7110, MSP_WR_DSP, reg: 0x000d, val: 0x1900); // prescale SCART |
728 | |
729 | if (i2c_writereg(av7110, id: 0x48, reg: 0x01, val: 0x00)!=1) { |
730 | pr_info("saa7113 not accessible\n" ); |
731 | } else { |
732 | u8 *i = saa7113_init_regs; |
733 | |
734 | if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { |
735 | /* Fujitsu/Siemens DVB-Cable */ |
736 | av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; |
737 | } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) { |
738 | /* Hauppauge/TT DVB-C premium */ |
739 | av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; |
740 | } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) { |
741 | /* Hauppauge/TT DVB-C premium */ |
742 | av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297; |
743 | } |
744 | |
745 | /* setup for DVB by default */ |
746 | if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { |
747 | if (ves1820_writereg(dev: av7110->dev, addr: 0x09, reg: 0x0f, data: 0x20)) |
748 | dprintk(1, "setting band in demodulator failed\n" ); |
749 | } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { |
750 | saa7146_setgpio(dev: av7110->dev, port: 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) |
751 | saa7146_setgpio(dev: av7110->dev, port: 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) |
752 | } |
753 | |
754 | /* init the saa7113 */ |
755 | while (*i != 0xff) { |
756 | if (i2c_writereg(av7110, id: 0x48, reg: i[0], val: i[1]) != 1) { |
757 | dprintk(1, "saa7113 initialization failed @ card %d" , av7110->dvb_adapter.num); |
758 | break; |
759 | } |
760 | i += 2; |
761 | } |
762 | /* setup msp for analog sound: B/G Dual-FM */ |
763 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x00bb, val: 0x02d0); // AD_CV |
764 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0001, val: 3); // FIR1 |
765 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0001, val: 18); // FIR1 |
766 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0001, val: 27); // FIR1 |
767 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0001, val: 48); // FIR1 |
768 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0001, val: 66); // FIR1 |
769 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0001, val: 72); // FIR1 |
770 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0005, val: 4); // FIR2 |
771 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0005, val: 64); // FIR2 |
772 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0005, val: 0); // FIR2 |
773 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0005, val: 3); // FIR2 |
774 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0005, val: 18); // FIR2 |
775 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0005, val: 27); // FIR2 |
776 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0005, val: 48); // FIR2 |
777 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0005, val: 66); // FIR2 |
778 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0005, val: 72); // FIR2 |
779 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0083, val: 0xa000); // MODE_REG |
780 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0093, val: 0x00aa); // DCO1_LO 5.74MHz |
781 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x009b, val: 0x04fc); // DCO1_HI |
782 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x00a3, val: 0x038e); // DCO2_LO 5.5MHz |
783 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x00ab, val: 0x04c6); // DCO2_HI |
784 | msp_writereg(av7110, MSP_WR_DEM, reg: 0x0056, val: 0); // LOAD_REG 1/2 |
785 | } |
786 | |
787 | memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); |
788 | /* set dd1 stream a & b */ |
789 | saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); |
790 | saa7146_write(av7110->dev, DD1_INIT, 0x03000700); |
791 | saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); |
792 | |
793 | return 0; |
794 | } |
795 | |
796 | int av7110_init_v4l(struct av7110 *av7110) |
797 | { |
798 | struct saa7146_dev* dev = av7110->dev; |
799 | struct saa7146_ext_vv *vv_data; |
800 | int ret; |
801 | |
802 | /* special case DVB-C: these cards have an analog tuner |
803 | plus need some special handling, so we have separate |
804 | saa7146_ext_vv data for these... */ |
805 | if (av7110->analog_tuner_flags) |
806 | vv_data = &av7110_vv_data_c; |
807 | else |
808 | vv_data = &av7110_vv_data_st; |
809 | ret = saa7146_vv_init(dev, ext_vv: vv_data); |
810 | |
811 | if (ret) { |
812 | ERR("cannot init capture device. skipping\n" ); |
813 | return -ENODEV; |
814 | } |
815 | vv_data->vid_ops.vidioc_enum_input = vidioc_enum_input; |
816 | vv_data->vid_ops.vidioc_g_input = vidioc_g_input; |
817 | vv_data->vid_ops.vidioc_s_input = vidioc_s_input; |
818 | vv_data->vid_ops.vidioc_g_tuner = vidioc_g_tuner; |
819 | vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner; |
820 | vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency; |
821 | vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency; |
822 | vv_data->vid_ops.vidioc_enumaudio = vidioc_enumaudio; |
823 | vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio; |
824 | vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio; |
825 | vv_data->vid_ops.vidioc_g_fmt_vbi_cap = NULL; |
826 | |
827 | vv_data->vbi_ops.vidioc_enum_output = vidioc_enum_output; |
828 | vv_data->vbi_ops.vidioc_g_output = vidioc_g_output; |
829 | vv_data->vbi_ops.vidioc_s_output = vidioc_s_output; |
830 | vv_data->vbi_ops.vidioc_g_parm = NULL; |
831 | vv_data->vbi_ops.vidioc_g_fmt_vbi_cap = NULL; |
832 | vv_data->vbi_ops.vidioc_try_fmt_vbi_cap = NULL; |
833 | vv_data->vbi_ops.vidioc_s_fmt_vbi_cap = NULL; |
834 | vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap; |
835 | vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out; |
836 | vv_data->vbi_ops.vidioc_try_fmt_sliced_vbi_out = vidioc_try_fmt_sliced_vbi_out; |
837 | vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out; |
838 | |
839 | if (FW_VERSION(av7110->arm_app) < 0x2623) |
840 | vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT; |
841 | |
842 | if (saa7146_register_device(vid: &av7110->v4l_dev, dev, name: "av7110" , type: VFL_TYPE_VIDEO)) { |
843 | ERR("cannot register capture device. skipping\n" ); |
844 | saa7146_vv_release(dev); |
845 | return -ENODEV; |
846 | } |
847 | if (FW_VERSION(av7110->arm_app) >= 0x2623) { |
848 | if (saa7146_register_device(vid: &av7110->vbi_dev, dev, name: "av7110" , type: VFL_TYPE_VBI)) |
849 | ERR("cannot register vbi v4l2 device. skipping\n" ); |
850 | } |
851 | return 0; |
852 | } |
853 | |
854 | int av7110_exit_v4l(struct av7110 *av7110) |
855 | { |
856 | struct saa7146_dev* dev = av7110->dev; |
857 | |
858 | saa7146_unregister_device(vid: &av7110->v4l_dev, dev: av7110->dev); |
859 | saa7146_unregister_device(vid: &av7110->vbi_dev, dev: av7110->dev); |
860 | |
861 | saa7146_vv_release(dev); |
862 | |
863 | return 0; |
864 | } |
865 | |
866 | |
867 | |
868 | /* FIXME: these values are experimental values that look better than the |
869 | values from the latest "official" driver -- at least for me... (MiHu) */ |
870 | static struct saa7146_standard standard[] = { |
871 | { |
872 | .name = "PAL" , .id = V4L2_STD_PAL_BG, |
873 | .v_offset = 0x15, .v_field = 288, |
874 | .h_offset = 0x48, .h_pixels = 708, |
875 | .v_max_out = 576, .h_max_out = 768, |
876 | }, { |
877 | .name = "NTSC" , .id = V4L2_STD_NTSC_M, |
878 | .v_offset = 0x10, .v_field = 244, |
879 | .h_offset = 0x40, .h_pixels = 708, |
880 | .v_max_out = 480, .h_max_out = 640, |
881 | } |
882 | }; |
883 | |
884 | static struct saa7146_standard analog_standard[] = { |
885 | { |
886 | .name = "PAL" , .id = V4L2_STD_PAL_BG, |
887 | .v_offset = 0x1b, .v_field = 288, |
888 | .h_offset = 0x08, .h_pixels = 708, |
889 | .v_max_out = 576, .h_max_out = 768, |
890 | }, { |
891 | .name = "NTSC" , .id = V4L2_STD_NTSC_M, |
892 | .v_offset = 0x10, .v_field = 244, |
893 | .h_offset = 0x40, .h_pixels = 708, |
894 | .v_max_out = 480, .h_max_out = 640, |
895 | } |
896 | }; |
897 | |
898 | static struct saa7146_standard dvb_standard[] = { |
899 | { |
900 | .name = "PAL" , .id = V4L2_STD_PAL_BG, |
901 | .v_offset = 0x14, .v_field = 288, |
902 | .h_offset = 0x48, .h_pixels = 708, |
903 | .v_max_out = 576, .h_max_out = 768, |
904 | }, { |
905 | .name = "NTSC" , .id = V4L2_STD_NTSC_M, |
906 | .v_offset = 0x10, .v_field = 244, |
907 | .h_offset = 0x40, .h_pixels = 708, |
908 | .v_max_out = 480, .h_max_out = 640, |
909 | } |
910 | }; |
911 | |
912 | static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) |
913 | { |
914 | struct av7110 *av7110 = (struct av7110*) dev->ext_priv; |
915 | |
916 | if (std->id & V4L2_STD_PAL) { |
917 | av7110->vidmode = AV7110_VIDEO_MODE_PAL; |
918 | av7110_set_vidmode(av7110, mode: av7110->vidmode); |
919 | } |
920 | else if (std->id & V4L2_STD_NTSC) { |
921 | av7110->vidmode = AV7110_VIDEO_MODE_NTSC; |
922 | av7110_set_vidmode(av7110, mode: av7110->vidmode); |
923 | } |
924 | else |
925 | return -1; |
926 | |
927 | return 0; |
928 | } |
929 | |
930 | |
931 | static struct saa7146_ext_vv av7110_vv_data_st = { |
932 | .inputs = 1, |
933 | .audios = 1, |
934 | .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, |
935 | .flags = 0, |
936 | |
937 | .stds = &standard[0], |
938 | .num_stds = ARRAY_SIZE(standard), |
939 | .std_callback = &std_callback, |
940 | |
941 | .vbi_fops.write = av7110_vbi_write, |
942 | }; |
943 | |
944 | static struct saa7146_ext_vv av7110_vv_data_c = { |
945 | .inputs = 1, |
946 | .audios = 1, |
947 | .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, |
948 | .flags = SAA7146_USE_PORT_B_FOR_VBI, |
949 | |
950 | .stds = &standard[0], |
951 | .num_stds = ARRAY_SIZE(standard), |
952 | .std_callback = &std_callback, |
953 | |
954 | .vbi_fops.write = av7110_vbi_write, |
955 | }; |
956 | |
957 | |