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