radio-maxiradio.c 10.7 KB
Newer Older
1 2
/*
 * Guillemot Maxi Radio FM 2000 PCI radio card driver for Linux
L
Linus Torvalds 已提交
3 4 5 6 7 8
 * (C) 2001 Dimitromanolakis Apostolos <apdim@grecian.net>
 *
 * Based in the radio Maestro PCI driver. Actually it uses the same chip
 * for radio but different pci controller.
 *
 * I didn't have any specs I reversed engineered the protocol from
9
 * the windows driver (radio.dll).
L
Linus Torvalds 已提交
10 11
 *
 * The card uses the TEA5757 chip that includes a search function but it
12
 * is useless as I haven't found any way to read back the frequency. If
L
Linus Torvalds 已提交
13 14 15 16 17 18 19 20 21 22
 * anybody does please mail me.
 *
 * For the pdf file see:
 * http://www.semiconductors.philips.com/pip/TEA5757H/V1
 *
 *
 * CHANGES:
 *   0.75b
 *     - better pci interface thanks to Francois Romieu <romieu@cogenit.fr>
 *
23
 *   0.75      Sun Feb  4 22:51:27 EET 2001
L
Linus Torvalds 已提交
24 25 26
 *     - tiding up
 *     - removed support for multiple devices as it didn't work anyway
 *
27
 * BUGS:
L
Linus Torvalds 已提交
28 29
 *   - card unmutes if you change frequency
 *
30 31 32
 * (c) 2006, 2007 by Mauro Carvalho Chehab <mchehab@infradead.org>:
 *	- Conversion to V4L2 API
 *      - Uses video_ioctl2 for parsing and to add debug support
L
Linus Torvalds 已提交
33 34 35 36 37 38 39 40 41
 */


#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>
42 43
#include <linux/mutex.h>

L
Linus Torvalds 已提交
44
#include <linux/pci.h>
45
#include <linux/videodev2.h>
46
#include <media/v4l2-common.h>
L
Linus Torvalds 已提交
47

48
#define DRIVER_VERSION	"0.77"
49 50

#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
51 52 53 54 55 56 57 58 59
#define RADIO_VERSION KERNEL_VERSION(0,7,7)

static struct video_device maxiradio_radio;

#define dprintk(num, fmt, arg...)                                          \
	do {                                                               \
		if (maxiradio_radio.debug >= num)                          \
			printk(KERN_DEBUG "%s: " fmt,                      \
				maxiradio_radio.name, ## arg); } while (0)
60 61 62 63 64 65 66 67 68 69 70

static struct v4l2_queryctrl radio_qctrl[] = {
	{
		.id            = V4L2_CID_AUDIO_MUTE,
		.name          = "Mute",
		.minimum       = 0,
		.maximum       = 1,
		.default_value = 1,
		.type          = V4L2_CTRL_TYPE_BOOLEAN,
	}
};
L
Linus Torvalds 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

#ifndef PCI_VENDOR_ID_GUILLEMOT
#define PCI_VENDOR_ID_GUILLEMOT 0x5046
#endif

#ifndef PCI_DEVICE_ID_GUILLEMOT
#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
#endif


/* TEA5757 pin mappings */
static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16 ;

static int radio_nr = -1;
module_param(radio_nr, int, 0);


#define FREQ_LO		 50*16000
#define FREQ_HI		150*16000

#define FREQ_IF         171200 /* 10.7*16000   */
#define FREQ_STEP       200    /* 12.5*16      */

94 95 96
/* (x==fmhz*16*1000) -> bits */
#define FREQ2BITS(x)	((( (unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1)) \
			/(FREQ_STEP<<2))<<2)
L
Linus Torvalds 已提交
97 98 99 100

#define BITS2FREQ(x)	((x) * FREQ_STEP - FREQ_IF)


101
static const struct file_operations maxiradio_fops = {
L
Linus Torvalds 已提交
102 103 104
	.owner		= THIS_MODULE,
	.open           = video_exclusive_open,
	.release        = video_exclusive_release,
105
	.ioctl          = video_ioctl2,
106
	.compat_ioctl	= v4l_compat_ioctl32,
L
Linus Torvalds 已提交
107 108 109 110 111 112 113
	.llseek         = no_llseek,
};

