1 /* radio-aztech.c - Aztech radio card driver for Linux 2.2 2 * 3 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 4 * Adapted to support the Video for Linux API by 5 * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by: 6 * 7 * Quay Ly 8 * Donald Song 9 * Jason Lewis (jlewis@twilight.vtc.vsc.edu) 10 * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) 11 * William McGrath (wmcgrath@twilight.vtc.vsc.edu) 12 * 13 * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/ 14 * along with more information on the card itself. 15 * 16 * History: 17 * 1999-02-24 Russell Kroll <rkroll@exploits.org> 18 * Fine tuning/VIDEO_TUNER_LOW 19 * Range expanded to 87-108 MHz (from 87.9-107.8) 20 * 21 * Notable changes from the original source: 22 * - includes stripped down to the essentials 23 * - for loops used as delays replaced with udelay() 24 * - #defines removed, changed to static values 25 * - tuning structure changed - no more character arrays, other changes 26 */ 27 28 #include <linux/module.h> /* Modules */ 29 #include <linux/init.h> /* Initdata */ 30 #include <linux/ioport.h> /* request_region */ 31 #include <linux/delay.h> /* udelay */ 32 #include <asm/io.h> /* outb, outb_p */ 33 #include <asm/uaccess.h> /* copy to/from user */ 34 #include <linux/videodev2.h> /* kernel radio structs */ 35 #include <media/v4l2-common.h> 36 37 #include <linux/version.h> /* for KERNEL_VERSION MACRO */ 38 #define RADIO_VERSION KERNEL_VERSION(0,0,2) 39 40 static struct v4l2_queryctrl radio_qctrl[] = { 41 { 42 .id = V4L2_CID_AUDIO_MUTE, 43 .name = "Mute", 44 .minimum = 0, 45 .maximum = 1, 46 .default_value = 1, 47 .type = V4L2_CTRL_TYPE_BOOLEAN, 48 },{ 49 .id = V4L2_CID_AUDIO_VOLUME, 50 .name = "Volume", 51 .minimum = 0, 52 .maximum = 0xff, 53 .step = 1, 54 .default_value = 0xff, 55 .type = V4L2_CTRL_TYPE_INTEGER, 56 } 57 }; 58 59 /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ 60 61 #ifndef CONFIG_RADIO_AZTECH_PORT 62 #define CONFIG_RADIO_AZTECH_PORT -1 63 #endif 64 65 static int io = CONFIG_RADIO_AZTECH_PORT; 66 static int radio_nr = -1; 67 static int radio_wait_time = 1000; 68 static struct mutex lock; 69 70 struct az_device 71 { 72 int curvol; 73 unsigned long curfreq; 74 int stereo; 75 }; 76 77 static int volconvert(int level) 78 { 79 level>>=14; /* Map 16bits down to 2 bit */ 80 level&=3; 81 82 /* convert to card-friendly values */ 83 switch (level) 84 { 85 case 0: 86 return 0; 87 case 1: 88 return 1; 89 case 2: 90 return 4; 91 case 3: 92 return 5; 93 } 94 return 0; /* Quieten gcc */ 95 } 96 97 static void send_0_byte (struct az_device *dev) 98 { 99 udelay(radio_wait_time); 100 outb_p(2+volconvert(dev->curvol), io); 101 outb_p(64+2+volconvert(dev->curvol), io); 102 } 103 104 static void send_1_byte (struct az_device *dev) 105 { 106 udelay (radio_wait_time); 107 outb_p(128+2+volconvert(dev->curvol), io); 108 outb_p(128+64+2+volconvert(dev->curvol), io); 109 } 110 111 static int az_setvol(struct az_device *dev, int vol) 112 { 113 mutex_lock(&lock); 114 outb (volconvert(vol), io); 115 mutex_unlock(&lock); 116 return 0; 117 } 118 119 /* thanks to Michael Dwyer for giving me a dose of clues in 120 * the signal strength department.. 121 * 122 * This card has a stereo bit - bit 0 set = mono, not set = stereo 123 * It also has a "signal" bit - bit 1 set = bad signal, not set = good 124 * 125 */ 126 127 static int az_getsigstr(struct az_device *dev) 128 { 129 if (inb(io) & 2) /* bit set = no signal present */ 130 return 0; 131 return 1; /* signal present */ 132 } 133 134 static int az_getstereo(struct az_device *dev) 135 { 136 if (inb(io) & 1) /* bit set = mono */ 137 return 0; 138 return 1; /* stereo */ 139 } 140 141 static int az_setfreq(struct az_device *dev, unsigned long frequency) 142 { 143 int i; 144 145 frequency += 171200; /* Add 10.7 MHz IF */ 146 frequency /= 800; /* Convert to 50 kHz units */ 147 148 mutex_lock(&lock); 149 150 send_0_byte (dev); /* 0: LSB of frequency */ 151 152 for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ 153 if (frequency & (1 << i)) 154 send_1_byte (dev); 155 else 156 send_0_byte (dev); 157 158 send_0_byte (dev); /* 14: test bit - always 0 */ 159 send_0_byte (dev); /* 15: test bit - always 0 */ 160 send_0_byte (dev); /* 16: band data 0 - always 0 */ 161 if (dev->stereo) /* 17: stereo (1 to enable) */ 162 send_1_byte (dev); 163 else 164 send_0_byte (dev); 165 166 send_1_byte (dev); /* 18: band data 1 - unknown */ 167 send_0_byte (dev); /* 19: time base - always 0 */ 168 send_0_byte (dev); /* 20: spacing (0 = 25 kHz) */ 169 send_1_byte (dev); /* 21: spacing (1 = 25 kHz) */ 170 send_0_byte (dev); /* 22: spacing (0 = 25 kHz) */ 171 send_1_byte (dev); /* 23: AM/FM (FM = 1, always) */ 172 173 /* latch frequency */ 174 175 udelay (radio_wait_time); 176 outb_p(128+64+volconvert(dev->curvol), io); 177 178 mutex_unlock(&lock); 179 180 return 0; 181 } 182 183 static int vidioc_querycap (struct file *file, void *priv, 184 struct v4l2_capability *v) 185 { 186 strlcpy(v->driver, "radio-aztech", sizeof (v->driver)); 187 strlcpy(v->card, "Aztech Radio", sizeof (v->card)); 188 sprintf(v->bus_info,"ISA"); 189 v->version = RADIO_VERSION; 190 v->capabilities = V4L2_CAP_TUNER; 191 return 0; 192 } 193 194 static int vidioc_g_tuner (struct file *file, void *priv, 195 struct v4l2_tuner *v) 196 { 197 struct video_device *dev = video_devdata(file); 198 struct az_device *az = dev->priv; 199 200 if (v->index > 0) 201 return -EINVAL; 202 203 strcpy(v->name, "FM"); 204 v->type = V4L2_TUNER_RADIO; 205 206 v->rangelow=(87*16000); 207 v->rangehigh=(108*16000); 208 v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; 209 v->capability=V4L2_TUNER_CAP_LOW; 210 if(az_getstereo(az)) 211 v->audmode = V4L2_TUNER_MODE_STEREO; 212 else 213 v->audmode = V4L2_TUNER_MODE_MONO; 214 v->signal=0xFFFF*az_getsigstr(az); 215 216 return 0; 217 } 218 219 220 static int vidioc_s_tuner (struct file *file, void *priv, 221 struct v4l2_tuner *v) 222 { 223 if (v->index > 0) 224 return -EINVAL; 225 226 return 0; 227 } 228 229 static int vidioc_g_audio (struct file *file, void *priv, 230 struct v4l2_audio *a) 231 { 232 if (a->index > 1) 233 return -EINVAL; 234 235 strcpy(a->name, "Radio"); 236 a->capability = V4L2_AUDCAP_STEREO; 237 return 0; 238 } 239 240 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 241 { 242 *i = 0; 243 return 0; 244 } 245 246 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 247 { 248 if (i != 0) 249 return -EINVAL; 250 return 0; 251 } 252 253 254 static int vidioc_s_audio (struct file *file, void *priv, 255 struct v4l2_audio *a) 256 { 257 if (a->index != 0) 258 return -EINVAL; 259 260 return 0; 261 } 262 263 static int vidioc_s_frequency (struct file *file, void *priv, 264 struct v4l2_frequency *f) 265 { 266 struct video_device *dev = video_devdata(file); 267 struct az_device *az = dev->priv; 268 269 az->curfreq = f->frequency; 270 az_setfreq(az, az->curfreq); 271 return 0; 272 } 273 274 static int vidioc_g_frequency (struct file *file, void *priv, 275 struct v4l2_frequency *f) 276 { 277 struct video_device *dev = video_devdata(file); 278 struct az_device *az = dev->priv; 279 280 f->type = V4L2_TUNER_RADIO; 281 f->frequency = az->curfreq; 282 283 return 0; 284 } 285 286 static int vidioc_queryctrl (struct file *file, void *priv, 287 struct v4l2_queryctrl *qc) 288 { 289 int i; 290 291 for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 292 if (qc->id && qc->id == radio_qctrl[i].id) { 293 memcpy(qc, &(radio_qctrl[i]), 294 sizeof(*qc)); 295 return (0); 296 } 297 } 298 return -EINVAL; 299 } 300 301 static int vidioc_g_ctrl (struct file *file, void *priv, 302 struct v4l2_control *ctrl) 303 { 304 struct video_device *dev = video_devdata(file); 305 struct az_device *az = dev->priv; 306 307 switch (ctrl->id) { 308 case V4L2_CID_AUDIO_MUTE: 309 if (az->curvol==0) 310 ctrl->value=1; 311 else 312 ctrl->value=0; 313 return (0); 314 case V4L2_CID_AUDIO_VOLUME: 315 ctrl->value=az->curvol * 6554; 316 return (0); 317 } 318 return -EINVAL; 319 } 320 321 static int vidioc_s_ctrl (struct file *file, void *priv, 322 struct v4l2_control *ctrl) 323 { 324 struct video_device *dev = video_devdata(file); 325 struct az_device *az = dev->priv; 326 327 switch (ctrl->id) { 328 case V4L2_CID_AUDIO_MUTE: 329 if (ctrl->value) { 330 az_setvol(az,0); 331 } else { 332 az_setvol(az,az->curvol); 333 } 334 return (0); 335 case V4L2_CID_AUDIO_VOLUME: 336 az_setvol(az,ctrl->value); 337 return (0); 338 } 339 return -EINVAL; 340 } 341 342 static struct az_device aztech_unit; 343 344 static const struct file_operations aztech_fops = { 345 .owner = THIS_MODULE, 346 .open = video_exclusive_open, 347 .release = video_exclusive_release, 348 .ioctl = video_ioctl2, 349 .compat_ioctl = v4l_compat_ioctl32, 350 .llseek = no_llseek, 351 }; 352 353 static struct video_device aztech_radio= 354 { 355 .owner = THIS_MODULE, 356 .name = "Aztech radio", 357 .type = VID_TYPE_TUNER, 358 .fops = &aztech_fops, 359 .vidioc_querycap = vidioc_querycap, 360 .vidioc_g_tuner = vidioc_g_tuner, 361 .vidioc_s_tuner = vidioc_s_tuner, 362 .vidioc_g_audio = vidioc_g_audio, 363 .vidioc_s_audio = vidioc_s_audio, 364 .vidioc_g_input = vidioc_g_input, 365 .vidioc_s_input = vidioc_s_input, 366 .vidioc_g_frequency = vidioc_g_frequency, 367 .vidioc_s_frequency = vidioc_s_frequency, 368 .vidioc_queryctrl = vidioc_queryctrl, 369 .vidioc_g_ctrl = vidioc_g_ctrl, 370 .vidioc_s_ctrl = vidioc_s_ctrl, 371 }; 372 373 module_param_named(debug,aztech_radio.debug, int, 0644); 374 MODULE_PARM_DESC(debug,"activates debug info"); 375 376 static int __init aztech_init(void) 377 { 378 if(io==-1) 379 { 380 printk(KERN_ERR "You must set an I/O address with io=0x???\n"); 381 return -EINVAL; 382 } 383 384 if (!request_region(io, 2, "aztech")) 385 { 386 printk(KERN_ERR "aztech: port 0x%x already in use\n", io); 387 return -EBUSY; 388 } 389 390 mutex_init(&lock); 391 aztech_radio.priv=&aztech_unit; 392 393 if(video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr)==-1) 394 { 395 release_region(io,2); 396 return -EINVAL; 397 } 398 399 printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n"); 400 /* mute card - prevents noisy bootups */ 401 outb (0, io); 402 return 0; 403 } 404 405 MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); 406 MODULE_DESCRIPTION("A driver for the Aztech radio card."); 407 MODULE_LICENSE("GPL"); 408 409 module_param(io, int, 0); 410 module_param(radio_nr, int, 0); 411 MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)"); 412 413 static void __exit aztech_cleanup(void) 414 { 415 video_unregister_device(&aztech_radio); 416 release_region(io,2); 417 } 418 419 module_init(aztech_init); 420 module_exit(aztech_cleanup); 421