Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * $Id: tuner-core.c,v 1.5 2005/02/15 15:59:35 kraxel Exp $ | |
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 | ||
26 | #define UNSET (-1U) | |
27 | ||
28 | /* standard i2c insmod options */ | |
29 | static unsigned short normal_i2c[] = { | |
30 | 0x4b, /* tda8290 */ | |
31 | I2C_CLIENT_END | |
32 | }; | |
33 | static unsigned short normal_i2c_range[] = { | |
34 | 0x60, 0x6f, | |
35 | I2C_CLIENT_END | |
36 | }; | |
37 | I2C_CLIENT_INSMOD; | |
38 | ||
39 | /* insmod options used at init time => read/only */ | |
40 | static unsigned int addr = 0; | |
41 | module_param(addr, int, 0444); | |
42 | ||
43 | /* insmod options used at runtime => read/write */ | |
44 | unsigned int tuner_debug = 0; | |
45 | module_param(tuner_debug, int, 0644); | |
46 | ||
47 | static unsigned int tv_range[2] = { 44, 958 }; | |
48 | static unsigned int radio_range[2] = { 65, 108 }; | |
49 | ||
50 | module_param_array(tv_range, int, NULL, 0644); | |
51 | module_param_array(radio_range, int, NULL, 0644); | |
52 | ||
53 | MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners"); | |
54 | MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); | |
55 | MODULE_LICENSE("GPL"); | |
56 | ||
57 | static int this_adap; | |
58 | ||
59 | static struct i2c_driver driver; | |
60 | static struct i2c_client client_template; | |
61 | ||
62 | /* ---------------------------------------------------------------------- */ | |
63 | ||
64 | // Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz | |
65 | static void set_tv_freq(struct i2c_client *c, unsigned int freq) | |
66 | { | |
67 | struct tuner *t = i2c_get_clientdata(c); | |
68 | ||
69 | if (t->type == UNSET) { | |
70 | tuner_info("tuner type not set\n"); | |
71 | return; | |
72 | } | |
73 | if (NULL == t->tv_freq) { | |
74 | tuner_info("Huh? tv_set is NULL?\n"); | |
75 | return; | |
76 | } | |
77 | if (freq < tv_range[0]*16 || freq > tv_range[1]*16) { | |
78 | /* FIXME: better do that chip-specific, but | |
79 | right now we don't have that in the config | |
80 | struct and this way is still better than no | |
81 | check at all */ | |
82 | tuner_info("TV freq (%d.%02d) out of range (%d-%d)\n", | |
83 | freq/16,freq%16*100/16,tv_range[0],tv_range[1]); | |
84 | return; | |
85 | } | |
86 | t->tv_freq(c,freq); | |
87 | } | |
88 | ||
89 | static void set_radio_freq(struct i2c_client *c, unsigned int freq) | |
90 | { | |
91 | struct tuner *t = i2c_get_clientdata(c); | |
92 | ||
93 | if (t->type == UNSET) { | |
94 | tuner_info("tuner type not set\n"); | |
95 | return; | |
96 | } | |
97 | if (NULL == t->radio_freq) { | |
98 | tuner_info("no radio tuning for this one, sorry.\n"); | |
99 | return; | |
100 | } | |
101 | if (freq < radio_range[0]*16 || freq > radio_range[1]*16) { | |
102 | tuner_info("radio freq (%d.%02d) out of range (%d-%d)\n", | |
103 | freq/16,freq%16*100/16, | |
104 | radio_range[0],radio_range[1]); | |
105 | return; | |
106 | } | |
107 | t->radio_freq(c,freq); | |
108 | } | |
109 | ||
110 | static void set_freq(struct i2c_client *c, unsigned long freq) | |
111 | { | |
112 | struct tuner *t = i2c_get_clientdata(c); | |
113 | ||
114 | switch (t->mode) { | |
115 | case V4L2_TUNER_RADIO: | |
116 | tuner_dbg("radio freq set to %lu.%02lu\n", | |
117 | freq/16,freq%16*100/16); | |
118 | set_radio_freq(c,freq); | |
119 | break; | |
120 | case V4L2_TUNER_ANALOG_TV: | |
121 | case V4L2_TUNER_DIGITAL_TV: | |
122 | tuner_dbg("tv freq set to %lu.%02lu\n", | |
123 | freq/16,freq%16*100/16); | |
124 | set_tv_freq(c, freq); | |
125 | break; | |
126 | } | |
127 | t->freq = freq; | |
128 | } | |
129 | ||
130 | static void set_type(struct i2c_client *c, unsigned int type) | |
131 | { | |
132 | struct tuner *t = i2c_get_clientdata(c); | |
133 | ||
134 | /* sanity check */ | |
135 | if (type == UNSET || type == TUNER_ABSENT) | |
136 | return; | |
137 | if (type >= tuner_count) | |
138 | return; | |
139 | ||
140 | if (NULL == t->i2c.dev.driver) { | |
141 | /* not registered yet */ | |
142 | t->type = type; | |
143 | return; | |
144 | } | |
145 | if (t->initialized) | |
146 | /* run only once */ | |
147 | return; | |
148 | ||
149 | t->initialized = 1; | |
150 | t->type = type; | |
151 | switch (t->type) { | |
152 | case TUNER_MT2032: | |
153 | microtune_init(c); | |
154 | break; | |
155 | case TUNER_PHILIPS_TDA8290: | |
156 | tda8290_init(c); | |
157 | break; | |
158 | default: | |
159 | default_tuner_init(c); | |
160 | break; | |
161 | } | |
162 | } | |
163 | ||
164 | static char pal[] = "-"; | |
975e046c | 165 | module_param_string(pal, pal, sizeof(pal), 0644); |
1da177e4 LT |
166 | |
167 | static int tuner_fixup_std(struct tuner *t) | |
168 | { | |
169 | if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) { | |
170 | /* get more precise norm info from insmod option */ | |
171 | switch (pal[0]) { | |
172 | case 'b': | |
173 | case 'B': | |
174 | case 'g': | |
175 | case 'G': | |
176 | tuner_dbg("insmod fixup: PAL => PAL-BG\n"); | |
177 | t->std = V4L2_STD_PAL_BG; | |
178 | break; | |
179 | case 'i': | |
180 | case 'I': | |
181 | tuner_dbg("insmod fixup: PAL => PAL-I\n"); | |
182 | t->std = V4L2_STD_PAL_I; | |
183 | break; | |
184 | case 'd': | |
185 | case 'D': | |
186 | case 'k': | |
187 | case 'K': | |
188 | tuner_dbg("insmod fixup: PAL => PAL-DK\n"); | |
189 | t->std = V4L2_STD_PAL_DK; | |
190 | break; | |
191 | } | |
192 | } | |
193 | return 0; | |
194 | } | |
195 | ||
196 | /* ---------------------------------------------------------------------- */ | |
197 | ||
198 | static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) | |
199 | { | |
200 | struct tuner *t; | |
201 | ||
202 | if (this_adap > 0) | |
203 | return -1; | |
204 | this_adap++; | |
205 | ||
206 | client_template.adapter = adap; | |
207 | client_template.addr = addr; | |
208 | ||
209 | t = kmalloc(sizeof(struct tuner),GFP_KERNEL); | |
210 | if (NULL == t) | |
211 | return -ENOMEM; | |
212 | memset(t,0,sizeof(struct tuner)); | |
213 | memcpy(&t->i2c,&client_template,sizeof(struct i2c_client)); | |
214 | i2c_set_clientdata(&t->i2c, t); | |
215 | t->type = UNSET; | |
216 | t->radio_if2 = 10700*1000; // 10.7MHz - FM radio | |
217 | ||
218 | i2c_attach_client(&t->i2c); | |
219 | tuner_info("chip found @ 0x%x (%s)\n", | |
220 | addr << 1, adap->name); | |
221 | set_type(&t->i2c, t->type); | |
222 | return 0; | |
223 | } | |
224 | ||
225 | static int tuner_probe(struct i2c_adapter *adap) | |
226 | { | |
227 | if (0 != addr) { | |
228 | normal_i2c[0] = addr; | |
229 | normal_i2c_range[0] = addr; | |
230 | normal_i2c_range[1] = addr; | |
231 | } | |
232 | this_adap = 0; | |
233 | ||
234 | if (adap->class & I2C_CLASS_TV_ANALOG) | |
235 | return i2c_probe(adap, &addr_data, tuner_attach); | |
236 | return 0; | |
237 | } | |
238 | ||
239 | static int tuner_detach(struct i2c_client *client) | |
240 | { | |
241 | struct tuner *t = i2c_get_clientdata(client); | |
242 | ||
243 | i2c_detach_client(&t->i2c); | |
244 | kfree(t); | |
245 | return 0; | |
246 | } | |
247 | ||
248 | #define SWITCH_V4L2 if (!t->using_v4l2 && tuner_debug) \ | |
249 | tuner_info("switching to v4l2\n"); \ | |
250 | t->using_v4l2 = 1; | |
251 | #define CHECK_V4L2 if (t->using_v4l2) { if (tuner_debug) \ | |
252 | tuner_info("ignore v4l1 call\n"); \ | |
253 | return 0; } | |
254 | ||
255 | static int | |
256 | tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | |
257 | { | |
258 | struct tuner *t = i2c_get_clientdata(client); | |
259 | unsigned int *iarg = (int*)arg; | |
260 | ||
261 | switch (cmd) { | |
262 | ||
263 | /* --- configuration --- */ | |
264 | case TUNER_SET_TYPE: | |
265 | set_type(client,*iarg); | |
266 | break; | |
267 | case AUDC_SET_RADIO: | |
268 | if (V4L2_TUNER_RADIO != t->mode) { | |
269 | set_tv_freq(client,400 * 16); | |
270 | t->mode = V4L2_TUNER_RADIO; | |
271 | } | |
272 | break; | |
273 | case AUDC_CONFIG_PINNACLE: | |
274 | switch (*iarg) { | |
275 | case 2: | |
276 | tuner_dbg("pinnacle pal\n"); | |
277 | t->radio_if2 = 33300 * 1000; | |
278 | break; | |
279 | case 3: | |
280 | tuner_dbg("pinnacle ntsc\n"); | |
281 | t->radio_if2 = 41300 * 1000; | |
282 | break; | |
283 | } | |
284 | break; | |
285 | ||
286 | /* --- v4l ioctls --- */ | |
287 | /* take care: bttv does userspace copying, we'll get a | |
288 | kernel pointer here... */ | |
289 | case VIDIOCSCHAN: | |
290 | { | |
291 | static const v4l2_std_id map[] = { | |
292 | [ VIDEO_MODE_PAL ] = V4L2_STD_PAL, | |
293 | [ VIDEO_MODE_NTSC ] = V4L2_STD_NTSC_M, | |
294 | [ VIDEO_MODE_SECAM ] = V4L2_STD_SECAM, | |
295 | [ 4 /* bttv */ ] = V4L2_STD_PAL_M, | |
296 | [ 5 /* bttv */ ] = V4L2_STD_PAL_N, | |
297 | [ 6 /* bttv */ ] = V4L2_STD_NTSC_M_JP, | |
298 | }; | |
299 | struct video_channel *vc = arg; | |
300 | ||
301 | CHECK_V4L2; | |
302 | t->mode = V4L2_TUNER_ANALOG_TV; | |
303 | if (vc->norm < ARRAY_SIZE(map)) | |
304 | t->std = map[vc->norm]; | |
305 | tuner_fixup_std(t); | |
306 | if (t->freq) | |
307 | set_tv_freq(client,t->freq); | |
308 | return 0; | |
309 | } | |
310 | case VIDIOCSFREQ: | |
311 | { | |
312 | unsigned long *v = arg; | |
313 | ||
314 | CHECK_V4L2; | |
315 | set_freq(client,*v); | |
316 | return 0; | |
317 | } | |
318 | case VIDIOCGTUNER: | |
319 | { | |
320 | struct video_tuner *vt = arg; | |
321 | ||
322 | CHECK_V4L2; | |
323 | if (V4L2_TUNER_RADIO == t->mode && t->has_signal) | |
324 | vt->signal = t->has_signal(client); | |
325 | return 0; | |
326 | } | |
327 | case VIDIOCGAUDIO: | |
328 | { | |
329 | struct video_audio *va = arg; | |
330 | ||
331 | CHECK_V4L2; | |
332 | if (V4L2_TUNER_RADIO == t->mode && t->is_stereo) | |
333 | va->mode = t->is_stereo(client) | |
334 | ? VIDEO_SOUND_STEREO | |
335 | : VIDEO_SOUND_MONO; | |
336 | return 0; | |
337 | } | |
338 | ||
339 | case VIDIOC_S_STD: | |
340 | { | |
341 | v4l2_std_id *id = arg; | |
342 | ||
343 | SWITCH_V4L2; | |
344 | t->mode = V4L2_TUNER_ANALOG_TV; | |
345 | t->std = *id; | |
346 | tuner_fixup_std(t); | |
347 | if (t->freq) | |
348 | set_freq(client,t->freq); | |
349 | break; | |
350 | } | |
351 | case VIDIOC_S_FREQUENCY: | |
352 | { | |
353 | struct v4l2_frequency *f = arg; | |
354 | ||
355 | SWITCH_V4L2; | |
356 | if (V4L2_TUNER_RADIO == f->type && | |
357 | V4L2_TUNER_RADIO != t->mode) | |
358 | set_tv_freq(client,400*16); | |
359 | t->mode = f->type; | |
e99d3438 | 360 | set_freq(client,f->frequency); |
1da177e4 LT |
361 | break; |
362 | } | |
363 | case VIDIOC_G_TUNER: | |
364 | { | |
365 | struct v4l2_tuner *tuner = arg; | |
366 | ||
367 | SWITCH_V4L2; | |
368 | if (V4L2_TUNER_RADIO == t->mode && t->has_signal) | |
369 | tuner->signal = t->has_signal(client); | |
370 | break; | |
371 | } | |
372 | default: | |
373 | /* nothing */ | |
374 | break; | |
375 | } | |
376 | ||
377 | return 0; | |
378 | } | |
379 | ||
a2910689 | 380 | static int tuner_suspend(struct device * dev, pm_message_t state, u32 level) |
1da177e4 LT |
381 | { |
382 | struct i2c_client *c = container_of(dev, struct i2c_client, dev); | |
383 | struct tuner *t = i2c_get_clientdata(c); | |
384 | ||
385 | tuner_dbg("suspend\n"); | |
386 | /* FIXME: power down ??? */ | |
387 | return 0; | |
388 | } | |
389 | ||
390 | static int tuner_resume(struct device * dev, u32 level) | |
391 | { | |
392 | struct i2c_client *c = container_of(dev, struct i2c_client, dev); | |
393 | struct tuner *t = i2c_get_clientdata(c); | |
394 | ||
395 | tuner_dbg("resume\n"); | |
396 | if (t->freq) | |
397 | set_freq(c,t->freq); | |
398 | return 0; | |
399 | } | |
400 | ||
401 | /* ----------------------------------------------------------------------- */ | |
402 | ||
403 | static struct i2c_driver driver = { | |
404 | .owner = THIS_MODULE, | |
405 | .name = "tuner", | |
406 | .id = I2C_DRIVERID_TUNER, | |
407 | .flags = I2C_DF_NOTIFY, | |
408 | .attach_adapter = tuner_probe, | |
409 | .detach_client = tuner_detach, | |
410 | .command = tuner_command, | |
411 | .driver = { | |
412 | .suspend = tuner_suspend, | |
413 | .resume = tuner_resume, | |
414 | }, | |
415 | }; | |
416 | static struct i2c_client client_template = | |
417 | { | |
418 | I2C_DEVNAME("(tuner unset)"), | |
419 | .flags = I2C_CLIENT_ALLOW_USE, | |
420 | .driver = &driver, | |
421 | }; | |
422 | ||
423 | static int __init tuner_init_module(void) | |
424 | { | |
425 | return i2c_add_driver(&driver); | |
426 | } | |
427 | ||
428 | static void __exit tuner_cleanup_module(void) | |
429 | { | |
430 | i2c_del_driver(&driver); | |
431 | } | |
432 | ||
433 | module_init(tuner_init_module); | |
434 | module_exit(tuner_cleanup_module); | |
435 | ||
436 | /* | |
437 | * Overrides for Emacs so that we follow Linus's tabbing style. | |
438 | * --------------------------------------------------------------------------- | |
439 | * Local variables: | |
440 | * c-basic-offset: 8 | |
441 | * End: | |
442 | */ |