1 /*
2  ***************************************************************************
3  *
4  *     radio-gemtek-pci.c - Gemtek PCI Radio driver
5  *     (C) 2001 Vladimir Shebordaev <vshebordaev@mail.ru>
6  *
7  ***************************************************************************
8  *
9  *     This program is free software; you can redistribute it and/or
10  *     modify it under the terms of the GNU General Public License as
11  *     published by the Free Software Foundation; either version 2 of
12  *     the License, or (at your option) any later version.
13  *
14  *     This program is distributed in the hope that it will be useful,
15  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *     GNU General Public License for more details.
18  *
19  *     You should have received a copy of the GNU General Public
20  *     License along with this program; if not, write to the Free
21  *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
22  *     USA.
23  *
24  ***************************************************************************
25  *
26  *     Gemtek Corp still silently refuses to release any specifications
27  *     of their multimedia devices, so the protocol still has to be
28  *     reverse engineered.
29  *
30  *     The v4l code was inspired by Jonas Munsin's  Gemtek serial line
31  *     radio device driver.
32  *
33  *     Please, let me know if this piece of code was useful :)
34  *
35  *     TODO: multiple device support and portability were not tested
36  *
37  ***************************************************************************
38  */
39 
40 #include <linux/version.h>
41 #include <linux/config.h>
42 #include <linux/types.h>
43 #include <linux/list.h>
44 #include <linux/module.h>
45 #include <linux/init.h>
46 #include <linux/pci.h>
47 #include <linux/videodev.h>
48 #include <linux/errno.h>
49 
50 #include <asm/io.h>
51 #include <asm/uaccess.h>
52 
53 #ifndef PCI_VENDOR_ID_GEMTEK
54 #define PCI_VENDOR_ID_GEMTEK 0x5046
55 #endif
56 
57 #ifndef PCI_DEVICE_ID_GEMTEK_PR103
58 #define PCI_DEVICE_ID_GEMTEK_PR103 0x1001
59 #endif
60 
61 #ifndef GEMTEK_PCI_RANGE_LOW
62 #define GEMTEK_PCI_RANGE_LOW (87*16000)
63 #endif
64 
65 #ifndef GEMTEK_PCI_RANGE_HIGH
66 #define GEMTEK_PCI_RANGE_HIGH (108*16000)
67 #endif
68 
69 #ifndef TRUE
70 #define TRUE (1)
71 #endif
72 
73 #ifndef FALSE
74 #define FALSE (0)
75 #endif
76 
77 struct gemtek_pci_card {
78 	struct video_device *videodev;
79 
80 	u32 iobase;
81 	u32 length;
82 	u8  chiprev;
83 	u16 model;
84 
85 	u32 current_frequency;
86 	u8  mute;
87 };
88 
89 static const char rcsid[] = "$Id: radio-gemtek-pci.c,v 1.1 2001/07/23 08:08:16 ted Exp ted $";
90 
91 static int nr_radio = -1;
92 
gemtek_pci_open(struct video_device * dev,int flags)93 static int gemtek_pci_open( struct video_device *dev, int flags)
94 {
95 	struct gemtek_pci_card *card =  dev->priv;
96 
97 /* Paranoid check */
98 	if ( !card )
99 		return -ENODEV;
100 
101 	return 0;
102 }
103 
gemtek_pci_close(struct video_device * dev)104 static void gemtek_pci_close( struct video_device *dev )
105 {
106 /*
107  *  The module usage is managed by 'videodev'
108  */
109 }
110 
gemtek_pci_out(u16 value,u32 port)111 static inline u8 gemtek_pci_out( u16 value, u32 port )
112 {
113 	outw( value, port );
114 
115 	return (u8)value;
116 }
117 
118 #define _b0( v ) *((u8 *)&v)
__gemtek_pci_cmd(u16 value,u32 port,u8 * last_byte,int keep)119 static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep )
120 {
121 	register u8 byte = *last_byte;
122 
123 	if ( !value ) {
124 		if ( !keep )
125 			value = (u16)port;
126 		byte &= 0xfd;
127 	} else
128 		byte |= 2;
129 
130 	_b0( value ) = byte;
131 	outw( value, port );
132 	byte |= 1;
133 	_b0( value ) = byte;
134 	outw( value, port );
135 	byte &= 0xfe;
136 	_b0( value ) = byte;
137 	outw( value, port );
138 
139 	*last_byte = byte;
140 }
141 
gemtek_pci_nil(u32 port,u8 * last_byte)142 static inline void gemtek_pci_nil( u32 port, u8 *last_byte )
143 {
144 	__gemtek_pci_cmd( 0x00, port, last_byte, FALSE );
145 }
146 
gemtek_pci_cmd(u16 cmd,u32 port,u8 * last_byte)147 static inline void gemtek_pci_cmd( u16 cmd, u32 port, u8 *last_byte )
148 {
149 	__gemtek_pci_cmd( cmd, port, last_byte, TRUE );
150 }
151 
gemtek_pci_setfrequency(struct gemtek_pci_card * card,unsigned long frequency)152 static void gemtek_pci_setfrequency( struct gemtek_pci_card *card, unsigned long frequency )
153 {
154 	register int i;
155 	register u32 value = frequency / 200 + 856;
156 	register u16 mask = 0x8000;
157 	u8 last_byte;
158 	u32 port = card->iobase;
159 
160 	last_byte = gemtek_pci_out( 0x06, port );
161 
162 	i = 0;
163 	do {
164 		gemtek_pci_nil( port, &last_byte );
165 		i++;
166 	} while ( i < 9 );
167 
168 	i = 0;
169 	do {
170 		gemtek_pci_cmd( value & mask, port, &last_byte );
171 		mask >>= 1;
172 		i++;
173 	} while ( i < 16 );
174 
175 	outw( 0x10, port );
176 }
177 
178 
gemtek_pci_mute(struct gemtek_pci_card * card)179 static inline void gemtek_pci_mute( struct gemtek_pci_card *card )
180 {
181 	outb( 0x1f, card->iobase );
182 	card->mute = TRUE;
183 }
184 
gemtek_pci_unmute(struct gemtek_pci_card * card)185 static inline void gemtek_pci_unmute( struct gemtek_pci_card *card )
186 {
187 	if ( card->mute ) {
188 		gemtek_pci_setfrequency( card, card->current_frequency );
189 		card->mute = FALSE;
190 	}
191 }
192 
gemtek_pci_getsignal(struct gemtek_pci_card * card)193 static inline unsigned int gemtek_pci_getsignal( struct gemtek_pci_card *card )
194 {
195 	return ( inb( card->iobase ) & 0x08 ) ? 0 : 1;
196 }
197 
gemtek_pci_ioctl(struct video_device * dev,unsigned int cmd,void * arg)198 static int gemtek_pci_ioctl( struct video_device *dev, unsigned int cmd, void *arg)
199 {
200 	struct gemtek_pci_card *card = dev->priv;
201 
202 	switch ( cmd ) {
203 		case VIDIOCGCAP:
204 		{
205 			struct video_capability c;
206 
207 			c.type = VID_TYPE_TUNER;
208 			c.channels = 1;
209 			c.audios = 1;
210 			c.maxwidth = 0;
211 			c.maxheight = 0;
212 			c.minwidth = 0;
213 			c.minheight = 0;
214 			strcpy( c.name, "Gemtek PCI Radio" );
215 			if ( copy_to_user( arg, &c, sizeof( c ) ) )
216 				return -EFAULT;
217 
218 			return 0;
219 		}
220 
221 		case VIDIOCGTUNER:
222 		{
223 			struct video_tuner t;
224 			int signal;
225 
226 			if ( copy_from_user( &t, arg, sizeof( struct video_tuner ) ) )
227 				return -EFAULT;
228 
229 			if ( t.tuner )
230 				return -EINVAL;
231 
232 			signal = gemtek_pci_getsignal( card );
233 			t.rangelow = GEMTEK_PCI_RANGE_LOW;
234 			t.rangehigh = GEMTEK_PCI_RANGE_HIGH;
235 			t.flags = VIDEO_TUNER_LOW | (7 << signal) ;
236 			t.mode = VIDEO_MODE_AUTO;
237 			t.signal = 0xFFFF * signal;
238 			strcpy( t.name, "FM" );
239 
240 			if ( copy_to_user( arg, &t, sizeof( struct video_tuner ) ) )
241 				return -EFAULT;
242 
243 			return 0;
244 		}
245 
246 		case VIDIOCSTUNER:
247 		{
248 			struct video_tuner t;
249 
250 			if ( copy_from_user( &t, arg, sizeof( struct video_tuner ) ) )
251 				return -EFAULT;
252 
253 			if ( t.tuner )
254 				return -EINVAL;
255 
256 			return 0;
257 		}
258 
259 		case VIDIOCGFREQ:
260 			return put_user( card->current_frequency, (u32 *)arg );
261 
262 		case VIDIOCSFREQ:
263 		{
264 			u32 frequency;
265 
266 			if ( get_user( frequency, (u32 *)arg ) )
267 				return -EFAULT;
268 
269 			if ( (frequency < GEMTEK_PCI_RANGE_LOW) || (frequency > GEMTEK_PCI_RANGE_HIGH) )
270 				return -EINVAL;
271 
272 			gemtek_pci_setfrequency( card, frequency );
273 			card->current_frequency = frequency;
274 			card->mute = FALSE;
275 
276 			return 0;
277 		}
278 
279 		case VIDIOCGAUDIO:
280 		{
281 			struct video_audio a;
282 
283 			memset( &a, 0, sizeof( a ) );
284 			a.flags |= VIDEO_AUDIO_MUTABLE;
285 			a.volume = 1;
286 			a.step = 65535;
287                         a.mode = (1 << gemtek_pci_getsignal( card ));
288 			strcpy( a.name, "Radio" );
289 
290 			if ( copy_to_user( arg, &a, sizeof( struct video_audio ) ) )
291 				return -EFAULT;
292 
293 			return 0;
294 		}
295 
296 		case VIDIOCSAUDIO:
297 		{
298 			struct video_audio a;
299 
300 			if ( copy_from_user( &a, arg, sizeof( struct video_audio ) ) )
301 				return -EFAULT;
302 
303 			if ( a.audio )
304 				return -EINVAL;
305 
306 			if ( a.flags & VIDEO_AUDIO_MUTE )
307 				gemtek_pci_mute( card );
308 
309 			else
310 				gemtek_pci_unmute( card );
311 
312 			return 0;
313 		}
314 
315 		default:
316 			return -ENOIOCTLCMD;
317 	}
318 }
319 
320 enum {
321 	GEMTEK_PR103
322 };
323 
324 static char *card_names[] __devinitdata = {
325 	"GEMTEK_PR103"
326 };
327 
328 static struct pci_device_id gemtek_pci_id[] =
329 {
330 	{ PCI_VENDOR_ID_GEMTEK, PCI_DEVICE_ID_GEMTEK_PR103,
331 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, GEMTEK_PR103 },
332 	{ 0 }
333 };
334 
335 MODULE_DEVICE_TABLE( pci, gemtek_pci_id );
336 
337 static u8 mx = 1;
338 
339 static char gemtek_pci_videodev_name[] = "Gemtek PCI Radio";
340 
gemtek_pci_init_struct(struct video_device * dev)341 static inline void gemtek_pci_init_struct( struct video_device *dev )
342 {
343 	memset( dev, 0, sizeof( struct video_device ) );
344 	dev->owner = THIS_MODULE;
345 	strcpy( dev->name , gemtek_pci_videodev_name );
346 	dev->type = VID_TYPE_TUNER;
347 	dev->hardware = VID_HARDWARE_GEMTEK;
348 	dev->open = gemtek_pci_open;
349 	dev->close = gemtek_pci_close;
350 	dev->ioctl = gemtek_pci_ioctl;
351 }
352 
gemtek_pci_probe(struct pci_dev * pci_dev,const struct pci_device_id * pci_id)353 static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id )
354 {
355 	struct gemtek_pci_card *card;
356 	struct video_device *devradio;
357 
358 	if ( (card = kmalloc( sizeof( struct gemtek_pci_card ), GFP_KERNEL )) == NULL ) {
359 		printk( KERN_ERR "gemtek_pci: out of memory\n" );
360 		return -ENOMEM;
361 	}
362 	memset( card, 0, sizeof( struct gemtek_pci_card ) );
363 
364 	if ( pci_enable_device( pci_dev ) )
365 		goto err_pci;
366 
367 	card->iobase = pci_resource_start( pci_dev, 0 );
368 	card->length = pci_resource_len( pci_dev, 0 );
369 
370 	if ( request_region( card->iobase, card->length, card_names[pci_id->driver_data] ) == NULL ) {
371 		printk( KERN_ERR "gemtek_pci: i/o port already in use\n" );
372 		goto err_pci;
373 	}
374 
375 	pci_read_config_byte( pci_dev, PCI_REVISION_ID, &card->chiprev );
376 	pci_read_config_word( pci_dev, PCI_SUBSYSTEM_ID, &card->model );
377 
378 	pci_set_drvdata( pci_dev, card );
379 
380 	if ( (devradio = kmalloc( sizeof( struct video_device ), GFP_KERNEL )) == NULL ) {
381 		printk( KERN_ERR "gemtek_pci: out of memory\n" );
382 		goto err_video;
383 	}
384 	gemtek_pci_init_struct( devradio );
385 
386 	if ( video_register_device( devradio, VFL_TYPE_RADIO , nr_radio) == -1 ) {
387 		kfree( devradio );
388 		goto err_video;
389 	}
390 
391 	card->videodev = devradio;
392 	devradio->priv = card;
393 	gemtek_pci_mute( card );
394 
395 	printk( KERN_INFO "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n",
396 		card->chiprev, card->iobase, card->iobase + card->length - 1 );
397 
398 	return 0;
399 
400 err_video:
401 	release_region( card->iobase, card->length );
402 
403 err_pci:
404 	kfree( card );
405 	return -ENODEV;
406 }
407 
gemtek_pci_remove(struct pci_dev * pci_dev)408 static void __devexit gemtek_pci_remove( struct pci_dev *pci_dev )
409 {
410 	struct gemtek_pci_card *card = pci_get_drvdata( pci_dev );
411 
412 	video_unregister_device( card->videodev );
413 	kfree( card->videodev );
414 
415 	release_region( card->iobase, card->length );
416 
417 	if ( mx )
418 		gemtek_pci_mute( card );
419 
420 	kfree( card );
421 
422 	pci_set_drvdata( pci_dev, NULL );
423 }
424 
425 static struct pci_driver gemtek_pci_driver =
426 {
427     name:	"gemtek_pci",
428 id_table:	gemtek_pci_id,
429    probe:	gemtek_pci_probe,
430   remove:	__devexit_p(gemtek_pci_remove),
431 };
432 
gemtek_pci_init_module(void)433 static int __init gemtek_pci_init_module( void )
434 {
435 	return pci_module_init( &gemtek_pci_driver );
436 }
437 
gemtek_pci_cleanup_module(void)438 static void __exit gemtek_pci_cleanup_module( void )
439 {
440 	return pci_unregister_driver( &gemtek_pci_driver );
441 }
442 
443 MODULE_AUTHOR( "Vladimir Shebordaev <vshebordaev@mail.ru>" );
444 MODULE_DESCRIPTION( "The video4linux driver for the Gemtek PCI Radio Card" );
445 MODULE_LICENSE("GPL");
446 
447 MODULE_PARM( mx, "b" );
448 MODULE_PARM_DESC( mx, "single digit: 1 - turn off the turner upon module exit (default), 0 - do not" );
449 MODULE_PARM( nr_radio, "i");
450 MODULE_PARM_DESC( nr_radio, "video4linux device number to use");
451 
452 EXPORT_NO_SYMBOLS;
453 
454 module_init( gemtek_pci_init_module );
455 module_exit( gemtek_pci_cleanup_module );
456 
457