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>
47
#include <media/v4l2-ioctl.h>
L
Linus Torvalds 已提交
48

49
#define DRIVER_VERSION	"0.77"
50 51

#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
52 53 54 55 56 57 58 59 60
#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)
61 62 63 64 65 66 67 68 69 70 71

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 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94

#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      */

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

242 243
	return 0;
}
244

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

251 252
	return 0;
}
253

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

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

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

269 270 271 272 273 274 275
	return 0;
}

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

277 278 279 280
	return 0;
}


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

	return 0;
}

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

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

302
		return -EINVAL;
303
	}
304

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

309 310
	return 0;
}
311

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

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

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

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

340 341
	return -EINVAL;
}
342

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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