radio-maxiradio.c 10.8 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
#ifdef CONFIG_COMPAT
107
	.compat_ioctl	= v4l_compat_ioctl32,
108
#endif
L
Linus Torvalds 已提交
109 110 111 112 113 114 115
	.llseek         = no_llseek,
};

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

L
Linus Torvalds 已提交
119
	unsigned long freq;
120

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

static void outbit(unsigned long bit, __u16 io)
{
129
	if (bit != 0)
L
Linus Torvalds 已提交
130 131 132 133 134
		{
			outb(  power|wren|data     ,io); udelay(4);
			outb(  power|wren|data|clk ,io); udelay(4);
			outb(  power|wren|data     ,io); udelay(4);
		}
135
	else
L
Linus Torvalds 已提交
136 137 138 139 140 141 142 143 144
		{
			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)
{
145 146 147 148 149 150 151
	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 已提交
152 153
}

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

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

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

L
Linus Torvalds 已提交
165 166 167 168 169 170 171
	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 ?
172

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

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

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

	turn_power(io, 1);
L
Linus Torvalds 已提交
187 188 189
}

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

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

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

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


206 207 208 209 210 211 212 213 214 215 216 217 218 219
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 已提交
220 221 222 223
{
	struct video_device *dev = video_devdata(file);
	struct radio_device *card=dev->priv;

224 225
	if (v->index > 0)
		return -EINVAL;
226

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

231 232 233 234 235 236 237 238 239
	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);
240

241 242
	return 0;
}
243

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

250 251
	return 0;
}
252

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

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

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

268 269 270 271 272 273 274
	return 0;
}

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

276 277 278 279
	return 0;
}


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

	return 0;
}

289 290 291 292 293
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;
294

295 296 297 298 299 300
	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);

301
		return -EINVAL;
302
	}
303

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

308 309
	return 0;
}
310

311 312 313 314 315
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;
316

317 318 319
	f->type = V4L2_TUNER_RADIO;
	f->frequency = card->freq;

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

324 325 326 327 328 329 330 331 332 333 334 335
	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);
336
		}
337
	}
338

339 340
	return -EINVAL;
}
341

342 343 344 345 346
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;
347

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

354
	return -EINVAL;
L
Linus Torvalds 已提交
355 356
}

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

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

373
	return -EINVAL;
L
Linus Torvalds 已提交
374 375
}

376 377 378 379 380 381 382 383 384 385
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,
386 387
	.vidioc_g_audio     = vidioc_g_audio,
	.vidioc_s_audio     = vidioc_s_audio,
388 389
	.vidioc_g_input     = vidioc_g_input,
	.vidioc_s_input     = vidioc_s_input,
390 391 392 393 394 395
	.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 已提交
396 397 398 399

static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
	if(!request_region(pci_resource_start(pdev, 0),
400 401 402
			   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 已提交
403 404 405
	}

	if (pci_enable_device(pdev))
406
		goto err_out_free_region;
L
Linus Torvalds 已提交
407 408

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

412
	if (video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr)==-1) {
413 414
		printk("radio-maxiradio: can't register device!");
		goto err_out_free_region;
L
Linus Torvalds 已提交
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 455 456
	}

	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)
{
457
	return pci_register_driver(&maxiradio_driver);
L
Linus Torvalds 已提交
458 459 460 461 462 463 464 465 466
}

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

module_init(maxiradio_radio_init);
module_exit(maxiradio_radio_exit);
467 468 469 470 471 472 473

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");