static struct radio_device
{
	__u16	io,	/* base of radio io */
		muted,	/* VIDEO_AUDIO_MUTE */
114
		stereo,	/* VIDEO_TUNER_STEREO_ON */
L
Linus Torvalds 已提交
115
		tuned;	/* signal strength (0 or 0xffff) */
116

L
Linus Torvalds 已提交
117
	unsigned long freq;
118

119
	struct mutex lock;
120 121 122 123
} radio_unit = {
	.muted =1,
	.freq = FREQ_LO,
};
L
Linus Torvalds 已提交
124 125 126

static void outbit(unsigned long bit, __u16 io)
{
127
	if (bit != 0)
L
Linus Torvalds 已提交
128 129 130 131 132
		{
			outb(  power|wren|data     ,io); udelay(4);
			outb(  power|wren|data|clk ,io); udelay(4);
			outb(  power|wren|data     ,io); udelay(4);
		}
133
	else
L
Linus Torvalds 已提交
134 135 136 137 138 139 140 141 142
		{
			outb(  power|wren          ,io); udelay(4);
			outb(  power|wren|clk      ,io); udelay(4);
			outb(  power|wren          ,io); udelay(4);
		}
}

static void turn_power(__u16 io, int p)
{
143 144 145 146 147 148 149
	if (p != 0) {
		dprintk(1, "Radio powered on\n");
		outb(power, io);
	} else {
		dprintk(1, "Radio powered off\n");
		outb(0,io);
	}
L
Linus Torvalds 已提交
150 151
}

152
static void set_freq(__u16 io, __u32 freq)
L
Linus Torvalds 已提交
153 154 155
{
	unsigned long int si;
	int bl;
156
	int data = FREQ2BITS(freq);
157

L
Linus Torvalds 已提交
158 159
	/* TEA5757 shift register bits (see pdf) */

160
	outbit(0,io); // 24  search
L
Linus Torvalds 已提交
161
	outbit(1,io); // 23  search up/down
162

L
Linus Torvalds 已提交
163 164 165 166 167 168 169
	outbit(0,io); // 22  stereo/mono

	outbit(0,io); // 21  band
	outbit(0,io); // 20  band (only 00=FM works I think)

	outbit(0,io); // 19  port ?
	outbit(0,io); // 18  port ?
170

L
Linus Torvalds 已提交
171 172
	outbit(0,io); // 17  search level
	outbit(0,io); // 16  search level
173

L
Linus Torvalds 已提交
174
	si = 0x8000;
175 176 177 178
	for (bl = 1; bl <= 16 ; bl++) {
		outbit(data & si,io);
		si >>=1;
	}
179

180 181 182 183 184
	dprintk(1, "Radio freq set to %d.%02d MHz\n",
				freq / 16000,
				freq % 16000 * 100 / 16000);

	turn_power(io, 1);
L
Linus Torvalds 已提交
185 186 187
}

static int get_stereo(__u16 io)
188
{
189 190 191
	outb(power,io);
	udelay(4);

L
Linus Torvalds 已提交
192 193 194 195
	return !(inb(io) & mo_st);
}

static int get_tune(__u16 io)
196
{
197 198 199
	outb(power+clk,io);
	udelay(4);

L
Linus Torvalds 已提交
200 201 202 203
	return !(inb(io) & mo_st);
}


204 205 206 207 208 209 210 211 212 213 214 215 216 217
static int vidioc_querycap (struct file *file, void  *priv,
			    struct v4l2_capability *v)
{
	strlcpy(v->driver, "radio-maxiradio", sizeof (v->driver));
	strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof (v->card));
	sprintf(v->bus_info,"ISA");
	v->version = RADIO_VERSION;
	v->capabilities = V4L2_CAP_TUNER;

	return 0;
}

static int vidioc_g_tuner (struct file *file, void *priv,
			   struct v4l2_tuner *v)
