diff options
author | Mauro Carvalho Chehab <mchehab@brturbo.com.br> | 2005-06-24 01:02:43 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-24 03:05:31 -0400 |
commit | 391cd727eac2e10be7685efd739a3ea9de87393c (patch) | |
tree | 564ac5faefc87d6a8806d56a82d22a0404da0fdc /drivers | |
parent | 55f51efdb696ff6e9d2056377d05268a97f3d4e4 (diff) |
[PATCH] tuner-core.c improvments and Ymec Tvision TVF8533MF support
tuner-core.c, tuner.h:
- tuner-core changed to support multiple I2C devices used on some
adapters;
- Kconfig now has an option (CONFIG_TUNER_MULTI_I2C) to enable this new
behavor;
- By default, even enabling CONFIG_TUNER_MULTI_I2C, tuner-core emulates
the old behavor, using first I2C device for both FM and TV;
- There is a new i2c command (TUNER_SET_ADDR) to allow tuner clients to
select I2C address for FM or TV tuner;
- Tuner I2C dettach now generates a warning on syslog if failed.
tuner-simple.c:
- TVision TVF-8531MF and TVF-5533 MF tuner included. It uses, by
default, I2C on 0xC2 address for TV and on 0xC0 for Radio. Both TV and
FM Radio mode are working.
Signed-off-by: Mauro Carvalho Chehab <mchehab@brturbo.com.br>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/Kconfig | 13 | ||||
-rw-r--r-- | drivers/media/video/tuner-core.c | 79 | ||||
-rw-r--r-- | drivers/media/video/tuner-simple.c | 28 |
3 files changed, 116 insertions, 4 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 6c05fddb69ab..8c349706f850 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig | |||
@@ -7,6 +7,19 @@ menu "Video For Linux" | |||
7 | 7 | ||
8 | comment "Video Adapters" | 8 | comment "Video Adapters" |
9 | 9 | ||
10 | config CONFIG_TUNER_MULTI_I2C | ||
11 | bool "Enable support for multiple I2C devices on Video Adapters (EXPERIMENTAL)" | ||
12 | depends on VIDEO_DEV && EXPERIMENTAL | ||
13 | ---help--- | ||
14 | Some video adapters have more than one tuner inside. This patch | ||
15 | enables support for using more than one tuner. This is required | ||
16 | for some cards to allow tunning both video and radio. | ||
17 | It also improves I2C autodetection for these cards. | ||
18 | |||
19 | Only few tuners currently is supporting this. More to come. | ||
20 | |||
21 | It is safe to say 'Y' here even if your card has only one I2C tuner. | ||
22 | |||
10 | config VIDEO_BT848 | 23 | config VIDEO_BT848 |
11 | tristate "BT848 Video For Linux" | 24 | tristate "BT848 Video For Linux" |
12 | depends on VIDEO_DEV && PCI && I2C | 25 | depends on VIDEO_DEV && PCI && I2C |
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 81882ddab859..71423ae3b4dd 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * $Id: tuner-core.c,v 1.5 2005/02/15 15:59:35 kraxel Exp $ | 2 | * $Id: tuner-core.c,v 1.7 2005/05/30 02:02:47 mchehab Exp $ |
3 | * | 3 | * |
4 | * i2c tv tuner chip device driver | 4 | * i2c tv tuner chip device driver |
5 | * core core, i.e. kernel interfaces, registering and so on | 5 | * core core, i.e. kernel interfaces, registering and so on |
@@ -23,6 +23,11 @@ | |||
23 | #include <media/tuner.h> | 23 | #include <media/tuner.h> |
24 | #include <media/audiochip.h> | 24 | #include <media/audiochip.h> |
25 | 25 | ||
26 | /* | ||
27 | * comment line bellow to return to old behavor, where only one I2C device is supported | ||
28 | */ | ||
29 | /* #define CONFIG_TUNER_MULTI_I2C */ | ||
30 | |||
26 | #define UNSET (-1U) | 31 | #define UNSET (-1U) |
27 | 32 | ||
28 | /* standard i2c insmod options */ | 33 | /* standard i2c insmod options */ |
@@ -53,6 +58,9 @@ MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); | |||
53 | MODULE_LICENSE("GPL"); | 58 | MODULE_LICENSE("GPL"); |
54 | 59 | ||
55 | static int this_adap; | 60 | static int this_adap; |
61 | #ifdef CONFIG_TUNER_MULTI_I2C | ||
62 | static unsigned short tv_tuner, radio_tuner; | ||
63 | #endif | ||
56 | 64 | ||
57 | static struct i2c_driver driver; | 65 | static struct i2c_driver driver; |
58 | static struct i2c_client client_template; | 66 | static struct i2c_client client_template; |
@@ -125,6 +133,28 @@ static void set_freq(struct i2c_client *c, unsigned long freq) | |||
125 | t->freq = freq; | 133 | t->freq = freq; |
126 | } | 134 | } |
127 | 135 | ||
136 | #ifdef CONFIG_TUNER_MULTI_I2C | ||
137 | static void set_addr(struct i2c_client *c, struct tuner_addr *tun_addr) | ||
138 | { | ||
139 | struct tuner *t = i2c_get_clientdata(c); | ||
140 | |||
141 | switch (tun_addr->type) { | ||
142 | case V4L2_TUNER_RADIO: | ||
143 | radio_tuner=tun_addr->addr; | ||
144 | tuner_dbg("radio tuner set to I2C address 0x%02x\n",radio_tuner<<1); | ||
145 | |||
146 | break; | ||
147 | default: | ||
148 | tv_tuner=tun_addr->addr; | ||
149 | tuner_dbg("TV tuner set to I2C address 0x%02x\n",tv_tuner<<1); | ||
150 | break; | ||
151 | } | ||
152 | } | ||
153 | #else | ||
154 | #define set_addr(c,tun_addr) \ | ||
155 | tuner_warn("It is recommended to enable CONFIG_TUNER_MULTI_I2C for this card.\n"); | ||
156 | #endif | ||
157 | |||
128 | static void set_type(struct i2c_client *c, unsigned int type) | 158 | static void set_type(struct i2c_client *c, unsigned int type) |
129 | { | 159 | { |
130 | struct tuner *t = i2c_get_clientdata(c); | 160 | struct tuner *t = i2c_get_clientdata(c); |
@@ -197,8 +227,16 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) | |||
197 | { | 227 | { |
198 | struct tuner *t; | 228 | struct tuner *t; |
199 | 229 | ||
230 | #ifndef CONFIG_TUNER_MULTI_I2C | ||
200 | if (this_adap > 0) | 231 | if (this_adap > 0) |
201 | return -1; | 232 | return -1; |
233 | #else | ||
234 | /* by default, first I2C card is both tv and radio tuner */ | ||
235 | if (this_adap == 0) { | ||
236 | tv_tuner = addr; | ||
237 | radio_tuner = addr; | ||
238 | } | ||
239 | #endif | ||
202 | this_adap++; | 240 | this_adap++; |
203 | 241 | ||
204 | client_template.adapter = adap; | 242 | client_template.adapter = adap; |
@@ -228,6 +266,11 @@ static int tuner_probe(struct i2c_adapter *adap) | |||
228 | } | 266 | } |
229 | this_adap = 0; | 267 | this_adap = 0; |
230 | 268 | ||
269 | #ifdef CONFIG_TUNER_MULTI_I2C | ||
270 | tv_tuner = 0; | ||
271 | radio_tuner = 0; | ||
272 | #endif | ||
273 | |||
231 | if (adap->class & I2C_CLASS_TV_ANALOG) | 274 | if (adap->class & I2C_CLASS_TV_ANALOG) |
232 | return i2c_probe(adap, &addr_data, tuner_attach); | 275 | return i2c_probe(adap, &addr_data, tuner_attach); |
233 | return 0; | 276 | return 0; |
@@ -236,8 +279,14 @@ static int tuner_probe(struct i2c_adapter *adap) | |||
236 | static int tuner_detach(struct i2c_client *client) | 279 | static int tuner_detach(struct i2c_client *client) |
237 | { | 280 | { |
238 | struct tuner *t = i2c_get_clientdata(client); | 281 | struct tuner *t = i2c_get_clientdata(client); |
282 | int err; | ||
283 | |||
284 | err=i2c_detach_client(&t->i2c); | ||
285 | if (err) { | ||
286 | tuner_warn ("Client deregistration failed, client not detached.\n"); | ||
287 | return err; | ||
288 | } | ||
239 | 289 | ||
240 | i2c_detach_client(&t->i2c); | ||
241 | kfree(t); | 290 | kfree(t); |
242 | return 0; | 291 | return 0; |
243 | } | 292 | } |
@@ -249,6 +298,17 @@ static int tuner_detach(struct i2c_client *client) | |||
249 | tuner_info("ignore v4l1 call\n"); \ | 298 | tuner_info("ignore v4l1 call\n"); \ |
250 | return 0; } | 299 | return 0; } |
251 | 300 | ||
301 | #ifdef CONFIG_TUNER_MULTI_I2C | ||
302 | #define CHECK_ADDR(tp,cmd) if (client->addr!=tp) { \ | ||
303 | tuner_info ("Cmd %s to addr 0x%02x rejected.\n",cmd,client->addr<<1); \ | ||
304 | return 0; } | ||
305 | #define CHECK_MODE(cmd) if (t->mode == V4L2_TUNER_RADIO) { \ | ||
306 | CHECK_ADDR(radio_tuner,cmd) } else { CHECK_ADDR(tv_tuner,cmd); } | ||
307 | #else | ||
308 | #define CHECK_ADDR(tp,cmd) | ||
309 | #define CHECK_MODE(cmd) | ||
310 | #endif | ||
311 | |||
252 | static int | 312 | static int |
253 | tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | 313 | tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) |
254 | { | 314 | { |
@@ -256,18 +316,23 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
256 | unsigned int *iarg = (int*)arg; | 316 | unsigned int *iarg = (int*)arg; |
257 | 317 | ||
258 | switch (cmd) { | 318 | switch (cmd) { |
259 | |||
260 | /* --- configuration --- */ | 319 | /* --- configuration --- */ |
261 | case TUNER_SET_TYPE: | 320 | case TUNER_SET_TYPE: |
262 | set_type(client,*iarg); | 321 | set_type(client,*iarg); |
263 | break; | 322 | break; |
323 | case TUNER_SET_ADDR: | ||
324 | set_addr(client,(struct tuner_addr *)arg); | ||
325 | break; | ||
264 | case AUDC_SET_RADIO: | 326 | case AUDC_SET_RADIO: |
327 | CHECK_ADDR(radio_tuner,"AUDC_SET_RADIO"); | ||
328 | |||
265 | if (V4L2_TUNER_RADIO != t->mode) { | 329 | if (V4L2_TUNER_RADIO != t->mode) { |
266 | set_tv_freq(client,400 * 16); | 330 | set_tv_freq(client,400 * 16); |
267 | t->mode = V4L2_TUNER_RADIO; | 331 | t->mode = V4L2_TUNER_RADIO; |
268 | } | 332 | } |
269 | break; | 333 | break; |
270 | case AUDC_CONFIG_PINNACLE: | 334 | case AUDC_CONFIG_PINNACLE: |
335 | CHECK_ADDR(tv_tuner,"AUDC_CONFIG_PINNACLE"); | ||
271 | switch (*iarg) { | 336 | switch (*iarg) { |
272 | case 2: | 337 | case 2: |
273 | tuner_dbg("pinnacle pal\n"); | 338 | tuner_dbg("pinnacle pal\n"); |
@@ -295,6 +360,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
295 | }; | 360 | }; |
296 | struct video_channel *vc = arg; | 361 | struct video_channel *vc = arg; |
297 | 362 | ||
363 | CHECK_ADDR(tv_tuner,"VIDIOCSCHAN"); | ||
298 | CHECK_V4L2; | 364 | CHECK_V4L2; |
299 | t->mode = V4L2_TUNER_ANALOG_TV; | 365 | t->mode = V4L2_TUNER_ANALOG_TV; |
300 | if (vc->norm < ARRAY_SIZE(map)) | 366 | if (vc->norm < ARRAY_SIZE(map)) |
@@ -308,6 +374,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
308 | { | 374 | { |
309 | unsigned long *v = arg; | 375 | unsigned long *v = arg; |
310 | 376 | ||
377 | CHECK_MODE("VIDIOCSFREQ"); | ||
311 | CHECK_V4L2; | 378 | CHECK_V4L2; |
312 | set_freq(client,*v); | 379 | set_freq(client,*v); |
313 | return 0; | 380 | return 0; |
@@ -316,6 +383,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
316 | { | 383 | { |
317 | struct video_tuner *vt = arg; | 384 | struct video_tuner *vt = arg; |
318 | 385 | ||
386 | CHECK_ADDR(radio_tuner,"VIDIOCGTUNER:"); | ||
319 | CHECK_V4L2; | 387 | CHECK_V4L2; |
320 | if (V4L2_TUNER_RADIO == t->mode && t->has_signal) | 388 | if (V4L2_TUNER_RADIO == t->mode && t->has_signal) |
321 | vt->signal = t->has_signal(client); | 389 | vt->signal = t->has_signal(client); |
@@ -325,6 +393,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
325 | { | 393 | { |
326 | struct video_audio *va = arg; | 394 | struct video_audio *va = arg; |
327 | 395 | ||
396 | CHECK_ADDR(radio_tuner,"VIDIOCGAUDIO"); | ||
328 | CHECK_V4L2; | 397 | CHECK_V4L2; |
329 | if (V4L2_TUNER_RADIO == t->mode && t->is_stereo) | 398 | if (V4L2_TUNER_RADIO == t->mode && t->is_stereo) |
330 | va->mode = t->is_stereo(client) | 399 | va->mode = t->is_stereo(client) |
@@ -337,6 +406,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
337 | { | 406 | { |
338 | v4l2_std_id *id = arg; | 407 | v4l2_std_id *id = arg; |
339 | 408 | ||
409 | CHECK_ADDR(tv_tuner,"VIDIOC_S_STD"); | ||
340 | SWITCH_V4L2; | 410 | SWITCH_V4L2; |
341 | t->mode = V4L2_TUNER_ANALOG_TV; | 411 | t->mode = V4L2_TUNER_ANALOG_TV; |
342 | t->std = *id; | 412 | t->std = *id; |
@@ -349,6 +419,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
349 | { | 419 | { |
350 | struct v4l2_frequency *f = arg; | 420 | struct v4l2_frequency *f = arg; |
351 | 421 | ||
422 | CHECK_MODE("VIDIOC_S_FREQUENCY"); | ||
352 | SWITCH_V4L2; | 423 | SWITCH_V4L2; |
353 | if (V4L2_TUNER_RADIO == f->type && | 424 | if (V4L2_TUNER_RADIO == f->type && |
354 | V4L2_TUNER_RADIO != t->mode) | 425 | V4L2_TUNER_RADIO != t->mode) |
@@ -361,6 +432,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
361 | { | 432 | { |
362 | struct v4l2_frequency *f = arg; | 433 | struct v4l2_frequency *f = arg; |
363 | 434 | ||
435 | CHECK_MODE("VIDIOC_G_FREQUENCY"); | ||
364 | SWITCH_V4L2; | 436 | SWITCH_V4L2; |
365 | f->type = t->mode; | 437 | f->type = t->mode; |
366 | f->frequency = t->freq; | 438 | f->frequency = t->freq; |
@@ -370,6 +442,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
370 | { | 442 | { |
371 | struct v4l2_tuner *tuner = arg; | 443 | struct v4l2_tuner *tuner = arg; |
372 | 444 | ||
445 | CHECK_MODE("VIDIOC_G_TUNER"); | ||
373 | SWITCH_V4L2; | 446 | SWITCH_V4L2; |
374 | if (V4L2_TUNER_RADIO == t->mode && t->has_signal) | 447 | if (V4L2_TUNER_RADIO == t->mode && t->has_signal) |
375 | tuner->signal = t->has_signal(client); | 448 | tuner->signal = t->has_signal(client); |
diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c index 48c6ceff1dc2..f7305c8d53de 100644 --- a/drivers/media/video/tuner-simple.c +++ b/drivers/media/video/tuner-simple.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * $Id: tuner-simple.c,v 1.10 2005/03/08 08:38:00 kraxel Exp $ | 2 | * $Id: tuner-simple.c,v 1.14 2005/05/30 02:02:47 mchehab Exp $ |
3 | * | 3 | * |
4 | * i2c tv tuner chip device driver | 4 | * i2c tv tuner chip device driver |
5 | * controls all those simple 4-control-bytes style tuners. | 5 | * controls all those simple 4-control-bytes style tuners. |
@@ -212,6 +212,11 @@ static struct tunertype tuners[] = { | |||
212 | { "Philips FQ1236A MK4", Philips, NTSC, | 212 | { "Philips FQ1236A MK4", Philips, NTSC, |
213 | 16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732 }, | 213 | 16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732 }, |
214 | 214 | ||
215 | /* Should work for TVF8531MF, TVF8831MF, TVF8731MF */ | ||
216 | { "Ymec TVision TVF-8531MF", Philips, NTSC, | ||
217 | 16*160.00,16*454.00,0xa0,0x90,0x30,0x8e,732}, | ||
218 | { "Ymec TVision TVF-5533MF", Philips, NTSC, | ||
219 | 16*160.00,16*454.00,0x01,0x02,0x04,0x8e,732}, | ||
215 | }; | 220 | }; |
216 | unsigned const int tuner_count = ARRAY_SIZE(tuners); | 221 | unsigned const int tuner_count = ARRAY_SIZE(tuners); |
217 | 222 | ||
@@ -424,6 +429,13 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq) | |||
424 | buffer[2] = tun->config; | 429 | buffer[2] = tun->config; |
425 | 430 | ||
426 | switch (t->type) { | 431 | switch (t->type) { |
432 | case TUNER_YMEC_TVF_5533MF: | ||
433 | |||
434 | /*These values are empirically determinated */ | ||
435 | div = (freq*122)/16 - 20; | ||
436 | buffer[2] = 0x88; /* could be also 0x80 */ | ||
437 | buffer[3] = 0x19; /* could be also 0x10, 0x18, 0x99 */ | ||
438 | break; | ||
427 | case TUNER_PHILIPS_FM1216ME_MK3: | 439 | case TUNER_PHILIPS_FM1216ME_MK3: |
428 | case TUNER_PHILIPS_FM1236_MK3: | 440 | case TUNER_PHILIPS_FM1236_MK3: |
429 | buffer[3] = 0x19; | 441 | buffer[3] = 0x19; |
@@ -458,6 +470,20 @@ int default_tuner_init(struct i2c_client *c) | |||
458 | t->type, tuners[t->type].name); | 470 | t->type, tuners[t->type].name); |
459 | strlcpy(c->name, tuners[t->type].name, sizeof(c->name)); | 471 | strlcpy(c->name, tuners[t->type].name, sizeof(c->name)); |
460 | 472 | ||
473 | switch (t->type) { | ||
474 | case TUNER_YMEC_TVF_5533MF: | ||
475 | { | ||
476 | struct tuner_addr tun_addr = { V4L2_TUNER_ANALOG_TV, 0xc2>>1 }; | ||
477 | |||
478 | if (c->driver->command) { | ||
479 | c->driver->command(c, TUNER_SET_ADDR, &tun_addr); | ||
480 | } else { | ||
481 | tuner_warn("Couldn't set TV tuner I2C address to 0x%02x\n",tun_addr.addr<<1); | ||
482 | } | ||
483 | break; | ||
484 | } | ||
485 | } | ||
486 | |||
461 | t->tv_freq = default_set_tv_freq; | 487 | t->tv_freq = default_set_tv_freq; |
462 | t->radio_freq = default_set_radio_freq; | 488 | t->radio_freq = default_set_radio_freq; |
463 | t->has_signal = tuner_signal; | 489 | t->has_signal = tuner_signal; |