• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Guillemot Maxi Radio FM 2000 PCI radio card driver for Linux
3  * (C) 2001 Dimitromanolakis Apostolos <apdim@grecian.net>
4  *
5  * Based in the radio Maestro PCI driver. Actually it uses the same chip
6  * for radio but different pci controller.
7  *
8  * I didn't have any specs I reversed engineered the protocol from
9  * the windows driver (radio.dll).
10  *
11  * The card uses the TEA5757 chip that includes a search function but it
12  * is useless as I haven't found any way to read back the frequency. If
13  * anybody does please mail me.
14  *
15  * For the pdf file see:
16  * http://www.semiconductors.philips.com/pip/TEA5757H/V1
17  *
18  *
19  * CHANGES:
20  *   0.75b
21  *     - better pci interface thanks to Francois Romieu <romieu@cogenit.fr>
22  *
23  *   0.75      Sun Feb  4 22:51:27 EET 2001
24  *     - tiding up
25  *     - removed support for multiple devices as it didn't work anyway
26  *
27  * BUGS:
28  *   - card unmutes if you change frequency
29  *
30  * (c) 2006, 2007 by Mauro Carvalho Chehab <mchehab@infradead.org>:
31  *	- Conversion to V4L2 API
32  *      - Uses video_ioctl2 for parsing and to add debug support
33  */
34 
35 
36 #include <linux/module.h>
37 #include <linux/init.h>
38 #include <linux/ioport.h>
39 #include <linux/delay.h>
40 #include <asm/io.h>
41 #include <asm/uaccess.h>
42 #include <linux/mutex.h>
43 
44 #include <linux/pci.h>
45 #include <linux/videodev2.h>
46 #include <media/v4l2-common.h>
47 #include <media/v4l2-ioctl.h>
48 
49 #define DRIVER_VERSION	"0.77"
50 
51 #include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
52 #define RADIO_VERSION KERNEL_VERSION(0,7,7)
53 
54 static struct video_device maxiradio_radio;
55 
56 #define dprintk(num, fmt, arg...)                                          \
57 	do {                                                               \
58 		if (maxiradio_radio.debug >= num)                          \
59 			printk(KERN_DEBUG "%s: " fmt,                      \
60 				maxiradio_radio.name, ## arg); } while (0)
61 
62 static struct v4l2_queryctrl radio_qctrl[] = {
63 	{
64 		.id            = V4L2_CID_AUDIO_MUTE,
65 		.name          = "Mute",
66 		.minimum       = 0,
67 		.maximum       = 1,
68 		.default_value = 1,
69 		.type          = V4L2_CTRL_TYPE_BOOLEAN,
70 	}
71 };
72 
73 #ifndef PCI_VENDOR_ID_GUILLEMOT
74 #define PCI_VENDOR_ID_GUILLEMOT 0x5046
75 #endif
76 
77 #ifndef PCI_DEVICE_ID_GUILLEMOT
78 #define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
79 #endif
80 
81 
82 /* TEA5757 pin mappings */
83 static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16 ;
84 
85 static int radio_nr = -1;
86 module_param(radio_nr, int, 0);
87 
88 static unsigned long in_use;
89 
90 #define FREQ_LO		 50*16000
91 #define FREQ_HI		150*16000
92 
93 #define FREQ_IF         171200 /* 10.7*16000   */
94 #define FREQ_STEP       200    /* 12.5*16      */
95 
96 /* (x==fmhz*16*1000) -> bits */
97 #define FREQ2BITS(x)	((( (unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1)) \
98 			/(FREQ_STEP<<2))<<2)
99 
100 #define BITS2FREQ(x)	((x) * FREQ_STEP - FREQ_IF)
101 
102 
maxiradio_exclusive_open(struct file * file)103 static int maxiradio_exclusive_open(struct file *file)
104 {
105 	return test_and_set_bit(0, &in_use) ? -EBUSY : 0;
106 }
107 
maxiradio_exclusive_release(struct file * file)108 static int maxiradio_exclusive_release(struct file *file)
109 {
110 	clear_bit(0, &in_use);
111 	return 0;
112 }
113 
114 static const struct v4l2_file_operations maxiradio_fops = {
115 	.owner		= THIS_MODULE,
116 	.open           = maxiradio_exclusive_open,
117 	.release        = maxiradio_exclusive_release,
118 	.ioctl          = video_ioctl2,
119 };
120 
121 static struct radio_device
122 {
123 	__u16	io,	/* base of radio io */
124 		muted,	/* VIDEO_AUDIO_MUTE */
125 		stereo,	/* VIDEO_TUNER_STEREO_ON */
126 		tuned;	/* signal strength (0 or 0xffff) */
127 
128 	unsigned long freq;
129 
130 	struct mutex lock;
131 } radio_unit = {
132 	.muted =1,
133 	.freq = FREQ_LO,
134 };
135 
outbit(unsigned long bit,__u16 io)136 static void outbit(unsigned long bit, __u16 io)
137 {
138 	if (bit != 0)
139 		{
140 			outb(  power|wren|data     ,io); udelay(4);
141 			outb(  power|wren|data|clk ,io); udelay(4);
142 			outb(  power|wren|data     ,io); udelay(4);
143 		}
144 	else
145 		{
146 			outb(  power|wren          ,io); udelay(4);
147 			outb(  power|wren|clk      ,io); udelay(4);
148 			outb(  power|wren          ,io); udelay(4);
149 		}
150 }
151 
turn_power(__u16 io,int p)152 static void turn_power(__u16 io, int p)
153 {
154 	if (p != 0) {
155 		dprintk(1, "Radio powered on\n");
156 		outb(power, io);
157 	} else {
158 		dprintk(1, "Radio powered off\n");
159 		outb(0,io);
160 	}
161 }
162 
set_freq(__u16 io,__u32 freq)163 static void set_freq(__u16 io, __u32 freq)
164 {
165 	unsigned long int si;
166 	int bl;
167 	int val = FREQ2BITS(freq);
168 
169 	/* TEA5757 shift register bits (see pdf) */
170 
171 	outbit(0, io); /* 24  search */
172 	outbit(1, io); /* 23  search up/down */
173 
174 	outbit(0, io); /* 22  stereo/mono */
175 
176 	outbit(0, io); /* 21  band */
177 	outbit(0, io); /* 20  band (only 00=FM works I think) */
178 
179 	outbit(0, io); /* 19  port ? */
180 	outbit(0, io); /* 18  port ? */
181 
182 	outbit(0, io); /* 17  search level */
183 	outbit(0, io); /* 16  search level */
184 
185 	si = 0x8000;
186 	for (bl = 1; bl <= 16; bl++) {
187 		outbit(val & si, io);
188 		si >>= 1;
189 	}
190 
191 	dprintk(1, "Radio freq set to %d.%02d MHz\n",
192 				freq / 16000,
193 				freq % 16000 * 100 / 16000);
194 
195 	turn_power(io, 1);
196 }
197 
get_stereo(__u16 io)198 static int get_stereo(__u16 io)
199 {
200 	outb(power,io);
201 	udelay(4);
202 
203 	return !(inb(io) & mo_st);
204 }
205 
get_tune(__u16 io)206 static int get_tune(__u16 io)
207 {
208 	outb(power+clk,io);
209 	udelay(4);
210 
211 	return !(inb(io) & mo_st);
212 }
213 
214 
vidioc_querycap(struct file * file,void * priv,struct v4l2_capability * v)215 static int vidioc_querycap (struct file *file, void  *priv,
216 			    struct v4l2_capability *v)
217 {
218 	strlcpy(v->driver, "radio-maxiradio", sizeof (v->driver));
219 	strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof (v->card));
220 	sprintf(v->bus_info,"ISA");
221 	v->version = RADIO_VERSION;
222 	v->capabilities = V4L2_CAP_TUNER;
223 
224 	return 0;
225 }
226 
vidioc_g_tuner(struct file * file,void * priv,struct v4l2_tuner * v)227 static int vidioc_g_tuner (struct file *file, void *priv,
228 			   struct v4l2_tuner *v)
229 {
230 	struct radio_device *card = video_drvdata(file);
231 
232 	if (v->index > 0)
233 		return -EINVAL;
234 
235 	memset(v,0,sizeof(*v));
236 	strcpy(v->name, "FM");
237 	v->type = V4L2_TUNER_RADIO;
238 
239 	v->rangelow=FREQ_LO;
240 	v->rangehigh=FREQ_HI;
241 	v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
242 	v->capability=V4L2_TUNER_CAP_LOW;
243 	if(get_stereo(card->io))
244 		v->audmode = V4L2_TUNER_MODE_STEREO;
245 	else
246 		v->audmode = V4L2_TUNER_MODE_MONO;
247 	v->signal=0xffff*get_tune(card->io);
248 
249 	return 0;
250 }
251 
vidioc_s_tuner(struct file * file,void * priv,struct v4l2_tuner * v)252 static int vidioc_s_tuner (struct file *file, void *priv,
253 			   struct v4l2_tuner *v)
254 {
255 	if (v->index > 0)
256 		return -EINVAL;
257 
258 	return 0;
259 }
260 
vidioc_g_audio(struct file * file,void * priv,struct v4l2_audio * a)261 static int vidioc_g_audio (struct file *file, void *priv,
262 			   struct v4l2_audio *a)
263 {
264 	if (a->index > 1)
265 		return -EINVAL;
266 
267 	strcpy(a->name, "FM");
268 	a->capability = V4L2_AUDCAP_STEREO;
269 	return 0;
270 }
271 
vidioc_g_input(struct file * filp,void * priv,unsigned int * i)272 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
273 {
274 	*i = 0;
275 
276 	return 0;
277 }
278 
vidioc_s_input(struct file * filp,void * priv,unsigned int i)279 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
280 {
281 	if (i != 0)
282 		return -EINVAL;
283 
284 	return 0;
285 }
286 
287 
vidioc_s_audio(struct file * file,void * priv,struct v4l2_audio * a)288 static int vidioc_s_audio (struct file *file, void *priv,
289 			   struct v4l2_audio *a)
290 {
291 	if (a->index != 0)
292 		return -EINVAL;
293 
294 	return 0;
295 }
296 
vidioc_s_frequency(struct file * file,void * priv,struct v4l2_frequency * f)297 static int vidioc_s_frequency (struct file *file, void *priv,
298 			       struct v4l2_frequency *f)
299 {
300 	struct radio_device *card = video_drvdata(file);
301 
302 	if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) {
303 		dprintk(1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n",
304 					f->frequency / 16000,
305 					f->frequency % 16000 * 100 / 16000,
306 					FREQ_LO / 16000, FREQ_HI / 16000);
307 
308 		return -EINVAL;
309 	}
310 
311 	card->freq = f->frequency;
312 	set_freq(card->io, card->freq);
313 	msleep(125);
314 
315 	return 0;
316 }
317 
vidioc_g_frequency(struct file * file,void * priv,struct v4l2_frequency * f)318 static int vidioc_g_frequency (struct file *file, void *priv,
319 			       struct v4l2_frequency *f)
320 {
321 	struct radio_device *card = video_drvdata(file);
322 
323 	f->type = V4L2_TUNER_RADIO;
324 	f->frequency = card->freq;
325 
326 	dprintk(4, "radio freq is %d.%02d MHz",
327 				f->frequency / 16000,
328 				f->frequency % 16000 * 100 / 16000);
329 
330 	return 0;
331 }
332 
vidioc_queryctrl(struct file * file,void * priv,struct v4l2_queryctrl * qc)333 static int vidioc_queryctrl (struct file *file, void *priv,
334 			     struct v4l2_queryctrl *qc)
335 {
336 	int i;
337 
338 	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
339 		if (qc->id && qc->id == radio_qctrl[i].id) {
340 			memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
341 			return (0);
342 		}
343 	}
344 
345 	return -EINVAL;
346 }
347 
vidioc_g_ctrl(struct file * file,void * priv,struct v4l2_control * ctrl)348 static int vidioc_g_ctrl (struct file *file, void *priv,
349 			    struct v4l2_control *ctrl)
350 {
351 	struct radio_device *card = video_drvdata(file);
352 
353 	switch (ctrl->id) {
354 		case V4L2_CID_AUDIO_MUTE:
355 			ctrl->value=card->muted;
356 			return (0);
357 	}
358 
359 	return -EINVAL;
360 }
361 
vidioc_s_ctrl(struct file * file,void * priv,struct v4l2_control * ctrl)362 static int vidioc_s_ctrl (struct file *file, void *priv,
363 			  struct v4l2_control *ctrl)
364 {
365 	struct radio_device *card = video_drvdata(file);
366 
367 	switch (ctrl->id) {
368 		case V4L2_CID_AUDIO_MUTE:
369 			card->muted = ctrl->value;
370 			if(card->muted)
371 				turn_power(card->io, 0);
372 			else
373 				set_freq(card->io, card->freq);
374 			return 0;
375 	}
376 
377 	return -EINVAL;
378 }
379 
380 static const struct v4l2_ioctl_ops maxiradio_ioctl_ops = {
381 	.vidioc_querycap    = vidioc_querycap,
382 	.vidioc_g_tuner     = vidioc_g_tuner,
383 	.vidioc_s_tuner     = vidioc_s_tuner,
384 	.vidioc_g_audio     = vidioc_g_audio,
385 	.vidioc_s_audio     = vidioc_s_audio,
386 	.vidioc_g_input     = vidioc_g_input,
387 	.vidioc_s_input     = vidioc_s_input,
388 	.vidioc_g_frequency = vidioc_g_frequency,
389 	.vidioc_s_frequency = vidioc_s_frequency,
390 	.vidioc_queryctrl   = vidioc_queryctrl,
391 	.vidioc_g_ctrl      = vidioc_g_ctrl,
392 	.vidioc_s_ctrl      = vidioc_s_ctrl,
393 };
394 
395 static struct video_device maxiradio_radio = {
396 	.name		= "Maxi Radio FM2000 radio",
397 	.fops           = &maxiradio_fops,
398 	.ioctl_ops 	= &maxiradio_ioctl_ops,
399 	.release	= video_device_release_empty,
400 };
401 
maxiradio_init_one(struct pci_dev * pdev,const struct pci_device_id * ent)402 static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
403 {
404 	if(!request_region(pci_resource_start(pdev, 0),
405 			   pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) {
406 		printk(KERN_ERR "radio-maxiradio: can't reserve I/O ports\n");
407 		goto err_out;
408 	}
409 
410 	if (pci_enable_device(pdev))
411 		goto err_out_free_region;
412 
413 	radio_unit.io = pci_resource_start(pdev, 0);
414 	mutex_init(&radio_unit.lock);
415 	video_set_drvdata(&maxiradio_radio, &radio_unit);
416 
417 	if (video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
418 		printk("radio-maxiradio: can't register device!");
419 		goto err_out_free_region;
420 	}
421 
422 	printk(KERN_INFO "radio-maxiradio: version "
423 	       DRIVER_VERSION
424 	       " time "
425 	       __TIME__ "  "
426 	       __DATE__
427 	       "\n");
428 
429 	printk(KERN_INFO "radio-maxiradio: found Guillemot MAXI Radio device (io = 0x%x)\n",
430 	       radio_unit.io);
431 	return 0;
432 
433 err_out_free_region:
434 	release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
435 err_out:
436 	return -ENODEV;
437 }
438 
maxiradio_remove_one(struct pci_dev * pdev)439 static void __devexit maxiradio_remove_one(struct pci_dev *pdev)
440 {
441 	video_unregister_device(&maxiradio_radio);
442 	release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
443 }
444 
445 static struct pci_device_id maxiradio_pci_tbl[] = {
446 	{ PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO,
447 		PCI_ANY_ID, PCI_ANY_ID, },
448 	{ 0,}
449 };
450 
451 MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl);
452 
453 static struct pci_driver maxiradio_driver = {
454 	.name		= "radio-maxiradio",
455 	.id_table	= maxiradio_pci_tbl,
456 	.probe		= maxiradio_init_one,
457 	.remove		= __devexit_p(maxiradio_remove_one),
458 };
459 
maxiradio_radio_init(void)460 static int __init maxiradio_radio_init(void)
461 {
462 	return pci_register_driver(&maxiradio_driver);
463 }
464 
maxiradio_radio_exit(void)465 static void __exit maxiradio_radio_exit(void)
466 {
467 	pci_unregister_driver(&maxiradio_driver);
468 }
469 
470 module_init(maxiradio_radio_init);
471 module_exit(maxiradio_radio_exit);
472 
473 MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
474 MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
475 MODULE_LICENSE("GPL");
476 
477 module_param_named(debug,maxiradio_radio.debug, int, 0644);
478 MODULE_PARM_DESC(debug,"activates debug info");
479