1 /* zoltrix radio plus driver for Linux radio support
2  * (c) 1998 C. van Schaik <carl@leg.uct.ac.za>
3  *
4  * BUGS
5  *  Due to the inconsistency in reading from the signal flags
6  *  it is difficult to get an accurate tuned signal.
7  *
8  *  It seems that the card is not linear to 0 volume. It cuts off
9  *  at a low volume, and it is not possible (at least I have not found)
10  *  to get fine volume control over the low volume range.
11  *
12  *  Some code derived from code by Romolo Manfredini
13  *				   romolo@bicnet.it
14  *
15  * 1999-05-06 - (C. van Schaik)
16  *	      - Make signal strength and stereo scans
17  *		kinder to cpu while in delay
18  * 1999-01-05 - (C. van Schaik)
19  *	      - Changed tuning to 1/160Mhz accuracy
20  *	      - Added stereo support
21  *		(card defaults to stereo)
22  *		(can explicitly force mono on the card)
23  *		(can detect if station is in stereo)
24  *	      - Added unmute function
25  *	      - Reworked ioctl functions
26  * 2002-07-15 - Fix Stereo typo
27  *
28  * 2006-07-24 - Converted to V4L2 API
29  *		by Mauro Carvalho Chehab <mchehab@infradead.org>
30  */
31 
32 #include <linux/module.h>	/* Modules                        */
33 #include <linux/init.h>		/* Initdata                       */
34 #include <linux/ioport.h>	/* request_region		  */
35 #include <linux/delay.h>	/* udelay, msleep                 */
36 #include <asm/io.h>		/* outb, outb_p                   */
37 #include <asm/uaccess.h>	/* copy to/from user              */
38 #include <linux/videodev2.h>	/* kernel radio structs           */
39 #include <media/v4l2-common.h>
40 
41 #include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
42 #define RADIO_VERSION KERNEL_VERSION(0,0,2)
43 
44 static struct v4l2_queryctrl radio_qctrl[] = {
45 	{
46 		.id            = V4L2_CID_AUDIO_MUTE,
47 		.name          = "Mute",
48 		.minimum       = 0,
49 		.maximum       = 1,
50 		.default_value = 1,
51 		.type          = V4L2_CTRL_TYPE_BOOLEAN,
52 	},{
53 		.id            = V4L2_CID_AUDIO_VOLUME,
54 		.name          = "Volume",
55 		.minimum       = 0,
56 		.maximum       = 65535,
57 		.step          = 4096,
58 		.default_value = 0xff,
59 		.type          = V4L2_CTRL_TYPE_INTEGER,
60 	}
61 };
62 
63 #ifndef CONFIG_RADIO_ZOLTRIX_PORT
64 #define CONFIG_RADIO_ZOLTRIX_PORT -1
65 #endif
66 
67 static int io = CONFIG_RADIO_ZOLTRIX_PORT;
68 static int radio_nr = -1;
69 
70 struct zol_device {
71 	int port;
72 	int curvol;
73 	unsigned long curfreq;
74 	int muted;
75 	unsigned int stereo;
76 	struct mutex lock;
77 };
78 
79 static int zol_setvol(struct zol_device *dev, int vol)
80 {
81 	dev->curvol = vol;
82 	if (dev->muted)
83 		return 0;
84 
85 	mutex_lock(&dev->lock);
86 	if (vol == 0) {
87 		outb(0, io);
88 		outb(0, io);
89 		inb(io + 3);    /* Zoltrix needs to be read to confirm */
90 		mutex_unlock(&dev->lock);
91 		return 0;
92 	}
93 
94 	outb(dev->curvol-1, io);
95 	msleep(10);
96 	inb(io + 2);
97 	mutex_unlock(&dev->lock);
98 	return 0;
99 }
100 
101 static void zol_mute(struct zol_device *dev)
102 {
103 	dev->muted = 1;
104 	mutex_lock(&dev->lock);
105 	outb(0, io);
106 	outb(0, io);
107 	inb(io + 3);            /* Zoltrix needs to be read to confirm */
108 	mutex_unlock(&dev->lock);
109 }
110 
111 static void zol_unmute(struct zol_device *dev)
112 {
113 	dev->muted = 0;
114 	zol_setvol(dev, dev->curvol);
115 }
116 
117 static int zol_setfreq(struct zol_device *dev, unsigned long freq)
118 {
119 	/* tunes the radio to the desired frequency */
120 	unsigned long long bitmask, f, m;
121 	unsigned int stereo = dev->stereo;
122 	int i;
123 
124 	if (freq == 0)
125 		return 1;
126 	m = (freq / 160 - 8800) * 2;
127 	f = (unsigned long long) m + 0x4d1c;
128 
129 	bitmask = 0xc480402c10080000ull;
130 	i = 45;
131 
132 	mutex_lock(&dev->lock);
133 
134 	outb(0, io);
135 	outb(0, io);
136 	inb(io + 3);            /* Zoltrix needs to be read to confirm */
137 
138 	outb(0x40, io);
139 	outb(0xc0, io);
140 
141 	bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ ( stereo << 31));
142 	while (i--) {
143 		if ((bitmask & 0x8000000000000000ull) != 0) {
144 			outb(0x80, io);
145 			udelay(50);
146 			outb(0x00, io);
147 			udelay(50);
148 			outb(0x80, io);
149 			udelay(50);
150 		} else {
151 			outb(0xc0, io);
152 			udelay(50);
153 			outb(0x40, io);
154 			udelay(50);
155 			outb(0xc0, io);
156 			udelay(50);
157 		}
158 		bitmask *= 2;
159 	}
160 	/* termination sequence */
161 	outb(0x80, io);
162 	outb(0xc0, io);
163 	outb(0x40, io);
164 	udelay(1000);
165 	inb(io+2);
166 
167 	udelay(1000);
168 
169 	if (dev->muted)
170 	{
171 		outb(0, io);
172 		outb(0, io);
173 		inb(io + 3);
174 		udelay(1000);
175 	}
176 
177 	mutex_unlock(&dev->lock);
178 
179 	if(!dev->muted)
180 	{
181 		zol_setvol(dev, dev->curvol);
182 	}
183 	return 0;
184 }
185 
186 /* Get signal strength */
187 
188 static int zol_getsigstr(struct zol_device *dev)
189 {
190 	int a, b;
191 
192 	mutex_lock(&dev->lock);
193 	outb(0x00, io);         /* This stuff I found to do nothing */
194 	outb(dev->curvol, io);
195 	msleep(20);
196 
197 	a = inb(io);
198 	msleep(10);
199 	b = inb(io);
200 
201 	mutex_unlock(&dev->lock);
202 
203 	if (a != b)
204 		return (0);
205 
206 	if ((a == 0xcf) || (a == 0xdf)  /* I found this out by playing */
207 		|| (a == 0xef))       /* with a binary scanner on the card io */
208 		return (1);
209 	return (0);
210 }
211 
212 static int zol_is_stereo (struct zol_device *dev)
213 {
214 	int x1, x2;
215 
216 	mutex_lock(&dev->lock);
217 
218 	outb(0x00, io);
219 	outb(dev->curvol, io);
220 	msleep(20);
221 
222 	x1 = inb(io);
223 	msleep(10);
224 	x2 = inb(io);
225 
226 	mutex_unlock(&dev->lock);
227 
228 	if ((x1 == x2) && (x1 == 0xcf))
229 		return 1;
230 	return 0;
231 }
232 
233 static int vidioc_querycap(struct file *file, void  *priv,
234 					struct v4l2_capability *v)
235 {
236 	strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver));
237 	strlcpy(v->card, "Zoltrix Radio", sizeof(v->card));
238 	sprintf(v->bus_info, "ISA");
239 	v->version = RADIO_VERSION;
240 	v->capabilities = V4L2_CAP_TUNER;
241 	return 0;
242 }
243 
244 static int vidioc_g_tuner(struct file *file, void *priv,
245 					struct v4l2_tuner *v)
246 {
247 	struct video_device *dev = video_devdata(file);
248 	struct zol_device *zol = dev->priv;
249 
250 	if (v->index > 0)
251 		return -EINVAL;
252 
253 	strcpy(v->name, "FM");
254 	v->type = V4L2_TUNER_RADIO;
255 	v->rangelow = (88*16000);
256 	v->rangehigh = (108*16000);
257 	v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
258 	v->capability = V4L2_TUNER_CAP_LOW;
259 	if (zol_is_stereo(zol))
260 		v->audmode = V4L2_TUNER_MODE_STEREO;
261 	else
262 		v->audmode = V4L2_TUNER_MODE_MONO;
263 	v->signal = 0xFFFF*zol_getsigstr(zol);
264 	return 0;
265 }
266 
267 static int vidioc_s_tuner(struct file *file, void *priv,
268 					struct v4l2_tuner *v)
269 {
270 	if (v->index > 0)
271 		return -EINVAL;
272 	return 0;
273 }
274 
275 static int vidioc_s_frequency(struct file *file, void *priv,
276 					struct v4l2_frequency *f)
277 {
278 	struct video_device *dev = video_devdata(file);
279 	struct zol_device *zol = dev->priv;
280 
281 	zol->curfreq = f->frequency;
282 	zol_setfreq(zol, zol->curfreq);
283 	return 0;
284 }
285 
286 static int vidioc_g_frequency(struct file *file, void *priv,
287 					struct v4l2_frequency *f)
288 {
289 	struct video_device *dev = video_devdata(file);
290 	struct zol_device *zol = dev->priv;
291 
292 	f->type = V4L2_TUNER_RADIO;
293 	f->frequency = zol->curfreq;
294 	return 0;
295 }
296 
297 static int vidioc_queryctrl(struct file *file, void *priv,
298 					struct v4l2_queryctrl *qc)
299 {
300 	int i;
301 
302 	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
303 		if (qc->id && qc->id == radio_qctrl[i].id) {
304 			memcpy(qc, &(radio_qctrl[i]),
305 						sizeof(*qc));
306 			return 0;
307 		}
308 	}
309 	return -EINVAL;
310 }
311 
312 static int vidioc_g_ctrl(struct file *file, void *priv,
313 				struct v4l2_control *ctrl)
314 {
315 	struct video_device *dev = video_devdata(file);
316 	struct zol_device *zol = dev->priv;
317 
318 	switch (ctrl->id) {
319 	case V4L2_CID_AUDIO_MUTE:
320 		ctrl->value = zol->muted;
321 		return 0;
322 	case V4L2_CID_AUDIO_VOLUME:
323 		ctrl->value = zol->curvol * 4096;
324 		return 0;
325 	}
326 	return -EINVAL;
327 }
328 
329 static int vidioc_s_ctrl(struct file *file, void *priv,
330 				struct v4l2_control *ctrl)
331 {
332 	struct video_device *dev = video_devdata(file);
333 	struct zol_device *zol = dev->priv;
334 
335 	switch (ctrl->id) {
336 	case V4L2_CID_AUDIO_MUTE:
337 		if (ctrl->value)
338 			zol_mute(zol);
339 		else {
340 			zol_unmute(zol);
341 			zol_setvol(zol,zol->curvol);
342 		}
343 		return 0;
344 	case V4L2_CID_AUDIO_VOLUME:
345 		zol_setvol(zol,ctrl->value/4096);
346 		return 0;
347 	}
348 	zol->stereo = 1;
349 	zol_setfreq(zol, zol->curfreq);
350 #if 0
351 /* FIXME: Implement stereo/mono switch on V4L2 */
352 			if (v->mode & VIDEO_SOUND_STEREO) {
353 				zol->stereo = 1;
354 				zol_setfreq(zol, zol->curfreq);
355 			}
356 			if (v->mode & VIDEO_SOUND_MONO) {
357 				zol->stereo = 0;
358 				zol_setfreq(zol, zol->curfreq);
359 			}
360 #endif
361 	return -EINVAL;
362 }
363 
364 static int vidioc_g_audio(struct file *file, void *priv,
365 					struct v4l2_audio *a)
366 {
367 	if (a->index > 1)
368 		return -EINVAL;
369 
370 	strcpy(a->name, "Radio");
371 	a->capability = V4L2_AUDCAP_STEREO;
372 	return 0;
373 }
374 
375 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
376 {
377 	*i = 0;
378 	return 0;
379 }
380 
381 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
382 {
383 	if (i != 0)
384 		return -EINVAL;
385 	return 0;
386 }
387 
388 static int vidioc_s_audio(struct file *file, void *priv,
389 					struct v4l2_audio *a)
390 {
391 	if (a->index != 0)
392 		return -EINVAL;
393 	return 0;
394 }
395 
396 static struct zol_device zoltrix_unit;
397 
398 static const struct file_operations zoltrix_fops =
399 {
400 	.owner		= THIS_MODULE,
401 	.open           = video_exclusive_open,
402 	.release        = video_exclusive_release,
403 	.ioctl		= video_ioctl2,
404 	.compat_ioctl	= v4l_compat_ioctl32,
405 	.llseek         = no_llseek,
406 };
407 
408 static struct video_device zoltrix_radio =
409 {
410 	.owner		= THIS_MODULE,
411 	.name		= "Zoltrix Radio Plus",
412 	.type		= VID_TYPE_TUNER,
413 	.fops           = &zoltrix_fops,
414 	.vidioc_querycap    = vidioc_querycap,
415 	.vidioc_g_tuner     = vidioc_g_tuner,
416 	.vidioc_s_tuner     = vidioc_s_tuner,
417 	.vidioc_g_audio     = vidioc_g_audio,
418 	.vidioc_s_audio     = vidioc_s_audio,
419 	.vidioc_g_input     = vidioc_g_input,
420 	.vidioc_s_input     = vidioc_s_input,
421 	.vidioc_g_frequency = vidioc_g_frequency,
422 	.vidioc_s_frequency = vidioc_s_frequency,
423 	.vidioc_queryctrl   = vidioc_queryctrl,
424 	.vidioc_g_ctrl      = vidioc_g_ctrl,
425 	.vidioc_s_ctrl      = vidioc_s_ctrl,
426 };
427 
428 static int __init zoltrix_init(void)
429 {
430 	if (io == -1) {
431 		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
432 		return -EINVAL;
433 	}
434 	if ((io != 0x20c) && (io != 0x30c)) {
435 		printk(KERN_ERR "zoltrix: invalid port, try 0x20c or 0x30c\n");
436 		return -ENXIO;
437 	}
438 
439 	zoltrix_radio.priv = &zoltrix_unit;
440 	if (!request_region(io, 2, "zoltrix")) {
441 		printk(KERN_ERR "zoltrix: port 0x%x already in use\n", io);
442 		return -EBUSY;
443 	}
444 
445 	if (video_register_device(&zoltrix_radio, VFL_TYPE_RADIO, radio_nr) == -1)
446 	{
447 		release_region(io, 2);
448 		return -EINVAL;
449 	}
450 	printk(KERN_INFO "Zoltrix Radio Plus card driver.\n");
451 
452 	mutex_init(&zoltrix_unit.lock);
453 
454 	/* mute card - prevents noisy bootups */
455 
456 	/* this ensures that the volume is all the way down  */
457 
458 	outb(0, io);
459 	outb(0, io);
460 	msleep(20);
461 	inb(io + 3);
462 
463 	zoltrix_unit.curvol = 0;
464 	zoltrix_unit.stereo = 1;
465 
466 	return 0;
467 }
468 
469 MODULE_AUTHOR("C.van Schaik");
470 MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
471 MODULE_LICENSE("GPL");
472 
473 module_param(io, int, 0);
474 MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)");
475 module_param(radio_nr, int, 0);
476 
477 static void __exit zoltrix_cleanup_module(void)
478 {
479 	video_unregister_device(&zoltrix_radio);
480 	release_region(io, 2);
481 }
482 
483 module_init(zoltrix_init);
484 module_exit(zoltrix_cleanup_module);
485 
486