diff options
Diffstat (limited to 'drivers/media/video/mxb.c')
-rw-r--r-- | drivers/media/video/mxb.c | 1035 |
1 files changed, 1035 insertions, 0 deletions
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c new file mode 100644 index 000000000000..70bf1f1fad59 --- /dev/null +++ b/drivers/media/video/mxb.c | |||
@@ -0,0 +1,1035 @@ | |||
1 | /* | ||
2 | mxb - v4l2 driver for the Multimedia eXtension Board | ||
3 | |||
4 | Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> | ||
5 | |||
6 | Visit http://www.mihu.de/linux/saa7146/mxb/ | ||
7 | for further details about this card. | ||
8 | |||
9 | This program is free software; you can redistribute it and/or modify | ||
10 | it under the terms of the GNU General Public License as published by | ||
11 | the Free Software Foundation; either version 2 of the License, or | ||
12 | (at your option) any later version. | ||
13 | |||
14 | This program is distributed in the hope that it will be useful, | ||
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | GNU General Public License for more details. | ||
18 | |||
19 | You should have received a copy of the GNU General Public License | ||
20 | along with this program; if not, write to the Free Software | ||
21 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #define DEBUG_VARIABLE debug | ||
25 | |||
26 | #include <media/saa7146_vv.h> | ||
27 | #include <media/tuner.h> | ||
28 | #include <linux/video_decoder.h> | ||
29 | |||
30 | #include "mxb.h" | ||
31 | #include "tea6415c.h" | ||
32 | #include "tea6420.h" | ||
33 | #include "tda9840.h" | ||
34 | |||
35 | #define I2C_SAA7111 0x24 | ||
36 | |||
37 | #define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0) | ||
38 | |||
39 | /* global variable */ | ||
40 | static int mxb_num = 0; | ||
41 | |||
42 | /* initial frequence the tuner will be tuned to. | ||
43 | in verden (lower saxony, germany) 4148 is a | ||
44 | channel called "phoenix" */ | ||
45 | static int freq = 4148; | ||
46 | module_param(freq, int, 0644); | ||
47 | MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup"); | ||
48 | |||
49 | static int debug = 0; | ||
50 | module_param(debug, int, 0644); | ||
51 | MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); | ||
52 | |||
53 | #define MXB_INPUTS 4 | ||
54 | enum { TUNER, AUX1, AUX3, AUX3_YC }; | ||
55 | |||
56 | static struct v4l2_input mxb_inputs[MXB_INPUTS] = { | ||
57 | { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 }, | ||
58 | { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 }, | ||
59 | { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 }, | ||
60 | { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 }, | ||
61 | }; | ||
62 | |||
63 | /* this array holds the information, which port of the saa7146 each | ||
64 | input actually uses. the mxb uses port 0 for every input */ | ||
65 | static struct { | ||
66 | int hps_source; | ||
67 | int hps_sync; | ||
68 | } input_port_selection[MXB_INPUTS] = { | ||
69 | { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, | ||
70 | { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, | ||
71 | { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, | ||
72 | { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, | ||
73 | }; | ||
74 | |||
75 | /* this array holds the information of the audio source (mxb_audios), | ||
76 | which has to be switched corresponding to the video source (mxb_channels) */ | ||
77 | static int video_audio_connect[MXB_INPUTS] = | ||
78 | { 0, 1, 3, 3 }; | ||
79 | |||
80 | /* these are the necessary input-output-pins for bringing one audio source | ||
81 | (see above) to the CD-output */ | ||
82 | static struct tea6420_multiplex TEA6420_cd[MXB_AUDIOS+1][2] = | ||
83 | { | ||
84 | {{1,1,0},{1,1,0}}, /* Tuner */ | ||
85 | {{5,1,0},{6,1,0}}, /* AUX 1 */ | ||
86 | {{4,1,0},{6,1,0}}, /* AUX 2 */ | ||
87 | {{3,1,0},{6,1,0}}, /* AUX 3 */ | ||
88 | {{1,1,0},{3,1,0}}, /* Radio */ | ||
89 | {{1,1,0},{2,1,0}}, /* CD-Rom */ | ||
90 | {{6,1,0},{6,1,0}} /* Mute */ | ||
91 | }; | ||
92 | |||
93 | /* these are the necessary input-output-pins for bringing one audio source | ||
94 | (see above) to the line-output */ | ||
95 | static struct tea6420_multiplex TEA6420_line[MXB_AUDIOS+1][2] = | ||
96 | { | ||
97 | {{2,3,0},{1,2,0}}, | ||
98 | {{5,3,0},{6,2,0}}, | ||
99 | {{4,3,0},{6,2,0}}, | ||
100 | {{3,3,0},{6,2,0}}, | ||
101 | {{2,3,0},{3,2,0}}, | ||
102 | {{2,3,0},{2,2,0}}, | ||
103 | {{6,3,0},{6,2,0}} /* Mute */ | ||
104 | }; | ||
105 | |||
106 | #define MAXCONTROLS 1 | ||
107 | static struct v4l2_queryctrl mxb_controls[] = { | ||
108 | { V4L2_CID_AUDIO_MUTE, V4L2_CTRL_TYPE_BOOLEAN, "Mute", 0, 1, 1, 0, 0 }, | ||
109 | }; | ||
110 | |||
111 | static struct saa7146_extension_ioctls ioctls[] = { | ||
112 | { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE }, | ||
113 | { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE }, | ||
114 | { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE }, | ||
115 | { VIDIOC_QUERYCTRL, SAA7146_BEFORE }, | ||
116 | { VIDIOC_G_CTRL, SAA7146_BEFORE }, | ||
117 | { VIDIOC_S_CTRL, SAA7146_BEFORE }, | ||
118 | { VIDIOC_G_TUNER, SAA7146_EXCLUSIVE }, | ||
119 | { VIDIOC_S_TUNER, SAA7146_EXCLUSIVE }, | ||
120 | { VIDIOC_G_FREQUENCY, SAA7146_EXCLUSIVE }, | ||
121 | { VIDIOC_S_FREQUENCY, SAA7146_EXCLUSIVE }, | ||
122 | { VIDIOC_G_AUDIO, SAA7146_EXCLUSIVE }, | ||
123 | { VIDIOC_S_AUDIO, SAA7146_EXCLUSIVE }, | ||
124 | { MXB_S_AUDIO_CD, SAA7146_EXCLUSIVE }, /* custom control */ | ||
125 | { MXB_S_AUDIO_LINE, SAA7146_EXCLUSIVE }, /* custom control */ | ||
126 | { 0, 0 } | ||
127 | }; | ||
128 | |||
129 | struct mxb | ||
130 | { | ||
131 | struct video_device *video_dev; | ||
132 | struct video_device *vbi_dev; | ||
133 | |||
134 | struct i2c_adapter i2c_adapter; | ||
135 | |||
136 | struct i2c_client* saa7111a; | ||
137 | struct i2c_client* tda9840; | ||
138 | struct i2c_client* tea6415c; | ||
139 | struct i2c_client* tuner; | ||
140 | struct i2c_client* tea6420_1; | ||
141 | struct i2c_client* tea6420_2; | ||
142 | |||
143 | int cur_mode; /* current audio mode (mono, stereo, ...) */ | ||
144 | int cur_input; /* current input */ | ||
145 | int cur_freq; /* current frequency the tuner is tuned to */ | ||
146 | int cur_mute; /* current mute status */ | ||
147 | }; | ||
148 | |||
149 | static struct saa7146_extension extension; | ||
150 | |||
151 | static int mxb_probe(struct saa7146_dev* dev) | ||
152 | { | ||
153 | struct mxb* mxb = NULL; | ||
154 | struct i2c_client *client; | ||
155 | struct list_head *item; | ||
156 | int result; | ||
157 | |||
158 | if ((result = request_module("saa7111")) < 0) { | ||
159 | printk("mxb: saa7111 i2c module not available.\n"); | ||
160 | return -ENODEV; | ||
161 | } | ||
162 | if ((result = request_module("tuner")) < 0) { | ||
163 | printk("mxb: tuner i2c module not available.\n"); | ||
164 | return -ENODEV; | ||
165 | } | ||
166 | if ((result = request_module("tea6420")) < 0) { | ||
167 | printk("mxb: tea6420 i2c module not available.\n"); | ||
168 | return -ENODEV; | ||
169 | } | ||
170 | if ((result = request_module("tea6415c")) < 0) { | ||
171 | printk("mxb: tea6415c i2c module not available.\n"); | ||
172 | return -ENODEV; | ||
173 | } | ||
174 | if ((result = request_module("tda9840")) < 0) { | ||
175 | printk("mxb: tda9840 i2c module not available.\n"); | ||
176 | return -ENODEV; | ||
177 | } | ||
178 | |||
179 | mxb = (struct mxb*)kmalloc(sizeof(struct mxb), GFP_KERNEL); | ||
180 | if( NULL == mxb ) { | ||
181 | DEB_D(("not enough kernel memory.\n")); | ||
182 | return -ENOMEM; | ||
183 | } | ||
184 | memset(mxb, 0x0, sizeof(struct mxb)); | ||
185 | |||
186 | mxb->i2c_adapter = (struct i2c_adapter) { | ||
187 | .class = I2C_CLASS_TV_ANALOG, | ||
188 | .name = "mxb", | ||
189 | }; | ||
190 | |||
191 | saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); | ||
192 | if(i2c_add_adapter(&mxb->i2c_adapter) < 0) { | ||
193 | DEB_S(("cannot register i2c-device. skipping.\n")); | ||
194 | kfree(mxb); | ||
195 | return -EFAULT; | ||
196 | } | ||
197 | |||
198 | /* loop through all i2c-devices on the bus and look who is there */ | ||
199 | list_for_each(item,&mxb->i2c_adapter.clients) { | ||
200 | client = list_entry(item, struct i2c_client, list); | ||
201 | if( I2C_TEA6420_1 == client->addr ) | ||
202 | mxb->tea6420_1 = client; | ||
203 | if( I2C_TEA6420_2 == client->addr ) | ||
204 | mxb->tea6420_2 = client; | ||
205 | if( I2C_TEA6415C_2 == client->addr ) | ||
206 | mxb->tea6415c = client; | ||
207 | if( I2C_TDA9840 == client->addr ) | ||
208 | mxb->tda9840 = client; | ||
209 | if( I2C_SAA7111 == client->addr ) | ||
210 | mxb->saa7111a = client; | ||
211 | if( 0x60 == client->addr ) | ||
212 | mxb->tuner = client; | ||
213 | } | ||
214 | |||
215 | /* check if all devices are present */ | ||
216 | if( 0 == mxb->tea6420_1 || 0 == mxb->tea6420_2 || 0 == mxb->tea6415c | ||
217 | || 0 == mxb->tda9840 || 0 == mxb->saa7111a || 0 == mxb->tuner ) { | ||
218 | |||
219 | printk("mxb: did not find all i2c devices. aborting\n"); | ||
220 | i2c_del_adapter(&mxb->i2c_adapter); | ||
221 | kfree(mxb); | ||
222 | return -ENODEV; | ||
223 | } | ||
224 | |||
225 | /* all devices are present, probe was successful */ | ||
226 | |||
227 | /* we store the pointer in our private data field */ | ||
228 | dev->ext_priv = mxb; | ||
229 | |||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | /* some init data for the saa7740, the so-called 'sound arena module'. | ||
234 | there are no specs available, so we simply use some init values */ | ||
235 | static struct { | ||
236 | int length; | ||
237 | char data[9]; | ||
238 | } mxb_saa7740_init[] = { | ||
239 | { 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } }, | ||
240 | { 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } }, | ||
241 | { 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } }, | ||
242 | { 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } }, | ||
243 | { 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } }, | ||
244 | { 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } }, | ||
245 | { 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } }, | ||
246 | { 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } }, | ||
247 | { 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } }, | ||
248 | { 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } }, | ||
249 | { 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } }, | ||
250 | { 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } }, | ||
251 | { 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } }, | ||
252 | { 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } }, | ||
253 | { 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } }, | ||
254 | { 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } }, | ||
255 | { 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } }, | ||
256 | { 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } }, | ||
257 | { 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } }, | ||
258 | { 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } }, | ||
259 | { 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } }, | ||
260 | { 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } }, | ||
261 | { 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } }, | ||
262 | { 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } }, | ||
263 | { 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } }, | ||
264 | { 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } }, | ||
265 | { 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } }, | ||
266 | { 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } }, | ||
267 | { 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } }, | ||
268 | { 3, { 0x48, 0x00, 0x01 } }, | ||
269 | { 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, | ||
270 | { 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, | ||
271 | { 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, | ||
272 | { 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, | ||
273 | { 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, | ||
274 | { 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, | ||
275 | { 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, | ||
276 | { 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, | ||
277 | { 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, | ||
278 | { 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, | ||
279 | { 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, | ||
280 | { 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, | ||
281 | { 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, | ||
282 | { 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, | ||
283 | { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, | ||
284 | { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, | ||
285 | { 3, { 0x80, 0xb3, 0x0a } }, | ||
286 | {-1, { 0} } | ||
287 | }; | ||
288 | |||
289 | static const unsigned char mxb_saa7111_init[] = { | ||
290 | 0x00, 0x00, /* 00 - ID byte */ | ||
291 | 0x01, 0x00, /* 01 - reserved */ | ||
292 | |||
293 | /*front end */ | ||
294 | 0x02, 0xd8, /* 02 - FUSE=x, GUDL=x, MODE=x */ | ||
295 | 0x03, 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */ | ||
296 | 0x04, 0x00, /* 04 - GAI1=256 */ | ||
297 | 0x05, 0x00, /* 05 - GAI2=256 */ | ||
298 | |||
299 | /* decoder */ | ||
300 | 0x06, 0xf0, /* 06 - HSB at xx(50Hz) / xx(60Hz) pixels after end of last line */ | ||
301 | 0x07, 0x30, /* 07 - HSS at xx(50Hz) / xx(60Hz) pixels after end of last line */ | ||
302 | 0x08, 0xa8, /* 08 - AUFD=x, FSEL=x, EXFIL=x, VTRC=x, HPLL=x, VNOI=x */ | ||
303 | 0x09, 0x02, /* 09 - BYPS=x, PREF=x, BPSS=x, VBLB=x, UPTCV=x, APER=x */ | ||
304 | 0x0a, 0x80, /* 0a - BRIG=128 */ | ||
305 | 0x0b, 0x47, /* 0b - CONT=1.109 */ | ||
306 | 0x0c, 0x40, /* 0c - SATN=1.0 */ | ||
307 | 0x0d, 0x00, /* 0d - HUE=0 */ | ||
308 | 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */ | ||
309 | 0x0f, 0x00, /* 0f - reserved */ | ||
310 | 0x10, 0xd0, /* 10 - OFTS=x, HDEL=x, VRLN=x, YDEL=x */ | ||
311 | 0x11, 0x8c, /* 11 - GPSW=x, CM99=x, FECO=x, COMPO=x, OEYC=1, OEHV=1, VIPB=0, COLO=0 */ | ||
312 | 0x12, 0x80, /* 12 - xx output control 2 */ | ||
313 | 0x13, 0x30, /* 13 - xx output control 3 */ | ||
314 | 0x14, 0x00, /* 14 - reserved */ | ||
315 | 0x15, 0x15, /* 15 - VBI */ | ||
316 | 0x16, 0x04, /* 16 - VBI */ | ||
317 | 0x17, 0x00, /* 17 - VBI */ | ||
318 | }; | ||
319 | |||
320 | /* bring hardware to a sane state. this has to be done, just in case someone | ||
321 | wants to capture from this device before it has been properly initialized. | ||
322 | the capture engine would badly fail, because no valid signal arrives on the | ||
323 | saa7146, thus leading to timeouts and stuff. */ | ||
324 | static int mxb_init_done(struct saa7146_dev* dev) | ||
325 | { | ||
326 | struct mxb* mxb = (struct mxb*)dev->ext_priv; | ||
327 | struct video_decoder_init init; | ||
328 | struct i2c_msg msg; | ||
329 | |||
330 | int i = 0, err = 0; | ||
331 | struct tea6415c_multiplex vm; | ||
332 | |||
333 | /* select video mode in saa7111a */ | ||
334 | i = VIDEO_MODE_PAL; | ||
335 | /* fixme: currently pointless: gets overwritten by configuration below */ | ||
336 | mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_NORM, &i); | ||
337 | |||
338 | /* write configuration to saa7111a */ | ||
339 | init.data = mxb_saa7111_init; | ||
340 | init.len = sizeof(mxb_saa7111_init); | ||
341 | mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_INIT, &init); | ||
342 | |||
343 | /* select tuner-output on saa7111a */ | ||
344 | i = 0; | ||
345 | mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_INPUT, &i); | ||
346 | |||
347 | /* enable vbi bypass */ | ||
348 | i = 1; | ||
349 | mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_VBI_BYPASS, &i); | ||
350 | |||
351 | /* select a tuner type */ | ||
352 | i = 5; | ||
353 | mxb->tuner->driver->command(mxb->tuner,TUNER_SET_TYPE, &i); | ||
354 | |||
355 | /* mute audio on tea6420s */ | ||
356 | mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[6][0]); | ||
357 | mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[6][1]); | ||
358 | mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_cd[6][0]); | ||
359 | mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_cd[6][1]); | ||
360 | |||
361 | /* switch to tuner-channel on tea6415c*/ | ||
362 | vm.out = 17; | ||
363 | vm.in = 3; | ||
364 | mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm); | ||
365 | |||
366 | /* select tuner-output on multicable on tea6415c*/ | ||
367 | vm.in = 3; | ||
368 | vm.out = 13; | ||
369 | mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm); | ||
370 | |||
371 | /* tune in some frequency on tuner */ | ||
372 | mxb->tuner->driver->command(mxb->tuner, VIDIOCSFREQ, &freq); | ||
373 | |||
374 | /* the rest for mxb */ | ||
375 | mxb->cur_input = 0; | ||
376 | mxb->cur_freq = freq; | ||
377 | mxb->cur_mute = 1; | ||
378 | |||
379 | mxb->cur_mode = V4L2_TUNER_MODE_STEREO; | ||
380 | mxb->tda9840->driver->command(mxb->tda9840, TDA9840_SWITCH, &mxb->cur_mode); | ||
381 | |||
382 | /* check if the saa7740 (aka 'sound arena module') is present | ||
383 | on the mxb. if so, we must initialize it. due to lack of | ||
384 | informations about the saa7740, the values were reverse | ||
385 | engineered. */ | ||
386 | msg.addr = 0x1b; | ||
387 | msg.flags = 0; | ||
388 | msg.len = mxb_saa7740_init[0].length; | ||
389 | msg.buf = &mxb_saa7740_init[0].data[0]; | ||
390 | |||
391 | if( 1 == (err = i2c_transfer(&mxb->i2c_adapter, &msg, 1))) { | ||
392 | /* the sound arena module is a pos, that's probably the reason | ||
393 | philips refuses to hand out a datasheet for the saa7740... | ||
394 | it seems to screw up the i2c bus, so we disable fast irq | ||
395 | based i2c transactions here and rely on the slow and safe | ||
396 | polling method ... */ | ||
397 | extension.flags &= ~SAA7146_USE_I2C_IRQ; | ||
398 | for(i = 1;;i++) { | ||
399 | if( -1 == mxb_saa7740_init[i].length ) { | ||
400 | break; | ||
401 | } | ||
402 | |||
403 | msg.len = mxb_saa7740_init[i].length; | ||
404 | msg.buf = &mxb_saa7740_init[i].data[0]; | ||
405 | if( 1 != (err = i2c_transfer(&mxb->i2c_adapter, &msg, 1))) { | ||
406 | DEB_D(("failed to initialize 'sound arena module'.\n")); | ||
407 | goto err; | ||
408 | } | ||
409 | } | ||
410 | INFO(("'sound arena module' detected.\n")); | ||
411 | } | ||
412 | err: | ||
413 | /* the rest for saa7146: you should definitely set some basic values | ||
414 | for the input-port handling of the saa7146. */ | ||
415 | |||
416 | /* ext->saa has been filled by the core driver */ | ||
417 | |||
418 | /* some stuff is done via variables */ | ||
419 | saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source, input_port_selection[mxb->cur_input].hps_sync); | ||
420 | |||
421 | /* some stuff is done via direct write to the registers */ | ||
422 | |||
423 | /* this is ugly, but because of the fact that this is completely | ||
424 | hardware dependend, it should be done directly... */ | ||
425 | saa7146_write(dev, DD1_STREAM_B, 0x00000000); | ||
426 | saa7146_write(dev, DD1_INIT, 0x02000200); | ||
427 | saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); | ||
428 | |||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | /* interrupt-handler. this gets called when irq_mask is != 0. | ||
433 | it must clear the interrupt-bits in irq_mask it has handled */ | ||
434 | /* | ||
435 | void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask) | ||
436 | { | ||
437 | struct mxb* mxb = (struct mxb*)dev->ext_priv; | ||
438 | } | ||
439 | */ | ||
440 | |||
441 | static struct saa7146_ext_vv vv_data; | ||
442 | |||
443 | /* this function only gets called when the probing was successful */ | ||
444 | static int mxb_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) | ||
445 | { | ||
446 | struct mxb* mxb = (struct mxb*)dev->ext_priv; | ||
447 | |||
448 | DEB_EE(("dev:%p\n",dev)); | ||
449 | |||
450 | /* checking for i2c-devices can be omitted here, because we | ||
451 | already did this in "mxb_vl42_probe" */ | ||
452 | |||
453 | saa7146_vv_init(dev,&vv_data); | ||
454 | if( 0 != saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { | ||
455 | ERR(("cannot register capture v4l2 device. skipping.\n")); | ||
456 | return -1; | ||
457 | } | ||
458 | |||
459 | /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ | ||
460 | if( 0 != MXB_BOARD_CAN_DO_VBI(dev)) { | ||
461 | if( 0 != saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) { | ||
462 | ERR(("cannot register vbi v4l2 device. skipping.\n")); | ||
463 | } | ||
464 | } | ||
465 | |||
466 | i2c_use_client(mxb->tea6420_1); | ||
467 | i2c_use_client(mxb->tea6420_2); | ||
468 | i2c_use_client(mxb->tea6415c); | ||
469 | i2c_use_client(mxb->tda9840); | ||
470 | i2c_use_client(mxb->saa7111a); | ||
471 | i2c_use_client(mxb->tuner); | ||
472 | |||
473 | printk("mxb: found 'Multimedia eXtension Board'-%d.\n",mxb_num); | ||
474 | |||
475 | mxb_num++; | ||
476 | mxb_init_done(dev); | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | static int mxb_detach(struct saa7146_dev* dev) | ||
481 | { | ||
482 | struct mxb* mxb = (struct mxb*)dev->ext_priv; | ||
483 | |||
484 | DEB_EE(("dev:%p\n",dev)); | ||
485 | |||
486 | i2c_release_client(mxb->tea6420_1); | ||
487 | i2c_release_client(mxb->tea6420_2); | ||
488 | i2c_release_client(mxb->tea6415c); | ||
489 | i2c_release_client(mxb->tda9840); | ||
490 | i2c_release_client(mxb->saa7111a); | ||
491 | i2c_release_client(mxb->tuner); | ||
492 | |||
493 | saa7146_unregister_device(&mxb->video_dev,dev); | ||
494 | if( 0 != MXB_BOARD_CAN_DO_VBI(dev)) { | ||
495 | saa7146_unregister_device(&mxb->vbi_dev,dev); | ||
496 | } | ||
497 | saa7146_vv_release(dev); | ||
498 | |||
499 | mxb_num--; | ||
500 | |||
501 | i2c_del_adapter(&mxb->i2c_adapter); | ||
502 | kfree(mxb); | ||
503 | |||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) | ||
508 | { | ||
509 | struct saa7146_dev *dev = fh->dev; | ||
510 | struct mxb* mxb = (struct mxb*)dev->ext_priv; | ||
511 | struct saa7146_vv *vv = dev->vv_data; | ||
512 | |||
513 | switch(cmd) { | ||
514 | case VIDIOC_ENUMINPUT: | ||
515 | { | ||
516 | struct v4l2_input *i = arg; | ||
517 | |||
518 | DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index)); | ||
519 | if( i->index < 0 || i->index >= MXB_INPUTS) { | ||
520 | return -EINVAL; | ||
521 | } | ||
522 | memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); | ||
523 | |||
524 | return 0; | ||
525 | } | ||
526 | /* the saa7146 provides some controls (brightness, contrast, saturation) | ||
527 | which gets registered *after* this function. because of this we have | ||
528 | to return with a value != 0 even if the function succeded.. */ | ||
529 | case VIDIOC_QUERYCTRL: | ||
530 | { | ||
531 | struct v4l2_queryctrl *qc = arg; | ||
532 | int i; | ||
533 | |||
534 | for (i = MAXCONTROLS - 1; i >= 0; i--) { | ||
535 | if (mxb_controls[i].id == qc->id) { | ||
536 | *qc = mxb_controls[i]; | ||
537 | DEB_D(("VIDIOC_QUERYCTRL %d.\n",qc->id)); | ||
538 | return 0; | ||
539 | } | ||
540 | } | ||
541 | return -EAGAIN; | ||
542 | } | ||
543 | case VIDIOC_G_CTRL: | ||
544 | { | ||
545 | struct v4l2_control *vc = arg; | ||
546 | int i; | ||
547 | |||
548 | for (i = MAXCONTROLS - 1; i >= 0; i--) { | ||
549 | if (mxb_controls[i].id == vc->id) { | ||
550 | break; | ||
551 | } | ||
552 | } | ||
553 | |||
554 | if( i < 0 ) { | ||
555 | return -EAGAIN; | ||
556 | } | ||
557 | |||
558 | switch (vc->id ) { | ||
559 | case V4L2_CID_AUDIO_MUTE: { | ||
560 | vc->value = mxb->cur_mute; | ||
561 | DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n",vc->value)); | ||
562 | return 0; | ||
563 | } | ||
564 | } | ||
565 | |||
566 | DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n",vc->value)); | ||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | case VIDIOC_S_CTRL: | ||
571 | { | ||
572 | struct v4l2_control *vc = arg; | ||
573 | int i = 0; | ||
574 | |||
575 | for (i = MAXCONTROLS - 1; i >= 0; i--) { | ||
576 | if (mxb_controls[i].id == vc->id) { | ||
577 | break; | ||
578 | } | ||
579 | } | ||
580 | |||
581 | if( i < 0 ) { | ||
582 | return -EAGAIN; | ||
583 | } | ||
584 | |||
585 | switch (vc->id ) { | ||
586 | case V4L2_CID_AUDIO_MUTE: { | ||
587 | mxb->cur_mute = vc->value; | ||
588 | if( 0 == vc->value ) { | ||
589 | /* switch the audio-source */ | ||
590 | mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][0]); | ||
591 | mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][1]); | ||
592 | } else { | ||
593 | mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[6][0]); | ||
594 | mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[6][1]); | ||
595 | } | ||
596 | DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n",vc->value)); | ||
597 | break; | ||
598 | } | ||
599 | } | ||
600 | return 0; | ||
601 | } | ||
602 | case VIDIOC_G_INPUT: | ||
603 | { | ||
604 | int *input = (int *)arg; | ||
605 | *input = mxb->cur_input; | ||
606 | |||
607 | DEB_EE(("VIDIOC_G_INPUT %d.\n",*input)); | ||
608 | return 0; | ||
609 | } | ||
610 | case VIDIOC_S_INPUT: | ||
611 | { | ||
612 | int input = *(int *)arg; | ||
613 | struct tea6415c_multiplex vm; | ||
614 | int i = 0; | ||
615 | |||
616 | DEB_EE(("VIDIOC_S_INPUT %d.\n",input)); | ||
617 | |||
618 | if (input < 0 || input >= MXB_INPUTS) { | ||
619 | return -EINVAL; | ||
620 | } | ||
621 | |||
622 | /* fixme: locke das setzen des inputs mit hilfe des mutexes | ||
623 | down(&dev->lock); | ||
624 | video_mux(dev,*i); | ||
625 | up(&dev->lock); | ||
626 | */ | ||
627 | |||
628 | /* fixme: check if streaming capture | ||
629 | if ( 0 != dev->streaming ) { | ||
630 | DEB_D(("VIDIOC_S_INPUT illegal while streaming.\n")); | ||
631 | return -EPERM; | ||
632 | } | ||
633 | */ | ||
634 | |||
635 | mxb->cur_input = input; | ||
636 | |||
637 | saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, input_port_selection[input].hps_sync); | ||
638 | |||
639 | /* prepare switching of tea6415c and saa7111a; | ||
640 | have a look at the 'background'-file for further informations */ | ||
641 | switch( input ) { | ||
642 | |||
643 | case TUNER: | ||
644 | { | ||
645 | i = 0; | ||
646 | vm.in = 3; | ||
647 | vm.out = 17; | ||
648 | |||
649 | if ( 0 != mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm)) { | ||
650 | printk("VIDIOC_S_INPUT: could not address tea6415c #1\n"); | ||
651 | return -EFAULT; | ||
652 | } | ||
653 | /* connect tuner-output always to multicable */ | ||
654 | vm.in = 3; | ||
655 | vm.out = 13; | ||
656 | break; | ||
657 | } | ||
658 | case AUX3_YC: | ||
659 | { | ||
660 | /* nothing to be done here. aux3_yc is | ||
661 | directly connected to the saa711a */ | ||
662 | i = 5; | ||
663 | break; | ||
664 | } | ||
665 | case AUX3: | ||
666 | { | ||
667 | /* nothing to be done here. aux3 is | ||
668 | directly connected to the saa711a */ | ||
669 | i = 1; | ||
670 | break; | ||
671 | } | ||
672 | case AUX1: | ||
673 | { | ||
674 | i = 0; | ||
675 | vm.in = 1; | ||
676 | vm.out = 17; | ||
677 | break; | ||
678 | } | ||
679 | } | ||
680 | |||
681 | /* switch video in tea6415c only if necessary */ | ||
682 | switch( input ) { | ||
683 | case TUNER: | ||
684 | case AUX1: | ||
685 | { | ||
686 | if ( 0 != mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm)) { | ||
687 | printk("VIDIOC_S_INPUT: could not address tea6415c #3\n"); | ||
688 | return -EFAULT; | ||
689 | } | ||
690 | break; | ||
691 | } | ||
692 | default: | ||
693 | { | ||
694 | break; | ||
695 | } | ||
696 | } | ||
697 | |||
698 | /* switch video in saa7111a */ | ||
699 | if ( 0 != mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_INPUT, &i)) { | ||
700 | printk("VIDIOC_S_INPUT: could not address saa7111a #1.\n"); | ||
701 | } | ||
702 | |||
703 | /* switch the audio-source only if necessary */ | ||
704 | if( 0 == mxb->cur_mute ) { | ||
705 | mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][0]); | ||
706 | mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][1]); | ||
707 | } | ||
708 | |||
709 | return 0; | ||
710 | } | ||
711 | case VIDIOC_G_TUNER: | ||
712 | { | ||
713 | struct v4l2_tuner *t = arg; | ||
714 | int byte = 0; | ||
715 | |||
716 | if( 0 != t->index ) { | ||
717 | DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index)); | ||
718 | return -EINVAL; | ||
719 | } | ||
720 | |||
721 | DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index)); | ||
722 | |||
723 | memset(t,0,sizeof(*t)); | ||
724 | strcpy(t->name, "Television"); | ||
725 | |||
726 | t->type = V4L2_TUNER_ANALOG_TV; | ||
727 | t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; | ||
728 | t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */ | ||
729 | t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */ | ||
730 | /* FIXME: add the real signal strength here */ | ||
731 | t->signal = 0xffff; | ||
732 | t->afc = 0; | ||
733 | |||
734 | mxb->tda9840->driver->command(mxb->tda9840,TDA9840_DETECT, &byte); | ||
735 | t->audmode = mxb->cur_mode; | ||
736 | |||
737 | if( byte < 0 ) { | ||
738 | t->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
739 | } else { | ||
740 | switch(byte) { | ||
741 | case TDA9840_MONO_DETECT: { | ||
742 | t->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
743 | DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_MONO.\n")); | ||
744 | break; | ||
745 | } | ||
746 | case TDA9840_DUAL_DETECT: { | ||
747 | t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; | ||
748 | DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_LANG1.\n")); | ||
749 | break; | ||
750 | } | ||
751 | case TDA9840_STEREO_DETECT: { | ||
752 | t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; | ||
753 | DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_STEREO.\n")); | ||
754 | break; | ||
755 | } | ||
756 | default: { /* TDA9840_INCORRECT_DETECT */ | ||
757 | t->rxsubchans = V4L2_TUNER_MODE_MONO; | ||
758 | DEB_D(("VIDIOC_G_TUNER: TDA9840_INCORRECT_DETECT => V4L2_TUNER_MODE_MONO\n")); | ||
759 | break; | ||
760 | } | ||
761 | } | ||
762 | } | ||
763 | |||
764 | return 0; | ||
765 | } | ||
766 | case VIDIOC_S_TUNER: | ||
767 | { | ||
768 | struct v4l2_tuner *t = arg; | ||
769 | int result = 0; | ||
770 | int byte = 0; | ||
771 | |||
772 | if( 0 != t->index ) { | ||
773 | DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n",t->index)); | ||
774 | return -EINVAL; | ||
775 | } | ||
776 | |||
777 | switch(t->audmode) { | ||
778 | case V4L2_TUNER_MODE_STEREO: { | ||
779 | mxb->cur_mode = V4L2_TUNER_MODE_STEREO; | ||
780 | byte = TDA9840_SET_STEREO; | ||
781 | DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n")); | ||
782 | break; | ||
783 | } | ||
784 | case V4L2_TUNER_MODE_LANG1: { | ||
785 | mxb->cur_mode = V4L2_TUNER_MODE_LANG1; | ||
786 | byte = TDA9840_SET_LANG1; | ||
787 | DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n")); | ||
788 | break; | ||
789 | } | ||
790 | case V4L2_TUNER_MODE_LANG2: { | ||
791 | mxb->cur_mode = V4L2_TUNER_MODE_LANG2; | ||
792 | byte = TDA9840_SET_LANG2; | ||
793 | DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n")); | ||
794 | break; | ||
795 | } | ||
796 | default: { /* case V4L2_TUNER_MODE_MONO: {*/ | ||
797 | mxb->cur_mode = V4L2_TUNER_MODE_MONO; | ||
798 | byte = TDA9840_SET_MONO; | ||
799 | DEB_D(("VIDIOC_S_TUNER: TDA9840_SET_MONO\n")); | ||
800 | break; | ||
801 | } | ||
802 | } | ||
803 | |||
804 | if( 0 != (result = mxb->tda9840->driver->command(mxb->tda9840, TDA9840_SWITCH, &byte))) { | ||
805 | printk("VIDIOC_S_TUNER error. result:%d, byte:%d\n",result,byte); | ||
806 | } | ||
807 | |||
808 | return 0; | ||
809 | } | ||
810 | case VIDIOC_G_FREQUENCY: | ||
811 | { | ||
812 | struct v4l2_frequency *f = arg; | ||
813 | |||
814 | if(0 != mxb->cur_input) { | ||
815 | DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",mxb->cur_input)); | ||
816 | return -EINVAL; | ||
817 | } | ||
818 | |||
819 | memset(f,0,sizeof(*f)); | ||
820 | f->type = V4L2_TUNER_ANALOG_TV; | ||
821 | f->frequency = mxb->cur_freq; | ||
822 | |||
823 | DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq)); | ||
824 | return 0; | ||
825 | } | ||
826 | case VIDIOC_S_FREQUENCY: | ||
827 | { | ||
828 | struct v4l2_frequency *f = arg; | ||
829 | int t_locked = 0; | ||
830 | int v_byte = 0; | ||
831 | |||
832 | if (0 != f->tuner) | ||
833 | return -EINVAL; | ||
834 | |||
835 | if (V4L2_TUNER_ANALOG_TV != f->type) | ||
836 | return -EINVAL; | ||
837 | |||
838 | if(0 != mxb->cur_input) { | ||
839 | DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n",mxb->cur_input)); | ||
840 | return -EINVAL; | ||
841 | } | ||
842 | |||
843 | DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n",f->frequency)); | ||
844 | |||
845 | mxb->cur_freq = f->frequency; | ||
846 | |||
847 | /* tune in desired frequency */ | ||
848 | mxb->tuner->driver->command(mxb->tuner, VIDIOCSFREQ, &mxb->cur_freq); | ||
849 | |||
850 | /* check if pll of tuner & saa7111a is locked */ | ||
851 | // mxb->tuner->driver->command(mxb->tuner,TUNER_IS_LOCKED, &t_locked); | ||
852 | mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_GET_STATUS, &v_byte); | ||
853 | |||
854 | /* not locked -- anything to do here ? */ | ||
855 | if( 0 == t_locked || 0 == (v_byte & DECODER_STATUS_GOOD)) { | ||
856 | } | ||
857 | |||
858 | /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */ | ||
859 | spin_lock(&dev->slock); | ||
860 | vv->vbi_fieldcount = 0; | ||
861 | spin_unlock(&dev->slock); | ||
862 | |||
863 | return 0; | ||
864 | } | ||
865 | case MXB_S_AUDIO_CD: | ||
866 | { | ||
867 | int i = *(int*)arg; | ||
868 | |||
869 | if( i < 0 || i >= MXB_AUDIOS ) { | ||
870 | DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n",i)); | ||
871 | return -EINVAL; | ||
872 | } | ||
873 | |||
874 | DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n",i)); | ||
875 | |||
876 | mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_cd[i][0]); | ||
877 | mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_cd[i][1]); | ||
878 | |||
879 | return 0; | ||
880 | } | ||
881 | case MXB_S_AUDIO_LINE: | ||
882 | { | ||
883 | int i = *(int*)arg; | ||
884 | |||
885 | if( i < 0 || i >= MXB_AUDIOS ) { | ||
886 | DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n",i)); | ||
887 | return -EINVAL; | ||
888 | } | ||
889 | |||
890 | DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n",i)); | ||
891 | mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[i][0]); | ||
892 | mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[i][1]); | ||
893 | |||
894 | return 0; | ||
895 | } | ||
896 | case VIDIOC_G_AUDIO: | ||
897 | { | ||
898 | struct v4l2_audio *a = arg; | ||
899 | |||
900 | if( a->index < 0 || a->index > MXB_INPUTS ) { | ||
901 | DEB_D(("VIDIOC_G_AUDIO %d out of range.\n",a->index)); | ||
902 | return -EINVAL; | ||
903 | } | ||
904 | |||
905 | DEB_EE(("VIDIOC_G_AUDIO %d.\n",a->index)); | ||
906 | memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio)); | ||
907 | |||
908 | return 0; | ||
909 | } | ||
910 | case VIDIOC_S_AUDIO: | ||
911 | { | ||
912 | struct v4l2_audio *a = arg; | ||
913 | DEB_D(("VIDIOC_S_AUDIO %d.\n",a->index)); | ||
914 | return 0; | ||
915 | } | ||
916 | default: | ||
917 | /* | ||
918 | DEB2(printk("does not handle this ioctl.\n")); | ||
919 | */ | ||
920 | return -ENOIOCTLCMD; | ||
921 | } | ||
922 | return 0; | ||
923 | } | ||
924 | |||
925 | static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) | ||
926 | { | ||
927 | struct mxb* mxb = (struct mxb*)dev->ext_priv; | ||
928 | int zero = 0; | ||
929 | int one = 1; | ||
930 | |||
931 | if(V4L2_STD_PAL_I == std->id ) { | ||
932 | DEB_D(("VIDIOC_S_STD: setting mxb for PAL_I.\n")); | ||
933 | /* set the 7146 gpio register -- I don't know what this does exactly */ | ||
934 | saa7146_write(dev, GPIO_CTRL, 0x00404050); | ||
935 | /* unset the 7111 gpio register -- I don't know what this does exactly */ | ||
936 | mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_GPIO, &zero); | ||
937 | } else { | ||
938 | DEB_D(("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM.\n")); | ||
939 | /* set the 7146 gpio register -- I don't know what this does exactly */ | ||
940 | saa7146_write(dev, GPIO_CTRL, 0x00404050); | ||
941 | /* set the 7111 gpio register -- I don't know what this does exactly */ | ||
942 | mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_GPIO, &one); | ||
943 | } | ||
944 | return 0; | ||
945 | } | ||
946 | |||
947 | static struct saa7146_standard standard[] = { | ||
948 | { | ||
949 | .name = "PAL-BG", .id = V4L2_STD_PAL_BG, | ||
950 | .v_offset = 0x17, .v_field = 288, | ||
951 | .h_offset = 0x14, .h_pixels = 680, | ||
952 | .v_max_out = 576, .h_max_out = 768, | ||
953 | }, { | ||
954 | .name = "PAL-I", .id = V4L2_STD_PAL_I, | ||
955 | .v_offset = 0x17, .v_field = 288, | ||
956 | .h_offset = 0x14, .h_pixels = 680, | ||
957 | .v_max_out = 576, .h_max_out = 768, | ||
958 | }, { | ||
959 | .name = "NTSC", .id = V4L2_STD_NTSC, | ||
960 | .v_offset = 0x16, .v_field = 240, | ||
961 | .h_offset = 0x06, .h_pixels = 708, | ||
962 | .v_max_out = 480, .h_max_out = 640, | ||
963 | }, { | ||
964 | .name = "SECAM", .id = V4L2_STD_SECAM, | ||
965 | .v_offset = 0x14, .v_field = 288, | ||
966 | .h_offset = 0x14, .h_pixels = 720, | ||
967 | .v_max_out = 576, .h_max_out = 768, | ||
968 | } | ||
969 | }; | ||
970 | |||
971 | static struct saa7146_pci_extension_data mxb = { | ||
972 | .ext_priv = "Multimedia eXtension Board", | ||
973 | .ext = &extension, | ||
974 | }; | ||
975 | |||
976 | static struct pci_device_id pci_tbl[] = { | ||
977 | { | ||
978 | .vendor = PCI_VENDOR_ID_PHILIPS, | ||
979 | .device = PCI_DEVICE_ID_PHILIPS_SAA7146, | ||
980 | .subvendor = 0x0000, | ||
981 | .subdevice = 0x0000, | ||
982 | .driver_data = (unsigned long)&mxb, | ||
983 | }, { | ||
984 | .vendor = 0, | ||
985 | } | ||
986 | }; | ||
987 | |||
988 | MODULE_DEVICE_TABLE(pci, pci_tbl); | ||
989 | |||
990 | static struct saa7146_ext_vv vv_data = { | ||
991 | .inputs = MXB_INPUTS, | ||
992 | .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE, | ||
993 | .stds = &standard[0], | ||
994 | .num_stds = sizeof(standard)/sizeof(struct saa7146_standard), | ||
995 | .std_callback = &std_callback, | ||
996 | .ioctls = &ioctls[0], | ||
997 | .ioctl = mxb_ioctl, | ||
998 | }; | ||
999 | |||
1000 | static struct saa7146_extension extension = { | ||
1001 | .name = MXB_IDENTIFIER, | ||
1002 | .flags = SAA7146_USE_I2C_IRQ, | ||
1003 | |||
1004 | .pci_tbl = &pci_tbl[0], | ||
1005 | .module = THIS_MODULE, | ||
1006 | |||
1007 | .probe = mxb_probe, | ||
1008 | .attach = mxb_attach, | ||
1009 | .detach = mxb_detach, | ||
1010 | |||
1011 | .irq_mask = 0, | ||
1012 | .irq_func = NULL, | ||
1013 | }; | ||
1014 | |||
1015 | static int __init mxb_init_module(void) | ||
1016 | { | ||
1017 | if( 0 != saa7146_register_extension(&extension)) { | ||
1018 | DEB_S(("failed to register extension.\n")); | ||
1019 | return -ENODEV; | ||
1020 | } | ||
1021 | |||
1022 | return 0; | ||
1023 | } | ||
1024 | |||
1025 | static void __exit mxb_cleanup_module(void) | ||
1026 | { | ||
1027 | saa7146_unregister_extension(&extension); | ||
1028 | } | ||
1029 | |||
1030 | module_init(mxb_init_module); | ||
1031 | module_exit(mxb_cleanup_module); | ||
1032 | |||
1033 | MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'"); | ||
1034 | MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); | ||
1035 | MODULE_LICENSE("GPL"); | ||