]>
Commit | Line | Data |
---|---|---|
866b8695 GKH |
1 | /* |
2 | * Copyright (C) 2005-2006 Micronas USA Inc. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License (Version 2) as | |
6 | * published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * along with this program; if not, write to the Free Software Foundation, | |
15 | * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | |
16 | */ | |
17 | ||
18 | #include <linux/module.h> | |
19 | #include <linux/init.h> | |
866b8695 | 20 | #include <linux/i2c.h> |
df20d69e | 21 | #include <linux/videodev2.h> |
5a0e3ad6 | 22 | #include <linux/slab.h> |
866b8695 GKH |
23 | #include <media/tuner.h> |
24 | #include <media/v4l2-common.h> | |
df20d69e | 25 | #include <media/v4l2-ioctl.h> |
866b8695 GKH |
26 | |
27 | #include "wis-i2c.h" | |
28 | ||
29 | /* #define MPX_DEBUG */ | |
30 | ||
31 | /* AS(IF/MPX) pin: LOW HIGH/OPEN | |
32 | * IF/MPX address: 0x42/0x40 0x43/0x44 | |
33 | */ | |
34 | #define IF_I2C_ADDR 0x43 | |
35 | #define MPX_I2C_ADDR 0x44 | |
36 | ||
37 | static v4l2_std_id force_band; | |
38 | static char force_band_str[] = "-"; | |
39 | module_param_string(force_band, force_band_str, sizeof(force_band_str), 0644); | |
40 | static int force_mpx_mode = -1; | |
41 | module_param(force_mpx_mode, int, 0644); | |
42 | ||
43 | /* Store tuner info in the same format as tuner.c, so maybe we can put the | |
44 | * Sony tuner support in there. */ | |
45 | struct sony_tunertype { | |
46 | char *name; | |
47 | unsigned char Vendor; /* unused here */ | |
48 | unsigned char Type; /* unused here */ | |
49 | ||
50 | unsigned short thresh1; /* band switch VHF_LO <=> VHF_HI */ | |
51 | unsigned short thresh2; /* band switch VHF_HI <=> UHF */ | |
52 | unsigned char VHF_L; | |
53 | unsigned char VHF_H; | |
54 | unsigned char UHF; | |
55 | unsigned char config; | |
56 | unsigned short IFPCoff; | |
57 | }; | |
58 | ||
59 | /* This array is indexed by (tuner_type - 200) */ | |
60 | static struct sony_tunertype sony_tuners[] = { | |
61 | { "Sony PAL+SECAM (BTF-PG472Z)", 0, 0, | |
62 | 16*144.25, 16*427.25, 0x01, 0x02, 0x04, 0xc6, 623}, | |
63 | { "Sony NTSC_JP (BTF-PK467Z)", 0, 0, | |
64 | 16*220.25, 16*467.25, 0x01, 0x02, 0x04, 0xc6, 940}, | |
65 | { "Sony NTSC (BTF-PB463Z)", 0, 0, | |
66 | 16*130.25, 16*364.25, 0x01, 0x02, 0x04, 0xc6, 732}, | |
67 | }; | |
68 | ||
69 | struct wis_sony_tuner { | |
70 | int type; | |
71 | v4l2_std_id std; | |
72 | unsigned int freq; | |
73 | int mpxmode; | |
74 | u32 audmode; | |
75 | }; | |
76 | ||
77 | /* Basically the same as default_set_tv_freq() in tuner.c */ | |
78 | static int set_freq(struct i2c_client *client, int freq) | |
79 | { | |
80 | struct wis_sony_tuner *t = i2c_get_clientdata(client); | |
81 | char *band_name; | |
82 | int n; | |
83 | int band_select; | |
84 | struct sony_tunertype *tun; | |
85 | u8 buffer[4]; | |
86 | ||
87 | tun = &sony_tuners[t->type - 200]; | |
88 | if (freq < tun->thresh1) { | |
89 | band_name = "VHF_L"; | |
90 | band_select = tun->VHF_L; | |
91 | } else if (freq < tun->thresh2) { | |
92 | band_name = "VHF_H"; | |
93 | band_select = tun->VHF_H; | |
94 | } else { | |
95 | band_name = "UHF"; | |
96 | band_select = tun->UHF; | |
97 | } | |
98 | printk(KERN_DEBUG "wis-sony-tuner: tuning to frequency %d.%04d (%s)\n", | |
99 | freq / 16, (freq % 16) * 625, band_name); | |
100 | n = freq + tun->IFPCoff; | |
101 | ||
102 | buffer[0] = n >> 8; | |
103 | buffer[1] = n & 0xff; | |
104 | buffer[2] = tun->config; | |
105 | buffer[3] = band_select; | |
106 | i2c_master_send(client, buffer, 4); | |
107 | ||
108 | return 0; | |
109 | } | |
110 | ||
111 | static int mpx_write(struct i2c_client *client, int dev, int addr, int val) | |
112 | { | |
113 | u8 buffer[5]; | |
114 | struct i2c_msg msg; | |
115 | ||
116 | buffer[0] = dev; | |
117 | buffer[1] = addr >> 8; | |
118 | buffer[2] = addr & 0xff; | |
119 | buffer[3] = val >> 8; | |
120 | buffer[4] = val & 0xff; | |
121 | msg.addr = MPX_I2C_ADDR; | |
122 | msg.flags = 0; | |
123 | msg.len = 5; | |
124 | msg.buf = buffer; | |
125 | i2c_transfer(client->adapter, &msg, 1); | |
126 | return 0; | |
127 | } | |
128 | ||
129 | /* | |
130 | * MPX register values for the BTF-PG472Z: | |
131 | * | |
132 | * FM_ NICAM_ SCART_ | |
133 | * MODUS SOURCE ACB PRESCAL PRESCAL PRESCAL SYSTEM VOLUME | |
134 | * 10/0030 12/0008 12/0013 12/000E 12/0010 12/0000 10/0020 12/0000 | |
135 | * --------------------------------------------------------------- | |
136 | * Auto 1003 0020 0100 2603 5000 XXXX 0001 7500 | |
137 | * | |
138 | * B/G | |
139 | * Mono 1003 0020 0100 2603 5000 XXXX 0003 7500 | |
140 | * A2 1003 0020 0100 2601 5000 XXXX 0003 7500 | |
141 | * NICAM 1003 0120 0100 2603 5000 XXXX 0008 7500 | |
142 | * | |
143 | * I | |
144 | * Mono 1003 0020 0100 2603 7900 XXXX 000A 7500 | |
145 | * NICAM 1003 0120 0100 2603 7900 XXXX 000A 7500 | |
146 | * | |
147 | * D/K | |
148 | * Mono 1003 0020 0100 2603 5000 XXXX 0004 7500 | |
149 | * A2-1 1003 0020 0100 2601 5000 XXXX 0004 7500 | |
150 | * A2-2 1003 0020 0100 2601 5000 XXXX 0005 7500 | |
151 | * A2-3 1003 0020 0100 2601 5000 XXXX 0007 7500 | |
152 | * NICAM 1003 0120 0100 2603 5000 XXXX 000B 7500 | |
153 | * | |
154 | * L/L' | |
155 | * Mono 0003 0200 0100 7C03 5000 2200 0009 7500 | |
156 | * NICAM 0003 0120 0100 7C03 5000 XXXX 0009 7500 | |
157 | * | |
158 | * M | |
159 | * Mono 1003 0200 0100 2B03 5000 2B00 0002 7500 | |
160 | * | |
161 | * For Asia, replace the 0x26XX in FM_PRESCALE with 0x14XX. | |
162 | * | |
163 | * Bilingual selection in A2/NICAM: | |
164 | * | |
165 | * High byte of SOURCE Left chan Right chan | |
166 | * 0x01 MAIN SUB | |
167 | * 0x03 MAIN MAIN | |
168 | * 0x04 SUB SUB | |
169 | * | |
170 | * Force mono in NICAM by setting the high byte of SOURCE to 0x02 (L/L') or | |
171 | * 0x00 (all other bands). Force mono in A2 with FMONO_A2: | |
172 | * | |
173 | * FMONO_A2 | |
174 | * 10/0022 | |
175 | * -------- | |
176 | * Forced mono ON 07F0 | |
177 | * Forced mono OFF 0190 | |
178 | */ | |
179 | ||
180 | static struct { | |
181 | enum { AUD_MONO, AUD_A2, AUD_NICAM, AUD_NICAM_L } audio_mode; | |
182 | u16 modus; | |
183 | u16 source; | |
184 | u16 acb; | |
185 | u16 fm_prescale; | |
186 | u16 nicam_prescale; | |
187 | u16 scart_prescale; | |
188 | u16 system; | |
189 | u16 volume; | |
190 | } mpx_audio_modes[] = { | |
191 | /* Auto */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, | |
192 | 0x5000, 0x0000, 0x0001, 0x7500 }, | |
193 | /* B/G Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, | |
194 | 0x5000, 0x0000, 0x0003, 0x7500 }, | |
195 | /* B/G A2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, | |
196 | 0x5000, 0x0000, 0x0003, 0x7500 }, | |
197 | /* B/G NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, | |
198 | 0x5000, 0x0000, 0x0008, 0x7500 }, | |
199 | /* I Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, | |
200 | 0x7900, 0x0000, 0x000A, 0x7500 }, | |
201 | /* I NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, | |
202 | 0x7900, 0x0000, 0x000A, 0x7500 }, | |
203 | /* D/K Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, | |
204 | 0x5000, 0x0000, 0x0004, 0x7500 }, | |
205 | /* D/K A2-1 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, | |
206 | 0x5000, 0x0000, 0x0004, 0x7500 }, | |
207 | /* D/K A2-2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, | |
208 | 0x5000, 0x0000, 0x0005, 0x7500 }, | |
209 | /* D/K A2-3 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, | |
210 | 0x5000, 0x0000, 0x0007, 0x7500 }, | |
211 | /* D/K NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, | |
212 | 0x5000, 0x0000, 0x000B, 0x7500 }, | |
213 | /* L/L' Mono */ { AUD_MONO, 0x0003, 0x0200, 0x0100, 0x7C03, | |
214 | 0x5000, 0x2200, 0x0009, 0x7500 }, | |
215 | /* L/L' NICAM */{ AUD_NICAM_L, 0x0003, 0x0120, 0x0100, 0x7C03, | |
216 | 0x5000, 0x0000, 0x0009, 0x7500 }, | |
217 | }; | |
218 | ||
219 | #define MPX_NUM_MODES ARRAY_SIZE(mpx_audio_modes) | |
220 | ||
221 | static int mpx_setup(struct i2c_client *client) | |
222 | { | |
223 | struct wis_sony_tuner *t = i2c_get_clientdata(client); | |
224 | u16 source = 0; | |
225 | u8 buffer[3]; | |
226 | struct i2c_msg msg; | |
227 | ||
228 | /* reset MPX */ | |
229 | buffer[0] = 0x00; | |
230 | buffer[1] = 0x80; | |
231 | buffer[2] = 0x00; | |
232 | msg.addr = MPX_I2C_ADDR; | |
233 | msg.flags = 0; | |
234 | msg.len = 3; | |
235 | msg.buf = buffer; | |
236 | i2c_transfer(client->adapter, &msg, 1); | |
237 | buffer[1] = 0x00; | |
238 | i2c_transfer(client->adapter, &msg, 1); | |
239 | ||
240 | if (mpx_audio_modes[t->mpxmode].audio_mode != AUD_MONO) { | |
241 | switch (t->audmode) { | |
242 | case V4L2_TUNER_MODE_MONO: | |
243 | switch (mpx_audio_modes[t->mpxmode].audio_mode) { | |
244 | case AUD_A2: | |
245 | source = mpx_audio_modes[t->mpxmode].source; | |
246 | break; | |
247 | case AUD_NICAM: | |
248 | source = 0x0000; | |
249 | break; | |
250 | case AUD_NICAM_L: | |
251 | source = 0x0200; | |
252 | break; | |
253 | default: | |
254 | break; | |
255 | } | |
256 | break; | |
257 | case V4L2_TUNER_MODE_STEREO: | |
258 | source = mpx_audio_modes[t->mpxmode].source; | |
259 | break; | |
260 | case V4L2_TUNER_MODE_LANG1: | |
261 | source = 0x0300; | |
262 | break; | |
263 | case V4L2_TUNER_MODE_LANG2: | |
264 | source = 0x0400; | |
265 | break; | |
266 | } | |
267 | source |= mpx_audio_modes[t->mpxmode].source & 0x00ff; | |
268 | } else | |
269 | source = mpx_audio_modes[t->mpxmode].source; | |
270 | ||
271 | mpx_write(client, 0x10, 0x0030, mpx_audio_modes[t->mpxmode].modus); | |
272 | mpx_write(client, 0x12, 0x0008, source); | |
273 | mpx_write(client, 0x12, 0x0013, mpx_audio_modes[t->mpxmode].acb); | |
274 | mpx_write(client, 0x12, 0x000e, | |
275 | mpx_audio_modes[t->mpxmode].fm_prescale); | |
276 | mpx_write(client, 0x12, 0x0010, | |
277 | mpx_audio_modes[t->mpxmode].nicam_prescale); | |
278 | mpx_write(client, 0x12, 0x000d, | |
279 | mpx_audio_modes[t->mpxmode].scart_prescale); | |
280 | mpx_write(client, 0x10, 0x0020, mpx_audio_modes[t->mpxmode].system); | |
281 | mpx_write(client, 0x12, 0x0000, mpx_audio_modes[t->mpxmode].volume); | |
282 | if (mpx_audio_modes[t->mpxmode].audio_mode == AUD_A2) | |
283 | mpx_write(client, 0x10, 0x0022, | |
284 | t->audmode == V4L2_TUNER_MODE_MONO ? 0x07f0 : 0x0190); | |
285 | ||
286 | #ifdef MPX_DEBUG | |
287 | { | |
288 | u8 buf1[3], buf2[2]; | |
289 | struct i2c_msg msgs[2]; | |
290 | ||
291 | printk(KERN_DEBUG "wis-sony-tuner: MPX registers: %04x %04x " | |
292 | "%04x %04x %04x %04x %04x %04x\n", | |
293 | mpx_audio_modes[t->mpxmode].modus, | |
294 | source, | |
295 | mpx_audio_modes[t->mpxmode].acb, | |
296 | mpx_audio_modes[t->mpxmode].fm_prescale, | |
297 | mpx_audio_modes[t->mpxmode].nicam_prescale, | |
298 | mpx_audio_modes[t->mpxmode].scart_prescale, | |
299 | mpx_audio_modes[t->mpxmode].system, | |
300 | mpx_audio_modes[t->mpxmode].volume); | |
301 | buf1[0] = 0x11; | |
302 | buf1[1] = 0x00; | |
303 | buf1[2] = 0x7e; | |
304 | msgs[0].addr = MPX_I2C_ADDR; | |
305 | msgs[0].flags = 0; | |
306 | msgs[0].len = 3; | |
307 | msgs[0].buf = buf1; | |
308 | msgs[1].addr = MPX_I2C_ADDR; | |
309 | msgs[1].flags = I2C_M_RD; | |
310 | msgs[1].len = 2; | |
311 | msgs[1].buf = buf2; | |
312 | i2c_transfer(client->adapter, msgs, 2); | |
313 | printk(KERN_DEBUG "wis-sony-tuner: MPX system: %02x%02x\n", | |
314 | buf2[0], buf2[1]); | |
315 | buf1[0] = 0x11; | |
316 | buf1[1] = 0x02; | |
317 | buf1[2] = 0x00; | |
318 | i2c_transfer(client->adapter, msgs, 2); | |
319 | printk(KERN_DEBUG "wis-sony-tuner: MPX status: %02x%02x\n", | |
320 | buf2[0], buf2[1]); | |
321 | } | |
322 | #endif | |
323 | return 0; | |
324 | } | |
325 | ||
326 | /* | |
327 | * IF configuration values for the BTF-PG472Z: | |
328 | * | |
329 | * B/G: 0x94 0x70 0x49 | |
330 | * I: 0x14 0x70 0x4a | |
331 | * D/K: 0x14 0x70 0x4b | |
332 | * L: 0x04 0x70 0x4b | |
333 | * L': 0x44 0x70 0x53 | |
334 | * M: 0x50 0x30 0x4c | |
335 | */ | |
336 | ||
337 | static int set_if(struct i2c_client *client) | |
338 | { | |
339 | struct wis_sony_tuner *t = i2c_get_clientdata(client); | |
340 | u8 buffer[4]; | |
341 | struct i2c_msg msg; | |
342 | int default_mpx_mode = 0; | |
343 | ||
344 | /* configure IF */ | |
345 | buffer[0] = 0; | |
346 | if (t->std & V4L2_STD_PAL_BG) { | |
347 | buffer[1] = 0x94; | |
348 | buffer[2] = 0x70; | |
349 | buffer[3] = 0x49; | |
350 | default_mpx_mode = 1; | |
351 | } else if (t->std & V4L2_STD_PAL_I) { | |
352 | buffer[1] = 0x14; | |
353 | buffer[2] = 0x70; | |
354 | buffer[3] = 0x4a; | |
355 | default_mpx_mode = 4; | |
356 | } else if (t->std & V4L2_STD_PAL_DK) { | |
357 | buffer[1] = 0x14; | |
358 | buffer[2] = 0x70; | |
359 | buffer[3] = 0x4b; | |
360 | default_mpx_mode = 6; | |
361 | } else if (t->std & V4L2_STD_SECAM_L) { | |
362 | buffer[1] = 0x04; | |
363 | buffer[2] = 0x70; | |
364 | buffer[3] = 0x4b; | |
365 | default_mpx_mode = 11; | |
366 | } | |
367 | msg.addr = IF_I2C_ADDR; | |
368 | msg.flags = 0; | |
369 | msg.len = 4; | |
370 | msg.buf = buffer; | |
371 | i2c_transfer(client->adapter, &msg, 1); | |
372 | ||
373 | /* Select MPX mode if not forced by the user */ | |
315d7fa9 | 374 | if (force_mpx_mode >= 0 && force_mpx_mode < MPX_NUM_MODES) |
866b8695 GKH |
375 | t->mpxmode = force_mpx_mode; |
376 | else | |
377 | t->mpxmode = default_mpx_mode; | |
378 | printk(KERN_DEBUG "wis-sony-tuner: setting MPX to mode %d\n", | |
379 | t->mpxmode); | |
380 | mpx_setup(client); | |
381 | ||
382 | return 0; | |
383 | } | |
384 | ||
385 | static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | |
386 | { | |
387 | struct wis_sony_tuner *t = i2c_get_clientdata(client); | |
388 | ||
389 | switch (cmd) { | |
86a79d2e | 390 | #if 0 |
866b8695 GKH |
391 | #ifdef TUNER_SET_TYPE_ADDR |
392 | case TUNER_SET_TYPE_ADDR: | |
393 | { | |
394 | struct tuner_setup *tun_setup = arg; | |
395 | int *type = &tun_setup->type; | |
396 | #else | |
397 | case TUNER_SET_TYPE: | |
398 | { | |
399 | int *type = arg; | |
400 | #endif | |
401 | ||
402 | if (t->type >= 0) { | |
403 | if (t->type != *type) | |
404 | printk(KERN_ERR "wis-sony-tuner: type already " | |
405 | "set to %d, ignoring request for %d\n", | |
406 | t->type, *type); | |
407 | break; | |
408 | } | |
409 | t->type = *type; | |
410 | switch (t->type) { | |
411 | case TUNER_SONY_BTF_PG472Z: | |
412 | switch (force_band_str[0]) { | |
413 | case 'b': | |
414 | case 'B': | |
415 | case 'g': | |
416 | case 'G': | |
417 | printk(KERN_INFO "wis-sony-tuner: forcing " | |
418 | "tuner to PAL-B/G bands\n"); | |
419 | force_band = V4L2_STD_PAL_BG; | |
420 | break; | |
421 | case 'i': | |
422 | case 'I': | |
423 | printk(KERN_INFO "wis-sony-tuner: forcing " | |
424 | "tuner to PAL-I band\n"); | |
425 | force_band = V4L2_STD_PAL_I; | |
426 | break; | |
427 | case 'd': | |
428 | case 'D': | |
429 | case 'k': | |
430 | case 'K': | |
431 | printk(KERN_INFO "wis-sony-tuner: forcing " | |
432 | "tuner to PAL-D/K bands\n"); | |
433 | force_band = V4L2_STD_PAL_I; | |
434 | break; | |
435 | case 'l': | |
436 | case 'L': | |
437 | printk(KERN_INFO "wis-sony-tuner: forcing " | |
438 | "tuner to SECAM-L band\n"); | |
439 | force_band = V4L2_STD_SECAM_L; | |
440 | break; | |
441 | default: | |
442 | force_band = 0; | |
443 | break; | |
444 | } | |
445 | if (force_band) | |
446 | t->std = force_band; | |
447 | else | |
448 | t->std = V4L2_STD_PAL_BG; | |
449 | set_if(client); | |
450 | break; | |
451 | case TUNER_SONY_BTF_PK467Z: | |
452 | t->std = V4L2_STD_NTSC_M_JP; | |
453 | break; | |
454 | case TUNER_SONY_BTF_PB463Z: | |
455 | t->std = V4L2_STD_NTSC_M; | |
456 | break; | |
457 | default: | |
458 | printk(KERN_ERR "wis-sony-tuner: tuner type %d is not " | |
459 | "supported by this module\n", *type); | |
460 | break; | |
461 | } | |
462 | if (type >= 0) | |
463 | printk(KERN_INFO | |
464 | "wis-sony-tuner: type set to %d (%s)\n", | |
465 | t->type, sony_tuners[t->type - 200].name); | |
466 | break; | |
467 | } | |
86a79d2e | 468 | #endif |
866b8695 GKH |
469 | case VIDIOC_G_FREQUENCY: |
470 | { | |
471 | struct v4l2_frequency *f = arg; | |
472 | ||
473 | f->frequency = t->freq; | |
474 | break; | |
475 | } | |
476 | case VIDIOC_S_FREQUENCY: | |
477 | { | |
478 | struct v4l2_frequency *f = arg; | |
479 | ||
480 | t->freq = f->frequency; | |
481 | set_freq(client, t->freq); | |
482 | break; | |
483 | } | |
484 | case VIDIOC_ENUMSTD: | |
485 | { | |
486 | struct v4l2_standard *std = arg; | |
487 | ||
488 | switch (t->type) { | |
489 | case TUNER_SONY_BTF_PG472Z: | |
490 | switch (std->index) { | |
491 | case 0: | |
492 | v4l2_video_std_construct(std, | |
493 | V4L2_STD_PAL_BG, "PAL-B/G"); | |
494 | break; | |
495 | case 1: | |
496 | v4l2_video_std_construct(std, | |
497 | V4L2_STD_PAL_I, "PAL-I"); | |
498 | break; | |
499 | case 2: | |
500 | v4l2_video_std_construct(std, | |
501 | V4L2_STD_PAL_DK, "PAL-D/K"); | |
502 | break; | |
503 | case 3: | |
504 | v4l2_video_std_construct(std, | |
505 | V4L2_STD_SECAM_L, "SECAM-L"); | |
506 | break; | |
507 | default: | |
508 | std->id = 0; /* hack to indicate EINVAL */ | |
509 | break; | |
510 | } | |
511 | break; | |
512 | case TUNER_SONY_BTF_PK467Z: | |
513 | if (std->index != 0) { | |
514 | std->id = 0; /* hack to indicate EINVAL */ | |
515 | break; | |
516 | } | |
517 | v4l2_video_std_construct(std, | |
518 | V4L2_STD_NTSC_M_JP, "NTSC-J"); | |
519 | break; | |
520 | case TUNER_SONY_BTF_PB463Z: | |
521 | if (std->index != 0) { | |
522 | std->id = 0; /* hack to indicate EINVAL */ | |
523 | break; | |
524 | } | |
525 | v4l2_video_std_construct(std, V4L2_STD_NTSC_M, "NTSC"); | |
526 | break; | |
527 | } | |
528 | break; | |
529 | } | |
530 | case VIDIOC_G_STD: | |
531 | { | |
532 | v4l2_std_id *std = arg; | |
533 | ||
534 | *std = t->std; | |
535 | break; | |
536 | } | |
537 | case VIDIOC_S_STD: | |
538 | { | |
539 | v4l2_std_id *std = arg; | |
540 | v4l2_std_id old = t->std; | |
541 | ||
542 | switch (t->type) { | |
543 | case TUNER_SONY_BTF_PG472Z: | |
544 | if (force_band && (*std & force_band) != *std && | |
545 | *std != V4L2_STD_PAL && | |
546 | *std != V4L2_STD_SECAM) { | |
547 | printk(KERN_DEBUG "wis-sony-tuner: ignoring " | |
548 | "requested TV standard in " | |
549 | "favor of force_band value\n"); | |
550 | t->std = force_band; | |
551 | } else if (*std & V4L2_STD_PAL_BG) { /* default */ | |
552 | t->std = V4L2_STD_PAL_BG; | |
553 | } else if (*std & V4L2_STD_PAL_I) { | |
554 | t->std = V4L2_STD_PAL_I; | |
555 | } else if (*std & V4L2_STD_PAL_DK) { | |
556 | t->std = V4L2_STD_PAL_DK; | |
557 | } else if (*std & V4L2_STD_SECAM_L) { | |
558 | t->std = V4L2_STD_SECAM_L; | |
559 | } else { | |
560 | printk(KERN_ERR "wis-sony-tuner: TV standard " | |
561 | "not supported\n"); | |
562 | *std = 0; /* hack to indicate EINVAL */ | |
563 | break; | |
564 | } | |
565 | if (old != t->std) | |
566 | set_if(client); | |
567 | break; | |
568 | case TUNER_SONY_BTF_PK467Z: | |
569 | if (!(*std & V4L2_STD_NTSC_M_JP)) { | |
570 | printk(KERN_ERR "wis-sony-tuner: TV standard " | |
571 | "not supported\n"); | |
572 | *std = 0; /* hack to indicate EINVAL */ | |
573 | } | |
574 | break; | |
575 | case TUNER_SONY_BTF_PB463Z: | |
576 | if (!(*std & V4L2_STD_NTSC_M)) { | |
577 | printk(KERN_ERR "wis-sony-tuner: TV standard " | |
578 | "not supported\n"); | |
579 | *std = 0; /* hack to indicate EINVAL */ | |
580 | } | |
581 | break; | |
582 | } | |
583 | break; | |
584 | } | |
585 | case VIDIOC_QUERYSTD: | |
586 | { | |
587 | v4l2_std_id *std = arg; | |
588 | ||
589 | switch (t->type) { | |
590 | case TUNER_SONY_BTF_PG472Z: | |
591 | if (force_band) | |
592 | *std = force_band; | |
593 | else | |
594 | *std = V4L2_STD_PAL_BG | V4L2_STD_PAL_I | | |
595 | V4L2_STD_PAL_DK | V4L2_STD_SECAM_L; | |
596 | break; | |
597 | case TUNER_SONY_BTF_PK467Z: | |
598 | *std = V4L2_STD_NTSC_M_JP; | |
599 | break; | |
600 | case TUNER_SONY_BTF_PB463Z: | |
601 | *std = V4L2_STD_NTSC_M; | |
602 | break; | |
603 | } | |
604 | break; | |
605 | } | |
606 | case VIDIOC_G_TUNER: | |
607 | { | |
608 | struct v4l2_tuner *tun = arg; | |
609 | ||
d73f822c | 610 | memset(tun, 0, sizeof(*tun)); |
866b8695 GKH |
611 | strcpy(tun->name, "Television"); |
612 | tun->type = V4L2_TUNER_ANALOG_TV; | |
613 | tun->rangelow = 0UL; /* does anything use these? */ | |
614 | tun->rangehigh = 0xffffffffUL; | |
615 | switch (t->type) { | |
616 | case TUNER_SONY_BTF_PG472Z: | |
617 | tun->capability = V4L2_TUNER_CAP_NORM | | |
618 | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | | |
619 | V4L2_TUNER_CAP_LANG2; | |
620 | tun->rxsubchans = V4L2_TUNER_SUB_MONO | | |
621 | V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 | | |
622 | V4L2_TUNER_SUB_LANG2; | |
623 | break; | |
624 | case TUNER_SONY_BTF_PK467Z: | |
625 | case TUNER_SONY_BTF_PB463Z: | |
626 | tun->capability = V4L2_TUNER_CAP_STEREO; | |
627 | tun->rxsubchans = V4L2_TUNER_SUB_MONO | | |
628 | V4L2_TUNER_SUB_STEREO; | |
629 | break; | |
630 | } | |
631 | tun->audmode = t->audmode; | |
632 | return 0; | |
633 | } | |
634 | case VIDIOC_S_TUNER: | |
635 | { | |
636 | struct v4l2_tuner *tun = arg; | |
637 | ||
638 | switch (t->type) { | |
639 | case TUNER_SONY_BTF_PG472Z: | |
640 | if (tun->audmode != t->audmode) { | |
641 | t->audmode = tun->audmode; | |
642 | mpx_setup(client); | |
643 | } | |
644 | break; | |
645 | case TUNER_SONY_BTF_PK467Z: | |
646 | case TUNER_SONY_BTF_PB463Z: | |
647 | break; | |
648 | } | |
649 | return 0; | |
650 | } | |
651 | default: | |
652 | break; | |
653 | } | |
654 | return 0; | |
655 | } | |
656 | ||
7400516a JD |
657 | static int wis_sony_tuner_probe(struct i2c_client *client, |
658 | const struct i2c_device_id *id) | |
866b8695 | 659 | { |
7400516a | 660 | struct i2c_adapter *adapter = client->adapter; |
866b8695 GKH |
661 | struct wis_sony_tuner *t; |
662 | ||
663 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) | |
7400516a | 664 | return -ENODEV; |
866b8695 GKH |
665 | |
666 | t = kmalloc(sizeof(struct wis_sony_tuner), GFP_KERNEL); | |
7400516a | 667 | if (t == NULL) |
866b8695 | 668 | return -ENOMEM; |
7400516a | 669 | |
866b8695 GKH |
670 | t->type = -1; |
671 | t->freq = 0; | |
672 | t->mpxmode = 0; | |
673 | t->audmode = V4L2_TUNER_MODE_STEREO; | |
674 | i2c_set_clientdata(client, t); | |
675 | ||
676 | printk(KERN_DEBUG | |
677 | "wis-sony-tuner: initializing tuner at address %d on %s\n", | |
7400516a | 678 | client->addr, adapter->name); |
866b8695 GKH |
679 | |
680 | return 0; | |
681 | } | |
682 | ||
7400516a | 683 | static int wis_sony_tuner_remove(struct i2c_client *client) |
866b8695 GKH |
684 | { |
685 | struct wis_sony_tuner *t = i2c_get_clientdata(client); | |
866b8695 GKH |
686 | |
687 | kfree(t); | |
866b8695 GKH |
688 | return 0; |
689 | } | |
690 | ||
b76a3263 | 691 | static const struct i2c_device_id wis_sony_tuner_id[] = { |
7400516a JD |
692 | { "wis_sony_tuner", 0 }, |
693 | { } | |
694 | }; | |
695 | ||
866b8695 GKH |
696 | static struct i2c_driver wis_sony_tuner_driver = { |
697 | .driver = { | |
698 | .name = "WIS Sony TV Tuner I2C driver", | |
699 | }, | |
7400516a JD |
700 | .probe = wis_sony_tuner_probe, |
701 | .remove = wis_sony_tuner_remove, | |
866b8695 | 702 | .command = tuner_command, |
7400516a | 703 | .id_table = wis_sony_tuner_id, |
866b8695 GKH |
704 | }; |
705 | ||
706 | static int __init wis_sony_tuner_init(void) | |
707 | { | |
7400516a | 708 | return i2c_add_driver(&wis_sony_tuner_driver); |
866b8695 GKH |
709 | } |
710 | ||
711 | static void __exit wis_sony_tuner_cleanup(void) | |
712 | { | |
866b8695 GKH |
713 | i2c_del_driver(&wis_sony_tuner_driver); |
714 | } | |
715 | ||
716 | module_init(wis_sony_tuner_init); | |
717 | module_exit(wis_sony_tuner_cleanup); | |
718 | ||
719 | MODULE_LICENSE("GPL v2"); |