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 82 /* i2c addresses */ 83 #define TDA7318_ADDR 0x88 84 #define TSA6060T_ADDR 0xc4 85 86 #define TR_DELAY do { inb(io); inb(io); inb(io); } while(0) 87 #define TR_SET_SCL outb(ioval |= 2, io) 88 #define TR_CLR_SCL outb(ioval &= 0xfd, io) 89 #define TR_SET_SDA outb(ioval |= 1, io) 90 #define TR_CLR_SDA outb(ioval &= 0xfe, io) 91 92 static void write_i2c(int n, ...) 93 { 94 unsigned char val, mask; 95 va_list args; 96 97 va_start(args, n); 98 99 /* start condition */ 100 TR_SET_SDA; 101 TR_SET_SCL; 102 TR_DELAY; 103 TR_CLR_SDA; 104 TR_CLR_SCL; 105 TR_DELAY; 106 107 for(; n; n--) { 108 val = va_arg(args, unsigned); 109 for(mask = 0x80; mask; mask >>= 1) { 110 if(val & mask) 111 TR_SET_SDA; 112 else 113 TR_CLR_SDA; 114 TR_SET_SCL; 115 TR_DELAY; 116 TR_CLR_SCL; 117 TR_DELAY; 118 } 119 /* acknowledge bit */ 120 TR_SET_SDA; 121 TR_SET_SCL; 122 TR_DELAY; 123 TR_CLR_SCL; 124 TR_DELAY; 125 } 126 127 /* stop condition */ 128 TR_CLR_SDA; 129 TR_DELAY; 130 TR_SET_SCL; 131 TR_DELAY; 132 TR_SET_SDA; 133 TR_DELAY; 134 135 va_end(args); 136 } 137 138 static void tr_setvol(__u16 vol) 139 { 140 curvol = vol / 2048; 141 write_i2c(2, TDA7318_ADDR, curvol ^ 0x1f); 142 } 143 144 static int basstreble2chip[15] = { 145 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8 146 }; 147 148 static void tr_setbass(__u16 bass) 149 { 150 curbass = bass / 4370; 151 write_i2c(2, TDA7318_ADDR, 0x60 | basstreble2chip[curbass]); 152 } 153 154 static void tr_settreble(__u16 treble) 155 { 156 curtreble = treble / 4370; 157 write_i2c(2, TDA7318_ADDR, 0x70 | basstreble2chip[curtreble]); 158 } 159 160 static void tr_setstereo(int stereo) 161 { 162 curstereo = !!stereo; 163 ioval = (ioval & 0xfb) | (!curstereo << 2); 164 outb(ioval, io); 165 } 166 167 static void tr_setmute(int mute) 168 { 169 curmute = !!mute; 170 ioval = (ioval & 0xf7) | (curmute << 3); 171 outb(ioval, io); 172 } 173 174 static int tr_getsigstr(void) 175 { 176 int i, v; 177 178 for(i = 0, v = 0; i < 100; i++) v |= inb(io); 179 return (v & 1)? 0 : 0xffff; 180 } 181 182 static int tr_getstereo(void) 183 { 184 /* don't know how to determine it, just return the setting */ 185 return curstereo; 186 } 187 188 static void tr_setfreq(unsigned long f) 189 { 190 f /= 160; /* Convert to 10 kHz units */ 191 f += 1070; /* Add 10.7 MHz IF */ 192 193 write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0); 194 } 195 196 static int vidioc_querycap(struct file *file, void *priv, 197 struct v4l2_capability *v) 198 { 199 strlcpy(v->driver, "radio-trust", sizeof(v->driver)); 200 strlcpy(v->card, "Trust FM Radio", sizeof(v->card)); 201 sprintf(v->bus_info, "ISA"); 202 v->version = RADIO_VERSION; 203 v->capabilities = V4L2_CAP_TUNER; 204 return 0; 205 } 206 207 static int vidioc_g_tuner(struct file *file, void *priv, 208 struct v4l2_tuner *v) 209 { 210 if (v->index > 0) 211 return -EINVAL; 212 213 strcpy(v->name, "FM"); 214 v->type = V4L2_TUNER_RADIO; 215 v->rangelow = (87.5*16000); 216 v->rangehigh = (108*16000); 217 v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; 218 v->capability = V4L2_TUNER_CAP_LOW; 219 if (tr_getstereo()) 220 v->audmode = V4L2_TUNER_MODE_STEREO; 221 else 222 v->audmode = V4L2_TUNER_MODE_MONO; 223 v->signal = tr_getsigstr(); 224 return 0; 225 } 226 227 static int vidioc_s_tuner(struct file *file, void *priv, 228 struct v4l2_tuner *v) 229 { 230 if (v->index > 0) 231 return -EINVAL; 232 233 return 0; 234 } 235 236 static int vidioc_s_frequency(struct file *file, void *priv, 237 struct v4l2_frequency *f) 238 { 239 curfreq = f->frequency; 240 tr_setfreq(curfreq); 241 return 0; 242 } 243 244 static int vidioc_g_frequency(struct file *file, void *priv, 245 struct v4l2_frequency *f) 246 { 247 f->type = V4L2_TUNER_RADIO; 248 f->frequency = curfreq; 249 return 0; 250 } 251 252 static int vidioc_queryctrl(struct file *file, void *priv, 253 struct v4l2_queryctrl *qc) 254 { 255 int i; 256 257 for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 258 if (qc->id && qc->id == radio_qctrl[i].id) { 259 memcpy(qc, &(radio_qctrl[i]), 260 sizeof(*qc)); 261 return 0; 262 } 263 } 264 return -EINVAL; 265 } 266 267 static int vidioc_g_ctrl(struct file *file, void *priv, 268 struct v4l2_control *ctrl) 269 { 270 switch (ctrl->id) { 271 case V4L2_CID_AUDIO_MUTE: 272 ctrl->value = curmute; 273 return 0; 274 case V4L2_CID_AUDIO_VOLUME: 275 ctrl->value = curvol * 2048; 276 return 0; 277 case V4L2_CID_AUDIO_BASS: 278 ctrl->value = curbass * 4370; 279 return 0; 280 case V4L2_CID_AUDIO_TREBLE: 281 ctrl->value = curtreble * 4370; 282 return 0; 283 } 284 return -EINVAL; 285 } 286 287 static int vidioc_s_ctrl(struct file *file, void *priv, 288 struct v4l2_control *ctrl) 289 { 290 switch (ctrl->id) { 291 case V4L2_CID_AUDIO_MUTE: 292 tr_setmute(ctrl->value); 293 return 0; 294 case V4L2_CID_AUDIO_VOLUME: 295 tr_setvol(ctrl->value); 296 return 0; 297 case V4L2_CID_AUDIO_BASS: 298 tr_setbass(ctrl->value); 299 return 0; 300 case V4L2_CID_AUDIO_TREBLE: 301 tr_settreble(ctrl->value); 302 return 0; 303 } 304 return -EINVAL; 305 } 306 307 static int vidioc_g_audio(struct file *file, void *priv, 308 struct v4l2_audio *a) 309 { 310 if (a->index > 1) 311 return -EINVAL; 312 313 strcpy(a->name, "Radio"); 314 a->capability = V4L2_AUDCAP_STEREO; 315 return 0; 316 } 317 318 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 319 { 320 *i = 0; 321 return 0; 322 } 323 324 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 325 { 326 if (i != 0) 327 return -EINVAL; 328 return 0; 329 } 330 331 static int vidioc_s_audio(struct file *file, void *priv, 332 struct v4l2_audio *a) 333 { 334 if (a->index != 0) 335 return -EINVAL; 336 return 0; 337 } 338 339 static const struct file_operations trust_fops = { 340 .owner = THIS_MODULE, 341 .open = video_exclusive_open, 342 .release = video_exclusive_release, 343 .ioctl = video_ioctl2, 344 #ifdef CONFIG_COMPAT 345 .compat_ioctl = v4l_compat_ioctl32, 346 #endif 347 .llseek = no_llseek, 348 }; 349 350 static const struct v4l2_ioctl_ops trust_ioctl_ops = { 351 .vidioc_querycap = vidioc_querycap, 352 .vidioc_g_tuner = vidioc_g_tuner, 353 .vidioc_s_tuner = vidioc_s_tuner, 354 .vidioc_g_frequency = vidioc_g_frequency, 355 .vidioc_s_frequency = vidioc_s_frequency, 356 .vidioc_queryctrl = vidioc_queryctrl, 357 .vidioc_g_ctrl = vidioc_g_ctrl, 358 .vidioc_s_ctrl = vidioc_s_ctrl, 359 .vidioc_g_audio = vidioc_g_audio, 360 .vidioc_s_audio = vidioc_s_audio, 361 .vidioc_g_input = vidioc_g_input, 362 .vidioc_s_input = vidioc_s_input, 363 }; 364 365 static struct video_device trust_radio = { 366 .name = "Trust FM Radio", 367 .fops = &trust_fops, 368 .ioctl_ops = &trust_ioctl_ops, 369 }; 370 371 static int __init trust_init(void) 372 { 373 if(io == -1) { 374 printk(KERN_ERR "You must set an I/O address with io=0x???\n"); 375 return -EINVAL; 376 } 377 if(!request_region(io, 2, "Trust FM Radio")) { 378 printk(KERN_ERR "trust: port 0x%x already in use\n", io); 379 return -EBUSY; 380 } 381 if (video_register_device(&trust_radio, VFL_TYPE_RADIO, radio_nr) < 0) { 382 release_region(io, 2); 383 return -EINVAL; 384 } 385 386 printk(KERN_INFO "Trust FM Radio card driver v1.0.\n"); 387 388 write_i2c(2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */ 389 write_i2c(2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */ 390 write_i2c(2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */ 391 write_i2c(2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */ 392 write_i2c(2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */ 393 394 tr_setvol(0x8000); 395 tr_setbass(0x8000); 396 tr_settreble(0x8000); 397 tr_setstereo(1); 398 399 /* mute card - prevents noisy bootups */ 400 tr_setmute(1); 401 402 return 0; 403 } 404 405 MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); 406 MODULE_DESCRIPTION("A driver for the Trust FM Radio card."); 407 MODULE_LICENSE("GPL"); 408 409 module_param(io, int, 0); 410 MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)"); 411 module_param(radio_nr, int, 0); 412 413 static void __exit cleanup_trust_module(void) 414 { 415 video_unregister_device(&trust_radio); 416 release_region(io, 2); 417 } 418 419 module_init(trust_init); 420 module_exit(cleanup_trust_module); 421