• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/drivers/media/radio/
1/* Terratec ActiveRadio ISA Standalone card driver for Linux radio support
2 * (c) 1999 R. Offermanns (rolf@offermanns.de)
3 * based on the aimslab radio driver from M. Kirkwood
4 * many thanks to Michael Becker and Friedhelm Birth (from TerraTec)
5 *
6 *
7 * History:
8 * 1999-05-21	First preview release
9 *
10 *  Notes on the hardware:
11 *  There are two "main" chips on the card:
12 *  - Philips OM5610 (http://www-us.semiconductors.philips.com/acrobat/datasheets/OM5610_2.pdf)
13 *  - Philips SAA6588 (http://www-us.semiconductors.philips.com/acrobat/datasheets/SAA6588_1.pdf)
14 *  (you can get the datasheet at the above links)
15 *
16 *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
17 *  Volume Control is done digitally
18 *
19 *  there is a I2C controlled RDS decoder (SAA6588)  onboard, which i would like to support someday
20 *  (as soon i have understand how to get started :)
21 *  If you can help me out with that, please contact me!!
22 *
23 *
24 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
25 */
26
27#include <linux/module.h>	/* Modules 			*/
28#include <linux/init.h>		/* Initdata			*/
29#include <linux/ioport.h>	/* request_region		*/
30#include <linux/videodev2.h>	/* kernel radio structs		*/
31#include <linux/mutex.h>
32#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
33#include <linux/io.h>		/* outb, outb_p			*/
34#include <media/v4l2-device.h>
35#include <media/v4l2-ioctl.h>
36
37MODULE_AUTHOR("R.OFFERMANNS & others");
38MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
39MODULE_LICENSE("GPL");
40
41#ifndef CONFIG_RADIO_TERRATEC_PORT
42#define CONFIG_RADIO_TERRATEC_PORT 0x590
43#endif
44
45static int io = CONFIG_RADIO_TERRATEC_PORT;
46static int radio_nr = -1;
47
48module_param(io, int, 0);
49MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
50module_param(radio_nr, int, 0);
51
52#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
53
54static struct v4l2_queryctrl radio_qctrl[] = {
55	{
56		.id            = V4L2_CID_AUDIO_MUTE,
57		.name          = "Mute",
58		.minimum       = 0,
59		.maximum       = 1,
60		.default_value = 1,
61		.type          = V4L2_CTRL_TYPE_BOOLEAN,
62	},{
63		.id            = V4L2_CID_AUDIO_VOLUME,
64		.name          = "Volume",
65		.minimum       = 0,
66		.maximum       = 0xff,
67		.step          = 1,
68		.default_value = 0xff,
69		.type          = V4L2_CTRL_TYPE_INTEGER,
70	}
71};
72
73#define WRT_DIS 	0x00
74#define CLK_OFF		0x00
75#define IIC_DATA	0x01
76#define IIC_CLK		0x02
77#define DATA		0x04
78#define CLK_ON 		0x08
79#define WRT_EN		0x10
80
81struct terratec
82{
83	struct v4l2_device v4l2_dev;
84	struct video_device vdev;
85	int io;
86	int curvol;
87	unsigned long curfreq;
88	int muted;
89	struct mutex lock;
90};
91
92static struct terratec terratec_card;
93
94/* local things */
95
96static void tt_write_vol(struct terratec *tt, int volume)
97{
98	int i;
99
100	volume = volume + (volume * 32); /* change both channels */
101	mutex_lock(&tt->lock);
102	for (i = 0; i < 8; i++) {
103		if (volume & (0x80 >> i))
104			outb(0x80, tt->io + 1);
105		else
106			outb(0x00, tt->io + 1);
107	}
108	mutex_unlock(&tt->lock);
109}
110
111
112
113static void tt_mute(struct terratec *tt)
114{
115	tt->muted = 1;
116	tt_write_vol(tt, 0);
117}
118
119static int tt_setvol(struct terratec *tt, int vol)
120{
121	if (vol == tt->curvol) {	/* requested volume = current */
122		if (tt->muted) {	/* user is unmuting the card  */
123			tt->muted = 0;
124			tt_write_vol(tt, vol);	/* enable card */
125		}
126		return 0;
127	}
128
129	if (vol == 0) {			/* volume = 0 means mute the card */
130		tt_write_vol(tt, 0);	/* "turn off card" by setting vol to 0 */
131		tt->curvol = vol;	/* track the volume state!	*/
132		return 0;
133	}
134
135	tt->muted = 0;
136	tt_write_vol(tt, vol);
137	tt->curvol = vol;
138	return 0;
139}
140
141
142/* this is the worst part in this driver */
143/* many more or less strange things are going on here, but hey, it works :) */
144
145static int tt_setfreq(struct terratec *tt, unsigned long freq1)
146{
147	int freq;
148	int i;
149	int p;
150	int  temp;
151	long rest;
152	unsigned char buffer[25];		/* we have to bit shift 25 registers */
153
154	mutex_lock(&tt->lock);
155
156	tt->curfreq = freq1;
157
158	freq = freq1 / 160;			/* convert the freq. to a nice to handle value */
159	memset(buffer, 0, sizeof(buffer));
160
161	rest = freq * 10 + 10700;	/* I once had understood what is going on here */
162					/* maybe some wise guy (friedhelm?) can comment this stuff */
163	i = 13;
164	p = 10;
165	temp = 102400;
166	while (rest != 0) {
167		if (rest % temp  == rest)
168			buffer[i] = 0;
169		else {
170			buffer[i] = 1;
171			rest = rest - temp;
172		}
173		i--;
174		p--;
175		temp = temp / 2;
176	}
177
178	for (i = 24; i > -1; i--) {	/* bit shift the values to the radiocard */
179		if (buffer[i] == 1) {
180			outb(WRT_EN | DATA, tt->io);
181			outb(WRT_EN | DATA | CLK_ON, tt->io);
182			outb(WRT_EN | DATA, tt->io);
183		} else {
184			outb(WRT_EN | 0x00, tt->io);
185			outb(WRT_EN | 0x00 | CLK_ON, tt->io);
186		}
187	}
188	outb(0x00, tt->io);
189
190	mutex_unlock(&tt->lock);
191
192	return 0;
193}
194
195static int tt_getsigstr(struct terratec *tt)
196{
197	if (inb(tt->io) & 2)	/* bit set = no signal present	*/
198		return 0;
199	return 1;		/* signal present		*/
200}
201
202static int vidioc_querycap(struct file *file, void *priv,
203					struct v4l2_capability *v)
204{
205	strlcpy(v->driver, "radio-terratec", sizeof(v->driver));
206	strlcpy(v->card, "ActiveRadio", sizeof(v->card));
207	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
208	v->version = RADIO_VERSION;
209	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
210	return 0;
211}
212
213static int vidioc_g_tuner(struct file *file, void *priv,
214					struct v4l2_tuner *v)
215{
216	struct terratec *tt = video_drvdata(file);
217
218	if (v->index > 0)
219		return -EINVAL;
220
221	strlcpy(v->name, "FM", sizeof(v->name));
222	v->type = V4L2_TUNER_RADIO;
223	v->rangelow = 87 * 16000;
224	v->rangehigh = 108 * 16000;
225	v->rxsubchans = V4L2_TUNER_SUB_MONO;
226	v->capability = V4L2_TUNER_CAP_LOW;
227	v->audmode = V4L2_TUNER_MODE_MONO;
228	v->signal = 0xFFFF * tt_getsigstr(tt);
229	return 0;
230}
231
232static int vidioc_s_tuner(struct file *file, void *priv,
233					struct v4l2_tuner *v)
234{
235	return v->index ? -EINVAL : 0;
236}
237
238static int vidioc_s_frequency(struct file *file, void *priv,
239					struct v4l2_frequency *f)
240{
241	struct terratec *tt = video_drvdata(file);
242
243	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
244		return -EINVAL;
245	tt_setfreq(tt, f->frequency);
246	return 0;
247}
248
249static int vidioc_g_frequency(struct file *file, void *priv,
250					struct v4l2_frequency *f)
251{
252	struct terratec *tt = video_drvdata(file);
253
254	if (f->tuner != 0)
255		return -EINVAL;
256	f->type = V4L2_TUNER_RADIO;
257	f->frequency = tt->curfreq;
258	return 0;
259}
260
261static int vidioc_queryctrl(struct file *file, void *priv,
262					struct v4l2_queryctrl *qc)
263{
264	int i;
265
266	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
267		if (qc->id && qc->id == radio_qctrl[i].id) {
268			memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
269			return 0;
270		}
271	}
272	return -EINVAL;
273}
274
275static int vidioc_g_ctrl(struct file *file, void *priv,
276					struct v4l2_control *ctrl)
277{
278	struct terratec *tt = video_drvdata(file);
279
280	switch (ctrl->id) {
281	case V4L2_CID_AUDIO_MUTE:
282		if (tt->muted)
283			ctrl->value = 1;
284		else
285			ctrl->value = 0;
286		return 0;
287	case V4L2_CID_AUDIO_VOLUME:
288		ctrl->value = tt->curvol * 6554;
289		return 0;
290	}
291	return -EINVAL;
292}
293
294static int vidioc_s_ctrl(struct file *file, void *priv,
295					struct v4l2_control *ctrl)
296{
297	struct terratec *tt = video_drvdata(file);
298
299	switch (ctrl->id) {
300	case V4L2_CID_AUDIO_MUTE:
301		if (ctrl->value)
302			tt_mute(tt);
303		else
304			tt_setvol(tt,tt->curvol);
305		return 0;
306	case V4L2_CID_AUDIO_VOLUME:
307		tt_setvol(tt,ctrl->value);
308		return 0;
309	}
310	return -EINVAL;
311}
312
313static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
314{
315	*i = 0;
316	return 0;
317}
318
319static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
320{
321	return i ? -EINVAL : 0;
322}
323
324static int vidioc_g_audio(struct file *file, void *priv,
325					struct v4l2_audio *a)
326{
327	a->index = 0;
328	strlcpy(a->name, "Radio", sizeof(a->name));
329	a->capability = V4L2_AUDCAP_STEREO;
330	return 0;
331}
332
333static int vidioc_s_audio(struct file *file, void *priv,
334					struct v4l2_audio *a)
335{
336	return a->index ? -EINVAL : 0;
337}
338
339static const struct v4l2_file_operations terratec_fops = {
340	.owner		= THIS_MODULE,
341	.ioctl		= video_ioctl2,
342};
343
344static const struct v4l2_ioctl_ops terratec_ioctl_ops = {
345	.vidioc_querycap    = vidioc_querycap,
346	.vidioc_g_tuner     = vidioc_g_tuner,
347	.vidioc_s_tuner     = vidioc_s_tuner,
348	.vidioc_g_frequency = vidioc_g_frequency,
349	.vidioc_s_frequency = vidioc_s_frequency,
350	.vidioc_queryctrl   = vidioc_queryctrl,
351	.vidioc_g_ctrl      = vidioc_g_ctrl,
352	.vidioc_s_ctrl      = vidioc_s_ctrl,
353	.vidioc_g_audio     = vidioc_g_audio,
354	.vidioc_s_audio     = vidioc_s_audio,
355	.vidioc_g_input     = vidioc_g_input,
356	.vidioc_s_input     = vidioc_s_input,
357};
358
359static int __init terratec_init(void)
360{
361	struct terratec *tt = &terratec_card;
362	struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
363	int res;
364
365	strlcpy(v4l2_dev->name, "terratec", sizeof(v4l2_dev->name));
366	tt->io = io;
367	if (tt->io == -1) {
368		v4l2_err(v4l2_dev, "you must set an I/O address with io=0x590 or 0x591\n");
369		return -EINVAL;
370	}
371	if (!request_region(tt->io, 2, "terratec")) {
372		v4l2_err(v4l2_dev, "port 0x%x already in use\n", io);
373		return -EBUSY;
374	}
375
376	res = v4l2_device_register(NULL, v4l2_dev);
377	if (res < 0) {
378		release_region(tt->io, 2);
379		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
380		return res;
381	}
382
383	strlcpy(tt->vdev.name, v4l2_dev->name, sizeof(tt->vdev.name));
384	tt->vdev.v4l2_dev = v4l2_dev;
385	tt->vdev.fops = &terratec_fops;
386	tt->vdev.ioctl_ops = &terratec_ioctl_ops;
387	tt->vdev.release = video_device_release_empty;
388	video_set_drvdata(&tt->vdev, tt);
389
390	mutex_init(&tt->lock);
391
392	if (video_register_device(&tt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
393		v4l2_device_unregister(&tt->v4l2_dev);
394		release_region(tt->io, 2);
395		return -EINVAL;
396	}
397
398	v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver.\n");
399
400	/* mute card - prevents noisy bootups */
401	tt_write_vol(tt, 0);
402	return 0;
403}
404
405static void __exit terratec_exit(void)
406{
407	struct terratec *tt = &terratec_card;
408	struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
409
410	video_unregister_device(&tt->vdev);
411	v4l2_device_unregister(&tt->v4l2_dev);
412	release_region(tt->io, 2);
413	v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver unloaded.\n");
414}
415
416module_init(terratec_init);
417module_exit(terratec_exit);
418