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
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  */
31 
32 
33 #include <linux/module.h>
34 #include <linux/init.h>
35 #include <linux/ioport.h>
36 #include <linux/delay.h>
37 #include <linux/sched.h>
38 #include <asm/io.h>
39 #include <asm/uaccess.h>
40 #include <asm/semaphore.h>
41 #include <linux/pci.h>
42 #include <linux/videodev.h>
43 
44 /* version 0.75      Sun Feb  4 22:51:27 EET 2001 */
45 #define DRIVER_VERSION	"0.75"
46 
47 #ifndef PCI_VENDOR_ID_GUILLEMOT
48 #define PCI_VENDOR_ID_GUILLEMOT 0x5046
49 #endif
50 
51 #ifndef PCI_DEVICE_ID_GUILLEMOT
52 #define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
53 #endif
54 
55 
56 /* TEA5757 pin mappings */
57 static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16 ;
58 
59 static int radio_nr = -1;
60 module_param(radio_nr, int, 0);
61 
62 
63 #define FREQ_LO		 50*16000
64 #define FREQ_HI		150*16000
65 
66 #define FREQ_IF         171200 /* 10.7*16000   */
67 #define FREQ_STEP       200    /* 12.5*16      */
68 
69 #define FREQ2BITS(x)	((( (unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\
70 			/(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */
71 
72 #define BITS2FREQ(x)	((x) * FREQ_STEP - FREQ_IF)
73 
74 
75 static int radio_ioctl(struct inode *inode, struct file *file,
76 		       unsigned int cmd, unsigned long arg);
77 
78 static struct file_operations maxiradio_fops = {
79 	.owner		= THIS_MODULE,
80 	.open           = video_exclusive_open,
81 	.release        = video_exclusive_release,
82 	.ioctl	        = radio_ioctl,
83 	.llseek         = no_llseek,
84 };
85 static struct video_device maxiradio_radio =
86 {
87 	.owner		= THIS_MODULE,
88 	.name		= "Maxi Radio FM2000 radio",
89 	.type		= VID_TYPE_TUNER,
90 	.hardware	= VID_HARDWARE_SF16MI,
91 	.fops           = &maxiradio_fops,
92 };
93 
94 static struct radio_device
95 {
96 	__u16	io,	/* base of radio io */
97 		muted,	/* VIDEO_AUDIO_MUTE */
98 		stereo,	/* VIDEO_TUNER_STEREO_ON */
99 		tuned;	/* signal strength (0 or 0xffff) */
100 
101 	unsigned long freq;
102 
103 	struct  semaphore lock;
104 } radio_unit = {0, 0, 0, 0, };
105 
106 
107 static void outbit(unsigned long bit, __u16 io)
108 {
109 	if(bit != 0)
110 		{
111 			outb(  power|wren|data     ,io); udelay(4);
112 			outb(  power|wren|data|clk ,io); udelay(4);
113 			outb(  power|wren|data     ,io); udelay(4);
114 		}
115 	else
116 		{
117 			outb(  power|wren          ,io); udelay(4);
118 			outb(  power|wren|clk      ,io); udelay(4);
119 			outb(  power|wren          ,io); udelay(4);
120 		}
121 }
122 
123 static void turn_power(__u16 io, int p)
124 {
125 	if(p != 0) outb(power, io); else outb(0,io);
126 }
127 
128 
129 static void set_freq(__u16 io, __u32 data)
130 {
131 	unsigned long int si;
132 	int bl;
133 
134 	/* TEA5757 shift register bits (see pdf) */
135 
136 	outbit(0,io); // 24  search
137 	outbit(1,io); // 23  search up/down
138 
139 	outbit(0,io); // 22  stereo/mono
140 
141 	outbit(0,io); // 21  band
142 	outbit(0,io); // 20  band (only 00=FM works I think)
143 
144 	outbit(0,io); // 19  port ?
145 	outbit(0,io); // 18  port ?
146 
147 	outbit(0,io); // 17  search level
148 	outbit(0,io); // 16  search level
149 
150 	si = 0x8000;
151 	for(bl = 1; bl <= 16 ; bl++) { outbit(data & si,io); si >>=1; }
152 
153 	outb(power,io);
154 }
155 
156 static int get_stereo(__u16 io)
157 {
158 	outb(power,io); udelay(4);
159 	return !(inb(io) & mo_st);
160 }
161 
162 static int get_tune(__u16 io)
163 {
164 	outb(power+clk,io); udelay(4);
165 	return !(inb(io) & mo_st);
166 }
167 
168 
169 inline static int radio_function(struct inode *inode, struct file *file,
170 				 unsigned int cmd, void *arg)
171 {
172 	struct video_device *dev = video_devdata(file);
173 	struct radio_device *card=dev->priv;
174 
175 	switch(cmd) {
176 		case VIDIOCGCAP: {
177 			struct video_capability *v = arg;
178 
179 			memset(v,0,sizeof(*v));
180 			strcpy(v->name, "Maxi Radio FM2000 radio");
181 			v->type=VID_TYPE_TUNER;
182 			v->channels=v->audios=1;
183 			return 0;
184 		}
185 		case VIDIOCGTUNER: {
186 			struct video_tuner *v = arg;
187 
188 			if(v->tuner)
189 				return -EINVAL;
190 
191 			card->stereo = 0xffff * get_stereo(card->io);
192 			card->tuned = 0xffff * get_tune(card->io);
193 
194 			v->flags = VIDEO_TUNER_LOW | card->stereo;
195 			v->signal = card->tuned;
196 
197 			strcpy(v->name, "FM");
198 
199 			v->rangelow = FREQ_LO;
200 			v->rangehigh = FREQ_HI;
201 			v->mode = VIDEO_MODE_AUTO;
202 
203 			return 0;
204 		}
205 		case VIDIOCSTUNER: {
206 			struct video_tuner *v = arg;
207 			if(v->tuner!=0)
208 				return -EINVAL;
209 			return 0;
210 		}
211 		case VIDIOCGFREQ: {
212 			unsigned long *freq = arg;
213 
214 			*freq = card->freq;
215 			return 0;
216 		}
217 		case VIDIOCSFREQ: {
218 			unsigned long *freq = arg;
219 
220 			if (*freq < FREQ_LO || *freq > FREQ_HI)
221 				return -EINVAL;
222 			card->freq = *freq;
223 			set_freq(card->io, FREQ2BITS(card->freq));
224 			msleep(125);
225 			return 0;
226 		}
227 		case VIDIOCGAUDIO: {
228 			struct video_audio *v = arg;
229 			memset(v,0,sizeof(*v));
230 			strcpy(v->name, "Radio");
231 			v->flags=VIDEO_AUDIO_MUTABLE | card->muted;
232 			v->mode=VIDEO_SOUND_STEREO;
233 			return 0;
234 		}
235 
236 		case VIDIOCSAUDIO: {
237 			struct video_audio *v = arg;
238 
239 			if(v->audio)
240 				return -EINVAL;
241 			card->muted = v->flags & VIDEO_AUDIO_MUTE;
242 			if(card->muted)
243 				turn_power(card->io, 0);
244 			else
245 				set_freq(card->io, FREQ2BITS(card->freq));
246 			return 0;
247 		}
248 		case VIDIOCGUNIT: {
249 			struct video_unit *v = arg;
250 
251 			v->video=VIDEO_NO_UNIT;
252 			v->vbi=VIDEO_NO_UNIT;
253 			v->radio=dev->minor;
254 			v->audio=0;
255 			v->teletext=VIDEO_NO_UNIT;
256 			return 0;
257 		}
258 		default: return -ENOIOCTLCMD;
259 	}
260 }
261 
262 static int radio_ioctl(struct inode *inode, struct file *file,
263 		       unsigned int cmd, unsigned long arg)
264 {
265 	struct video_device *dev = video_devdata(file);
266 	struct radio_device *card=dev->priv;
267 	int ret;
268 
269 	down(&card->lock);
270 	ret = video_usercopy(inode, file, cmd, arg, radio_function);
271 	up(&card->lock);
272 	return ret;
273 }
274 
275 MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
276 MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
277 MODULE_LICENSE("GPL");
278 
279 
280 static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
281 {
282 	if(!request_region(pci_resource_start(pdev, 0),
283 	                   pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) {
284 	        printk(KERN_ERR "radio-maxiradio: can't reserve I/O ports\n");
285 	        goto err_out;
286 	}
287 
288 	if (pci_enable_device(pdev))
289 	        goto err_out_free_region;
290 
291 	radio_unit.io = pci_resource_start(pdev, 0);
292 	init_MUTEX(&radio_unit.lock);
293 	maxiradio_radio.priv = &radio_unit;
294 
295 	if(video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr)==-1) {
296 	        printk("radio-maxiradio: can't register device!");
297 	        goto err_out_free_region;
298 	}
299 
300 	printk(KERN_INFO "radio-maxiradio: version "
301 	       DRIVER_VERSION
302 	       " time "
303 	       __TIME__ "  "
304 	       __DATE__
305 	       "\n");
306 
307 	printk(KERN_INFO "radio-maxiradio: found Guillemot MAXI Radio device (io = 0x%x)\n",
308 	       radio_unit.io);
309 	return 0;
310 
311 err_out_free_region:
312 	release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
313 err_out:
314 	return -ENODEV;
315 }
316 
317 static void __devexit maxiradio_remove_one(struct pci_dev *pdev)
318 {
319 	video_unregister_device(&maxiradio_radio);
320 	release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
321 }
322 
323 static struct pci_device_id maxiradio_pci_tbl[] = {
324 	{ PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO,
325 		PCI_ANY_ID, PCI_ANY_ID, },
326 	{ 0,}
327 };
328 
329 MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl);
330 
331 static struct pci_driver maxiradio_driver = {
332 	.name		= "radio-maxiradio",
333 	.id_table	= maxiradio_pci_tbl,
334 	.probe		= maxiradio_init_one,
335 	.remove		= __devexit_p(maxiradio_remove_one),
336 };
337 
338 static int __init maxiradio_radio_init(void)
339 {
340 	return pci_module_init(&maxiradio_driver);
341 }
342 
343 static void __exit maxiradio_radio_exit(void)
344 {
345 	pci_unregister_driver(&maxiradio_driver);
346 }
347 
348 module_init(maxiradio_radio_init);
349 module_exit(maxiradio_radio_exit);
350