Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
586b0cab | 2 | * $Id: tuner-core.c,v 1.29 2005/06/21 15:40:33 mchehab Exp $ |
1da177e4 LT |
3 | * |
4 | * i2c tv tuner chip device driver | |
5 | * core core, i.e. kernel interfaces, registering and so on | |
6 | */ | |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <linux/moduleparam.h> | |
10 | #include <linux/kernel.h> | |
11 | #include <linux/sched.h> | |
12 | #include <linux/string.h> | |
13 | #include <linux/timer.h> | |
14 | #include <linux/delay.h> | |
15 | #include <linux/errno.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/poll.h> | |
18 | #include <linux/i2c.h> | |
19 | #include <linux/types.h> | |
20 | #include <linux/videodev.h> | |
21 | #include <linux/init.h> | |
22 | ||
23 | #include <media/tuner.h> | |
24 | #include <media/audiochip.h> | |
25 | ||
391cd727 MCC |
26 | /* |
27 | * comment line bellow to return to old behavor, where only one I2C device is supported | |
28 | */ | |
391cd727 | 29 | |
1da177e4 LT |
30 | #define UNSET (-1U) |
31 | ||
32 | /* standard i2c insmod options */ | |
33 | static unsigned short normal_i2c[] = { | |
34 | 0x4b, /* tda8290 */ | |
f5bec396 MCC |
35 | 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, |
36 | 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, | |
1da177e4 LT |
37 | I2C_CLIENT_END |
38 | }; | |
39 | I2C_CLIENT_INSMOD; | |
40 | ||
41 | /* insmod options used at init time => read/only */ | |
42 | static unsigned int addr = 0; | |
43 | module_param(addr, int, 0444); | |
44 | ||
45 | /* insmod options used at runtime => read/write */ | |
46 | unsigned int tuner_debug = 0; | |
47 | module_param(tuner_debug, int, 0644); | |
48 | ||
49 | static unsigned int tv_range[2] = { 44, 958 }; | |
50 | static unsigned int radio_range[2] = { 65, 108 }; | |
51 | ||
52 | module_param_array(tv_range, int, NULL, 0644); | |
53 | module_param_array(radio_range, int, NULL, 0644); | |
54 | ||
55 | MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners"); | |
56 | MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); | |
57 | MODULE_LICENSE("GPL"); | |
58 | ||
59 | static int this_adap; | |
56fc08ca | 60 | static unsigned short first_tuner, tv_tuner, radio_tuner; |
1da177e4 LT |
61 | |
62 | static struct i2c_driver driver; | |
63 | static struct i2c_client client_template; | |
64 | ||
65 | /* ---------------------------------------------------------------------- */ | |
66 | ||
56fc08ca | 67 | /* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */ |
1da177e4 LT |
68 | static void set_tv_freq(struct i2c_client *c, unsigned int freq) |
69 | { | |
70 | struct tuner *t = i2c_get_clientdata(c); | |
71 | ||
72 | if (t->type == UNSET) { | |
73 | tuner_info("tuner type not set\n"); | |
74 | return; | |
75 | } | |
76 | if (NULL == t->tv_freq) { | |
77 | tuner_info("Huh? tv_set is NULL?\n"); | |
78 | return; | |
79 | } | |
80 | if (freq < tv_range[0]*16 || freq > tv_range[1]*16) { | |
56fc08ca MCC |
81 | tuner_info("TV freq (%d.%02d) out of range (%d-%d)\n", |
82 | freq/16,freq%16*100/16,tv_range[0],tv_range[1]); | |
1da177e4 LT |
83 | } |
84 | t->tv_freq(c,freq); | |
85 | } | |
86 | ||
87 | static void set_radio_freq(struct i2c_client *c, unsigned int freq) | |
88 | { | |
89 | struct tuner *t = i2c_get_clientdata(c); | |
90 | ||
91 | if (t->type == UNSET) { | |
92 | tuner_info("tuner type not set\n"); | |
93 | return; | |
94 | } | |
95 | if (NULL == t->radio_freq) { | |
96 | tuner_info("no radio tuning for this one, sorry.\n"); | |
97 | return; | |
98 | } | |
586b0cab MCC |
99 | if (freq >= radio_range[0]*16000 && freq <= radio_range[1]*16000) { |
100 | if (tuner_debug) | |
101 | tuner_info("radio freq step 62.5Hz (%d.%06d)\n", | |
102 | freq/16000,freq%16000*1000/16); | |
103 | t->radio_freq(c,freq); | |
104 | } else { | |
105 | tuner_info("radio freq (%d.%02d) out of range (%d-%d)\n", | |
106 | freq/16,freq%16*100/16, | |
107 | radio_range[0],radio_range[1]); | |
1da177e4 | 108 | } |
586b0cab MCC |
109 | |
110 | return; | |
1da177e4 LT |
111 | } |
112 | ||
113 | static void set_freq(struct i2c_client *c, unsigned long freq) | |
114 | { | |
115 | struct tuner *t = i2c_get_clientdata(c); | |
116 | ||
117 | switch (t->mode) { | |
118 | case V4L2_TUNER_RADIO: | |
119 | tuner_dbg("radio freq set to %lu.%02lu\n", | |
120 | freq/16,freq%16*100/16); | |
121 | set_radio_freq(c,freq); | |
122 | break; | |
123 | case V4L2_TUNER_ANALOG_TV: | |
124 | case V4L2_TUNER_DIGITAL_TV: | |
125 | tuner_dbg("tv freq set to %lu.%02lu\n", | |
126 | freq/16,freq%16*100/16); | |
127 | set_tv_freq(c, freq); | |
128 | break; | |
129 | } | |
130 | t->freq = freq; | |
131 | } | |
132 | ||
133 | static void set_type(struct i2c_client *c, unsigned int type) | |
134 | { | |
135 | struct tuner *t = i2c_get_clientdata(c); | |
586b0cab | 136 | unsigned char buffer[4]; |
1da177e4 LT |
137 | |
138 | /* sanity check */ | |
56fc08ca | 139 | if (type == UNSET || type == TUNER_ABSENT) |
1da177e4 LT |
140 | return; |
141 | if (type >= tuner_count) | |
142 | return; | |
143 | ||
144 | if (NULL == t->i2c.dev.driver) { | |
145 | /* not registered yet */ | |
146 | t->type = type; | |
147 | return; | |
148 | } | |
586b0cab MCC |
149 | if ((t->initialized) && (t->type == type)) |
150 | /* run only once except type change Hac 04/05*/ | |
1da177e4 LT |
151 | return; |
152 | ||
153 | t->initialized = 1; | |
56fc08ca | 154 | |
1da177e4 LT |
155 | t->type = type; |
156 | switch (t->type) { | |
157 | case TUNER_MT2032: | |
158 | microtune_init(c); | |
159 | break; | |
160 | case TUNER_PHILIPS_TDA8290: | |
161 | tda8290_init(c); | |
162 | break; | |
586b0cab MCC |
163 | case TUNER_TEA5767: |
164 | if (tea5767_tuner_init(c)==EINVAL) t->type=TUNER_ABSENT; | |
165 | break; | |
166 | case TUNER_PHILIPS_FMD1216ME_MK3: | |
167 | buffer[0] = 0x0b; | |
168 | buffer[1] = 0xdc; | |
169 | buffer[2] = 0x9c; | |
170 | buffer[3] = 0x60; | |
171 | i2c_master_send(c,buffer,4); | |
172 | mdelay(1); | |
173 | buffer[2] = 0x86; | |
174 | buffer[3] = 0x54; | |
175 | i2c_master_send(c,buffer,4); | |
176 | default_tuner_init(c); | |
177 | break; | |
1da177e4 | 178 | default: |
586b0cab MCC |
179 | /* TEA5767 autodetection code */ |
180 | if (tea5767_tuner_init(c)!=EINVAL) { | |
181 | t->type = TUNER_TEA5767; | |
182 | if (first_tuner == 0x60) | |
183 | first_tuner++; | |
184 | break; | |
185 | } | |
186 | ||
1da177e4 LT |
187 | default_tuner_init(c); |
188 | break; | |
189 | } | |
586b0cab | 190 | tuner_dbg ("I2C addr 0x%02x with type %d\n",c->addr<<1,type); |
1da177e4 LT |
191 | } |
192 | ||
56fc08ca | 193 | #define CHECK_ADDR(tp,cmd,tun) if (client->addr!=tp) { \ |
586b0cab | 194 | return 0; } else if (tuner_debug) \ |
56fc08ca MCC |
195 | tuner_info ("Cmd %s accepted to "tun"\n",cmd); |
196 | #define CHECK_MODE(cmd) if (t->mode == V4L2_TUNER_RADIO) { \ | |
197 | CHECK_ADDR(radio_tuner,cmd,"radio") } else \ | |
198 | { CHECK_ADDR(tv_tuner,cmd,"TV"); } | |
56fc08ca MCC |
199 | |
200 | static void set_addr(struct i2c_client *c, struct tuner_addr *tun_addr) | |
201 | { | |
202 | /* ADDR_UNSET defaults to first available tuner */ | |
203 | if ( tun_addr->addr == ADDR_UNSET ) { | |
204 | if (first_tuner != c->addr) | |
205 | return; | |
206 | switch (tun_addr->v4l2_tuner) { | |
207 | case V4L2_TUNER_RADIO: | |
208 | radio_tuner=c->addr; | |
209 | break; | |
210 | default: | |
211 | tv_tuner=c->addr; | |
212 | break; | |
213 | } | |
214 | } else { | |
215 | /* Sets tuner to its configured value */ | |
216 | switch (tun_addr->v4l2_tuner) { | |
217 | case V4L2_TUNER_RADIO: | |
218 | radio_tuner=tun_addr->addr; | |
219 | if ( tun_addr->addr == c->addr ) set_type(c,tun_addr->type); | |
220 | return; | |
221 | default: | |
222 | tv_tuner=tun_addr->addr; | |
223 | if ( tun_addr->addr == c->addr ) set_type(c,tun_addr->type); | |
224 | return; | |
225 | } | |
226 | } | |
227 | set_type(c,tun_addr->type); | |
228 | } | |
56fc08ca | 229 | |
1da177e4 | 230 | static char pal[] = "-"; |
975e046c | 231 | module_param_string(pal, pal, sizeof(pal), 0644); |
1da177e4 LT |
232 | |
233 | static int tuner_fixup_std(struct tuner *t) | |
234 | { | |
235 | if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) { | |
236 | /* get more precise norm info from insmod option */ | |
237 | switch (pal[0]) { | |
238 | case 'b': | |
239 | case 'B': | |
240 | case 'g': | |
241 | case 'G': | |
242 | tuner_dbg("insmod fixup: PAL => PAL-BG\n"); | |
243 | t->std = V4L2_STD_PAL_BG; | |
244 | break; | |
245 | case 'i': | |
246 | case 'I': | |
247 | tuner_dbg("insmod fixup: PAL => PAL-I\n"); | |
248 | t->std = V4L2_STD_PAL_I; | |
249 | break; | |
250 | case 'd': | |
251 | case 'D': | |
252 | case 'k': | |
253 | case 'K': | |
254 | tuner_dbg("insmod fixup: PAL => PAL-DK\n"); | |
255 | t->std = V4L2_STD_PAL_DK; | |
256 | break; | |
257 | } | |
258 | } | |
259 | return 0; | |
260 | } | |
261 | ||
262 | /* ---------------------------------------------------------------------- */ | |
263 | ||
264 | static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) | |
265 | { | |
266 | struct tuner *t; | |
267 | ||
391cd727 MCC |
268 | /* by default, first I2C card is both tv and radio tuner */ |
269 | if (this_adap == 0) { | |
56fc08ca | 270 | first_tuner = addr; |
391cd727 MCC |
271 | tv_tuner = addr; |
272 | radio_tuner = addr; | |
273 | } | |
1da177e4 LT |
274 | this_adap++; |
275 | ||
276 | client_template.adapter = adap; | |
277 | client_template.addr = addr; | |
278 | ||
279 | t = kmalloc(sizeof(struct tuner),GFP_KERNEL); | |
280 | if (NULL == t) | |
281 | return -ENOMEM; | |
282 | memset(t,0,sizeof(struct tuner)); | |
283 | memcpy(&t->i2c,&client_template,sizeof(struct i2c_client)); | |
284 | i2c_set_clientdata(&t->i2c, t); | |
285 | t->type = UNSET; | |
56fc08ca | 286 | t->radio_if2 = 10700*1000; /* 10.7MHz - FM radio */ |
586b0cab | 287 | t->audmode = V4L2_TUNER_MODE_STEREO; |
1da177e4 LT |
288 | |
289 | i2c_attach_client(&t->i2c); | |
290 | tuner_info("chip found @ 0x%x (%s)\n", | |
291 | addr << 1, adap->name); | |
56fc08ca | 292 | |
1da177e4 LT |
293 | set_type(&t->i2c, t->type); |
294 | return 0; | |
295 | } | |
296 | ||
297 | static int tuner_probe(struct i2c_adapter *adap) | |
298 | { | |
299 | if (0 != addr) { | |
f5bec396 MCC |
300 | normal_i2c[0] = addr; |
301 | normal_i2c[1] = I2C_CLIENT_END; | |
1da177e4 LT |
302 | } |
303 | this_adap = 0; | |
304 | ||
56fc08ca | 305 | first_tuner = 0; |
391cd727 MCC |
306 | tv_tuner = 0; |
307 | radio_tuner = 0; | |
391cd727 | 308 | |
1da177e4 LT |
309 | if (adap->class & I2C_CLASS_TV_ANALOG) |
310 | return i2c_probe(adap, &addr_data, tuner_attach); | |
311 | return 0; | |
312 | } | |
313 | ||
314 | static int tuner_detach(struct i2c_client *client) | |
315 | { | |
316 | struct tuner *t = i2c_get_clientdata(client); | |
391cd727 MCC |
317 | int err; |
318 | ||
319 | err=i2c_detach_client(&t->i2c); | |
320 | if (err) { | |
321 | tuner_warn ("Client deregistration failed, client not detached.\n"); | |
322 | return err; | |
323 | } | |
1da177e4 | 324 | |
1da177e4 LT |
325 | kfree(t); |
326 | return 0; | |
327 | } | |
328 | ||
329 | #define SWITCH_V4L2 if (!t->using_v4l2 && tuner_debug) \ | |
330 | tuner_info("switching to v4l2\n"); \ | |
331 | t->using_v4l2 = 1; | |
332 | #define CHECK_V4L2 if (t->using_v4l2) { if (tuner_debug) \ | |
333 | tuner_info("ignore v4l1 call\n"); \ | |
334 | return 0; } | |
335 | ||
336 | static int | |
337 | tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | |
338 | { | |
339 | struct tuner *t = i2c_get_clientdata(client); | |
340 | unsigned int *iarg = (int*)arg; | |
341 | ||
342 | switch (cmd) { | |
1da177e4 LT |
343 | /* --- configuration --- */ |
344 | case TUNER_SET_TYPE: | |
345 | set_type(client,*iarg); | |
346 | break; | |
56fc08ca | 347 | case TUNER_SET_TYPE_ADDR: |
391cd727 MCC |
348 | set_addr(client,(struct tuner_addr *)arg); |
349 | break; | |
1da177e4 | 350 | case AUDC_SET_RADIO: |
56fc08ca MCC |
351 | t->mode = V4L2_TUNER_RADIO; |
352 | CHECK_ADDR(tv_tuner,"AUDC_SET_RADIO","TV"); | |
391cd727 | 353 | |
1da177e4 LT |
354 | if (V4L2_TUNER_RADIO != t->mode) { |
355 | set_tv_freq(client,400 * 16); | |
1da177e4 LT |
356 | } |
357 | break; | |
358 | case AUDC_CONFIG_PINNACLE: | |
56fc08ca | 359 | CHECK_ADDR(tv_tuner,"AUDC_CONFIG_PINNACLE","TV"); |
1da177e4 LT |
360 | switch (*iarg) { |
361 | case 2: | |
362 | tuner_dbg("pinnacle pal\n"); | |
363 | t->radio_if2 = 33300 * 1000; | |
364 | break; | |
365 | case 3: | |
366 | tuner_dbg("pinnacle ntsc\n"); | |
367 | t->radio_if2 = 41300 * 1000; | |
368 | break; | |
369 | } | |
586b0cab | 370 | break; |
1da177e4 LT |
371 | /* --- v4l ioctls --- */ |
372 | /* take care: bttv does userspace copying, we'll get a | |
373 | kernel pointer here... */ | |
374 | case VIDIOCSCHAN: | |
375 | { | |
376 | static const v4l2_std_id map[] = { | |
377 | [ VIDEO_MODE_PAL ] = V4L2_STD_PAL, | |
378 | [ VIDEO_MODE_NTSC ] = V4L2_STD_NTSC_M, | |
379 | [ VIDEO_MODE_SECAM ] = V4L2_STD_SECAM, | |
380 | [ 4 /* bttv */ ] = V4L2_STD_PAL_M, | |
381 | [ 5 /* bttv */ ] = V4L2_STD_PAL_N, | |
382 | [ 6 /* bttv */ ] = V4L2_STD_NTSC_M_JP, | |
383 | }; | |
384 | struct video_channel *vc = arg; | |
385 | ||
386 | CHECK_V4L2; | |
387 | t->mode = V4L2_TUNER_ANALOG_TV; | |
56fc08ca MCC |
388 | CHECK_ADDR(tv_tuner,"VIDIOCSCHAN","TV"); |
389 | ||
1da177e4 LT |
390 | if (vc->norm < ARRAY_SIZE(map)) |
391 | t->std = map[vc->norm]; | |
392 | tuner_fixup_std(t); | |
393 | if (t->freq) | |
394 | set_tv_freq(client,t->freq); | |
395 | return 0; | |
396 | } | |
397 | case VIDIOCSFREQ: | |
398 | { | |
399 | unsigned long *v = arg; | |
400 | ||
391cd727 | 401 | CHECK_MODE("VIDIOCSFREQ"); |
1da177e4 LT |
402 | CHECK_V4L2; |
403 | set_freq(client,*v); | |
404 | return 0; | |
405 | } | |
406 | case VIDIOCGTUNER: | |
407 | { | |
408 | struct video_tuner *vt = arg; | |
409 | ||
56fc08ca | 410 | CHECK_ADDR(radio_tuner,"VIDIOCGTUNER","radio"); |
1da177e4 | 411 | CHECK_V4L2; |
56fc08ca MCC |
412 | if (V4L2_TUNER_RADIO == t->mode) { |
413 | if (t->has_signal) | |
414 | vt->signal = t->has_signal(client); | |
415 | if (t->is_stereo) { | |
416 | if (t->is_stereo(client)) | |
586b0cab | 417 | vt->flags |= VIDEO_TUNER_STEREO_ON; |
56fc08ca | 418 | else |
586b0cab | 419 | vt->flags &= ~VIDEO_TUNER_STEREO_ON; |
56fc08ca MCC |
420 | } |
421 | vt->flags |= V4L2_TUNER_CAP_LOW; /* Allow freqs at 62.5 Hz */ | |
586b0cab MCC |
422 | |
423 | vt->rangelow = radio_range[0] * 16000; | |
424 | vt->rangehigh = radio_range[1] * 16000; | |
425 | ||
426 | } else { | |
427 | vt->rangelow = tv_range[0] * 16; | |
428 | vt->rangehigh = tv_range[1] * 16; | |
56fc08ca MCC |
429 | } |
430 | ||
1da177e4 LT |
431 | return 0; |
432 | } | |
433 | case VIDIOCGAUDIO: | |
434 | { | |
435 | struct video_audio *va = arg; | |
436 | ||
56fc08ca | 437 | CHECK_ADDR(radio_tuner,"VIDIOCGAUDIO","radio"); |
1da177e4 LT |
438 | CHECK_V4L2; |
439 | if (V4L2_TUNER_RADIO == t->mode && t->is_stereo) | |
440 | va->mode = t->is_stereo(client) | |
441 | ? VIDEO_SOUND_STEREO | |
442 | : VIDEO_SOUND_MONO; | |
443 | return 0; | |
444 | } | |
445 | ||
446 | case VIDIOC_S_STD: | |
447 | { | |
448 | v4l2_std_id *id = arg; | |
449 | ||
450 | SWITCH_V4L2; | |
451 | t->mode = V4L2_TUNER_ANALOG_TV; | |
56fc08ca MCC |
452 | CHECK_ADDR(tv_tuner,"VIDIOC_S_STD","TV"); |
453 | ||
1da177e4 LT |
454 | t->std = *id; |
455 | tuner_fixup_std(t); | |
456 | if (t->freq) | |
457 | set_freq(client,t->freq); | |
458 | break; | |
459 | } | |
460 | case VIDIOC_S_FREQUENCY: | |
461 | { | |
462 | struct v4l2_frequency *f = arg; | |
463 | ||
391cd727 | 464 | CHECK_MODE("VIDIOC_S_FREQUENCY"); |
1da177e4 LT |
465 | SWITCH_V4L2; |
466 | if (V4L2_TUNER_RADIO == f->type && | |
467 | V4L2_TUNER_RADIO != t->mode) | |
468 | set_tv_freq(client,400*16); | |
469 | t->mode = f->type; | |
e99d3438 | 470 | set_freq(client,f->frequency); |
1da177e4 LT |
471 | break; |
472 | } | |
c184ca36 JB |
473 | case VIDIOC_G_FREQUENCY: |
474 | { | |
475 | struct v4l2_frequency *f = arg; | |
476 | ||
391cd727 | 477 | CHECK_MODE("VIDIOC_G_FREQUENCY"); |
c184ca36 JB |
478 | SWITCH_V4L2; |
479 | f->type = t->mode; | |
480 | f->frequency = t->freq; | |
481 | break; | |
482 | } | |
1da177e4 LT |
483 | case VIDIOC_G_TUNER: |
484 | { | |
485 | struct v4l2_tuner *tuner = arg; | |
486 | ||
391cd727 | 487 | CHECK_MODE("VIDIOC_G_TUNER"); |
1da177e4 | 488 | SWITCH_V4L2; |
56fc08ca MCC |
489 | if (V4L2_TUNER_RADIO == t->mode) { |
490 | if (t->has_signal) | |
491 | tuner -> signal = t->has_signal(client); | |
492 | if (t->is_stereo) { | |
493 | if (t->is_stereo(client)) { | |
586b0cab | 494 | tuner -> rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; |
56fc08ca | 495 | } else { |
586b0cab | 496 | tuner -> rxsubchans = V4L2_TUNER_SUB_MONO; |
56fc08ca MCC |
497 | } |
498 | } | |
586b0cab MCC |
499 | tuner->capability |= V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; |
500 | tuner->audmode = t->audmode; | |
501 | ||
502 | tuner->rangelow = radio_range[0] * 16000; | |
503 | tuner->rangehigh = radio_range[1] * 16000; | |
504 | } else { | |
505 | tuner->rangelow = tv_range[0] * 16; | |
506 | tuner->rangehigh = tv_range[1] * 16; | |
56fc08ca | 507 | } |
1da177e4 LT |
508 | break; |
509 | } | |
586b0cab MCC |
510 | case VIDIOC_S_TUNER: /* Allow changing radio range and audio mode */ |
511 | { | |
512 | struct v4l2_tuner *tuner = arg; | |
513 | ||
514 | CHECK_ADDR(radio_tuner,"VIDIOC_S_TUNER","radio"); | |
515 | SWITCH_V4L2; | |
516 | ||
517 | /* To switch the audio mode, applications initialize the | |
518 | index and audmode fields and the reserved array and | |
519 | call the VIDIOC_S_TUNER ioctl. */ | |
520 | /* rxsubchannels: V4L2_TUNER_MODE_MONO, V4L2_TUNER_MODE_STEREO, | |
521 | V4L2_TUNER_MODE_LANG1, V4L2_TUNER_MODE_LANG2, | |
522 | V4L2_TUNER_MODE_SAP */ | |
523 | ||
524 | if (tuner->audmode == V4L2_TUNER_MODE_MONO) | |
525 | t->audmode = V4L2_TUNER_MODE_MONO; | |
526 | else | |
527 | t->audmode = V4L2_TUNER_MODE_STEREO; | |
528 | ||
529 | set_radio_freq(client, t->freq); | |
530 | break; | |
531 | } | |
532 | case TDA9887_SET_CONFIG: /* Nothing to do on tuner-core */ | |
533 | break; | |
1da177e4 | 534 | default: |
56fc08ca | 535 | tuner_dbg ("Unimplemented IOCTL 0x%08x called to tuner.\n", cmd); |
1da177e4 LT |
536 | /* nothing */ |
537 | break; | |
538 | } | |
539 | ||
540 | return 0; | |
541 | } | |
542 | ||
56fc08ca | 543 | static int tuner_suspend(struct device * dev, u32 state, u32 level) |
1da177e4 LT |
544 | { |
545 | struct i2c_client *c = container_of(dev, struct i2c_client, dev); | |
546 | struct tuner *t = i2c_get_clientdata(c); | |
547 | ||
548 | tuner_dbg("suspend\n"); | |
549 | /* FIXME: power down ??? */ | |
550 | return 0; | |
551 | } | |
552 | ||
553 | static int tuner_resume(struct device * dev, u32 level) | |
554 | { | |
555 | struct i2c_client *c = container_of(dev, struct i2c_client, dev); | |
556 | struct tuner *t = i2c_get_clientdata(c); | |
557 | ||
558 | tuner_dbg("resume\n"); | |
559 | if (t->freq) | |
560 | set_freq(c,t->freq); | |
561 | return 0; | |
562 | } | |
563 | ||
564 | /* ----------------------------------------------------------------------- */ | |
565 | ||
566 | static struct i2c_driver driver = { | |
567 | .owner = THIS_MODULE, | |
568 | .name = "tuner", | |
569 | .id = I2C_DRIVERID_TUNER, | |
570 | .flags = I2C_DF_NOTIFY, | |
571 | .attach_adapter = tuner_probe, | |
572 | .detach_client = tuner_detach, | |
573 | .command = tuner_command, | |
574 | .driver = { | |
575 | .suspend = tuner_suspend, | |
576 | .resume = tuner_resume, | |
577 | }, | |
578 | }; | |
579 | static struct i2c_client client_template = | |
580 | { | |
581 | I2C_DEVNAME("(tuner unset)"), | |
582 | .flags = I2C_CLIENT_ALLOW_USE, | |
583 | .driver = &driver, | |
584 | }; | |
585 | ||
586 | static int __init tuner_init_module(void) | |
587 | { | |
588 | return i2c_add_driver(&driver); | |
589 | } | |
590 | ||
591 | static void __exit tuner_cleanup_module(void) | |
592 | { | |
593 | i2c_del_driver(&driver); | |
594 | } | |
595 | ||
596 | module_init(tuner_init_module); | |
597 | module_exit(tuner_cleanup_module); | |
598 | ||
599 | /* | |
600 | * Overrides for Emacs so that we follow Linus's tabbing style. | |
601 | * --------------------------------------------------------------------------- | |
602 | * Local variables: | |
603 | * c-basic-offset: 8 | |
604 | * End: | |
605 | */ |