L
Linus Torvalds 已提交
218 219 220 221
{
	struct video_device *dev = video_devdata(file);
	struct radio_device *card=dev->priv;

222 223
	if (v->index > 0)
		return -EINVAL;
224

225 226 227
	memset(v,0,sizeof(*v));
	strcpy(v->name, "FM");
	v->type = V4L2_TUNER_RADIO;
228

229 230 231 232 233 234 235 236 237
	v->rangelow=FREQ_LO;
	v->rangehigh=FREQ_HI;
	v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
	v->capability=V4L2_TUNER_CAP_LOW;
	if(get_stereo(card->io))
		v->audmode = V4L2_TUNER_MODE_STEREO;
	else
		v->audmode = V4L2_TUNER_MODE_MONO;
	v->signal=0xffff*get_tune(card->io);
238

239 240
	return 0;
}
241

242 243 244 245 246
static int vidioc_s_tuner (struct file *file, void *priv,
			   struct v4l2_tuner *v)
{
	if (v->index > 0)
		return -EINVAL;
247

248 249
	return 0;
}
250

251 252 253 254 255 256
static int vidioc_g_audio (struct file *file, void *priv,
			   struct v4l2_audio *a)
{
	if (a->index > 1)
		return -EINVAL;

257
	strcpy(a->name, "FM");
258 259 260 261
	a->capability = V4L2_AUDCAP_STEREO;
	return 0;
}

262 263 264
static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
{
	*i = 0;
265

266 267 268 269 270 271 272
	return 0;
}

static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
{
	if (i != 0)
		return -EINVAL;
273

274 275 276 277
	return 0;
}


278 279 280 281 282 283 284 285 286
static int vidioc_s_audio (struct file *file, void *priv,
			   struct v4l2_audio *a)
{
	if (a->index != 0)
		return -EINVAL;

	return 0;
}

287 288 289 290 291
static int vidioc_s_frequency (struct file *file, void *priv,
			       struct v4l2_frequency *f)
{
	struct video_device *dev = video_devdata(file);
	struct radio_device *card=dev->priv;
292

293 294 295 296 297 298
	if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) {
		dprintk(1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n",
					f->frequency / 16000,
					f->frequency % 16000 * 100 / 16000,
					FREQ_LO / 16000, FREQ_HI / 16000);

299
		return -EINVAL;
300
	}
301

302
	card->freq = f->frequency;
303
	set_freq(card->io, card->freq);
304
	msleep(125);
305

306 307
	return 0;
}
308

309 310 311 312 313
static int vidioc_g_frequency (struct file *file, void *priv,
			       struct v4l2_frequency *f)
{
	struct video_device *dev = video_devdata(file);
	struct radio_device *card=dev->priv;
314

315 316 317
	f->type = V4L2_TUNER_RADIO;
	f->frequency = card->freq;

318 319 320 321
	dprintk(4, "radio freq is %d.%02d MHz",
				f->frequency / 16000,
				f->frequency % 16000 * 100 / 16000);

322 323 324 325 326 327 328 329 330 331 332 333
	return 0;
}

static int vidioc_queryctrl (struct file *file, void *priv,
			     struct v4l2_queryctrl *qc)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
		if (qc->id && qc->id == radio_qctrl[i].id) {
			memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
			return (0);
334
		}
335
	}
336

337 338
	return -EINVAL;
}
339

340 341 342 343 344
static int vidioc_g_ctrl (struct file *file, void *priv,
			    struct v4l2_control *ctrl)
{
	struct video_device *dev = video_devdata(file);
	struct radio_device *card=dev->priv;
345

346 347 348 349
	switch (ctrl->id) {
		case V4L2_CID_AUDIO_MUTE:
			ctrl->value=card->muted;
			return (0);
L
Linus Torvalds 已提交
350
	}
351

352
	return -EINVAL;
L
Linus Torvalds 已提交
353 354
}

355 356
static int vidioc_s_ctrl (struct file *file, void *priv,
			  struct v4l2_control *ctrl)
