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