1/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi>
2 *
3 * GemTek hasn't released any specs on the card, so the protocol had to
4 * be reverse engineered with dosemu.
5 *
6 * Besides the protocol changes, this is mostly a copy of:
7 *
8 *    RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
9 *
10 *    Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
11 *    Converted to new API by Alan Cox <Alan.Cox@linux.org>
12 *    Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
13 *
14 * TODO: Allow for more than one of these foolish entities :-)
15 *
16 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
17 */
18
19#include <linux/module.h>	/* Modules 			*/
20#include <linux/init.h>		/* Initdata			*/
21#include <linux/ioport.h>	/* request_region		*/
22#include <linux/delay.h>	/* udelay			*/
23#include <asm/io.h>		/* outb, outb_p			*/
24#include <asm/uaccess.h>	/* copy to/from user		*/
25#include <linux/videodev2.h>	/* kernel radio structs		*/
26#include <media/v4l2-common.h>
27#include <linux/spinlock.h>
28
29#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
30#define RADIO_VERSION KERNEL_VERSION(0,0,2)
31
32static struct v4l2_queryctrl radio_qctrl[] = {
33	{
34		.id            = V4L2_CID_AUDIO_MUTE,
35		.name          = "Mute",
36		.minimum       = 0,
37		.maximum       = 1,
38		.default_value = 1,
39		.type          = V4L2_CTRL_TYPE_BOOLEAN,
40	},{
41		.id            = V4L2_CID_AUDIO_VOLUME,
42		.name          = "Volume",
43		.minimum       = 0,
44		.maximum       = 65535,
45		.step          = 65535,
46		.default_value = 0xff,
47		.type          = V4L2_CTRL_TYPE_INTEGER,
48	}
49};
50
51#ifndef CONFIG_RADIO_GEMTEK_PORT
52#define CONFIG_RADIO_GEMTEK_PORT -1
53#endif
54
55static int io = CONFIG_RADIO_GEMTEK_PORT;
56static int radio_nr = -1;
57static spinlock_t lock;
58
59struct gemtek_device
60{
61	int port;
62	unsigned long curfreq;
63	int muted;
64};
65
66
67/* local things */
68
69/* the correct way to mute the gemtek may be to write the last written
70 * frequency || 0x10, but just writing 0x10 once seems to do it as well
71 */
72static void gemtek_mute(struct gemtek_device *dev)
73{
74	if(dev->muted)
75		return;
76	spin_lock(&lock);
77	outb(0x10, io);
78	spin_unlock(&lock);
79	dev->muted = 1;
80}
81
82static void gemtek_unmute(struct gemtek_device *dev)
83{
84	if(dev->muted == 0)
85		return;
86	spin_lock(&lock);
87	outb(0x20, io);
88	spin_unlock(&lock);
89	dev->muted = 0;
90}
91
92static void zero(void)
93{
94	outb_p(0x04, io);
95	udelay(5);
96	outb_p(0x05, io);
97	udelay(5);
98}
99
100static void one(void)
101{
102	outb_p(0x06, io);
103	udelay(5);
104	outb_p(0x07, io);
105	udelay(5);
106}
107
108static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq)
109{
110	int i;
111
112/*        freq = 78.25*((float)freq/16000.0 + 10.52); */
113
114	freq /= 16;
115	freq += 10520;
116	freq *= 7825;
117	freq /= 100000;
118
119	spin_lock(&lock);
120
121	/* 2 start bits */
122	outb_p(0x03, io);
123	udelay(5);
124	outb_p(0x07, io);
125	udelay(5);
126
127	/* 28 frequency bits (lsb first) */
128	for (i = 0; i < 14; i++)
129		if (freq & (1 << i))
130			one();
131		else
132			zero();
133	/* 36 unknown bits */
134	for (i = 0; i < 11; i++)
135		zero();
136	one();
137	for (i = 0; i < 4; i++)
138		zero();
139	one();
140	zero();
141
142	/* 2 end bits */
143	outb_p(0x03, io);
144	udelay(5);
145	outb_p(0x07, io);
146	udelay(5);
147
148	spin_unlock(&lock);
149
150	return 0;
151}
152
153static int gemtek_getsigstr(struct gemtek_device *dev)
154{
155	spin_lock(&lock);
156	inb(io);
157	udelay(5);
158	spin_unlock(&lock);
159	if (inb(io) & 8)		/* bit set = no signal present */
160		return 0;
161	return 1;		/* signal present */
162}
163
164static int vidioc_querycap(struct file *file, void  *priv,
165					struct v4l2_capability *v)
166{
167	strlcpy(v->driver, "radio-gemtek", sizeof(v->driver));
168	strlcpy(v->card, "GemTek", sizeof(v->card));
169	sprintf(v->bus_info, "ISA");
170	v->version = RADIO_VERSION;
171	v->capabilities = V4L2_CAP_TUNER;
172	return 0;
173}
174
175static int vidioc_g_tuner(struct file *file, void *priv,
176					struct v4l2_tuner *v)
177{
178	struct video_device *dev = video_devdata(file);
179	struct gemtek_device *rt = dev->priv;
180
181	if (v->index > 0)
182		return -EINVAL;
183
184	strcpy(v->name, "FM");
185	v->type = V4L2_TUNER_RADIO;
186	v->rangelow = (87*16000);
187	v->rangehigh = (108*16000);
188	v->rxsubchans = V4L2_TUNER_SUB_MONO;
189	v->capability = V4L2_TUNER_CAP_LOW;
190	v->audmode = V4L2_TUNER_MODE_MONO;
191	v->signal = 0xffff*gemtek_getsigstr(rt);
192	return 0;
193}
194
195static int vidioc_s_tuner(struct file *file, void *priv,
196					struct v4l2_tuner *v)
197{
198	if (v->index > 0)
199		return -EINVAL;
200	return 0;
201}
202
203static int vidioc_s_frequency(struct file *file, void *priv,
204					struct v4l2_frequency *f)
205{
206	struct video_device *dev = video_devdata(file);
207	struct gemtek_device *rt = dev->priv;
208
209	rt->curfreq = f->frequency;
210	/* needs to be called twice in order for getsigstr to work */
211	gemtek_setfreq(rt, rt->curfreq);
212	gemtek_setfreq(rt, rt->curfreq);
213	return 0;
214}
215
216static int vidioc_g_frequency(struct file *file, void *priv,
217					struct v4l2_frequency *f)
218{
219	struct video_device *dev = video_devdata(file);
220	struct gemtek_device *rt = dev->priv;
221
222	f->type = V4L2_TUNER_RADIO;
223	f->frequency = rt->curfreq;
224	return 0;
225}
226
227static int vidioc_queryctrl(struct file *file, void *priv,
228					struct v4l2_queryctrl *qc)
229{
230	int i;
231
232	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
233		if (qc->id && qc->id == radio_qctrl[i].id) {
234			memcpy(qc, &(radio_qctrl[i]),
235						sizeof(*qc));
236			return 0;
237		}
238	}
239	return -EINVAL;
240}
241
242static int vidioc_g_ctrl(struct file *file, void *priv,
243					struct v4l2_control *ctrl)
244{
245	struct video_device *dev = video_devdata(file);
246	struct gemtek_device *rt = dev->priv;
247
248	switch (ctrl->id) {
249	case V4L2_CID_AUDIO_MUTE:
250		ctrl->value = rt->muted;
251		return 0;
252	case V4L2_CID_AUDIO_VOLUME:
253		if (rt->muted)
254			ctrl->value = 0;
255		else
256			ctrl->value = 65535;
257		return 0;
258	}
259	return -EINVAL;
260}
261
262static int vidioc_s_ctrl(struct file *file, void *priv,
263					struct v4l2_control *ctrl)
264{
265	struct video_device *dev = video_devdata(file);
266	struct gemtek_device *rt = dev->priv;
267
268	switch (ctrl->id) {
269	case V4L2_CID_AUDIO_MUTE:
270		if (ctrl->value)
271			gemtek_mute(rt);
272		else
273			gemtek_unmute(rt);
274		return 0;
275	case V4L2_CID_AUDIO_VOLUME:
276		if (ctrl->value)
277			gemtek_unmute(rt);
278		else
279			gemtek_mute(rt);
280		return 0;
281	}
282	return -EINVAL;
283}
284
285static int vidioc_g_audio (struct file *file, void *priv,
286					struct v4l2_audio *a)
287{
288	if (a->index > 1)
289		return -EINVAL;
290
291	strcpy(a->name, "Radio");
292	a->capability = V4L2_AUDCAP_STEREO;
293	return 0;
294}
295
296static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
297{
298	*i = 0;
299	return 0;
300}
301
302static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
303{
304	if (i != 0)
305		return -EINVAL;
306	return 0;
307}
308
309static int vidioc_s_audio(struct file *file, void *priv,
310					struct v4l2_audio *a)
311{
312	if (a->index != 0)
313		return -EINVAL;
314	return 0;
315}
316
317static struct gemtek_device gemtek_unit;
318
319static const struct file_operations gemtek_fops = {
320	.owner		= THIS_MODULE,
321	.open           = video_exclusive_open,
322	.release        = video_exclusive_release,
323	.ioctl		= video_ioctl2,
324	.compat_ioctl	= v4l_compat_ioctl32,
325	.llseek         = no_llseek,
326};
327
328static struct video_device gemtek_radio=
329{
330	.owner		= THIS_MODULE,
331	.name		= "GemTek radio",
332	.type		= VID_TYPE_TUNER,
333	.hardware	= 0,
334	.fops           = &gemtek_fops,
335	.vidioc_querycap    = vidioc_querycap,
336	.vidioc_g_tuner     = vidioc_g_tuner,
337	.vidioc_s_tuner     = vidioc_s_tuner,
338	.vidioc_g_audio     = vidioc_g_audio,
339	.vidioc_s_audio     = vidioc_s_audio,
340	.vidioc_g_input     = vidioc_g_input,
341	.vidioc_s_input     = vidioc_s_input,
342	.vidioc_g_frequency = vidioc_g_frequency,
343	.vidioc_s_frequency = vidioc_s_frequency,
344	.vidioc_queryctrl   = vidioc_queryctrl,
345	.vidioc_g_ctrl      = vidioc_g_ctrl,
346	.vidioc_s_ctrl      = vidioc_s_ctrl,
347};
348
349static int __init gemtek_init(void)
350{
351	if(io==-1)
352	{
353		printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (io=0x020c or io=0x248 for the combined sound/radiocard)\n");
354		return -EINVAL;
355	}
356
357	if (!request_region(io, 4, "gemtek"))
358	{
359		printk(KERN_ERR "gemtek: port 0x%x already in use\n", io);
360		return -EBUSY;
361	}
362
363	gemtek_radio.priv=&gemtek_unit;
364
365	if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1)
366	{
367		release_region(io, 4);
368		return -EINVAL;
369	}
370	printk(KERN_INFO "GemTek Radio Card driver.\n");
371
372	spin_lock_init(&lock);
373
374	/* this is _maybe_ unnecessary */
375	outb(0x01, io);
376
377	/* mute card - prevents noisy bootups */
378	gemtek_unit.muted = 0;
379	gemtek_mute(&gemtek_unit);
380
381	return 0;
382}
383
384MODULE_AUTHOR("Jonas Munsin");
385MODULE_DESCRIPTION("A driver for the GemTek Radio Card");
386MODULE_LICENSE("GPL");
387
388module_param(io, int, 0);
389MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (0x20c or 0x248 have been reported to work for the combined sound/radiocard)).");
390module_param(radio_nr, int, 0);
391
392static void __exit gemtek_cleanup(void)
393{
394	video_unregister_device(&gemtek_radio);
395	release_region(io,4);
396}
397
398module_init(gemtek_init);
399module_exit(gemtek_cleanup);
400
401/*
402  Local variables:
403  compile-command: "gcc -c -DMODVERSIONS -D__KERNEL__ -DMODULE -O6 -Wall -Wstrict-prototypes -I /home/blp/tmp/linux-2.1.111-rtrack/include radio-rtrack2.c"
404  End:
405*/
406