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 *     Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
38 *
39 ***************************************************************************
40 */
41
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/videodev2.h>
48#include <media/v4l2-common.h>
49#include <linux/errno.h>
50
51#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
52#define RADIO_VERSION KERNEL_VERSION(0,0,2)
53
54static struct v4l2_queryctrl radio_qctrl[] = {
55	{
56		.id            = V4L2_CID_AUDIO_MUTE,
57		.name          = "Mute",
58		.minimum       = 0,
59		.maximum       = 1,
60		.default_value = 1,
61		.type          = V4L2_CTRL_TYPE_BOOLEAN,
62	},{
63		.id            = V4L2_CID_AUDIO_VOLUME,
64		.name          = "Volume",
65		.minimum       = 0,
66		.maximum       = 65535,
67		.step          = 65535,
68		.default_value = 0xff,
69		.type          = V4L2_CTRL_TYPE_INTEGER,
70	}
71};
72
73#include <asm/io.h>
74#include <asm/uaccess.h>
75
76#ifndef PCI_VENDOR_ID_GEMTEK
77#define PCI_VENDOR_ID_GEMTEK 0x5046
78#endif
79
80#ifndef PCI_DEVICE_ID_GEMTEK_PR103
81#define PCI_DEVICE_ID_GEMTEK_PR103 0x1001
82#endif
83
84#ifndef GEMTEK_PCI_RANGE_LOW
85#define GEMTEK_PCI_RANGE_LOW (87*16000)
86#endif
87
88#ifndef GEMTEK_PCI_RANGE_HIGH
89#define GEMTEK_PCI_RANGE_HIGH (108*16000)
90#endif
91
92struct gemtek_pci_card {
93	struct video_device *videodev;
94
95	u32 iobase;
96	u32 length;
97	u8  chiprev;
98	u16 model;
99
100	u32 current_frequency;
101	u8  mute;
102};
103
104static const char rcsid[] = "$Id: radio-gemtek-pci.c,v 1.1.1.1 2007-08-03 18:52:39 $";
105
106static int nr_radio = -1;
107
108static inline u8 gemtek_pci_out( u16 value, u32 port )
109{
110	outw( value, port );
111
112	return (u8)value;
113}
114
115#define _b0( v ) *((u8 *)&v)
116static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep )
117{
118	register u8 byte = *last_byte;
119
120	if ( !value ) {
121		if ( !keep )
122			value = (u16)port;
123		byte &= 0xfd;
124	} else
125		byte |= 2;
126
127	_b0( value ) = byte;
128	outw( value, port );
129	byte |= 1;
130	_b0( value ) = byte;
131	outw( value, port );
132	byte &= 0xfe;
133	_b0( value ) = byte;
134	outw( value, port );
135
136	*last_byte = byte;
137}
138
139static inline void gemtek_pci_nil( u32 port, u8 *last_byte )
140{
141	__gemtek_pci_cmd( 0x00, port, last_byte, false );
142}
143
144static inline void gemtek_pci_cmd( u16 cmd, u32 port, u8 *last_byte )
145{
146	__gemtek_pci_cmd( cmd, port, last_byte, true );
147}
148
149static void gemtek_pci_setfrequency( struct gemtek_pci_card *card, unsigned long frequency )
150{
151	register int i;
152	register u32 value = frequency / 200 + 856;
153	register u16 mask = 0x8000;
154	u8 last_byte;
155	u32 port = card->iobase;
156
157	last_byte = gemtek_pci_out( 0x06, port );
158
159	i = 0;
160	do {
161		gemtek_pci_nil( port, &last_byte );
162		i++;
163	} while ( i < 9 );
164
165	i = 0;
166	do {
167		gemtek_pci_cmd( value & mask, port, &last_byte );
168		mask >>= 1;
169		i++;
170	} while ( i < 16 );
171
172	outw( 0x10, port );
173}
174
175
176static inline void gemtek_pci_mute( struct gemtek_pci_card *card )
177{
178	outb( 0x1f, card->iobase );
179	card->mute = true;
180}
181
182static inline void gemtek_pci_unmute( struct gemtek_pci_card *card )
183{
184	if ( card->mute ) {
185		gemtek_pci_setfrequency( card, card->current_frequency );
186		card->mute = false;
187	}
188}
189
190static inline unsigned int gemtek_pci_getsignal( struct gemtek_pci_card *card )
191{
192	return ( inb( card->iobase ) & 0x08 ) ? 0 : 1;
193}
194
195static int vidioc_querycap(struct file *file, void *priv,
196					struct v4l2_capability *v)
197{
198	strlcpy(v->driver, "radio-gemtek-pci", sizeof(v->driver));
199	strlcpy(v->card, "GemTek PCI Radio", sizeof(v->card));
200	sprintf(v->bus_info, "ISA");
201	v->version = RADIO_VERSION;
202	v->capabilities = V4L2_CAP_TUNER;
203	return 0;
204}
205
206static int vidioc_g_tuner(struct file *file, void *priv,
207					struct v4l2_tuner *v)
208{
209	struct video_device *dev = video_devdata(file);
210	struct gemtek_pci_card *card = dev->priv;
211
212	if (v->index > 0)
213		return -EINVAL;
214
215	strcpy(v->name, "FM");
216	v->type = V4L2_TUNER_RADIO;
217	v->rangelow = GEMTEK_PCI_RANGE_LOW;
218	v->rangehigh = GEMTEK_PCI_RANGE_HIGH;
219	v->rxsubchans = V4L2_TUNER_SUB_MONO;
220	v->capability = V4L2_TUNER_CAP_LOW;
221	v->audmode = V4L2_TUNER_MODE_MONO;
222	v->signal = 0xffff * gemtek_pci_getsignal(card);
223	return 0;
224}
225
226static int vidioc_s_tuner(struct file *file, void *priv,
227					struct v4l2_tuner *v)
228{
229	if (v->index > 0)
230		return -EINVAL;
231	return 0;
232}
233
234static int vidioc_s_frequency(struct file *file, void *priv,
235					struct v4l2_frequency *f)
236{
237	struct video_device *dev = video_devdata(file);
238	struct gemtek_pci_card *card = dev->priv;
239
240	if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) ||
241			(f->frequency > GEMTEK_PCI_RANGE_HIGH) )
242		return -EINVAL;
243	gemtek_pci_setfrequency(card, f->frequency);
244	card->current_frequency = f->frequency;
245	card->mute = false;
246	return 0;
247}
248
249static int vidioc_g_frequency(struct file *file, void *priv,
250					struct v4l2_frequency *f)
251{
252	struct video_device *dev = video_devdata(file);
253	struct gemtek_pci_card *card = dev->priv;
254
255	f->type = V4L2_TUNER_RADIO;
256	f->frequency = card->current_frequency;
257	return 0;
258}
259
260static int vidioc_queryctrl(struct file *file, void *priv,
261					struct v4l2_queryctrl *qc)
262{
263	int i;
264	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
265		if (qc->id && qc->id == radio_qctrl[i].id) {
266			memcpy(qc, &(radio_qctrl[i]),
267						sizeof(*qc));
268			return 0;
269		}
270	}
271	return -EINVAL;
272}
273
274static int vidioc_g_ctrl(struct file *file, void *priv,
275					struct v4l2_control *ctrl)
276{
277	struct video_device *dev = video_devdata(file);
278	struct gemtek_pci_card *card = dev->priv;
279
280	switch (ctrl->id) {
281	case V4L2_CID_AUDIO_MUTE:
282		ctrl->value = card->mute;
283		return 0;
284	case V4L2_CID_AUDIO_VOLUME:
285		if (card->mute)
286			ctrl->value = 0;
287		else
288			ctrl->value = 65535;
289		return 0;
290	}
291	return -EINVAL;
292}
293
294static int vidioc_s_ctrl(struct file *file, void *priv,
295					struct v4l2_control *ctrl)
296{
297	struct video_device *dev = video_devdata(file);
298	struct gemtek_pci_card *card = dev->priv;
299
300	switch (ctrl->id) {
301	case V4L2_CID_AUDIO_MUTE:
302		if (ctrl->value)
303			gemtek_pci_mute(card);
304		else
305			gemtek_pci_unmute(card);
306		return 0;
307	case V4L2_CID_AUDIO_VOLUME:
308		if (ctrl->value)
309			gemtek_pci_unmute(card);
310		else
311			gemtek_pci_mute(card);
312		return 0;
313	}
314	return -EINVAL;
315}
316
317static int vidioc_g_audio(struct file *file, void *priv,
318					struct v4l2_audio *a)
319{
320	if (a->index > 1)
321		return -EINVAL;
322
323	strcpy(a->name, "Radio");
324	a->capability = V4L2_AUDCAP_STEREO;
325	return 0;
326}
327
328static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
329{
330	*i = 0;
331	return 0;
332}
333
334static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
335{
336	if (i != 0)
337		return -EINVAL;
338	return 0;
339}
340
341static int vidioc_s_audio(struct file *file, void *priv,
342					struct v4l2_audio *a)
343{
344	if (a->index != 0)
345		return -EINVAL;
346	return 0;
347}
348
349enum {
350	GEMTEK_PR103
351};
352
353static char *card_names[] __devinitdata = {
354	"GEMTEK_PR103"
355};
356
357static struct pci_device_id gemtek_pci_id[] =
358{
359	{ PCI_VENDOR_ID_GEMTEK, PCI_DEVICE_ID_GEMTEK_PR103,
360	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, GEMTEK_PR103 },
361	{ 0 }
362};
363
364MODULE_DEVICE_TABLE( pci, gemtek_pci_id );
365
366static int mx = 1;
367
368static const struct file_operations gemtek_pci_fops = {
369	.owner		= THIS_MODULE,
370	.open           = video_exclusive_open,
371	.release        = video_exclusive_release,
372	.ioctl		= video_ioctl2,
373	.compat_ioctl	= v4l_compat_ioctl32,
374	.llseek         = no_llseek,
375};
376
377static struct video_device vdev_template = {
378	.owner         = THIS_MODULE,
379	.name          = "Gemtek PCI Radio",
380	.type          = VID_TYPE_TUNER,
381	.hardware      = 0,
382	.fops          = &gemtek_pci_fops,
383	.vidioc_querycap    = vidioc_querycap,
384	.vidioc_g_tuner     = vidioc_g_tuner,
385	.vidioc_s_tuner     = vidioc_s_tuner,
386	.vidioc_g_audio     = vidioc_g_audio,
387	.vidioc_s_audio     = vidioc_s_audio,
388	.vidioc_g_input     = vidioc_g_input,
389	.vidioc_s_input     = vidioc_s_input,
390	.vidioc_g_frequency = vidioc_g_frequency,
391	.vidioc_s_frequency = vidioc_s_frequency,
392	.vidioc_queryctrl   = vidioc_queryctrl,
393	.vidioc_g_ctrl      = vidioc_g_ctrl,
394	.vidioc_s_ctrl      = vidioc_s_ctrl,
395};
396
397static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id )
398{
399	struct gemtek_pci_card *card;
400	struct video_device *devradio;
401
402	if ( (card = kzalloc( sizeof( struct gemtek_pci_card ), GFP_KERNEL )) == NULL ) {
403		printk( KERN_ERR "gemtek_pci: out of memory\n" );
404		return -ENOMEM;
405	}
406
407	if ( pci_enable_device( pci_dev ) )
408		goto err_pci;
409
410	card->iobase = pci_resource_start( pci_dev, 0 );
411	card->length = pci_resource_len( pci_dev, 0 );
412
413	if ( request_region( card->iobase, card->length, card_names[pci_id->driver_data] ) == NULL ) {
414		printk( KERN_ERR "gemtek_pci: i/o port already in use\n" );
415		goto err_pci;
416	}
417
418	pci_read_config_byte( pci_dev, PCI_REVISION_ID, &card->chiprev );
419	pci_read_config_word( pci_dev, PCI_SUBSYSTEM_ID, &card->model );
420
421	pci_set_drvdata( pci_dev, card );
422
423	if ( (devradio = kmalloc( sizeof( struct video_device ), GFP_KERNEL )) == NULL ) {
424		printk( KERN_ERR "gemtek_pci: out of memory\n" );
425		goto err_video;
426	}
427	*devradio = vdev_template;
428
429	if ( video_register_device( devradio, VFL_TYPE_RADIO , nr_radio) == -1 ) {
430		kfree( devradio );
431		goto err_video;
432	}
433
434	card->videodev = devradio;
435	devradio->priv = card;
436	gemtek_pci_mute( card );
437
438	printk( KERN_INFO "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n",
439		card->chiprev, card->iobase, card->iobase + card->length - 1 );
440
441	return 0;
442
443err_video:
444	release_region( card->iobase, card->length );
445
446err_pci:
447	kfree( card );
448	return -ENODEV;
449}
450
451static void __devexit gemtek_pci_remove( struct pci_dev *pci_dev )
452{
453	struct gemtek_pci_card *card = pci_get_drvdata( pci_dev );
454
455	video_unregister_device( card->videodev );
456	kfree( card->videodev );
457
458	release_region( card->iobase, card->length );
459
460	if ( mx )
461		gemtek_pci_mute( card );
462
463	kfree( card );
464
465	pci_set_drvdata( pci_dev, NULL );
466}
467
468static struct pci_driver gemtek_pci_driver =
469{
470	.name		= "gemtek_pci",
471	.id_table	= gemtek_pci_id,
472	.probe		= gemtek_pci_probe,
473	.remove		= __devexit_p(gemtek_pci_remove),
474};
475
476static int __init gemtek_pci_init_module( void )
477{
478	return pci_register_driver( &gemtek_pci_driver );
479}
480
481static void __exit gemtek_pci_cleanup_module( void )
482{
483	pci_unregister_driver(&gemtek_pci_driver);
484}
485
486MODULE_AUTHOR( "Vladimir Shebordaev <vshebordaev@mail.ru>" );
487MODULE_DESCRIPTION( "The video4linux driver for the Gemtek PCI Radio Card" );
488MODULE_LICENSE("GPL");
489
490module_param(mx, bool, 0);
491MODULE_PARM_DESC( mx, "single digit: 1 - turn off the turner upon module exit (default), 0 - do not" );
492module_param(nr_radio, int, 0);
493MODULE_PARM_DESC( nr_radio, "video4linux device number to use");
494
495module_init( gemtek_pci_init_module );
496module_exit( gemtek_pci_cleanup_module );
497