L
Linus Torvalds 已提交
357 358 359
{
	struct video_device *dev = video_devdata(file);
	struct radio_device *card=dev->priv;
360

361 362 363 364 365 366
	switch (ctrl->id) {
		case V4L2_CID_AUDIO_MUTE:
			card->muted = ctrl->value;
			if(card->muted)
				turn_power(card->io, 0);
			else
367
				set_freq(card->io, card->freq);
368 369
			return 0;
	}
370

371
	return -EINVAL;
L
Linus Torvalds 已提交
372 373
}

374 375 376 377 378 379 380 381 382 383
static struct video_device maxiradio_radio =
{
	.owner		    = THIS_MODULE,
	.name		    = "Maxi Radio FM2000 radio",
	.type		    = VID_TYPE_TUNER,
	.fops               = &maxiradio_fops,

	.vidioc_querycap    = vidioc_querycap,
	.vidioc_g_tuner     = vidioc_g_tuner,
	.vidioc_s_tuner     = vidioc_s_tuner,
384 385
	.vidioc_g_audio     = vidioc_g_audio,
	.vidioc_s_audio     = vidioc_s_audio,
386 387
	.vidioc_g_input     = vidioc_g_input,
	.vidioc_s_input     = vidioc_s_input,
388 389 390 391 392 393
	.vidioc_g_frequency = vidioc_g_frequency,
	.vidioc_s_frequency = vidioc_s_frequency,
	.vidioc_queryctrl   = vidioc_queryctrl,
	.vidioc_g_ctrl      = vidioc_g_ctrl,
	.vidioc_s_ctrl      = vidioc_s_ctrl,
};
L
Linus Torvalds 已提交
394 395 396 397

static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
	if(!request_region(pci_resource_start(pdev, 0),
398 399 400
			   pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) {
		printk(KERN_ERR "radio-maxiradio: can't reserve I/O ports\n");
		goto err_out;
L
Linus Torvalds 已提交
401 402 403
	}

	if (pci_enable_device(pdev))
404
		goto err_out_free_region;
L
Linus Torvalds 已提交
405 406

	radio_unit.io = pci_resource_start(pdev, 0);
407
	mutex_init(&radio_unit.lock);
L
Linus Torvalds 已提交
408 409
	maxiradio_radio.priv = &radio_unit;

410
	if (video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr)==-1) {
411 412
		printk("radio-maxiradio: can't register device!");
		goto err_out_free_region;
L
Linus Torvalds 已提交
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
	}

	printk(KERN_INFO "radio-maxiradio: version "
	       DRIVER_VERSION
	       " time "
	       __TIME__ "  "
	       __DATE__
	       "\n");

	printk(KERN_INFO "radio-maxiradio: found Guillemot MAXI Radio device (io = 0x%x)\n",
	       radio_unit.io);
	return 0;

err_out_free_region:
	release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
err_out:
	return -ENODEV;
}

static void __devexit maxiradio_remove_one(struct pci_dev *pdev)
{
	video_unregister_device(&maxiradio_radio);
	release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
}

static struct pci_device_id maxiradio_pci_tbl[] = {
	{ PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO,
		PCI_ANY_ID, PCI_ANY_ID, },
	{ 0,}
};

MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl);

static struct pci_driver maxiradio_driver = {
	.name		= "radio-maxiradio",
	.id_table	= maxiradio_pci_tbl,
	.probe		= maxiradio_init_one,
	.remove		= __devexit_p(maxiradio_remove_one),
};

static int __init maxiradio_radio_init(void)
{
455
	return pci_register_driver(&maxiradio_driver);
L
Linus Torvalds 已提交
456 457 458 459 460 461 462 463 464
}

static void __exit maxiradio_radio_exit(void)
{
	pci_unregister_driver(&maxiradio_driver);
}

module_init(maxiradio_radio_init);
module_exit(maxiradio_radio_exit);
465 466 467 468 469 470 471

MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
MODULE_LICENSE("GPL");

module_param_named(debug,maxiradio_radio.debug, int, 0644);
MODULE_PARM_DESC(debug,"activates debug info");