1 /* SF16FMI radio driver for Linux radio support 2 * heavily based on rtrack driver... 3 * (c) 1997 M. Kirkwood 4 * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz 5 * 6 * Fitted to new interface by Alan Cox <alan.cox@linux.org> 7 * Made working and cleaned up functions <mikael.hedin@irf.se> 8 * Support for ISAPnP by Ladislav Michl <ladis@psi.cz> 9 * 10 * Notes on the hardware 11 * 12 * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); 13 * No volume control - only mute/unmute - you have to use line volume 14 * control on SB-part of SF16FMI 15 * 16 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 17 */ 18 19 #include <linux/version.h> 20 #include <linux/kernel.h> /* __setup */ 21 #include <linux/module.h> /* Modules */ 22 #include <linux/init.h> /* Initdata */ 23 #include <linux/ioport.h> /* request_region */ 24 #include <linux/delay.h> /* udelay */ 25 #include <linux/videodev2.h> /* kernel radio structs */ 26 #include <media/v4l2-common.h> 27 #include <media/v4l2-ioctl.h> 28 #include <linux/isapnp.h> 29 #include <asm/io.h> /* outb, outb_p */ 30 #include <asm/uaccess.h> /* copy to/from user */ 31 #include <linux/mutex.h> 32 33 #define RADIO_VERSION KERNEL_VERSION(0,0,2) 34 35 static struct v4l2_queryctrl radio_qctrl[] = { 36 { 37 .id = V4L2_CID_AUDIO_MUTE, 38 .name = "Mute", 39 .minimum = 0, 40 .maximum = 1, 41 .default_value = 1, 42 .type = V4L2_CTRL_TYPE_BOOLEAN, 43 } 44 }; 45 46 struct fmi_device 47 { 48 unsigned long in_use; 49 int port; 50 int curvol; /* 1 or 0 */ 51 unsigned long curfreq; /* freq in kHz */ 52 __u32 flags; 53 }; 54 55 static int io = -1; 56 static int radio_nr = -1; 57 static struct pnp_dev *dev = NULL; 58 static struct mutex lock; 59 60 /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */ 61 /* It is only useful to give freq in intervall of 800 (=0.05Mhz), 62 * other bits will be truncated, e.g 92.7400016 -> 92.7, but 63 * 92.7400017 -> 92.75 64 */ 65 #define RSF16_ENCODE(x) ((x)/800+214) 66 #define RSF16_MINFREQ 87*16000 67 #define RSF16_MAXFREQ 108*16000 68 69 static void outbits(int bits, unsigned int data, int port) 70 { 71 while(bits--) { 72 if(data & 1) { 73 outb(5, port); 74 udelay(6); 75 outb(7, port); 76 udelay(6); 77 } else { 78 outb(1, port); 79 udelay(6); 80 outb(3, port); 81 udelay(6); 82 } 83 data>>=1; 84 } 85 } 86 87 static inline void fmi_mute(int port) 88 { 89 mutex_lock(&lock); 90 outb(0x00, port); 91 mutex_unlock(&lock); 92 } 93 94 static inline void fmi_unmute(int port) 95 { 96 mutex_lock(&lock); 97 outb(0x08, port); 98 mutex_unlock(&lock); 99 } 100 101 static inline int fmi_setfreq(struct fmi_device *dev) 102 { 103 int myport = dev->port; 104 unsigned long freq = dev->curfreq; 105 106 mutex_lock(&lock); 107 108 outbits(16, RSF16_ENCODE(freq), myport); 109 outbits(8, 0xC0, myport); 110 msleep(143); /* was schedule_timeout(HZ/7) */ 111 mutex_unlock(&lock); 112 if (dev->curvol) fmi_unmute(myport); 113 return 0; 114 } 115 116 static inline int fmi_getsigstr(struct fmi_device *dev) 117 { 118 int val; 119 int res; 120 int myport = dev->port; 121 122 123 mutex_lock(&lock); 124 val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */ 125 outb(val, myport); 126 outb(val | 0x10, myport); 127 msleep(143); /* was schedule_timeout(HZ/7) */ 128 res = (int)inb(myport+1); 129 outb(val, myport); 130 131 mutex_unlock(&lock); 132 return (res & 2) ? 0 : 0xFFFF; 133 } 134 135 static int vidioc_querycap(struct file *file, void *priv, 136 struct v4l2_capability *v) 137 { 138 strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver)); 139 strlcpy(v->card, "SF16-FMx radio", sizeof(v->card)); 140 sprintf(v->bus_info, "ISA"); 141 v->version = RADIO_VERSION; 142 v->capabilities = V4L2_CAP_TUNER; 143 return 0; 144 } 145 146 static int vidioc_g_tuner(struct file *file, void *priv, 147 struct v4l2_tuner *v) 148 { 149 int mult; 150 struct fmi_device *fmi = video_drvdata(file); 151 152 if (v->index > 0) 153 return -EINVAL; 154 155 strcpy(v->name, "FM"); 156 v->type = V4L2_TUNER_RADIO; 157 mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; 158 v->rangelow = RSF16_MINFREQ/mult; 159 v->rangehigh = RSF16_MAXFREQ/mult; 160 v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; 161 v->capability = fmi->flags&V4L2_TUNER_CAP_LOW; 162 v->audmode = V4L2_TUNER_MODE_STEREO; 163 v->signal = fmi_getsigstr(fmi); 164 return 0; 165 } 166 167 static int vidioc_s_tuner(struct file *file, void *priv, 168 struct v4l2_tuner *v) 169 { 170 if (v->index > 0) 171 return -EINVAL; 172 return 0; 173 } 174 175 static int vidioc_s_frequency(struct file *file, void *priv, 176 struct v4l2_frequency *f) 177 { 178 struct fmi_device *fmi = video_drvdata(file); 179 180 if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) 181 f->frequency *= 1000; 182 if (f->frequency < RSF16_MINFREQ || 183 f->frequency > RSF16_MAXFREQ ) 184 return -EINVAL; 185 /*rounding in steps of 800 to match th freq 186 that will be used */ 187 fmi->curfreq = (f->frequency/800)*800; 188 fmi_setfreq(fmi); 189 return 0; 190 } 191 192 static int vidioc_g_frequency(struct file *file, void *priv, 193 struct v4l2_frequency *f) 194 { 195 struct fmi_device *fmi = video_drvdata(file); 196 197 f->type = V4L2_TUNER_RADIO; 198 f->frequency = fmi->curfreq; 199 if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) 200 f->frequency /= 1000; 201 return 0; 202 } 203 204 static int vidioc_queryctrl(struct file *file, void *priv, 205 struct v4l2_queryctrl *qc) 206 { 207 int i; 208 209 for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 210 if (qc->id && qc->id == radio_qctrl[i].id) { 211 memcpy(qc, &(radio_qctrl[i]), 212 sizeof(*qc)); 213 return 0; 214 } 215 } 216 return -EINVAL; 217 } 218 219 static int vidioc_g_ctrl(struct file *file, void *priv, 220 struct v4l2_control *ctrl) 221 { 222 struct fmi_device *fmi = video_drvdata(file); 223 224 switch (ctrl->id) { 225 case V4L2_CID_AUDIO_MUTE: 226 ctrl->value = fmi->curvol; 227 return 0; 228 } 229 return -EINVAL; 230 } 231 232 static int vidioc_s_ctrl(struct file *file, void *priv, 233 struct v4l2_control *ctrl) 234 { 235 struct fmi_device *fmi = video_drvdata(file); 236 237 switch (ctrl->id) { 238 case V4L2_CID_AUDIO_MUTE: 239 if (ctrl->value) 240 fmi_mute(fmi->port); 241 else 242 fmi_unmute(fmi->port); 243 fmi->curvol = ctrl->value; 244 return 0; 245 } 246 return -EINVAL; 247 } 248 249 static int vidioc_g_audio(struct file *file, void *priv, 250 struct v4l2_audio *a) 251 { 252 if (a->index > 1) 253 return -EINVAL; 254 255 strcpy(a->name, "Radio"); 256 a->capability = V4L2_AUDCAP_STEREO; 257 return 0; 258 } 259 260 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 261 { 262 *i = 0; 263 return 0; 264 } 265 266 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 267 { 268 if (i != 0) 269 return -EINVAL; 270 return 0; 271 } 272 273 static int vidioc_s_audio(struct file *file, void *priv, 274 struct v4l2_audio *a) 275 { 276 if (a->index != 0) 277 return -EINVAL; 278 return 0; 279 } 280 281 static struct fmi_device fmi_unit; 282 283 static int fmi_exclusive_open(struct inode *inode, struct file *file) 284 { 285 return test_and_set_bit(0, &fmi_unit.in_use) ? -EBUSY : 0; 286 } 287 288 static int fmi_exclusive_release(struct inode *inode, struct file *file) 289 { 290 clear_bit(0, &fmi_unit.in_use); 291 return 0; 292 } 293 294 static const struct file_operations fmi_fops = { 295 .owner = THIS_MODULE, 296 .open = fmi_exclusive_open, 297 .release = fmi_exclusive_release, 298 .ioctl = video_ioctl2, 299 #ifdef CONFIG_COMPAT 300 .compat_ioctl = v4l_compat_ioctl32, 301 #endif 302 .llseek = no_llseek, 303 }; 304 305 static const struct v4l2_ioctl_ops fmi_ioctl_ops = { 306 .vidioc_querycap = vidioc_querycap, 307 .vidioc_g_tuner = vidioc_g_tuner, 308 .vidioc_s_tuner = vidioc_s_tuner, 309 .vidioc_g_audio = vidioc_g_audio, 310 .vidioc_s_audio = vidioc_s_audio, 311 .vidioc_g_input = vidioc_g_input, 312 .vidioc_s_input = vidioc_s_input, 313 .vidioc_g_frequency = vidioc_g_frequency, 314 .vidioc_s_frequency = vidioc_s_frequency, 315 .vidioc_queryctrl = vidioc_queryctrl, 316 .vidioc_g_ctrl = vidioc_g_ctrl, 317 .vidioc_s_ctrl = vidioc_s_ctrl, 318 }; 319 320 static struct video_device fmi_radio = { 321 .name = "SF16FMx radio", 322 .fops = &fmi_fops, 323 .ioctl_ops = &fmi_ioctl_ops, 324 .release = video_device_release_empty, 325 }; 326 327 /* ladis: this is my card. does any other types exist? */ 328 static struct isapnp_device_id id_table[] __devinitdata = { 329 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, 330 ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0}, 331 { ISAPNP_CARD_END, }, 332 }; 333 334 MODULE_DEVICE_TABLE(isapnp, id_table); 335 336 static int __init isapnp_fmi_probe(void) 337 { 338 int i = 0; 339 340 while (id_table[i].card_vendor != 0 && dev == NULL) { 341 dev = pnp_find_dev(NULL, id_table[i].vendor, 342 id_table[i].function, NULL); 343 i++; 344 } 345 346 if (!dev) 347 return -ENODEV; 348 if (pnp_device_attach(dev) < 0) 349 return -EAGAIN; 350 if (pnp_activate_dev(dev) < 0) { 351 printk ("radio-sf16fmi: PnP configure failed (out of resources?)\n"); 352 pnp_device_detach(dev); 353 return -ENOMEM; 354 } 355 if (!pnp_port_valid(dev, 0)) { 356 pnp_device_detach(dev); 357 return -ENODEV; 358 } 359 360 i = pnp_port_start(dev, 0); 361 printk ("radio-sf16fmi: PnP reports card at %#x\n", i); 362 363 return i; 364 } 365 366 static int __init fmi_init(void) 367 { 368 if (io < 0) 369 io = isapnp_fmi_probe(); 370 if (io < 0) { 371 printk(KERN_ERR "radio-sf16fmi: No PnP card found.\n"); 372 return io; 373 } 374 if (!request_region(io, 2, "radio-sf16fmi")) { 375 printk(KERN_ERR "radio-sf16fmi: port 0x%x already in use\n", io); 376 pnp_device_detach(dev); 377 return -EBUSY; 378 } 379 380 fmi_unit.port = io; 381 fmi_unit.curvol = 0; 382 fmi_unit.curfreq = 0; 383 fmi_unit.flags = V4L2_TUNER_CAP_LOW; 384 video_set_drvdata(&fmi_radio, &fmi_unit); 385 386 mutex_init(&lock); 387 388 if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) < 0) { 389 release_region(io, 2); 390 return -EINVAL; 391 } 392 393 printk(KERN_INFO "SF16FMx radio card driver at 0x%x\n", io); 394 /* mute card - prevents noisy bootups */ 395 fmi_mute(io); 396 return 0; 397 } 398 399 MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood"); 400 MODULE_DESCRIPTION("A driver for the SF16MI radio."); 401 MODULE_LICENSE("GPL"); 402 403 module_param(io, int, 0); 404 MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)"); 405 module_param(radio_nr, int, 0); 406 407 static void __exit fmi_cleanup_module(void) 408 { 409 video_unregister_device(&fmi_radio); 410 release_region(io, 2); 411 if (dev) 412 pnp_device_detach(dev); 413 } 414 415 module_init(fmi_init); 416 module_exit(fmi_cleanup_module); 417