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