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