1 /* radio-trust.c - Trust FM Radio card driver for Linux 2.2 2 * by Eric Lammerts <eric@scintilla.utwente.nl> 3 * 4 * Based on radio-aztech.c. Original notes: 5 * 6 * Adapted to support the Video for Linux API by 7 * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by: 8 * 9 * Quay Ly 10 * Donald Song 11 * Jason Lewis (jlewis@twilight.vtc.vsc.edu) 12 * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) 13 * William McGrath (wmcgrath@twilight.vtc.vsc.edu) 14 * 15 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 16 */ 17 18 #include <stdarg.h> 19 #include <linux/module.h> 20 #include <linux/init.h> 21 #include <linux/ioport.h> 22 #include <asm/io.h> 23 #include <asm/uaccess.h> 24 #include <linux/videodev2.h> 25 #include <media/v4l2-common.h> 26 #include <media/v4l2-ioctl.h> 27 28 #include <linux/version.h> /* for KERNEL_VERSION MACRO */ 29 #define RADIO_VERSION KERNEL_VERSION(0,0,2) 30 31 static struct v4l2_queryctrl radio_qctrl[] = { 32 { 33 .id = V4L2_CID_AUDIO_MUTE, 34 .name = "Mute", 35 .minimum = 0, 36 .maximum = 1, 37 .default_value = 1, 38 .type = V4L2_CTRL_TYPE_BOOLEAN, 39 },{ 40 .id = V4L2_CID_AUDIO_VOLUME, 41 .name = "Volume", 42 .minimum = 0, 43 .maximum = 65535, 44 .step = 2048, 45 .default_value = 65535, 46 .type = V4L2_CTRL_TYPE_INTEGER, 47 },{ 48 .id = V4L2_CID_AUDIO_BASS, 49 .name = "Bass", 50 .minimum = 0, 51 .maximum = 65535, 52 .step = 4370, 53 .default_value = 32768, 54 .type = V4L2_CTRL_TYPE_INTEGER, 55 },{ 56 .id = V4L2_CID_AUDIO_TREBLE, 57 .name = "Treble", 58 .minimum = 0, 59 .maximum = 65535, 60 .step = 4370, 61 .default_value = 32768, 62 .type = V4L2_CTRL_TYPE_INTEGER, 63 }, 64 }; 65 66 /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ 67 68 #ifndef CONFIG_RADIO_TRUST_PORT 69 #define CONFIG_RADIO_TRUST_PORT -1 70 #endif 71 72 static int io = CONFIG_RADIO_TRUST_PORT; 73 static int radio_nr = -1; 74 static int ioval = 0xf; 75 static __u16 curvol; 76 static __u16 curbass; 77 static __u16 curtreble; 78 static unsigned long curfreq; 79 static int curstereo; 80 static int curmute; 81 static unsigned long in_use; 82 83 /* i2c addresses */ 84 #define TDA7318_ADDR 0x88 85 #define TSA6060T_ADDR 0xc4 86 87 #define TR_DELAY do { inb(io); inb(io); inb(io); } while(0) 88 #define TR_SET_SCL outb(ioval |= 2, io) 89 #define TR_CLR_SCL outb(ioval &= 0xfd, io) 90 #define TR_SET_SDA outb(ioval |= 1, io) 91 #define TR_CLR_SDA outb(ioval &= 0xfe, io) 92 93 static void write_i2c(int n, ...) 94 { 95 unsigned char val, mask; 96 va_list args; 97 98 va_start(args, n); 99 100 /* start condition */ 101 TR_SET_SDA; 102 TR_SET_SCL; 103 TR_DELAY; 104 TR_CLR_SDA; 105 TR_CLR_SCL; 106 TR_DELAY; 107 108 for(; n; n--) { 109 val = va_arg(args, unsigned); 110 for(mask = 0x80; mask; mask >>= 1) { 111 if(val & mask) 112 TR_SET_SDA; 113 else 114 TR_CLR_SDA; 115 TR_SET_SCL; 116 TR_DELAY; 117 TR_CLR_SCL; 118 TR_DELAY; 119 } 120 /* acknowledge bit */ 121 TR_SET_SDA; 122 TR_SET_SCL; 123 TR_DELAY; 124 TR_CLR_SCL; 125 TR_DELAY; 126 } 127 128 /* stop condition */ 129 TR_CLR_SDA; 130 TR_DELAY; 131 TR_SET_SCL; 132 TR_DELAY; 133 TR_SET_SDA; 134 TR_DELAY; 135 136 va_end(args); 137 } 138 139 static void tr_setvol(__u16 vol) 140 { 141 curvol = vol / 2048; 142 write_i2c(2, TDA7318_ADDR, curvol ^ 0x1f); 143 } 144 145 static int basstreble2chip[15] = { 146 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8 147 }; 148 149 static void tr_setbass(__u16 bass) 150 { 151 curbass = bass / 4370; 152 write_i2c(2, TDA7318_ADDR, 0x60 | basstreble2chip[curbass]); 153 } 154 155 static void tr_settreble(__u16 treble) 156 { 157 curtreble = treble / 4370; 158 write_i2c(2, TDA7318_ADDR, 0x70 | basstreble2chip[curtreble]); 159 } 160 161 static void tr_setstereo(int stereo) 162 { 163 curstereo = !!stereo; 164 ioval = (ioval & 0xfb) | (!curstereo << 2); 165 outb(ioval, io); 166 } 167 168 static void tr_setmute(int mute) 169 { 170 curmute = !!mute; 171 ioval = (ioval & 0xf7) | (curmute << 3); 172 outb(ioval, io); 173 } 174 175 static int tr_getsigstr(void) 176 { 177 int i, v; 178 179 for(i = 0, v = 0; i < 100; i++) v |= inb(io); 180 return (v & 1)? 0 : 0xffff; 181 } 182 183 static int tr_getstereo(void) 184 { 185 /* don't know how to determine it, just return the setting */ 186 return curstereo; 187 } 188 189 static void tr_setfreq(unsigned long f) 190 { 191 f /= 160; /* Convert to 10 kHz units */ 192 f += 1070; /* Add 10.7 MHz IF */ 193 194 write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0); 195 } 196 197 static int vidioc_querycap(struct file *file, void *priv, 198 struct v4l2_capability *v) 199 { 200 strlcpy(v->driver, "radio-trust", sizeof(v->driver)); 201 strlcpy(v->card, "Trust FM Radio", sizeof(v->card)); 202 sprintf(v->bus_info, "ISA"); 203 v->version = RADIO_VERSION; 204 v->capabilities = V4L2_CAP_TUNER; 205 return 0; 206 } 207 208 static int vidioc_g_tuner(struct file *file, void *priv, 209 struct v4l2_tuner *v) 210 { 211 if (v->index > 0) 212 return -EINVAL; 213 214 strcpy(v->name, "FM"); 215 v->type = V4L2_TUNER_RADIO; 216 v->rangelow = (87.5*16000); 217 v->rangehigh = (108*16000); 218 v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; 219 v->capability = V4L2_TUNER_CAP_LOW; 220 if (tr_getstereo()) 221 v->audmode = V4L2_TUNER_MODE_STEREO; 222 else 223 v->audmode = V4L2_TUNER_MODE_MONO; 224 v->signal = tr_getsigstr(); 225 return 0; 226 } 227 228 static int vidioc_s_tuner(struct file *file, void *priv, 229 struct v4l2_tuner *v) 230 { 231 if (v->index > 0) 232 return -EINVAL; 233 234 return 0; 235 } 236 237 static int vidioc_s_frequency(struct file *file, void *priv, 238 struct v4l2_frequency *f) 239 { 240 curfreq = f->frequency; 241 tr_setfreq(curfreq); 242 return 0; 243 } 244 245 static int vidioc_g_frequency(struct file *file, void *priv, 246 struct v4l2_frequency *f) 247 { 248 f->type = V4L2_TUNER_RADIO; 249 f->frequency = curfreq; 250 return 0; 251 } 252 253 static int vidioc_queryctrl(struct file *file, void *priv, 254 struct v4l2_queryctrl *qc) 255 { 256 int i; 257 258 for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 259 if (qc->id && qc->id == radio_qctrl[i].id) { 260 memcpy(qc, &(radio_qctrl[i]), 261 sizeof(*qc)); 262 return 0; 263 } 264 } 265 return -EINVAL; 266 } 267 268 static int vidioc_g_ctrl(struct file *file, void *priv, 269 struct v4l2_control *ctrl) 270 { 271 switch (ctrl->id) { 272 case V4L2_CID_AUDIO_MUTE: 273 ctrl->value = curmute; 274 return 0; 275 case V4L2_CID_AUDIO_VOLUME: 276 ctrl->value = curvol * 2048; 277 return 0; 278 case V4L2_CID_AUDIO_BASS: 279 ctrl->value = curbass * 4370; 280 return 0; 281 case V4L2_CID_AUDIO_TREBLE: 282 ctrl->value = curtreble * 4370; 283 return 0; 284 } 285 return -EINVAL; 286 } 287 288 static int vidioc_s_ctrl(struct file *file, void *priv, 289 struct v4l2_control *ctrl) 290 { 291 switch (ctrl->id) { 292 case V4L2_CID_AUDIO_MUTE: 293 tr_setmute(ctrl->value); 294 return 0; 295 case V4L2_CID_AUDIO_VOLUME: 296 tr_setvol(ctrl->value); 297 return 0; 298 case V4L2_CID_AUDIO_BASS: 299 tr_setbass(ctrl->value); 300 return 0; 301 case V4L2_CID_AUDIO_TREBLE: 302 tr_settreble(ctrl->value); 303 return 0; 304 } 305 return -EINVAL; 306 } 307 308 static int vidioc_g_audio(struct file *file, void *priv, 309 struct v4l2_audio *a) 310 { 311 if (a->index > 1) 312 return -EINVAL; 313 314 strcpy(a->name, "Radio"); 315 a->capability = V4L2_AUDCAP_STEREO; 316 return 0; 317 } 318 319 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 320 { 321 *i = 0; 322 return 0; 323 } 324 325 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 326 { 327 if (i != 0) 328 return -EINVAL; 329 return 0; 330 } 331 332 static int vidioc_s_audio(struct file *file, void *priv, 333 struct v4l2_audio *a) 334 { 335 if (a->index != 0) 336 return -EINVAL; 337 return 0; 338 } 339 340 static int trust_exclusive_open(struct inode *inode, struct file *file) 341 { 342 return test_and_set_bit(0, &in_use) ? -EBUSY : 0; 343 } 344 345 static int trust_exclusive_release(struct inode *inode, struct file *file) 346 { 347 clear_bit(0, &in_use); 348 return 0; 349 } 350 351 static const struct file_operations trust_fops = { 352 .owner = THIS_MODULE, 353 .open = trust_exclusive_open, 354 .release = trust_exclusive_release, 355 .ioctl = video_ioctl2, 356 #ifdef CONFIG_COMPAT 357 .compat_ioctl = v4l_compat_ioctl32, 358 #endif 359 .llseek = no_llseek, 360 }; 361 362 static const struct v4l2_ioctl_ops trust_ioctl_ops = { 363 .vidioc_querycap = vidioc_querycap, 364 .vidioc_g_tuner = vidioc_g_tuner, 365 .vidioc_s_tuner = vidioc_s_tuner, 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 .vidioc_g_audio = vidioc_g_audio, 372 .vidioc_s_audio = vidioc_s_audio, 373 .vidioc_g_input = vidioc_g_input, 374 .vidioc_s_input = vidioc_s_input, 375 }; 376 377 static struct video_device trust_radio = { 378 .name = "Trust FM Radio", 379 .fops = &trust_fops, 380 .ioctl_ops = &trust_ioctl_ops, 381 .release = video_device_release_empty, 382 }; 383 384 static int __init trust_init(void) 385 { 386 if(io == -1) { 387 printk(KERN_ERR "You must set an I/O address with io=0x???\n"); 388 return -EINVAL; 389 } 390 if(!request_region(io, 2, "Trust FM Radio")) { 391 printk(KERN_ERR "trust: port 0x%x already in use\n", io); 392 return -EBUSY; 393 } 394 if (video_register_device(&trust_radio, VFL_TYPE_RADIO, radio_nr) < 0) { 395 release_region(io, 2); 396 return -EINVAL; 397 } 398 399 printk(KERN_INFO "Trust FM Radio card driver v1.0.\n"); 400 401 write_i2c(2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */ 402 write_i2c(2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */ 403 write_i2c(2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */ 404 write_i2c(2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */ 405 write_i2c(2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */ 406 407 tr_setvol(0x8000); 408 tr_setbass(0x8000); 409 tr_settreble(0x8000); 410 tr_setstereo(1); 411 412 /* mute card - prevents noisy bootups */ 413 tr_setmute(1); 414 415 return 0; 416 } 417 418 MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); 419 MODULE_DESCRIPTION("A driver for the Trust FM Radio card."); 420 MODULE_LICENSE("GPL"); 421 422 module_param(io, int, 0); 423 MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)"); 424 module_param(radio_nr, int, 0); 425 426 static void __exit cleanup_trust_module(void) 427 { 428 video_unregister_device(&trust_radio); 429 release_region(io, 2); 430 } 431 432 module_init(trust_init); 433 module_exit(cleanup_trust_module); 434