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