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 * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/ 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/videodev.h> 25 #include <linux/config.h> /* CONFIG_RADIO_TRUST_PORT */ 26 27 /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ 28 29 #ifndef CONFIG_RADIO_TRUST_PORT 30 #define CONFIG_RADIO_TRUST_PORT -1 31 #endif 32 33 static int io = CONFIG_RADIO_TRUST_PORT; 34 static int radio_nr = -1; 35 static int ioval = 0xf; 36 static __u16 curvol; 37 static __u16 curbass; 38 static __u16 curtreble; 39 static unsigned long curfreq; 40 static int curstereo; 41 static int curmute; 42 43 /* i2c addresses */ 44 #define TDA7318_ADDR 0x88 45 #define TSA6060T_ADDR 0xc4 46 47 #define TR_DELAY do { inb(io); inb(io); inb(io); } while(0) 48 #define TR_SET_SCL outb(ioval |= 2, io) 49 #define TR_CLR_SCL outb(ioval &= 0xfd, io) 50 #define TR_SET_SDA outb(ioval |= 1, io) 51 #define TR_CLR_SDA outb(ioval &= 0xfe, io) 52 53 static void write_i2c(int n, ...) 54 { 55 unsigned char val, mask; 56 va_list args; 57 58 va_start(args, n); 59 60 /* start condition */ 61 TR_SET_SDA; 62 TR_SET_SCL; 63 TR_DELAY; 64 TR_CLR_SDA; 65 TR_CLR_SCL; 66 TR_DELAY; 67 68 for(; n; n--) { 69 val = va_arg(args, unsigned); 70 for(mask = 0x80; mask; mask >>= 1) { 71 if(val & mask) 72 TR_SET_SDA; 73 else 74 TR_CLR_SDA; 75 TR_SET_SCL; 76 TR_DELAY; 77 TR_CLR_SCL; 78 TR_DELAY; 79 } 80 /* acknowledge bit */ 81 TR_SET_SDA; 82 TR_SET_SCL; 83 TR_DELAY; 84 TR_CLR_SCL; 85 TR_DELAY; 86 } 87 88 /* stop condition */ 89 TR_CLR_SDA; 90 TR_DELAY; 91 TR_SET_SCL; 92 TR_DELAY; 93 TR_SET_SDA; 94 TR_DELAY; 95 96 va_end(args); 97 } 98 99 static void tr_setvol(__u16 vol) 100 { 101 curvol = vol / 2048; 102 write_i2c(2, TDA7318_ADDR, curvol ^ 0x1f); 103 } 104 105 static int basstreble2chip[15] = { 106 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8 107 }; 108 109 static void tr_setbass(__u16 bass) 110 { 111 curbass = bass / 4370; 112 write_i2c(2, TDA7318_ADDR, 0x60 | basstreble2chip[curbass]); 113 } 114 115 static void tr_settreble(__u16 treble) 116 { 117 curtreble = treble / 4370; 118 write_i2c(2, TDA7318_ADDR, 0x70 | basstreble2chip[curtreble]); 119 } 120 121 static void tr_setstereo(int stereo) 122 { 123 curstereo = !!stereo; 124 ioval = (ioval & 0xfb) | (!curstereo << 2); 125 outb(ioval, io); 126 } 127 128 static void tr_setmute(int mute) 129 { 130 curmute = !!mute; 131 ioval = (ioval & 0xf7) | (curmute << 3); 132 outb(ioval, io); 133 } 134 135 static int tr_getsigstr(void) 136 { 137 int i, v; 138 139 for(i = 0, v = 0; i < 100; i++) v |= inb(io); 140 return (v & 1)? 0 : 0xffff; 141 } 142 143 static int tr_getstereo(void) 144 { 145 /* don't know how to determine it, just return the setting */ 146 return curstereo; 147 } 148 149 static void tr_setfreq(unsigned long f) 150 { 151 f /= 160; /* Convert to 10 kHz units */ 152 f += 1070; /* Add 10.7 MHz IF */ 153 154 write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0); 155 } 156 157 static int tr_do_ioctl(struct inode *inode, struct file *file, 158 unsigned int cmd, void *arg) 159 { 160 switch(cmd) 161 { 162 case VIDIOCGCAP: 163 { 164 struct video_capability *v = arg; 165 166 memset(v,0,sizeof(*v)); 167 v->type=VID_TYPE_TUNER; 168 v->channels=1; 169 v->audios=1; 170 strcpy(v->name, "Trust FM Radio"); 171 172 return 0; 173 } 174 case VIDIOCGTUNER: 175 { 176 struct video_tuner *v = arg; 177 178 if(v->tuner) /* Only 1 tuner */ 179 return -EINVAL; 180 181 v->rangelow = 87500 * 16; 182 v->rangehigh = 108000 * 16; 183 v->flags = VIDEO_TUNER_LOW; 184 v->mode = VIDEO_MODE_AUTO; 185 186 v->signal = tr_getsigstr(); 187 if(tr_getstereo()) 188 v->flags |= VIDEO_TUNER_STEREO_ON; 189 190 strcpy(v->name, "FM"); 191 192 return 0; 193 } 194 case VIDIOCSTUNER: 195 { 196 struct video_tuner *v = arg; 197 if(v->tuner != 0) 198 return -EINVAL; 199 return 0; 200 } 201 case VIDIOCGFREQ: 202 { 203 unsigned long *freq = arg; 204 *freq = curfreq; 205 return 0; 206 } 207 case VIDIOCSFREQ: 208 { 209 unsigned long *freq = arg; 210 tr_setfreq(*freq); 211 return 0; 212 } 213 case VIDIOCGAUDIO: 214 { 215 struct video_audio *v = arg; 216 217 memset(v,0, sizeof(*v)); 218 v->flags = VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME | 219 VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE; 220 v->mode = curstereo? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO; 221 v->volume = curvol * 2048; 222 v->step = 2048; 223 v->bass = curbass * 4370; 224 v->treble = curtreble * 4370; 225 226 strcpy(v->name, "Trust FM Radio"); 227 return 0; 228 } 229 case VIDIOCSAUDIO: 230 { 231 struct video_audio *v = arg; 232 233 if(v->audio) 234 return -EINVAL; 235 tr_setvol(v->volume); 236 tr_setbass(v->bass); 237 tr_settreble(v->treble); 238 tr_setstereo(v->mode & VIDEO_SOUND_STEREO); 239 tr_setmute(v->flags & VIDEO_AUDIO_MUTE); 240 return 0; 241 } 242 default: 243 return -ENOIOCTLCMD; 244 } 245 } 246 247 static int tr_ioctl(struct inode *inode, struct file *file, 248 unsigned int cmd, unsigned long arg) 249 { 250 return video_usercopy(inode, file, cmd, arg, tr_do_ioctl); 251 } 252 253 static struct file_operations trust_fops = { 254 .owner = THIS_MODULE, 255 .open = video_exclusive_open, 256 .release = video_exclusive_release, 257 .ioctl = tr_ioctl, 258 .compat_ioctl = v4l_compat_ioctl32, 259 .llseek = no_llseek, 260 }; 261 262 static struct video_device trust_radio= 263 { 264 .owner = THIS_MODULE, 265 .name = "Trust FM Radio", 266 .type = VID_TYPE_TUNER, 267 .hardware = VID_HARDWARE_TRUST, 268 .fops = &trust_fops, 269 }; 270 271 static int __init trust_init(void) 272 { 273 if(io == -1) { 274 printk(KERN_ERR "You must set an I/O address with io=0x???\n"); 275 return -EINVAL; 276 } 277 if(!request_region(io, 2, "Trust FM Radio")) { 278 printk(KERN_ERR "trust: port 0x%x already in use\n", io); 279 return -EBUSY; 280 } 281 if(video_register_device(&trust_radio, VFL_TYPE_RADIO, radio_nr)==-1) 282 { 283 release_region(io, 2); 284 return -EINVAL; 285 } 286 287 printk(KERN_INFO "Trust FM Radio card driver v1.0.\n"); 288 289 write_i2c(2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */ 290 write_i2c(2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */ 291 write_i2c(2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */ 292 write_i2c(2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */ 293 write_i2c(2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */ 294 295 tr_setvol(0x8000); 296 tr_setbass(0x8000); 297 tr_settreble(0x8000); 298 tr_setstereo(1); 299 300 /* mute card - prevents noisy bootups */ 301 tr_setmute(1); 302 303 return 0; 304 } 305 306 MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); 307 MODULE_DESCRIPTION("A driver for the Trust FM Radio card."); 308 MODULE_LICENSE("GPL"); 309 310 module_param(io, int, 0); 311 MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)"); 312 module_param(radio_nr, int, 0); 313 314 static void __exit cleanup_trust_module(void) 315 { 316 video_unregister_device(&trust_radio); 317 release_region(io, 2); 318 } 319 320 module_init(trust_init); 321 module_exit(cleanup_trust_module); 322