diff options
Diffstat (limited to 'drivers/media/video/msp3400-kthreads.c')
-rw-r--r-- | drivers/media/video/msp3400-kthreads.c | 1015 |
1 files changed, 1015 insertions, 0 deletions
diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c new file mode 100644 index 000000000000..934f0d3b9138 --- /dev/null +++ b/drivers/media/video/msp3400-kthreads.c | |||
@@ -0,0 +1,1015 @@ | |||
1 | /* | ||
2 | * Programming the mspx4xx sound processor family | ||
3 | * | ||
4 | * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version 2 | ||
9 | * of the License, or (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
19 | */ | ||
20 | |||
21 | |||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/i2c.h> | ||
26 | #include <linux/videodev.h> | ||
27 | #include <linux/videodev2.h> | ||
28 | #include <media/v4l2-common.h> | ||
29 | #include <media/audiochip.h> | ||
30 | #include <linux/kthread.h> | ||
31 | #include <linux/suspend.h> | ||
32 | #include "msp3400.h" | ||
33 | |||
34 | /* this one uses the automatic sound standard detection of newer msp34xx | ||
35 | chip versions */ | ||
36 | static struct { | ||
37 | int retval; | ||
38 | int main, second; | ||
39 | char *name; | ||
40 | } msp_modelist[] = { | ||
41 | { 0x0000, 0, 0, "could not detect sound standard" }, | ||
42 | { 0x0001, 0, 0, "autodetect start" }, | ||
43 | { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72 M Dual FM-Stereo" }, | ||
44 | { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74 B/G Dual FM-Stereo" }, | ||
45 | { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25 D/K1 Dual FM-Stereo" }, | ||
46 | { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74 D/K2 Dual FM-Stereo" }, | ||
47 | { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 D/K FM-Mono (HDEV3)" }, | ||
48 | { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85 B/G NICAM FM" }, | ||
49 | { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 L NICAM AM" }, | ||
50 | { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55 I NICAM FM" }, | ||
51 | { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM" }, | ||
52 | { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV2)" }, | ||
53 | { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Stereo" }, | ||
54 | { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Mono + SAP" }, | ||
55 | { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M EIA-J Japan Stereo" }, | ||
56 | { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7 FM-Stereo Radio" }, | ||
57 | { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 SAT-Mono" }, | ||
58 | { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20 SAT-Stereo" }, | ||
59 | { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" }, | ||
60 | { -1, 0, 0, NULL }, /* EOF */ | ||
61 | }; | ||
62 | |||
63 | static struct msp3400c_init_data_dem { | ||
64 | int fir1[6]; | ||
65 | int fir2[6]; | ||
66 | int cdo1; | ||
67 | int cdo2; | ||
68 | int ad_cv; | ||
69 | int mode_reg; | ||
70 | int dsp_src; | ||
71 | int dsp_matrix; | ||
72 | } msp3400c_init_data[] = { | ||
73 | { /* AM (for carrier detect / msp3400) */ | ||
74 | {75, 19, 36, 35, 39, 40}, | ||
75 | {75, 19, 36, 35, 39, 40}, | ||
76 | MSP_CARRIER(5.5), MSP_CARRIER(5.5), | ||
77 | 0x00d0, 0x0500, 0x0020, 0x3000 | ||
78 | },{ /* AM (for carrier detect / msp3410) */ | ||
79 | {-1, -1, -8, 2, 59, 126}, | ||
80 | {-1, -1, -8, 2, 59, 126}, | ||
81 | MSP_CARRIER(5.5), MSP_CARRIER(5.5), | ||
82 | 0x00d0, 0x0100, 0x0020, 0x3000 | ||
83 | },{ /* FM Radio */ | ||
84 | {-8, -8, 4, 6, 78, 107}, | ||
85 | {-8, -8, 4, 6, 78, 107}, | ||
86 | MSP_CARRIER(10.7), MSP_CARRIER(10.7), | ||
87 | 0x00d0, 0x0480, 0x0020, 0x3000 | ||
88 | },{ /* Terrestial FM-mono + FM-stereo */ | ||
89 | {3, 18, 27, 48, 66, 72}, | ||
90 | {3, 18, 27, 48, 66, 72}, | ||
91 | MSP_CARRIER(5.5), MSP_CARRIER(5.5), | ||
92 | 0x00d0, 0x0480, 0x0030, 0x3000 | ||
93 | },{ /* Sat FM-mono */ | ||
94 | { 1, 9, 14, 24, 33, 37}, | ||
95 | { 3, 18, 27, 48, 66, 72}, | ||
96 | MSP_CARRIER(6.5), MSP_CARRIER(6.5), | ||
97 | 0x00c6, 0x0480, 0x0000, 0x3000 | ||
98 | },{ /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ | ||
99 | {-2, -8, -10, 10, 50, 86}, | ||
100 | {3, 18, 27, 48, 66, 72}, | ||
101 | MSP_CARRIER(5.5), MSP_CARRIER(5.5), | ||
102 | 0x00d0, 0x0040, 0x0120, 0x3000 | ||
103 | },{ /* NICAM/FM -- I (6.0/6.552) */ | ||
104 | {2, 4, -6, -4, 40, 94}, | ||
105 | {3, 18, 27, 48, 66, 72}, | ||
106 | MSP_CARRIER(6.0), MSP_CARRIER(6.0), | ||
107 | 0x00d0, 0x0040, 0x0120, 0x3000 | ||
108 | },{ /* NICAM/AM -- L (6.5/5.85) */ | ||
109 | {-2, -8, -10, 10, 50, 86}, | ||
110 | {-4, -12, -9, 23, 79, 126}, | ||
111 | MSP_CARRIER(6.5), MSP_CARRIER(6.5), | ||
112 | 0x00c6, 0x0140, 0x0120, 0x7c03 | ||
113 | }, | ||
114 | }; | ||
115 | |||
116 | struct msp3400c_carrier_detect { | ||
117 | int cdo; | ||
118 | char *name; | ||
119 | }; | ||
120 | |||
121 | static struct msp3400c_carrier_detect msp3400c_carrier_detect_main[] = { | ||
122 | /* main carrier */ | ||
123 | { MSP_CARRIER(4.5), "4.5 NTSC" }, | ||
124 | { MSP_CARRIER(5.5), "5.5 PAL B/G" }, | ||
125 | { MSP_CARRIER(6.0), "6.0 PAL I" }, | ||
126 | { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" } | ||
127 | }; | ||
128 | |||
129 | static struct msp3400c_carrier_detect msp3400c_carrier_detect_55[] = { | ||
130 | /* PAL B/G */ | ||
131 | { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, | ||
132 | { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } | ||
133 | }; | ||
134 | |||
135 | static struct msp3400c_carrier_detect msp3400c_carrier_detect_65[] = { | ||
136 | /* PAL SAT / SECAM */ | ||
137 | { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, | ||
138 | { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, | ||
139 | { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, | ||
140 | { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, | ||
141 | { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, | ||
142 | { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, | ||
143 | }; | ||
144 | |||
145 | /* ------------------------------------------------------------------------ */ | ||
146 | |||
147 | const char *msp_standard_mode_name(int mode) | ||
148 | { | ||
149 | int i; | ||
150 | |||
151 | for (i = 0; msp_modelist[i].name != NULL; i++) | ||
152 | if (msp_modelist[i].retval == mode) | ||
153 | return msp_modelist[i].name; | ||
154 | return "unknown"; | ||
155 | } | ||
156 | |||
157 | void msp3400c_setcarrier(struct i2c_client *client, int cdo1, int cdo2) | ||
158 | { | ||
159 | msp_write_dem(client, 0x0093, cdo1 & 0xfff); | ||
160 | msp_write_dem(client, 0x009b, cdo1 >> 12); | ||
161 | msp_write_dem(client, 0x00a3, cdo2 & 0xfff); | ||
162 | msp_write_dem(client, 0x00ab, cdo2 >> 12); | ||
163 | msp_write_dem(client, 0x0056, 0); /*LOAD_REG_1/2*/ | ||
164 | } | ||
165 | |||
166 | void msp3400c_setmode(struct i2c_client *client, int type) | ||
167 | { | ||
168 | struct msp_state *state = i2c_get_clientdata(client); | ||
169 | int i; | ||
170 | |||
171 | v4l_dbg(1, client, "setmode: %d\n", type); | ||
172 | state->mode = type; | ||
173 | state->audmode = V4L2_TUNER_MODE_MONO; | ||
174 | state->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
175 | |||
176 | msp_write_dem(client, 0x00bb, msp3400c_init_data[type].ad_cv); | ||
177 | |||
178 | for (i = 5; i >= 0; i--) /* fir 1 */ | ||
179 | msp_write_dem(client, 0x0001, msp3400c_init_data[type].fir1[i]); | ||
180 | |||
181 | msp_write_dem(client, 0x0005, 0x0004); /* fir 2 */ | ||
182 | msp_write_dem(client, 0x0005, 0x0040); | ||
183 | msp_write_dem(client, 0x0005, 0x0000); | ||
184 | for (i = 5; i >= 0; i--) | ||
185 | msp_write_dem(client, 0x0005, msp3400c_init_data[type].fir2[i]); | ||
186 | |||
187 | msp_write_dem(client, 0x0083, msp3400c_init_data[type].mode_reg); | ||
188 | |||
189 | msp3400c_setcarrier(client, msp3400c_init_data[type].cdo1, | ||
190 | msp3400c_init_data[type].cdo2); | ||
191 | |||
192 | msp_write_dem(client, 0x0056, 0); /*LOAD_REG_1/2*/ | ||
193 | |||
194 | if (dolby) { | ||
195 | msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */ | ||
196 | msp_write_dsp(client, 0x0009, 0x0620); /* I2S2 */ | ||
197 | msp_write_dsp(client, 0x000b, msp3400c_init_data[type].dsp_src); | ||
198 | } else { | ||
199 | msp_write_dsp(client, 0x0008, msp3400c_init_data[type].dsp_src); | ||
200 | msp_write_dsp(client, 0x0009, msp3400c_init_data[type].dsp_src); | ||
201 | msp_write_dsp(client, 0x000b, msp3400c_init_data[type].dsp_src); | ||
202 | } | ||
203 | msp_write_dsp(client, 0x000a, msp3400c_init_data[type].dsp_src); | ||
204 | msp_write_dsp(client, 0x000e, msp3400c_init_data[type].dsp_matrix); | ||
205 | |||
206 | if (HAVE_NICAM(state)) { | ||
207 | /* nicam prescale */ | ||
208 | msp_write_dsp(client, 0x0010, 0x5a00); /* was: 0x3000 */ | ||
209 | } | ||
210 | } | ||
211 | |||
212 | /* given a bitmask of VIDEO_SOUND_XXX returns the "best" in the bitmask */ | ||
213 | static int msp3400c_best_video_sound(int rxsubchans) | ||
214 | { | ||
215 | if (rxsubchans & V4L2_TUNER_SUB_STEREO) | ||
216 | return V4L2_TUNER_MODE_STEREO; | ||
217 | if (rxsubchans & V4L2_TUNER_SUB_LANG1) | ||
218 | return V4L2_TUNER_MODE_LANG1; | ||
219 | if (rxsubchans & V4L2_TUNER_SUB_LANG2) | ||
220 | return V4L2_TUNER_MODE_LANG2; | ||
221 | return V4L2_TUNER_MODE_MONO; | ||
222 | } | ||
223 | |||
224 | /* turn on/off nicam + stereo */ | ||
225 | void msp3400c_setstereo(struct i2c_client *client, int mode) | ||
226 | { | ||
227 | static char *strmode[] = { "0", "mono", "stereo", "3", | ||
228 | "lang1", "5", "6", "7", "lang2" | ||
229 | }; | ||
230 | struct msp_state *state = i2c_get_clientdata(client); | ||
231 | int nicam = 0; /* channel source: FM/AM or nicam */ | ||
232 | int src = 0; | ||
233 | |||
234 | if (state->opmode == OPMODE_AUTOSELECT) { | ||
235 | /* this method would break everything, let's make sure | ||
236 | * it's never called | ||
237 | */ | ||
238 | v4l_dbg(1, client, "setstereo called with mode=%d instead of set_source (ignored)\n", | ||
239 | mode); | ||
240 | return; | ||
241 | } | ||
242 | |||
243 | /* switch demodulator */ | ||
244 | switch (state->mode) { | ||
245 | case MSP_MODE_FM_TERRA: | ||
246 | v4l_dbg(1, client, "FM setstereo: %s\n", strmode[mode]); | ||
247 | msp3400c_setcarrier(client,state->second,state->main); | ||
248 | switch (mode) { | ||
249 | case V4L2_TUNER_MODE_STEREO: | ||
250 | msp_write_dsp(client, 0x000e, 0x3001); | ||
251 | break; | ||
252 | case V4L2_TUNER_MODE_MONO: | ||
253 | case V4L2_TUNER_MODE_LANG1: | ||
254 | case V4L2_TUNER_MODE_LANG2: | ||
255 | msp_write_dsp(client, 0x000e, 0x3000); | ||
256 | break; | ||
257 | } | ||
258 | break; | ||
259 | case MSP_MODE_FM_SAT: | ||
260 | v4l_dbg(1, client, "SAT setstereo: %s\n", strmode[mode]); | ||
261 | switch (mode) { | ||
262 | case V4L2_TUNER_MODE_MONO: | ||
263 | msp3400c_setcarrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); | ||
264 | break; | ||
265 | case V4L2_TUNER_MODE_STEREO: | ||
266 | msp3400c_setcarrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); | ||
267 | break; | ||
268 | case V4L2_TUNER_MODE_LANG1: | ||
269 | msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); | ||
270 | break; | ||
271 | case V4L2_TUNER_MODE_LANG2: | ||
272 | msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); | ||
273 | break; | ||
274 | } | ||
275 | break; | ||
276 | case MSP_MODE_FM_NICAM1: | ||
277 | case MSP_MODE_FM_NICAM2: | ||
278 | case MSP_MODE_AM_NICAM: | ||
279 | v4l_dbg(1, client, "NICAM setstereo: %s\n",strmode[mode]); | ||
280 | msp3400c_setcarrier(client,state->second,state->main); | ||
281 | if (state->nicam_on) | ||
282 | nicam=0x0100; | ||
283 | break; | ||
284 | case MSP_MODE_BTSC: | ||
285 | v4l_dbg(1, client, "BTSC setstereo: %s\n",strmode[mode]); | ||
286 | nicam=0x0300; | ||
287 | break; | ||
288 | case MSP_MODE_EXTERN: | ||
289 | v4l_dbg(1, client, "extern setstereo: %s\n",strmode[mode]); | ||
290 | nicam = 0x0200; | ||
291 | break; | ||
292 | case MSP_MODE_FM_RADIO: | ||
293 | v4l_dbg(1, client, "FM-Radio setstereo: %s\n",strmode[mode]); | ||
294 | break; | ||
295 | default: | ||
296 | v4l_dbg(1, client, "mono setstereo\n"); | ||
297 | return; | ||
298 | } | ||
299 | |||
300 | /* switch audio */ | ||
301 | switch (msp3400c_best_video_sound(mode)) { | ||
302 | case V4L2_TUNER_MODE_STEREO: | ||
303 | src = 0x0020 | nicam; | ||
304 | break; | ||
305 | case V4L2_TUNER_MODE_MONO: | ||
306 | if (state->mode == MSP_MODE_AM_NICAM) { | ||
307 | v4l_dbg(1, client, "switching to AM mono\n"); | ||
308 | /* AM mono decoding is handled by tuner, not MSP chip */ | ||
309 | /* SCART switching control register */ | ||
310 | msp_set_scart(client, SCART_MONO, 0); | ||
311 | src = 0x0200; | ||
312 | break; | ||
313 | } | ||
314 | case V4L2_TUNER_MODE_LANG1: | ||
315 | src = 0x0000 | nicam; | ||
316 | break; | ||
317 | case V4L2_TUNER_MODE_LANG2: | ||
318 | src = 0x0010 | nicam; | ||
319 | break; | ||
320 | } | ||
321 | v4l_dbg(1, client, "setstereo final source/matrix = 0x%x\n", src); | ||
322 | |||
323 | if (dolby) { | ||
324 | msp_write_dsp(client, 0x0008, 0x0520); | ||
325 | msp_write_dsp(client, 0x0009, 0x0620); | ||
326 | msp_write_dsp(client, 0x000a, src); | ||
327 | msp_write_dsp(client, 0x000b, src); | ||
328 | } else { | ||
329 | msp_write_dsp(client, 0x0008, src); | ||
330 | msp_write_dsp(client, 0x0009, src); | ||
331 | msp_write_dsp(client, 0x000a, src); | ||
332 | msp_write_dsp(client, 0x000b, src); | ||
333 | } | ||
334 | } | ||
335 | |||
336 | static void msp3400c_print_mode(struct i2c_client *client) | ||
337 | { | ||
338 | struct msp_state *state = i2c_get_clientdata(client); | ||
339 | |||
340 | if (state->main == state->second) { | ||
341 | v4l_dbg(1, client, "mono sound carrier: %d.%03d MHz\n", | ||
342 | state->main / 910000, (state->main / 910) % 1000); | ||
343 | } else { | ||
344 | v4l_dbg(1, client, "main sound carrier: %d.%03d MHz\n", | ||
345 | state->main / 910000, (state->main / 910) % 1000); | ||
346 | } | ||
347 | if (state->mode == MSP_MODE_FM_NICAM1 || state->mode == MSP_MODE_FM_NICAM2) | ||
348 | v4l_dbg(1, client, "NICAM/FM carrier : %d.%03d MHz\n", | ||
349 | state->second / 910000, (state->second/910) % 1000); | ||
350 | if (state->mode == MSP_MODE_AM_NICAM) | ||
351 | v4l_dbg(1, client, "NICAM/AM carrier : %d.%03d MHz\n", | ||
352 | state->second / 910000, (state->second / 910) % 1000); | ||
353 | if (state->mode == MSP_MODE_FM_TERRA && state->main != state->second) { | ||
354 | v4l_dbg(1, client, "FM-stereo carrier : %d.%03d MHz\n", | ||
355 | state->second / 910000, (state->second / 910) % 1000); | ||
356 | } | ||
357 | } | ||
358 | |||
359 | /* ----------------------------------------------------------------------- */ | ||
360 | |||
361 | int autodetect_stereo(struct i2c_client *client) | ||
362 | { | ||
363 | struct msp_state *state = i2c_get_clientdata(client); | ||
364 | int val; | ||
365 | int rxsubchans = state->rxsubchans; | ||
366 | int newnicam = state->nicam_on; | ||
367 | int update = 0; | ||
368 | |||
369 | switch (state->mode) { | ||
370 | case MSP_MODE_FM_TERRA: | ||
371 | val = msp_read_dsp(client, 0x18); | ||
372 | if (val > 32767) | ||
373 | val -= 65536; | ||
374 | v4l_dbg(2, client, "stereo detect register: %d\n", val); | ||
375 | if (val > 4096) { | ||
376 | rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; | ||
377 | } else if (val < -4096) { | ||
378 | rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; | ||
379 | } else { | ||
380 | rxsubchans = V4L2_TUNER_SUB_MONO; | ||
381 | } | ||
382 | newnicam = 0; | ||
383 | break; | ||
384 | case MSP_MODE_FM_NICAM1: | ||
385 | case MSP_MODE_FM_NICAM2: | ||
386 | case MSP_MODE_AM_NICAM: | ||
387 | val = msp_read_dem(client, 0x23); | ||
388 | v4l_dbg(2, client, "nicam sync=%d, mode=%d\n", | ||
389 | val & 1, (val & 0x1e) >> 1); | ||
390 | |||
391 | if (val & 1) { | ||
392 | /* nicam synced */ | ||
393 | switch ((val & 0x1e) >> 1) { | ||
394 | case 0: | ||
395 | case 8: | ||
396 | rxsubchans = V4L2_TUNER_SUB_STEREO; | ||
397 | break; | ||
398 | case 1: | ||
399 | case 9: | ||
400 | rxsubchans = V4L2_TUNER_SUB_MONO | ||
401 | | V4L2_TUNER_SUB_LANG1; | ||
402 | break; | ||
403 | case 2: | ||
404 | case 10: | ||
405 | rxsubchans = V4L2_TUNER_SUB_MONO | ||
406 | | V4L2_TUNER_SUB_LANG1 | ||
407 | | V4L2_TUNER_SUB_LANG2; | ||
408 | break; | ||
409 | default: | ||
410 | rxsubchans = V4L2_TUNER_SUB_MONO; | ||
411 | break; | ||
412 | } | ||
413 | newnicam = 1; | ||
414 | } else { | ||
415 | newnicam = 0; | ||
416 | rxsubchans = V4L2_TUNER_SUB_MONO; | ||
417 | } | ||
418 | break; | ||
419 | case MSP_MODE_BTSC: | ||
420 | val = msp_read_dem(client, 0x200); | ||
421 | v4l_dbg(2, client, "status=0x%x (pri=%s, sec=%s, %s%s%s)\n", | ||
422 | val, | ||
423 | (val & 0x0002) ? "no" : "yes", | ||
424 | (val & 0x0004) ? "no" : "yes", | ||
425 | (val & 0x0040) ? "stereo" : "mono", | ||
426 | (val & 0x0080) ? ", nicam 2nd mono" : "", | ||
427 | (val & 0x0100) ? ", bilingual/SAP" : ""); | ||
428 | rxsubchans = V4L2_TUNER_SUB_MONO; | ||
429 | if (val & 0x0040) rxsubchans |= V4L2_TUNER_SUB_STEREO; | ||
430 | if (val & 0x0100) rxsubchans |= V4L2_TUNER_SUB_LANG1; | ||
431 | break; | ||
432 | } | ||
433 | if (rxsubchans != state->rxsubchans) { | ||
434 | update = 1; | ||
435 | v4l_dbg(1, client, "watch: rxsubchans %d => %d\n", | ||
436 | state->rxsubchans,rxsubchans); | ||
437 | state->rxsubchans = rxsubchans; | ||
438 | } | ||
439 | if (newnicam != state->nicam_on) { | ||
440 | update = 1; | ||
441 | v4l_dbg(1, client, "watch: nicam %d => %d\n", | ||
442 | state->nicam_on,newnicam); | ||
443 | state->nicam_on = newnicam; | ||
444 | } | ||
445 | return update; | ||
446 | } | ||
447 | |||
448 | /* | ||
449 | * A kernel thread for msp3400 control -- we don't want to block the | ||
450 | * in the ioctl while doing the sound carrier & stereo detect | ||
451 | */ | ||
452 | /* stereo/multilang monitoring */ | ||
453 | static void watch_stereo(struct i2c_client *client) | ||
454 | { | ||
455 | struct msp_state *state = i2c_get_clientdata(client); | ||
456 | |||
457 | if (autodetect_stereo(client)) { | ||
458 | if (state->stereo & V4L2_TUNER_MODE_STEREO) | ||
459 | msp3400c_setstereo(client, V4L2_TUNER_MODE_STEREO); | ||
460 | else if (state->stereo & VIDEO_SOUND_LANG1) | ||
461 | msp3400c_setstereo(client, V4L2_TUNER_MODE_LANG1); | ||
462 | else | ||
463 | msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); | ||
464 | } | ||
465 | |||
466 | if (once) | ||
467 | state->watch_stereo = 0; | ||
468 | } | ||
469 | |||
470 | int msp3400c_thread(void *data) | ||
471 | { | ||
472 | struct i2c_client *client = data; | ||
473 | struct msp_state *state = i2c_get_clientdata(client); | ||
474 | struct msp3400c_carrier_detect *cd; | ||
475 | int count, max1,max2,val1,val2, val,this; | ||
476 | |||
477 | |||
478 | v4l_dbg(1, client, "msp3400 daemon started\n"); | ||
479 | for (;;) { | ||
480 | v4l_dbg(2, client, "msp3400 thread: sleep\n"); | ||
481 | msp_sleep(state, -1); | ||
482 | v4l_dbg(2, client, "msp3400 thread: wakeup\n"); | ||
483 | |||
484 | restart: | ||
485 | v4l_dbg(1, client, "thread: restart scan\n"); | ||
486 | state->restart = 0; | ||
487 | if (kthread_should_stop()) | ||
488 | break; | ||
489 | |||
490 | if (VIDEO_MODE_RADIO == state->norm || | ||
491 | MSP_MODE_EXTERN == state->mode) { | ||
492 | /* no carrier scan, just unmute */ | ||
493 | v4l_dbg(1, client, "thread: no carrier scan\n"); | ||
494 | msp_set_audio(client); | ||
495 | continue; | ||
496 | } | ||
497 | |||
498 | /* mute */ | ||
499 | msp_set_mute(client); | ||
500 | msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ ); | ||
501 | val1 = val2 = 0; | ||
502 | max1 = max2 = -1; | ||
503 | state->watch_stereo = 0; | ||
504 | |||
505 | /* some time for the tuner to sync */ | ||
506 | if (msp_sleep(state,200)) | ||
507 | goto restart; | ||
508 | |||
509 | /* carrier detect pass #1 -- main carrier */ | ||
510 | cd = msp3400c_carrier_detect_main; | ||
511 | count = ARRAY_SIZE(msp3400c_carrier_detect_main); | ||
512 | |||
513 | if (amsound && (state->norm == VIDEO_MODE_SECAM)) { | ||
514 | /* autodetect doesn't work well with AM ... */ | ||
515 | max1 = 3; | ||
516 | count = 0; | ||
517 | v4l_dbg(1, client, "AM sound override\n"); | ||
518 | } | ||
519 | |||
520 | for (this = 0; this < count; this++) { | ||
521 | msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); | ||
522 | if (msp_sleep(state,100)) | ||
523 | goto restart; | ||
524 | val = msp_read_dsp(client, 0x1b); | ||
525 | if (val > 32767) | ||
526 | val -= 65536; | ||
527 | if (val1 < val) | ||
528 | val1 = val, max1 = this; | ||
529 | v4l_dbg(1, client, "carrier1 val: %5d / %s\n", val,cd[this].name); | ||
530 | } | ||
531 | |||
532 | /* carrier detect pass #2 -- second (stereo) carrier */ | ||
533 | switch (max1) { | ||
534 | case 1: /* 5.5 */ | ||
535 | cd = msp3400c_carrier_detect_55; | ||
536 | count = ARRAY_SIZE(msp3400c_carrier_detect_55); | ||
537 | break; | ||
538 | case 3: /* 6.5 */ | ||
539 | cd = msp3400c_carrier_detect_65; | ||
540 | count = ARRAY_SIZE(msp3400c_carrier_detect_65); | ||
541 | break; | ||
542 | case 0: /* 4.5 */ | ||
543 | case 2: /* 6.0 */ | ||
544 | default: | ||
545 | cd = NULL; | ||
546 | count = 0; | ||
547 | break; | ||
548 | } | ||
549 | |||
550 | if (amsound && (state->norm == VIDEO_MODE_SECAM)) { | ||
551 | /* autodetect doesn't work well with AM ... */ | ||
552 | cd = NULL; | ||
553 | count = 0; | ||
554 | max2 = 0; | ||
555 | } | ||
556 | for (this = 0; this < count; this++) { | ||
557 | msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); | ||
558 | if (msp_sleep(state,100)) | ||
559 | goto restart; | ||
560 | val = msp_read_dsp(client, 0x1b); | ||
561 | if (val > 32767) | ||
562 | val -= 65536; | ||
563 | if (val2 < val) | ||
564 | val2 = val, max2 = this; | ||
565 | v4l_dbg(1, client, "carrier2 val: %5d / %s\n", val,cd[this].name); | ||
566 | } | ||
567 | |||
568 | /* program the msp3400 according to the results */ | ||
569 | state->main = msp3400c_carrier_detect_main[max1].cdo; | ||
570 | switch (max1) { | ||
571 | case 1: /* 5.5 */ | ||
572 | if (max2 == 0) { | ||
573 | /* B/G FM-stereo */ | ||
574 | state->second = msp3400c_carrier_detect_55[max2].cdo; | ||
575 | msp3400c_setmode(client, MSP_MODE_FM_TERRA); | ||
576 | state->nicam_on = 0; | ||
577 | msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); | ||
578 | state->watch_stereo = 1; | ||
579 | } else if (max2 == 1 && HAVE_NICAM(state)) { | ||
580 | /* B/G NICAM */ | ||
581 | state->second = msp3400c_carrier_detect_55[max2].cdo; | ||
582 | msp3400c_setmode(client, MSP_MODE_FM_NICAM1); | ||
583 | state->nicam_on = 1; | ||
584 | msp3400c_setcarrier(client, state->second, state->main); | ||
585 | state->watch_stereo = 1; | ||
586 | } else { | ||
587 | goto no_second; | ||
588 | } | ||
589 | break; | ||
590 | case 2: /* 6.0 */ | ||
591 | /* PAL I NICAM */ | ||
592 | state->second = MSP_CARRIER(6.552); | ||
593 | msp3400c_setmode(client, MSP_MODE_FM_NICAM2); | ||
594 | state->nicam_on = 1; | ||
595 | msp3400c_setcarrier(client, state->second, state->main); | ||
596 | state->watch_stereo = 1; | ||
597 | break; | ||
598 | case 3: /* 6.5 */ | ||
599 | if (max2 == 1 || max2 == 2) { | ||
600 | /* D/K FM-stereo */ | ||
601 | state->second = msp3400c_carrier_detect_65[max2].cdo; | ||
602 | msp3400c_setmode(client, MSP_MODE_FM_TERRA); | ||
603 | state->nicam_on = 0; | ||
604 | msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); | ||
605 | state->watch_stereo = 1; | ||
606 | } else if (max2 == 0 && | ||
607 | state->norm == VIDEO_MODE_SECAM) { | ||
608 | /* L NICAM or AM-mono */ | ||
609 | state->second = msp3400c_carrier_detect_65[max2].cdo; | ||
610 | msp3400c_setmode(client, MSP_MODE_AM_NICAM); | ||
611 | state->nicam_on = 0; | ||
612 | msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); | ||
613 | msp3400c_setcarrier(client, state->second, state->main); | ||
614 | /* volume prescale for SCART (AM mono input) */ | ||
615 | msp_write_dsp(client, 0x000d, 0x1900); | ||
616 | state->watch_stereo = 1; | ||
617 | } else if (max2 == 0 && HAVE_NICAM(state)) { | ||
618 | /* D/K NICAM */ | ||
619 | state->second = msp3400c_carrier_detect_65[max2].cdo; | ||
620 | msp3400c_setmode(client, MSP_MODE_FM_NICAM1); | ||
621 | state->nicam_on = 1; | ||
622 | msp3400c_setcarrier(client, state->second, state->main); | ||
623 | state->watch_stereo = 1; | ||
624 | } else { | ||
625 | goto no_second; | ||
626 | } | ||
627 | break; | ||
628 | case 0: /* 4.5 */ | ||
629 | default: | ||
630 | no_second: | ||
631 | state->second = msp3400c_carrier_detect_main[max1].cdo; | ||
632 | msp3400c_setmode(client, MSP_MODE_FM_TERRA); | ||
633 | state->nicam_on = 0; | ||
634 | msp3400c_setcarrier(client, state->second, state->main); | ||
635 | state->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
636 | msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); | ||
637 | break; | ||
638 | } | ||
639 | |||
640 | /* unmute */ | ||
641 | msp_set_audio(client); | ||
642 | |||
643 | if (debug) | ||
644 | msp3400c_print_mode(client); | ||
645 | |||
646 | /* monitor tv audio mode */ | ||
647 | while (state->watch_stereo) { | ||
648 | if (msp_sleep(state,5000)) | ||
649 | goto restart; | ||
650 | watch_stereo(client); | ||
651 | } | ||
652 | } | ||
653 | v4l_dbg(1, client, "thread: exit\n"); | ||
654 | return 0; | ||
655 | } | ||
656 | |||
657 | |||
658 | int msp3410d_thread(void *data) | ||
659 | { | ||
660 | struct i2c_client *client = data; | ||
661 | struct msp_state *state = i2c_get_clientdata(client); | ||
662 | int mode,val,i,std; | ||
663 | |||
664 | v4l_dbg(1, client, "msp3410 daemon started\n"); | ||
665 | |||
666 | for (;;) { | ||
667 | v4l_dbg(2, client, "msp3410 thread: sleep\n"); | ||
668 | msp_sleep(state,-1); | ||
669 | v4l_dbg(2, client, "msp3410 thread: wakeup\n"); | ||
670 | |||
671 | restart: | ||
672 | v4l_dbg(1, client, "thread: restart scan\n"); | ||
673 | state->restart = 0; | ||
674 | if (kthread_should_stop()) | ||
675 | break; | ||
676 | |||
677 | if (state->mode == MSP_MODE_EXTERN) { | ||
678 | /* no carrier scan needed, just unmute */ | ||
679 | v4l_dbg(1, client, "thread: no carrier scan\n"); | ||
680 | msp_set_audio(client); | ||
681 | continue; | ||
682 | } | ||
683 | |||
684 | /* put into sane state (and mute) */ | ||
685 | msp_reset(client); | ||
686 | |||
687 | /* some time for the tuner to sync */ | ||
688 | if (msp_sleep(state,200)) | ||
689 | goto restart; | ||
690 | |||
691 | /* start autodetect */ | ||
692 | mode = msp_modus(client, state->norm); | ||
693 | std = msp_standard(state->norm); | ||
694 | msp_write_dem(client, 0x30, mode); | ||
695 | msp_write_dem(client, 0x20, std); | ||
696 | state->watch_stereo = 0; | ||
697 | |||
698 | if (debug) | ||
699 | v4l_dbg(1, client, "setting mode: %s (0x%04x)\n", | ||
700 | msp_standard_mode_name(std), std); | ||
701 | |||
702 | if (std != 1) { | ||
703 | /* programmed some specific mode */ | ||
704 | val = std; | ||
705 | } else { | ||
706 | /* triggered autodetect */ | ||
707 | for (;;) { | ||
708 | if (msp_sleep(state,100)) | ||
709 | goto restart; | ||
710 | |||
711 | /* check results */ | ||
712 | val = msp_read_dem(client, 0x7e); | ||
713 | if (val < 0x07ff) | ||
714 | break; | ||
715 | v4l_dbg(1, client, "detection still in progress\n"); | ||
716 | } | ||
717 | } | ||
718 | for (i = 0; msp_modelist[i].name != NULL; i++) | ||
719 | if (msp_modelist[i].retval == val) | ||
720 | break; | ||
721 | v4l_dbg(1, client, "current mode: %s (0x%04x)\n", | ||
722 | msp_standard_mode_name(val), val); | ||
723 | state->main = msp_modelist[i].main; | ||
724 | state->second = msp_modelist[i].second; | ||
725 | |||
726 | if (amsound && (state->norm == VIDEO_MODE_SECAM) && (val != 0x0009)) { | ||
727 | /* autodetection has failed, let backup */ | ||
728 | v4l_dbg(1, client, "autodetection failed," | ||
729 | " switching to backup mode: %s (0x%04x)\n", | ||
730 | msp_modelist[8].name ? msp_modelist[8].name : "unknown",val); | ||
731 | val = 0x0009; | ||
732 | msp_write_dem(client, 0x20, val); | ||
733 | } | ||
734 | |||
735 | /* set various prescales */ | ||
736 | msp_write_dsp(client, 0x0d, 0x1900); /* scart */ | ||
737 | msp_write_dsp(client, 0x0e, 0x2403); /* FM */ | ||
738 | msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ | ||
739 | |||
740 | /* set stereo */ | ||
741 | switch (val) { | ||
742 | case 0x0008: /* B/G NICAM */ | ||
743 | case 0x000a: /* I NICAM */ | ||
744 | if (val == 0x0008) | ||
745 | state->mode = MSP_MODE_FM_NICAM1; | ||
746 | else | ||
747 | state->mode = MSP_MODE_FM_NICAM2; | ||
748 | /* just turn on stereo */ | ||
749 | state->rxsubchans = V4L2_TUNER_SUB_STEREO; | ||
750 | state->nicam_on = 1; | ||
751 | state->watch_stereo = 1; | ||
752 | msp3400c_setstereo(client,V4L2_TUNER_MODE_STEREO); | ||
753 | break; | ||
754 | case 0x0009: | ||
755 | state->mode = MSP_MODE_AM_NICAM; | ||
756 | state->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
757 | state->nicam_on = 1; | ||
758 | msp3400c_setstereo(client,V4L2_TUNER_MODE_MONO); | ||
759 | state->watch_stereo = 1; | ||
760 | break; | ||
761 | case 0x0020: /* BTSC */ | ||
762 | /* just turn on stereo */ | ||
763 | state->mode = MSP_MODE_BTSC; | ||
764 | state->rxsubchans = V4L2_TUNER_SUB_STEREO; | ||
765 | state->nicam_on = 0; | ||
766 | state->watch_stereo = 1; | ||
767 | msp3400c_setstereo(client,V4L2_TUNER_MODE_STEREO); | ||
768 | break; | ||
769 | case 0x0040: /* FM radio */ | ||
770 | state->mode = MSP_MODE_FM_RADIO; | ||
771 | state->rxsubchans = V4L2_TUNER_SUB_STEREO; | ||
772 | state->audmode = V4L2_TUNER_MODE_STEREO; | ||
773 | state->nicam_on = 0; | ||
774 | state->watch_stereo = 0; | ||
775 | /* not needed in theory if HAVE_RADIO(), but | ||
776 | short programming enables carrier mute */ | ||
777 | msp3400c_setmode(client,MSP_MODE_FM_RADIO); | ||
778 | msp3400c_setcarrier(client, MSP_CARRIER(10.7), | ||
779 | MSP_CARRIER(10.7)); | ||
780 | /* scart routing */ | ||
781 | msp_set_scart(client,SCART_IN2,0); | ||
782 | /* msp34xx does radio decoding */ | ||
783 | msp_write_dsp(client, 0x08, 0x0020); | ||
784 | msp_write_dsp(client, 0x09, 0x0020); | ||
785 | msp_write_dsp(client, 0x0b, 0x0020); | ||
786 | break; | ||
787 | case 0x0003: | ||
788 | case 0x0004: | ||
789 | case 0x0005: | ||
790 | state->mode = MSP_MODE_FM_TERRA; | ||
791 | state->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
792 | state->audmode = V4L2_TUNER_MODE_MONO; | ||
793 | state->nicam_on = 0; | ||
794 | state->watch_stereo = 1; | ||
795 | break; | ||
796 | } | ||
797 | |||
798 | /* unmute, restore misc registers */ | ||
799 | msp_set_audio(client); | ||
800 | msp_write_dsp(client, 0x13, state->acb); | ||
801 | msp_write_dem(client, 0x40, state->i2s_mode); | ||
802 | |||
803 | /* monitor tv audio mode */ | ||
804 | while (state->watch_stereo) { | ||
805 | if (msp_sleep(state,5000)) | ||
806 | goto restart; | ||
807 | watch_stereo(client); | ||
808 | } | ||
809 | } | ||
810 | v4l_dbg(1, client, "thread: exit\n"); | ||
811 | return 0; | ||
812 | } | ||
813 | |||
814 | /* ----------------------------------------------------------------------- */ | ||
815 | |||
816 | /* msp34xxG + (autoselect no-thread) */ | ||
817 | /* this one uses both automatic standard detection and automatic sound */ | ||
818 | /* select which are available in the newer G versions */ | ||
819 | /* struct msp: only norm, acb and source are really used in this mode */ | ||
820 | |||
821 | /* set the same 'source' for the loudspeaker, scart and quasi-peak detector | ||
822 | * the value for source is the same as bit 15:8 of DSP registers 0x08, | ||
823 | * 0x0a and 0x0c: 0=mono, 1=stereo or A|B, 2=SCART, 3=stereo or A, 4=stereo or B | ||
824 | * | ||
825 | * this function replaces msp3400c_setstereo | ||
826 | */ | ||
827 | static void msp34xxg_set_source(struct i2c_client *client, int source) | ||
828 | { | ||
829 | struct msp_state *state = i2c_get_clientdata(client); | ||
830 | |||
831 | /* fix matrix mode to stereo and let the msp choose what | ||
832 | * to output according to 'source', as recommended | ||
833 | * for MONO (source==0) downmixing set bit[7:0] to 0x30 | ||
834 | */ | ||
835 | int value = (source & 0x07) << 8 | (source == 0 ? 0x30 : 0x20); | ||
836 | |||
837 | v4l_dbg(1, client, "set source to %d (0x%x)\n", source, value); | ||
838 | /* Loudspeaker Output */ | ||
839 | msp_write_dsp(client, 0x08, value); | ||
840 | /* SCART1 DA Output */ | ||
841 | msp_write_dsp(client, 0x0a, value); | ||
842 | /* Quasi-peak detector */ | ||
843 | msp_write_dsp(client, 0x0c, value); | ||
844 | /* | ||
845 | * set identification threshold. Personally, I | ||
846 | * I set it to a higher value that the default | ||
847 | * of 0x190 to ignore noisy stereo signals. | ||
848 | * this needs tuning. (recommended range 0x00a0-0x03c0) | ||
849 | * 0x7f0 = forced mono mode | ||
850 | */ | ||
851 | /* a2 threshold for stereo/bilingual */ | ||
852 | msp_write_dem(client, 0x22, stereo_threshold); | ||
853 | state->source = source; | ||
854 | } | ||
855 | |||
856 | /* (re-)initialize the msp34xxg, according to the current norm in state->norm | ||
857 | * return 0 if it worked, -1 if it failed | ||
858 | */ | ||
859 | static int msp34xxg_reset(struct i2c_client *client) | ||
860 | { | ||
861 | struct msp_state *state = i2c_get_clientdata(client); | ||
862 | int modus, std; | ||
863 | |||
864 | if (msp_reset(client)) | ||
865 | return -1; | ||
866 | |||
867 | /* make sure that input/output is muted (paranoid mode) */ | ||
868 | /* ACB, mute DSP input, mute SCART 1 */ | ||
869 | if (msp_write_dsp(client, 0x13, 0x0f20)) | ||
870 | return -1; | ||
871 | |||
872 | msp_write_dem(client, 0x40, state->i2s_mode); | ||
873 | |||
874 | /* step-by-step initialisation, as described in the manual */ | ||
875 | modus = msp_modus(client, state->norm); | ||
876 | std = msp_standard(state->norm); | ||
877 | modus &= ~0x03; /* STATUS_CHANGE = 0 */ | ||
878 | modus |= 0x01; /* AUTOMATIC_SOUND_DETECTION = 1 */ | ||
879 | if (msp_write_dem(client, 0x30, modus)) | ||
880 | return -1; | ||
881 | if (msp_write_dem(client, 0x20, std)) | ||
882 | return -1; | ||
883 | |||
884 | /* write the dsps that may have an influence on | ||
885 | standard/audio autodetection right now */ | ||
886 | msp34xxg_set_source(client, state->source); | ||
887 | |||
888 | /* AM/FM Prescale [15:8] 75khz deviation */ | ||
889 | if (msp_write_dsp(client, 0x0e, 0x3000)) | ||
890 | return -1; | ||
891 | |||
892 | /* NICAM Prescale 9db gain (as recommended) */ | ||
893 | if (msp_write_dsp(client, 0x10, 0x5a00)) | ||
894 | return -1; | ||
895 | |||
896 | return 0; | ||
897 | } | ||
898 | |||
899 | int msp34xxg_thread(void *data) | ||
900 | { | ||
901 | struct i2c_client *client = data; | ||
902 | struct msp_state *state = i2c_get_clientdata(client); | ||
903 | int val, std, i; | ||
904 | |||
905 | v4l_dbg(1, client, "msp34xxg daemon started\n"); | ||
906 | |||
907 | state->source = 1; /* default */ | ||
908 | for (;;) { | ||
909 | v4l_dbg(2, client, "msp34xxg thread: sleep\n"); | ||
910 | msp_sleep(state, -1); | ||
911 | v4l_dbg(2, client, "msp34xxg thread: wakeup\n"); | ||
912 | |||
913 | restart: | ||
914 | v4l_dbg(1, client, "thread: restart scan\n"); | ||
915 | state->restart = 0; | ||
916 | if (kthread_should_stop()) | ||
917 | break; | ||
918 | |||
919 | /* setup the chip*/ | ||
920 | msp34xxg_reset(client); | ||
921 | std = standard; | ||
922 | if (std != 0x01) | ||
923 | goto unmute; | ||
924 | |||
925 | /* watch autodetect */ | ||
926 | v4l_dbg(1, client, "triggered autodetect, waiting for result\n"); | ||
927 | for (i = 0; i < 10; i++) { | ||
928 | if (msp_sleep(state, 100)) | ||
929 | goto restart; | ||
930 | |||
931 | /* check results */ | ||
932 | val = msp_read_dem(client, 0x7e); | ||
933 | if (val < 0x07ff) { | ||
934 | std = val; | ||
935 | break; | ||
936 | } | ||
937 | v4l_dbg(2, client, "detection still in progress\n"); | ||
938 | } | ||
939 | if (std == 1) { | ||
940 | v4l_dbg(1, client, "detection still in progress after 10 tries. giving up.\n"); | ||
941 | continue; | ||
942 | } | ||
943 | |||
944 | unmute: | ||
945 | state->mode = std; | ||
946 | v4l_dbg(1, client, "current mode: %s (0x%04x)\n", | ||
947 | msp_standard_mode_name(std), std); | ||
948 | |||
949 | /* unmute: dispatch sound to scart output, set scart volume */ | ||
950 | msp_set_audio(client); | ||
951 | |||
952 | /* restore ACB */ | ||
953 | if (msp_write_dsp(client, 0x13, state->acb)) | ||
954 | return -1; | ||
955 | |||
956 | msp_write_dem(client, 0x40, state->i2s_mode); | ||
957 | } | ||
958 | v4l_dbg(1, client, "thread: exit\n"); | ||
959 | return 0; | ||
960 | } | ||
961 | |||
962 | void msp34xxg_detect_stereo(struct i2c_client *client) | ||
963 | { | ||
964 | struct msp_state *state = i2c_get_clientdata(client); | ||
965 | |||
966 | int status = msp_read_dem(client, 0x0200); | ||
967 | int is_bilingual = status & 0x100; | ||
968 | int is_stereo = status & 0x40; | ||
969 | |||
970 | state->rxsubchans = 0; | ||
971 | if (is_stereo) | ||
972 | state->rxsubchans |= V4L2_TUNER_SUB_STEREO; | ||
973 | else | ||
974 | state->rxsubchans |= V4L2_TUNER_SUB_MONO; | ||
975 | if (is_bilingual) { | ||
976 | state->rxsubchans |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; | ||
977 | /* I'm supposed to check whether it's SAP or not | ||
978 | * and set only LANG2/SAP in this case. Yet, the MSP | ||
979 | * does a lot of work to hide this and handle everything | ||
980 | * the same way. I don't want to work around it so unless | ||
981 | * this is a problem, I'll handle SAP just like lang1/lang2. | ||
982 | */ | ||
983 | } | ||
984 | v4l_dbg(1, client, "status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n", | ||
985 | status, is_stereo, is_bilingual, state->rxsubchans); | ||
986 | } | ||
987 | |||
988 | void msp34xxg_set_audmode(struct i2c_client *client, int audmode) | ||
989 | { | ||
990 | struct msp_state *state = i2c_get_clientdata(client); | ||
991 | int source; | ||
992 | |||
993 | switch (audmode) { | ||
994 | case V4L2_TUNER_MODE_MONO: | ||
995 | source = 0; /* mono only */ | ||
996 | break; | ||
997 | case V4L2_TUNER_MODE_STEREO: | ||
998 | source = 1; /* stereo or A|B, see comment in msp34xxg_get_v4l2_stereo() */ | ||
999 | /* problem: that could also mean 2 (scart input) */ | ||
1000 | break; | ||
1001 | case V4L2_TUNER_MODE_LANG1: | ||
1002 | source = 3; /* stereo or A */ | ||
1003 | break; | ||
1004 | case V4L2_TUNER_MODE_LANG2: | ||
1005 | source = 4; /* stereo or B */ | ||
1006 | break; | ||
1007 | default: | ||
1008 | audmode = 0; | ||
1009 | source = 1; | ||
1010 | break; | ||
1011 | } | ||
1012 | state->audmode = audmode; | ||
1013 | msp34xxg_set_source(client, source); | ||
1014 | } | ||
1015 | |||