diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/radio/radio-trust.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/media/radio/radio-trust.c')
-rw-r--r-- | drivers/media/radio/radio-trust.c | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c new file mode 100644 index 00000000000..b300bedf7c7 --- /dev/null +++ b/drivers/media/radio/radio-trust.c | |||
@@ -0,0 +1,320 @@ | |||
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 | .llseek = no_llseek, | ||
259 | }; | ||
260 | |||
261 | static struct video_device trust_radio= | ||
262 | { | ||
263 | .owner = THIS_MODULE, | ||
264 | .name = "Trust FM Radio", | ||
265 | .type = VID_TYPE_TUNER, | ||
266 | .hardware = VID_HARDWARE_TRUST, | ||
267 | .fops = &trust_fops, | ||
268 | }; | ||
269 | |||
270 | static int __init trust_init(void) | ||
271 | { | ||
272 | if(io == -1) { | ||
273 | printk(KERN_ERR "You must set an I/O address with io=0x???\n"); | ||
274 | return -EINVAL; | ||
275 | } | ||
276 | if(!request_region(io, 2, "Trust FM Radio")) { | ||
277 | printk(KERN_ERR "trust: port 0x%x already in use\n", io); | ||
278 | return -EBUSY; | ||
279 | } | ||
280 | if(video_register_device(&trust_radio, VFL_TYPE_RADIO, radio_nr)==-1) | ||
281 | { | ||
282 | release_region(io, 2); | ||
283 | return -EINVAL; | ||
284 | } | ||
285 | |||
286 | printk(KERN_INFO "Trust FM Radio card driver v1.0.\n"); | ||
287 | |||
288 | write_i2c(2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */ | ||
289 | write_i2c(2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */ | ||
290 | write_i2c(2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */ | ||
291 | write_i2c(2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */ | ||
292 | write_i2c(2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */ | ||
293 | |||
294 | tr_setvol(0x8000); | ||
295 | tr_setbass(0x8000); | ||
296 | tr_settreble(0x8000); | ||
297 | tr_setstereo(1); | ||
298 | |||
299 | /* mute card - prevents noisy bootups */ | ||
300 | tr_setmute(1); | ||
301 | |||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); | ||
306 | MODULE_DESCRIPTION("A driver for the Trust FM Radio card."); | ||
307 | MODULE_LICENSE("GPL"); | ||
308 | |||
309 | module_param(io, int, 0); | ||
310 | MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)"); | ||
311 | module_param(radio_nr, int, 0); | ||
312 | |||
313 | static void __exit cleanup_trust_module(void) | ||
314 | { | ||
315 | video_unregister_device(&trust_radio); | ||
316 | release_region(io, 2); | ||
317 | } | ||
318 | |||
319 | module_init(trust_init); | ||
320 | module_exit(cleanup_trust_module); | ||