1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | mxb - v4l2 driver for the Multimedia eXtension Board |
4 | |
5 | Copyright (C) 1998-2006 Michael Hunold <michael@mihu.de> |
6 | |
7 | Visit http://www.themm.net/~mihu/linux/saa7146/mxb.html |
8 | for further details about this card. |
9 | |
10 | */ |
11 | |
12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
13 | |
14 | #define DEBUG_VARIABLE debug |
15 | |
16 | #include <media/drv-intf/saa7146_vv.h> |
17 | #include <media/tuner.h> |
18 | #include <media/v4l2-common.h> |
19 | #include <media/i2c/saa7115.h> |
20 | #include <linux/module.h> |
21 | #include <linux/kernel.h> |
22 | |
23 | #include "tea6415c.h" |
24 | #include "tea6420.h" |
25 | |
26 | #define MXB_AUDIOS 6 |
27 | |
28 | #define I2C_SAA7111A 0x24 |
29 | #define I2C_TDA9840 0x42 |
30 | #define I2C_TEA6415C 0x43 |
31 | #define I2C_TEA6420_1 0x4c |
32 | #define I2C_TEA6420_2 0x4d |
33 | #define I2C_TUNER 0x60 |
34 | |
35 | #define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0) |
36 | |
37 | /* global variable */ |
38 | static int mxb_num; |
39 | |
40 | /* initial frequence the tuner will be tuned to. |
41 | in verden (lower saxony, germany) 4148 is a |
42 | channel called "phoenix" */ |
43 | static int freq = 4148; |
44 | module_param(freq, int, 0644); |
45 | MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup" ); |
46 | |
47 | static int debug; |
48 | module_param(debug, int, 0644); |
49 | MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)." ); |
50 | |
51 | #define MXB_STD (V4L2_STD_PAL_BG | V4L2_STD_PAL_I | V4L2_STD_SECAM | V4L2_STD_NTSC) |
52 | #define MXB_INPUTS 4 |
53 | enum { TUNER, AUX1, AUX3, AUX3_YC }; |
54 | |
55 | static struct v4l2_input mxb_inputs[MXB_INPUTS] = { |
56 | { TUNER, "Tuner" , V4L2_INPUT_TYPE_TUNER, 0x3f, 0, |
57 | V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD }, |
58 | { AUX1, "AUX1" , V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, |
59 | MXB_STD, 0, V4L2_IN_CAP_STD }, |
60 | { AUX3, "AUX3 Composite" , V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, |
61 | MXB_STD, 0, V4L2_IN_CAP_STD }, |
62 | { AUX3_YC, "AUX3 S-Video" , V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, |
63 | MXB_STD, 0, V4L2_IN_CAP_STD }, |
64 | }; |
65 | |
66 | /* this array holds the information, which port of the saa7146 each |
67 | input actually uses. the mxb uses port 0 for every input */ |
68 | static struct { |
69 | int hps_source; |
70 | int hps_sync; |
71 | } input_port_selection[MXB_INPUTS] = { |
72 | { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, |
73 | { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, |
74 | { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, |
75 | { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, |
76 | }; |
77 | |
78 | /* this array holds the information of the audio source (mxb_audios), |
79 | which has to be switched corresponding to the video source (mxb_channels) */ |
80 | static int video_audio_connect[MXB_INPUTS] = |
81 | { 0, 1, 3, 3 }; |
82 | |
83 | struct mxb_routing { |
84 | u32 input; |
85 | u32 output; |
86 | }; |
87 | |
88 | /* these are the available audio sources, which can switched |
89 | to the line- and cd-output individually */ |
90 | static struct v4l2_audio mxb_audios[MXB_AUDIOS] = { |
91 | { |
92 | .index = 0, |
93 | .name = "Tuner" , |
94 | .capability = V4L2_AUDCAP_STEREO, |
95 | } , { |
96 | .index = 1, |
97 | .name = "AUX1" , |
98 | .capability = V4L2_AUDCAP_STEREO, |
99 | } , { |
100 | .index = 2, |
101 | .name = "AUX2" , |
102 | .capability = V4L2_AUDCAP_STEREO, |
103 | } , { |
104 | .index = 3, |
105 | .name = "AUX3" , |
106 | .capability = V4L2_AUDCAP_STEREO, |
107 | } , { |
108 | .index = 4, |
109 | .name = "Radio (X9)" , |
110 | .capability = V4L2_AUDCAP_STEREO, |
111 | } , { |
112 | .index = 5, |
113 | .name = "CD-ROM (X10)" , |
114 | .capability = V4L2_AUDCAP_STEREO, |
115 | } |
116 | }; |
117 | |
118 | /* These are the necessary input-output-pins for bringing one audio source |
119 | (see above) to the CD-output. Note that gain is set to 0 in this table. */ |
120 | static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = { |
121 | { { 1, 1 }, { 1, 1 } }, /* Tuner */ |
122 | { { 5, 1 }, { 6, 1 } }, /* AUX 1 */ |
123 | { { 4, 1 }, { 6, 1 } }, /* AUX 2 */ |
124 | { { 3, 1 }, { 6, 1 } }, /* AUX 3 */ |
125 | { { 1, 1 }, { 3, 1 } }, /* Radio */ |
126 | { { 1, 1 }, { 2, 1 } }, /* CD-Rom */ |
127 | { { 6, 1 }, { 6, 1 } } /* Mute */ |
128 | }; |
129 | |
130 | /* These are the necessary input-output-pins for bringing one audio source |
131 | (see above) to the line-output. Note that gain is set to 0 in this table. */ |
132 | static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = { |
133 | { { 2, 3 }, { 1, 2 } }, |
134 | { { 5, 3 }, { 6, 2 } }, |
135 | { { 4, 3 }, { 6, 2 } }, |
136 | { { 3, 3 }, { 6, 2 } }, |
137 | { { 2, 3 }, { 3, 2 } }, |
138 | { { 2, 3 }, { 2, 2 } }, |
139 | { { 6, 3 }, { 6, 2 } } /* Mute */ |
140 | }; |
141 | |
142 | struct mxb |
143 | { |
144 | struct video_device video_dev; |
145 | struct video_device vbi_dev; |
146 | |
147 | struct i2c_adapter i2c_adapter; |
148 | |
149 | struct v4l2_subdev *saa7111a; |
150 | struct v4l2_subdev *tda9840; |
151 | struct v4l2_subdev *tea6415c; |
152 | struct v4l2_subdev *tuner; |
153 | struct v4l2_subdev *tea6420_1; |
154 | struct v4l2_subdev *tea6420_2; |
155 | |
156 | int cur_mode; /* current audio mode (mono, stereo, ...) */ |
157 | int cur_input; /* current input */ |
158 | int cur_audinput; /* current audio input */ |
159 | int cur_mute; /* current mute status */ |
160 | struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */ |
161 | }; |
162 | |
163 | #define saa7111a_call(mxb, o, f, args...) \ |
164 | v4l2_subdev_call(mxb->saa7111a, o, f, ##args) |
165 | #define tda9840_call(mxb, o, f, args...) \ |
166 | v4l2_subdev_call(mxb->tda9840, o, f, ##args) |
167 | #define tea6415c_call(mxb, o, f, args...) \ |
168 | v4l2_subdev_call(mxb->tea6415c, o, f, ##args) |
169 | #define tuner_call(mxb, o, f, args...) \ |
170 | v4l2_subdev_call(mxb->tuner, o, f, ##args) |
171 | #define call_all(dev, o, f, args...) \ |
172 | v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) |
173 | |
174 | static void mxb_update_audmode(struct mxb *mxb) |
175 | { |
176 | struct v4l2_tuner t = { |
177 | .audmode = mxb->cur_mode, |
178 | }; |
179 | |
180 | tda9840_call(mxb, tuner, s_tuner, &t); |
181 | } |
182 | |
183 | static inline void tea6420_route(struct mxb *mxb, int idx) |
184 | { |
185 | v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, |
186 | TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0); |
187 | v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, |
188 | TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0); |
189 | v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, |
190 | TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0); |
191 | v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, |
192 | TEA6420_line[idx][1].input, TEA6420_line[idx][1].output, 0); |
193 | } |
194 | |
195 | static struct saa7146_extension extension; |
196 | |
197 | static int mxb_s_ctrl(struct v4l2_ctrl *ctrl) |
198 | { |
199 | struct saa7146_dev *dev = container_of(ctrl->handler, |
200 | struct saa7146_dev, ctrl_handler); |
201 | struct mxb *mxb = dev->ext_priv; |
202 | |
203 | switch (ctrl->id) { |
204 | case V4L2_CID_AUDIO_MUTE: |
205 | mxb->cur_mute = ctrl->val; |
206 | /* switch the audio-source */ |
207 | tea6420_route(mxb, idx: ctrl->val ? 6 : |
208 | video_audio_connect[mxb->cur_input]); |
209 | break; |
210 | default: |
211 | return -EINVAL; |
212 | } |
213 | return 0; |
214 | } |
215 | |
216 | static const struct v4l2_ctrl_ops mxb_ctrl_ops = { |
217 | .s_ctrl = mxb_s_ctrl, |
218 | }; |
219 | |
220 | static int mxb_probe(struct saa7146_dev *dev) |
221 | { |
222 | struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; |
223 | struct mxb *mxb = NULL; |
224 | |
225 | v4l2_ctrl_new_std(hdl, ops: &mxb_ctrl_ops, |
226 | V4L2_CID_AUDIO_MUTE, min: 0, max: 1, step: 1, def: 1); |
227 | if (hdl->error) |
228 | return hdl->error; |
229 | mxb = kzalloc(size: sizeof(struct mxb), GFP_KERNEL); |
230 | if (mxb == NULL) { |
231 | DEB_D("not enough kernel memory\n" ); |
232 | return -ENOMEM; |
233 | } |
234 | |
235 | |
236 | snprintf(buf: mxb->i2c_adapter.name, size: sizeof(mxb->i2c_adapter.name), fmt: "mxb%d" , mxb_num); |
237 | |
238 | saa7146_i2c_adapter_prepare(dev, i2c_adapter: &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); |
239 | if (i2c_add_adapter(adap: &mxb->i2c_adapter) < 0) { |
240 | DEB_S("cannot register i2c-device. skipping.\n" ); |
241 | kfree(objp: mxb); |
242 | return -EFAULT; |
243 | } |
244 | |
245 | mxb->saa7111a = v4l2_i2c_new_subdev(v4l2_dev: &dev->v4l2_dev, adapter: &mxb->i2c_adapter, |
246 | client_type: "saa7111" , I2C_SAA7111A, NULL); |
247 | mxb->tea6420_1 = v4l2_i2c_new_subdev(v4l2_dev: &dev->v4l2_dev, adapter: &mxb->i2c_adapter, |
248 | client_type: "tea6420" , I2C_TEA6420_1, NULL); |
249 | mxb->tea6420_2 = v4l2_i2c_new_subdev(v4l2_dev: &dev->v4l2_dev, adapter: &mxb->i2c_adapter, |
250 | client_type: "tea6420" , I2C_TEA6420_2, NULL); |
251 | mxb->tea6415c = v4l2_i2c_new_subdev(v4l2_dev: &dev->v4l2_dev, adapter: &mxb->i2c_adapter, |
252 | client_type: "tea6415c" , I2C_TEA6415C, NULL); |
253 | mxb->tda9840 = v4l2_i2c_new_subdev(v4l2_dev: &dev->v4l2_dev, adapter: &mxb->i2c_adapter, |
254 | client_type: "tda9840" , I2C_TDA9840, NULL); |
255 | mxb->tuner = v4l2_i2c_new_subdev(v4l2_dev: &dev->v4l2_dev, adapter: &mxb->i2c_adapter, |
256 | client_type: "tuner" , I2C_TUNER, NULL); |
257 | |
258 | /* check if all devices are present */ |
259 | if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c || |
260 | !mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) { |
261 | pr_err("did not find all i2c devices. aborting\n" ); |
262 | i2c_del_adapter(adap: &mxb->i2c_adapter); |
263 | kfree(objp: mxb); |
264 | return -ENODEV; |
265 | } |
266 | |
267 | /* all devices are present, probe was successful */ |
268 | |
269 | /* we store the pointer in our private data field */ |
270 | dev->ext_priv = mxb; |
271 | |
272 | v4l2_ctrl_handler_setup(hdl); |
273 | |
274 | return 0; |
275 | } |
276 | |
277 | /* some init data for the saa7740, the so-called 'sound arena module'. |
278 | there are no specs available, so we simply use some init values */ |
279 | static struct { |
280 | int length; |
281 | char data[9]; |
282 | } mxb_saa7740_init[] = { |
283 | { 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } }, |
284 | { 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } }, |
285 | { 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } }, |
286 | { 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } }, |
287 | { 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } }, |
288 | { 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } }, |
289 | { 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } }, |
290 | { 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } }, |
291 | { 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } }, |
292 | { 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } }, |
293 | { 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } }, |
294 | { 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } }, |
295 | { 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } }, |
296 | { 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } }, |
297 | { 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } }, |
298 | { 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } }, |
299 | { 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } }, |
300 | { 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } }, |
301 | { 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } }, |
302 | { 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } }, |
303 | { 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } }, |
304 | { 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } }, |
305 | { 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } }, |
306 | { 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } }, |
307 | { 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } }, |
308 | { 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } }, |
309 | { 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } }, |
310 | { 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } }, |
311 | { 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } }, |
312 | { 3, { 0x48, 0x00, 0x01 } }, |
313 | { 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, |
314 | { 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, |
315 | { 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, |
316 | { 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, |
317 | { 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, |
318 | { 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, |
319 | { 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, |
320 | { 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, |
321 | { 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, |
322 | { 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, |
323 | { 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, |
324 | { 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, |
325 | { 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, |
326 | { 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, |
327 | { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, |
328 | { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, |
329 | { 3, { 0x80, 0xb3, 0x0a } }, |
330 | {-1, { 0 } } |
331 | }; |
332 | |
333 | /* bring hardware to a sane state. this has to be done, just in case someone |
334 | wants to capture from this device before it has been properly initialized. |
335 | the capture engine would badly fail, because no valid signal arrives on the |
336 | saa7146, thus leading to timeouts and stuff. */ |
337 | static int mxb_init_done(struct saa7146_dev* dev) |
338 | { |
339 | struct mxb* mxb = (struct mxb*)dev->ext_priv; |
340 | struct i2c_msg msg; |
341 | struct tuner_setup tun_setup; |
342 | v4l2_std_id std = V4L2_STD_PAL_BG; |
343 | |
344 | int i, err = 0; |
345 | |
346 | /* mute audio on tea6420s */ |
347 | tea6420_route(mxb, idx: 6); |
348 | |
349 | /* select video mode in saa7111a */ |
350 | saa7111a_call(mxb, video, s_std, std); |
351 | |
352 | /* select tuner-output on saa7111a */ |
353 | saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0, |
354 | SAA7111_FMT_CCIR, 0); |
355 | |
356 | /* select a tuner type */ |
357 | tun_setup.mode_mask = T_ANALOG_TV; |
358 | tun_setup.addr = ADDR_UNSET; |
359 | tun_setup.type = TUNER_PHILIPS_PAL; |
360 | tuner_call(mxb, tuner, s_type_addr, &tun_setup); |
361 | /* tune in some frequency on tuner */ |
362 | mxb->cur_freq.tuner = 0; |
363 | mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV; |
364 | mxb->cur_freq.frequency = freq; |
365 | tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); |
366 | |
367 | /* set a default video standard */ |
368 | /* These two gpio calls set the GPIO pins that control the tda9820 */ |
369 | saa7146_write(dev, GPIO_CTRL, 0x00404050); |
370 | saa7111a_call(mxb, core, s_gpio, 1); |
371 | saa7111a_call(mxb, video, s_std, std); |
372 | tuner_call(mxb, video, s_std, std); |
373 | |
374 | /* switch to tuner-channel on tea6415c */ |
375 | tea6415c_call(mxb, video, s_routing, 3, 17, 0); |
376 | |
377 | /* select tuner-output on multicable on tea6415c */ |
378 | tea6415c_call(mxb, video, s_routing, 3, 13, 0); |
379 | |
380 | /* the rest for mxb */ |
381 | mxb->cur_input = 0; |
382 | mxb->cur_audinput = video_audio_connect[mxb->cur_input]; |
383 | mxb->cur_mute = 1; |
384 | |
385 | mxb->cur_mode = V4L2_TUNER_MODE_STEREO; |
386 | mxb_update_audmode(mxb); |
387 | |
388 | /* check if the saa7740 (aka 'sound arena module') is present |
389 | on the mxb. if so, we must initialize it. due to lack of |
390 | information about the saa7740, the values were reverse |
391 | engineered. */ |
392 | msg.addr = 0x1b; |
393 | msg.flags = 0; |
394 | msg.len = mxb_saa7740_init[0].length; |
395 | msg.buf = &mxb_saa7740_init[0].data[0]; |
396 | |
397 | err = i2c_transfer(adap: &mxb->i2c_adapter, msgs: &msg, num: 1); |
398 | if (err == 1) { |
399 | /* the sound arena module is a pos, that's probably the reason |
400 | philips refuses to hand out a datasheet for the saa7740... |
401 | it seems to screw up the i2c bus, so we disable fast irq |
402 | based i2c transactions here and rely on the slow and safe |
403 | polling method ... */ |
404 | extension.flags &= ~SAA7146_USE_I2C_IRQ; |
405 | for (i = 1; ; i++) { |
406 | if (-1 == mxb_saa7740_init[i].length) |
407 | break; |
408 | |
409 | msg.len = mxb_saa7740_init[i].length; |
410 | msg.buf = &mxb_saa7740_init[i].data[0]; |
411 | err = i2c_transfer(adap: &mxb->i2c_adapter, msgs: &msg, num: 1); |
412 | if (err != 1) { |
413 | DEB_D("failed to initialize 'sound arena module'\n" ); |
414 | goto err; |
415 | } |
416 | } |
417 | pr_info("'sound arena module' detected\n" ); |
418 | } |
419 | err: |
420 | /* the rest for saa7146: you should definitely set some basic values |
421 | for the input-port handling of the saa7146. */ |
422 | |
423 | /* ext->saa has been filled by the core driver */ |
424 | |
425 | /* some stuff is done via variables */ |
426 | saa7146_set_hps_source_and_sync(saa: dev, source: input_port_selection[mxb->cur_input].hps_source, |
427 | sync: input_port_selection[mxb->cur_input].hps_sync); |
428 | |
429 | /* some stuff is done via direct write to the registers */ |
430 | |
431 | /* this is ugly, but because of the fact that this is completely |
432 | hardware dependend, it should be done directly... */ |
433 | saa7146_write(dev, DD1_STREAM_B, 0x00000000); |
434 | saa7146_write(dev, DD1_INIT, 0x02000200); |
435 | saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); |
436 | |
437 | return 0; |
438 | } |
439 | |
440 | /* interrupt-handler. this gets called when irq_mask is != 0. |
441 | it must clear the interrupt-bits in irq_mask it has handled */ |
442 | /* |
443 | void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask) |
444 | { |
445 | struct mxb* mxb = (struct mxb*)dev->ext_priv; |
446 | } |
447 | */ |
448 | |
449 | static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) |
450 | { |
451 | DEB_EE("VIDIOC_ENUMINPUT %d\n" , i->index); |
452 | if (i->index >= MXB_INPUTS) |
453 | return -EINVAL; |
454 | memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); |
455 | return 0; |
456 | } |
457 | |
458 | static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) |
459 | { |
460 | struct saa7146_dev *dev = video_drvdata(file); |
461 | struct mxb *mxb = (struct mxb *)dev->ext_priv; |
462 | *i = mxb->cur_input; |
463 | |
464 | DEB_EE("VIDIOC_G_INPUT %d\n" , *i); |
465 | return 0; |
466 | } |
467 | |
468 | static int vidioc_s_input(struct file *file, void *fh, unsigned int input) |
469 | { |
470 | struct saa7146_dev *dev = video_drvdata(file); |
471 | struct mxb *mxb = (struct mxb *)dev->ext_priv; |
472 | int err = 0; |
473 | int i = 0; |
474 | |
475 | DEB_EE("VIDIOC_S_INPUT %d\n" , input); |
476 | |
477 | if (input >= MXB_INPUTS) |
478 | return -EINVAL; |
479 | |
480 | mxb->cur_input = input; |
481 | |
482 | saa7146_set_hps_source_and_sync(saa: dev, source: input_port_selection[input].hps_source, |
483 | sync: input_port_selection[input].hps_sync); |
484 | |
485 | /* prepare switching of tea6415c and saa7111a; |
486 | have a look at the 'background'-file for further information */ |
487 | switch (input) { |
488 | case TUNER: |
489 | i = SAA7115_COMPOSITE0; |
490 | |
491 | err = tea6415c_call(mxb, video, s_routing, 3, 17, 0); |
492 | |
493 | /* connect tuner-output always to multicable */ |
494 | if (!err) |
495 | err = tea6415c_call(mxb, video, s_routing, 3, 13, 0); |
496 | break; |
497 | case AUX3_YC: |
498 | /* nothing to be done here. aux3_yc is |
499 | directly connected to the saa711a */ |
500 | i = SAA7115_SVIDEO1; |
501 | break; |
502 | case AUX3: |
503 | /* nothing to be done here. aux3 is |
504 | directly connected to the saa711a */ |
505 | i = SAA7115_COMPOSITE1; |
506 | break; |
507 | case AUX1: |
508 | i = SAA7115_COMPOSITE0; |
509 | err = tea6415c_call(mxb, video, s_routing, 1, 17, 0); |
510 | break; |
511 | } |
512 | |
513 | if (err) |
514 | return err; |
515 | |
516 | mxb->video_dev.tvnorms = mxb_inputs[input].std; |
517 | mxb->vbi_dev.tvnorms = mxb_inputs[input].std; |
518 | |
519 | /* switch video in saa7111a */ |
520 | if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0)) |
521 | pr_err("VIDIOC_S_INPUT: could not address saa7111a\n" ); |
522 | |
523 | mxb->cur_audinput = video_audio_connect[input]; |
524 | /* switch the audio-source only if necessary */ |
525 | if (0 == mxb->cur_mute) |
526 | tea6420_route(mxb, idx: mxb->cur_audinput); |
527 | if (mxb->cur_audinput == 0) |
528 | mxb_update_audmode(mxb); |
529 | |
530 | return 0; |
531 | } |
532 | |
533 | static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) |
534 | { |
535 | struct saa7146_dev *dev = video_drvdata(file); |
536 | struct mxb *mxb = (struct mxb *)dev->ext_priv; |
537 | |
538 | if (t->index) { |
539 | DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n" , |
540 | t->index); |
541 | return -EINVAL; |
542 | } |
543 | |
544 | DEB_EE("VIDIOC_G_TUNER: %d\n" , t->index); |
545 | |
546 | memset(t, 0, sizeof(*t)); |
547 | strscpy(t->name, "TV Tuner" , sizeof(t->name)); |
548 | t->type = V4L2_TUNER_ANALOG_TV; |
549 | t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | |
550 | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; |
551 | t->audmode = mxb->cur_mode; |
552 | return call_all(dev, tuner, g_tuner, t); |
553 | } |
554 | |
555 | static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *t) |
556 | { |
557 | struct saa7146_dev *dev = video_drvdata(file); |
558 | struct mxb *mxb = (struct mxb *)dev->ext_priv; |
559 | |
560 | if (t->index) { |
561 | DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n" , |
562 | t->index); |
563 | return -EINVAL; |
564 | } |
565 | |
566 | mxb->cur_mode = t->audmode; |
567 | return call_all(dev, tuner, s_tuner, t); |
568 | } |
569 | |
570 | static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm) |
571 | { |
572 | struct saa7146_dev *dev = video_drvdata(file); |
573 | |
574 | return call_all(dev, video, querystd, norm); |
575 | } |
576 | |
577 | static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) |
578 | { |
579 | struct saa7146_dev *dev = video_drvdata(file); |
580 | struct mxb *mxb = (struct mxb *)dev->ext_priv; |
581 | |
582 | if (f->tuner) |
583 | return -EINVAL; |
584 | *f = mxb->cur_freq; |
585 | |
586 | DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n" , mxb->cur_freq.frequency); |
587 | return 0; |
588 | } |
589 | |
590 | static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *f) |
591 | { |
592 | struct saa7146_dev *dev = video_drvdata(file); |
593 | struct mxb *mxb = (struct mxb *)dev->ext_priv; |
594 | |
595 | if (f->tuner) |
596 | return -EINVAL; |
597 | |
598 | if (V4L2_TUNER_ANALOG_TV != f->type) |
599 | return -EINVAL; |
600 | |
601 | DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n" , mxb->cur_freq.frequency); |
602 | |
603 | /* tune in desired frequency */ |
604 | tuner_call(mxb, tuner, s_frequency, f); |
605 | /* let the tuner subdev clamp the frequency to the tuner range */ |
606 | mxb->cur_freq = *f; |
607 | tuner_call(mxb, tuner, g_frequency, &mxb->cur_freq); |
608 | if (mxb->cur_audinput == 0) |
609 | mxb_update_audmode(mxb); |
610 | return 0; |
611 | } |
612 | |
613 | static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) |
614 | { |
615 | if (a->index >= MXB_AUDIOS) |
616 | return -EINVAL; |
617 | *a = mxb_audios[a->index]; |
618 | return 0; |
619 | } |
620 | |
621 | static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) |
622 | { |
623 | struct saa7146_dev *dev = video_drvdata(file); |
624 | struct mxb *mxb = (struct mxb *)dev->ext_priv; |
625 | |
626 | DEB_EE("VIDIOC_G_AUDIO\n" ); |
627 | *a = mxb_audios[mxb->cur_audinput]; |
628 | return 0; |
629 | } |
630 | |
631 | static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) |
632 | { |
633 | struct saa7146_dev *dev = video_drvdata(file); |
634 | struct mxb *mxb = (struct mxb *)dev->ext_priv; |
635 | |
636 | DEB_D("VIDIOC_S_AUDIO %d\n" , a->index); |
637 | if (a->index >= 32 || |
638 | !(mxb_inputs[mxb->cur_input].audioset & (1 << a->index))) |
639 | return -EINVAL; |
640 | |
641 | if (mxb->cur_audinput != a->index) { |
642 | mxb->cur_audinput = a->index; |
643 | tea6420_route(mxb, idx: a->index); |
644 | if (mxb->cur_audinput == 0) |
645 | mxb_update_audmode(mxb); |
646 | } |
647 | return 0; |
648 | } |
649 | |
650 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
651 | static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) |
652 | { |
653 | struct saa7146_dev *dev = video_drvdata(file); |
654 | |
655 | if (reg->reg > pci_resource_len(dev->pci, 0) - 4) |
656 | return -EINVAL; |
657 | reg->val = saa7146_read(dev, reg->reg); |
658 | reg->size = 4; |
659 | return 0; |
660 | } |
661 | |
662 | static int vidioc_s_register(struct file *file, void *fh, const struct v4l2_dbg_register *reg) |
663 | { |
664 | struct saa7146_dev *dev = video_drvdata(file); |
665 | |
666 | if (reg->reg > pci_resource_len(dev->pci, 0) - 4) |
667 | return -EINVAL; |
668 | saa7146_write(dev, reg->reg, reg->val); |
669 | return 0; |
670 | } |
671 | #endif |
672 | |
673 | static struct saa7146_ext_vv vv_data; |
674 | |
675 | /* this function only gets called when the probing was successful */ |
676 | static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) |
677 | { |
678 | struct mxb *mxb; |
679 | int ret; |
680 | |
681 | DEB_EE("dev:%p\n" , dev); |
682 | |
683 | ret = saa7146_vv_init(dev, ext_vv: &vv_data); |
684 | if (ret) { |
685 | ERR("Error in saa7146_vv_init()" ); |
686 | return ret; |
687 | } |
688 | |
689 | if (mxb_probe(dev)) { |
690 | saa7146_vv_release(dev); |
691 | return -1; |
692 | } |
693 | mxb = (struct mxb *)dev->ext_priv; |
694 | |
695 | vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; |
696 | vv_data.vid_ops.vidioc_g_input = vidioc_g_input; |
697 | vv_data.vid_ops.vidioc_s_input = vidioc_s_input; |
698 | vv_data.vid_ops.vidioc_querystd = vidioc_querystd; |
699 | vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner; |
700 | vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner; |
701 | vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency; |
702 | vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency; |
703 | vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio; |
704 | vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio; |
705 | vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio; |
706 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
707 | vv_data.vid_ops.vidioc_g_register = vidioc_g_register; |
708 | vv_data.vid_ops.vidioc_s_register = vidioc_s_register; |
709 | #endif |
710 | vv_data.vbi_ops.vidioc_enum_input = vidioc_enum_input; |
711 | vv_data.vbi_ops.vidioc_g_input = vidioc_g_input; |
712 | vv_data.vbi_ops.vidioc_s_input = vidioc_s_input; |
713 | vv_data.vbi_ops.vidioc_querystd = vidioc_querystd; |
714 | vv_data.vbi_ops.vidioc_g_tuner = vidioc_g_tuner; |
715 | vv_data.vbi_ops.vidioc_s_tuner = vidioc_s_tuner; |
716 | vv_data.vbi_ops.vidioc_g_frequency = vidioc_g_frequency; |
717 | vv_data.vbi_ops.vidioc_s_frequency = vidioc_s_frequency; |
718 | vv_data.vbi_ops.vidioc_enumaudio = vidioc_enumaudio; |
719 | vv_data.vbi_ops.vidioc_g_audio = vidioc_g_audio; |
720 | vv_data.vbi_ops.vidioc_s_audio = vidioc_s_audio; |
721 | if (saa7146_register_device(vid: &mxb->video_dev, dev, name: "mxb" , type: VFL_TYPE_VIDEO)) { |
722 | ERR("cannot register capture v4l2 device. skipping.\n" ); |
723 | saa7146_vv_release(dev); |
724 | return -1; |
725 | } |
726 | |
727 | /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ |
728 | if (MXB_BOARD_CAN_DO_VBI(dev)) { |
729 | if (saa7146_register_device(vid: &mxb->vbi_dev, dev, name: "mxb" , type: VFL_TYPE_VBI)) { |
730 | ERR("cannot register vbi v4l2 device. skipping.\n" ); |
731 | } |
732 | } |
733 | |
734 | pr_info("found Multimedia eXtension Board #%d\n" , mxb_num); |
735 | |
736 | mxb_num++; |
737 | mxb_init_done(dev); |
738 | return 0; |
739 | } |
740 | |
741 | static int mxb_detach(struct saa7146_dev *dev) |
742 | { |
743 | struct mxb *mxb = (struct mxb *)dev->ext_priv; |
744 | |
745 | DEB_EE("dev:%p\n" , dev); |
746 | |
747 | /* mute audio on tea6420s */ |
748 | tea6420_route(mxb, idx: 6); |
749 | |
750 | saa7146_unregister_device(vid: &mxb->video_dev,dev); |
751 | if (MXB_BOARD_CAN_DO_VBI(dev)) |
752 | saa7146_unregister_device(vid: &mxb->vbi_dev, dev); |
753 | saa7146_vv_release(dev); |
754 | |
755 | mxb_num--; |
756 | |
757 | i2c_del_adapter(adap: &mxb->i2c_adapter); |
758 | kfree(objp: mxb); |
759 | |
760 | return 0; |
761 | } |
762 | |
763 | static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard) |
764 | { |
765 | struct mxb *mxb = (struct mxb *)dev->ext_priv; |
766 | |
767 | if (V4L2_STD_PAL_I == standard->id) { |
768 | v4l2_std_id std = V4L2_STD_PAL_I; |
769 | |
770 | DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n" ); |
771 | /* These two gpio calls set the GPIO pins that control the tda9820 */ |
772 | saa7146_write(dev, GPIO_CTRL, 0x00404050); |
773 | saa7111a_call(mxb, core, s_gpio, 0); |
774 | saa7111a_call(mxb, video, s_std, std); |
775 | if (mxb->cur_input == 0) |
776 | tuner_call(mxb, video, s_std, std); |
777 | } else { |
778 | v4l2_std_id std = V4L2_STD_PAL_BG; |
779 | |
780 | if (mxb->cur_input) |
781 | std = standard->id; |
782 | DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n" ); |
783 | /* These two gpio calls set the GPIO pins that control the tda9820 */ |
784 | saa7146_write(dev, GPIO_CTRL, 0x00404050); |
785 | saa7111a_call(mxb, core, s_gpio, 1); |
786 | saa7111a_call(mxb, video, s_std, std); |
787 | if (mxb->cur_input == 0) |
788 | tuner_call(mxb, video, s_std, std); |
789 | } |
790 | return 0; |
791 | } |
792 | |
793 | static struct saa7146_standard standard[] = { |
794 | { |
795 | .name = "PAL-BG" , .id = V4L2_STD_PAL_BG, |
796 | .v_offset = 0x17, .v_field = 288, |
797 | .h_offset = 0x14, .h_pixels = 680, |
798 | .v_max_out = 576, .h_max_out = 768, |
799 | }, { |
800 | .name = "PAL-I" , .id = V4L2_STD_PAL_I, |
801 | .v_offset = 0x17, .v_field = 288, |
802 | .h_offset = 0x14, .h_pixels = 680, |
803 | .v_max_out = 576, .h_max_out = 768, |
804 | }, { |
805 | .name = "NTSC" , .id = V4L2_STD_NTSC, |
806 | .v_offset = 0x16, .v_field = 240, |
807 | .h_offset = 0x06, .h_pixels = 708, |
808 | .v_max_out = 480, .h_max_out = 640, |
809 | }, { |
810 | .name = "SECAM" , .id = V4L2_STD_SECAM, |
811 | .v_offset = 0x14, .v_field = 288, |
812 | .h_offset = 0x14, .h_pixels = 720, |
813 | .v_max_out = 576, .h_max_out = 768, |
814 | } |
815 | }; |
816 | |
817 | static struct saa7146_pci_extension_data mxb = { |
818 | .ext_priv = "Multimedia eXtension Board" , |
819 | .ext = &extension, |
820 | }; |
821 | |
822 | static const struct pci_device_id pci_tbl[] = { |
823 | { |
824 | .vendor = PCI_VENDOR_ID_PHILIPS, |
825 | .device = PCI_DEVICE_ID_PHILIPS_SAA7146, |
826 | .subvendor = 0x0000, |
827 | .subdevice = 0x0000, |
828 | .driver_data = (unsigned long)&mxb, |
829 | }, { |
830 | .vendor = 0, |
831 | } |
832 | }; |
833 | |
834 | MODULE_DEVICE_TABLE(pci, pci_tbl); |
835 | |
836 | static struct saa7146_ext_vv vv_data = { |
837 | .inputs = MXB_INPUTS, |
838 | .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO, |
839 | .stds = &standard[0], |
840 | .num_stds = ARRAY_SIZE(standard), |
841 | .std_callback = &std_callback, |
842 | }; |
843 | |
844 | static struct saa7146_extension extension = { |
845 | .name = "Multimedia eXtension Board" , |
846 | .flags = SAA7146_USE_I2C_IRQ, |
847 | |
848 | .pci_tbl = &pci_tbl[0], |
849 | .module = THIS_MODULE, |
850 | |
851 | .attach = mxb_attach, |
852 | .detach = mxb_detach, |
853 | |
854 | .irq_mask = 0, |
855 | .irq_func = NULL, |
856 | }; |
857 | |
858 | static int __init mxb_init_module(void) |
859 | { |
860 | if (saa7146_register_extension(&extension)) { |
861 | DEB_S("failed to register extension\n" ); |
862 | return -ENODEV; |
863 | } |
864 | |
865 | return 0; |
866 | } |
867 | |
868 | static void __exit mxb_cleanup_module(void) |
869 | { |
870 | saa7146_unregister_extension(&extension); |
871 | } |
872 | |
873 | module_init(mxb_init_module); |
874 | module_exit(mxb_cleanup_module); |
875 | |
876 | MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'" ); |
877 | MODULE_AUTHOR("Michael Hunold <michael@mihu.de>" ); |
878 | MODULE_LICENSE("GPL" ); |
879 | |