pms.c 20.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12
/*
 *	Media Vision Pro Movie Studio
 *			or
 *	"all you need is an I2C bus some RAM and a prayer"
 *
 *	This draws heavily on code
 *
 *	(c) Wolfgang Koehler,  wolf@first.gmd.de, Dec. 1994
 *	Kiefernring 15
 *	14478 Potsdam, Germany
 *
 *	Most of this code is directly derived from his userspace driver.
13 14
 *	His driver works so send any reports to alan@lxorguk.ukuu.org.uk
 *	unless the userspace driver also doesn't work for you...
15
 *
L
Linus Torvalds 已提交
16 17
 *      Changes:
 *      08/07/2003        Daniele Bellucci <bellucda@tiscali.it>
18
 *                        - pms_capture: report back -EFAULT
L
Linus Torvalds 已提交
19 20 21 22 23 24 25 26 27 28 29 30 31
 */

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/videodev.h>
32
#include <media/v4l2-common.h>
33
#include <media/v4l2-ioctl.h>
34 35
#include <linux/mutex.h>

L
Linus Torvalds 已提交
36 37 38 39 40 41 42 43 44 45 46 47 48 49
#include <asm/uaccess.h>


#define MOTOROLA	1
#define PHILIPS2	2
#define PHILIPS1	3
#define MVVMEMORYWIDTH	0x40		/* 512 bytes */

struct pms_device
{
	struct video_device v;
	struct video_picture picture;
	int height;
	int width;
50
	unsigned long in_use;
51
	struct mutex lock;
L
Linus Torvalds 已提交
52 53 54 55 56 57 58 59 60 61
};

struct i2c_info
{
	u8 slave;
	u8 sub;
	u8 data;
	u8 hits;
};

62
static int i2c_count;
L
Linus Torvalds 已提交
63 64 65
static struct i2c_info i2cinfo[64];

static int decoder 		= PHILIPS2;
66
static int standard;	/* 0 - auto 1 - ntsc 2 - pal 3 - secam */
L
Linus Torvalds 已提交
67 68 69 70

/*
 *	I/O ports and Shared Memory
 */
71

L
Linus Torvalds 已提交
72 73 74 75 76 77
static int io_port		=	0x250;
static int data_port		=	0x251;
static int mem_base		=	0xC8000;
static void __iomem *mem;
static int video_nr             =       -1;

78

L
Linus Torvalds 已提交
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94

static inline void mvv_write(u8 index, u8 value)
{
	outw(index|(value<<8), io_port);
}

static inline u8 mvv_read(u8 index)
{
	outb(index, io_port);
	return inb(data_port);
}

static int pms_i2c_stat(u8 slave)
{
	int counter;
	int i;
95

L
Linus Torvalds 已提交
96
	outb(0x28, io_port);
97

L
Linus Torvalds 已提交
98 99 100 101 102 103 104 105
	counter=0;
	while((inb(data_port)&0x01)==0)
		if(counter++==256)
			break;

	while((inb(data_port)&0x01)!=0)
		if(counter++==256)
			break;
106

L
Linus Torvalds 已提交
107
	outb(slave, io_port);
108

L
Linus Torvalds 已提交
109 110 111 112 113 114 115 116
	counter=0;
	while((inb(data_port)&0x01)==0)
		if(counter++==256)
			break;

	while((inb(data_port)&0x01)!=0)
		if(counter++==256)
			break;
117

L
Linus Torvalds 已提交
118 119 120 121 122 123 124 125 126
	for(i=0;i<12;i++)
	{
		char st=inb(data_port);
		if((st&2)!=0)
			return -1;
		if((st&1)==0)
			break;
	}
	outb(0x29, io_port);
127
	return inb(data_port);
L
Linus Torvalds 已提交
128 129 130 131 132 133 134
}

static int pms_i2c_write(u16 slave, u16 sub, u16 data)
{
	int skip=0;
	int count;
	int i;
135

L
Linus Torvalds 已提交
136 137 138 139 140
	for(i=0;i<i2c_count;i++)
	{
		if((i2cinfo[i].slave==slave) &&
		   (i2cinfo[i].sub == sub))
		{
141 142 143 144
			if(i2cinfo[i].data==data)
				skip=1;
			i2cinfo[i].data=data;
			i=i2c_count+1;
L
Linus Torvalds 已提交
145 146
		}
	}
147

L
Linus Torvalds 已提交
148 149 150 151 152 153 154
	if(i==i2c_count && i2c_count<64)
	{
		i2cinfo[i2c_count].slave=slave;
		i2cinfo[i2c_count].sub=sub;
		i2cinfo[i2c_count].data=data;
		i2c_count++;
	}
155

L
Linus Torvalds 已提交
156 157
	if(skip)
		return 0;
158

L
Linus Torvalds 已提交
159 160 161
	mvv_write(0x29, sub);
	mvv_write(0x2A, data);
	mvv_write(0x28, slave);
162

L
Linus Torvalds 已提交
163
	outb(0x28, io_port);
164

L
Linus Torvalds 已提交
165 166 167 168 169 170 171
	count=0;
	while((inb(data_port)&1)==0)
		if(count>255)
			break;
	while((inb(data_port)&1)!=0)
		if(count>255)
			break;
172

L
Linus Torvalds 已提交
173
	count=inb(data_port);
174

L
Linus Torvalds 已提交
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
	if(count&2)
		return -1;
	return count;
}

static int pms_i2c_read(int slave, int sub)
{
	int i=0;
	for(i=0;i<i2c_count;i++)
	{
		if(i2cinfo[i].slave==slave && i2cinfo[i].sub==sub)
			return i2cinfo[i].data;
	}
	return 0;
}


static void pms_i2c_andor(int slave, int sub, int and, int or)
{
194 195
	u8 tmp;

L
Linus Torvalds 已提交
196 197 198 199 200 201 202 203
	tmp=pms_i2c_read(slave, sub);
	tmp = (tmp&and)|or;
	pms_i2c_write(slave, sub, tmp);
}

/*
 *	Control functions
 */
204

L
Linus Torvalds 已提交
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238

static void pms_videosource(short source)
{
	mvv_write(0x2E, source?0x31:0x30);
}

static void pms_hue(short hue)
{
	switch(decoder)
	{
		case MOTOROLA:
			pms_i2c_write(0x8A, 0x00, hue);
			break;
		case PHILIPS2:
			pms_i2c_write(0x8A, 0x07, hue);
			break;
		case PHILIPS1:
			pms_i2c_write(0x42, 0x07, hue);
			break;
	}
}

static void pms_colour(short colour)
{
	switch(decoder)
	{
		case MOTOROLA:
			pms_i2c_write(0x8A, 0x00, colour);
			break;
		case PHILIPS1:
			pms_i2c_write(0x42, 0x12, colour);
			break;
	}
}
239 240


L
Linus Torvalds 已提交
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
static void pms_contrast(short contrast)
{
	switch(decoder)
	{
		case MOTOROLA:
			pms_i2c_write(0x8A, 0x00, contrast);
			break;
		case PHILIPS1:
			pms_i2c_write(0x42, 0x13, contrast);
			break;
	}
}

static void pms_brightness(short brightness)
{
	switch(decoder)
	{
		case MOTOROLA:
			pms_i2c_write(0x8A, 0x00, brightness);
			pms_i2c_write(0x8A, 0x00, brightness);
			pms_i2c_write(0x8A, 0x00, brightness);
			break;
		case PHILIPS1:
			pms_i2c_write(0x42, 0x19, brightness);
			break;
	}
}


static void pms_format(short format)
{
	int target;
	standard = format;
274

L
Linus Torvalds 已提交
275 276 277 278 279 280
	if(decoder==PHILIPS1)
		target=0x42;
	else if(decoder==PHILIPS2)
		target=0x8A;
	else
		return;
281

L
Linus Torvalds 已提交
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
	switch(format)
	{
		case 0:	/* Auto */
			pms_i2c_andor(target, 0x0D, 0xFE,0x00);
			pms_i2c_andor(target, 0x0F, 0x3F,0x80);
			break;
		case 1: /* NTSC */
			pms_i2c_andor(target, 0x0D, 0xFE, 0x00);
			pms_i2c_andor(target, 0x0F, 0x3F, 0x40);
			break;
		case 2: /* PAL */
			pms_i2c_andor(target, 0x0D, 0xFE, 0x00);
			pms_i2c_andor(target, 0x0F, 0x3F, 0x00);
			break;
		case 3:	/* SECAM */
			pms_i2c_andor(target, 0x0D, 0xFE, 0x01);
			pms_i2c_andor(target, 0x0F, 0x3F, 0x00);
			break;
	}
}

#ifdef FOR_FUTURE_EXPANSION

/*
 *	These features of the PMS card are not currently exposes. They
307
 *	could become a private v4l ioctl for PMSCONFIG or somesuch if
L
Linus Torvalds 已提交
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
 *	people need it. We also don't yet use the PMS interrupt.
 */

static void pms_hstart(short start)
{
	switch(decoder)
	{
		case PHILIPS1:
			pms_i2c_write(0x8A, 0x05, start);
			pms_i2c_write(0x8A, 0x18, start);
			break;
		case PHILIPS2:
			pms_i2c_write(0x42, 0x05, start);
			pms_i2c_write(0x42, 0x18, start);
			break;
	}
}

/*
 *	Bandpass filters
 */
329

L
Linus Torvalds 已提交
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 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 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
static void pms_bandpass(short pass)
{
	if(decoder==PHILIPS2)
		pms_i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4);
	else if(decoder==PHILIPS1)
		pms_i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4);
}

static void pms_antisnow(short snow)
{
	if(decoder==PHILIPS2)
		pms_i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2);
	else if(decoder==PHILIPS1)
		pms_i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2);
}

static void pms_sharpness(short sharp)
{
	if(decoder==PHILIPS2)
		pms_i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03);
	else if(decoder==PHILIPS1)
		pms_i2c_andor(0x42, 0x06, 0xFC, sharp&0x03);
}

static void pms_chromaagc(short agc)
{
	if(decoder==PHILIPS2)
		pms_i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5);
	else if(decoder==PHILIPS1)
		pms_i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5);
}

static void pms_vertnoise(short noise)
{
	if(decoder==PHILIPS2)
		pms_i2c_andor(0x8A, 0x10, 0xFC, noise&3);
	else if(decoder==PHILIPS1)
		pms_i2c_andor(0x42, 0x10, 0xFC, noise&3);
}

static void pms_forcecolour(short colour)
{
	if(decoder==PHILIPS2)
		pms_i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7);
	else if(decoder==PHILIPS1)
		pms_i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7);
}

static void pms_antigamma(short gamma)
{
	if(decoder==PHILIPS2)
		pms_i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7);
	else if(decoder==PHILIPS1)
		pms_i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7);
}

static void pms_prefilter(short filter)
{
	if(decoder==PHILIPS2)
		pms_i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6);
	else if(decoder==PHILIPS1)
		pms_i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6);
}

static void pms_hfilter(short filter)
{
	if(decoder==PHILIPS2)
		pms_i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5);
	else if(decoder==PHILIPS1)
		pms_i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5);
}

static void pms_vfilter(short filter)
{
	if(decoder==PHILIPS2)
		pms_i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5);
	else if(decoder==PHILIPS1)
		pms_i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5);
}

static void pms_killcolour(short colour)
{
	if(decoder==PHILIPS2)
	{
		pms_i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3);
		pms_i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3);
	}
	else if(decoder==PHILIPS1)
	{
		pms_i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3);
		pms_i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3);
	}
}

static void pms_chromagain(short chroma)
{
	if(decoder==PHILIPS2)
	{
		pms_i2c_write(0x8A, 0x11, chroma);
	}
	else if(decoder==PHILIPS1)
	{
		pms_i2c_write(0x42, 0x11, chroma);
	}
}


static void pms_spacialcompl(short data)
{
	mvv_write(0x3B, data);
}

static void pms_spacialcomph(short data)
{
	mvv_write(0x3A, data);
}

static void pms_vstart(short start)
{
	mvv_write(0x16, start);
	mvv_write(0x17, (start>>8)&0x01);
}

#endif

static void pms_secamcross(short cross)
{
	if(decoder==PHILIPS2)
		pms_i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5);
	else if(decoder==PHILIPS1)
		pms_i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5);
}


static void pms_swsense(short sense)
{
	if(decoder==PHILIPS2)
	{
		pms_i2c_write(0x8A, 0x0A, sense);
		pms_i2c_write(0x8A, 0x0B, sense);
	}
	else if(decoder==PHILIPS1)
	{
		pms_i2c_write(0x42, 0x0A, sense);
		pms_i2c_write(0x42, 0x0B, sense);
	}
}


static void pms_framerate(short frr)
{
	int fps=(standard==1)?30:25;
	if(frr==0)
		return;
	fps=fps/frr;
	mvv_write(0x14,0x80|fps);
	mvv_write(0x15,1);
}

static void pms_vert(u8 deciden, u8 decinum)
{
	mvv_write(0x1C, deciden);	/* Denominator */
	mvv_write(0x1D, decinum);	/* Numerator */
}

/*
 *	Turn 16bit ratios into best small ratio the chipset can grok
 */
498

L
Linus Torvalds 已提交
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
static void pms_vertdeci(unsigned short decinum, unsigned short deciden)
{
	/* Knock it down by /5 once */
	if(decinum%5==0)
	{
		deciden/=5;
		decinum/=5;
	}
	/*
	 *	3's
	 */
	while(decinum%3==0 && deciden%3==0)
	{
		deciden/=3;
		decinum/=3;
	}
	/*
	 *	2's
	 */
	while(decinum%2==0 && deciden%2==0)
	{
		decinum/=2;
		deciden/=2;
	}
	/*
	 *	Fudgyify
	 */
	while(deciden>32)
	{
		deciden/=2;
		decinum=(decinum+1)/2;
	}
	if(deciden==32)
		deciden--;
	pms_vert(deciden,decinum);
}

static void pms_horzdeci(short decinum, short deciden)
{
	if(decinum<=512)
	{
		if(decinum%5==0)
		{
			decinum/=5;
			deciden/=5;
		}
	}
	else
	{
		decinum=512;
		deciden=640;	/* 768 would be ideal */
	}
551

L
Linus Torvalds 已提交
552 553 554 555 556 557 558 559 560 561 562 563
	while(((decinum|deciden)&1)==0)
	{
		decinum>>=1;
		deciden>>=1;
	}
	while(deciden>32)
	{
		deciden>>=1;
		decinum=(decinum+1)>>1;
	}
	if(deciden==32)
		deciden--;
564

L
Linus Torvalds 已提交
565 566 567 568 569 570 571
	mvv_write(0x24, 0x80|deciden);
	mvv_write(0x25, decinum);
}

static void pms_resolution(short width, short height)
{
	int fg_height;
572

L
Linus Torvalds 已提交
573 574 575
	fg_height=height;
	if(fg_height>280)
		fg_height=280;
576

L
Linus Torvalds 已提交
577 578
	mvv_write(0x18, fg_height);
	mvv_write(0x19, fg_height>>8);
579

L
Linus Torvalds 已提交
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
	if(standard==1)
	{
		mvv_write(0x1A, 0xFC);
		mvv_write(0x1B, 0x00);
		if(height>fg_height)
			pms_vertdeci(240,240);
		else
			pms_vertdeci(fg_height,240);
	}
	else
	{
		mvv_write(0x1A, 0x1A);
		mvv_write(0x1B, 0x01);
		if(fg_height>256)
			pms_vertdeci(270,270);
		else
			pms_vertdeci(fg_height, 270);
	}
	mvv_write(0x12,0);
	mvv_write(0x13, MVVMEMORYWIDTH);
	mvv_write(0x42, 0x00);
	mvv_write(0x43, 0x00);
	mvv_write(0x44, MVVMEMORYWIDTH);
603

L
Linus Torvalds 已提交
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
	mvv_write(0x22, width+8);
	mvv_write(0x23, (width+8)>> 8);

	if(standard==1)
		pms_horzdeci(width,640);
	else
		pms_horzdeci(width+8, 768);

	mvv_write(0x30, mvv_read(0x30)&0xFE);
	mvv_write(0x08, mvv_read(0x08)|0x01);
	mvv_write(0x01, mvv_read(0x01)&0xFD);
	mvv_write(0x32, 0x00);
	mvv_write(0x33, MVVMEMORYWIDTH);
}


/*
 *	Set Input
 */
623

L
Linus Torvalds 已提交
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
static void pms_vcrinput(short input)
{
	if(decoder==PHILIPS2)
		pms_i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7);
	else if(decoder==PHILIPS1)
		pms_i2c_andor(0x42,0x0D,0x7F,(input&1)<<7);
}


static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int count)
{
	int y;
	int dw = 2*dev->width;

	char tmp[dw+32]; /* using a temp buffer is faster than direct  */
	int cnt = 0;
	int len=0;
	unsigned char r8 = 0x5;  /* value for reg8  */

	if (rgb555)
		r8 |= 0x20; /* else use untranslated rgb = 565 */
	mvv_write(0x08,r8); /* capture rgb555/565, init DRAM, PC enable */

/*	printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
648 649

	for (y = 0; y < dev->height; y++ )
L
Linus Torvalds 已提交
650 651
	{
		writeb(0, mem);  /* synchronisiert neue Zeile */
652

L
Linus Torvalds 已提交
653 654 655 656
		/*
		 *	This is in truth a fifo, be very careful as if you
		 *	forgot this odd things will occur 8)
		 */
657

L
Linus Torvalds 已提交
658 659
		memcpy_fromio(tmp, mem, dw+32); /* discard 16 word   */
		cnt -= dev->height;
660 661
		while (cnt <= 0)
		{
L
Linus Torvalds 已提交
662 663 664 665 666 667 668 669 670
			/*
			 *	Don't copy too far
			 */
			int dt=dw;
			if(dt+len>count)
				dt=count-len;
			cnt += dev->height;
			if (copy_to_user(buf, tmp+32, dt))
				return len ? len : -EFAULT;
671
			buf += dt;
L
Linus Torvalds 已提交
672 673 674 675 676 677 678 679 680 681 682
			len += dt;
		}
	}
	return len;
}


/*
 *	Video4linux interfacing
 */

683
static int pms_do_ioctl(struct file *file, unsigned int cmd, void *arg)
L
Linus Torvalds 已提交
684 685 686
{
	struct video_device *dev = video_devdata(file);
	struct pms_device *pd=(struct pms_device *)dev;
687

L
Linus Torvalds 已提交
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
	switch(cmd)
	{
		case VIDIOCGCAP:
		{
			struct video_capability *b = arg;
			strcpy(b->name, "Mediavision PMS");
			b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES;
			b->channels = 4;
			b->audios = 0;
			b->maxwidth = 640;
			b->maxheight = 480;
			b->minwidth = 16;
			b->minheight = 16;
			return 0;
		}
		case VIDIOCGCHAN:
		{
			struct video_channel *v = arg;
			if(v->channel<0 || v->channel>3)
				return -EINVAL;
			v->flags=0;
			v->tuners=1;
			/* Good question.. its composite or SVHS so.. */
			v->type = VIDEO_TYPE_CAMERA;
			switch(v->channel)
			{
				case 0:
					strcpy(v->name, "Composite");break;
				case 1:
					strcpy(v->name, "SVideo");break;
				case 2:
					strcpy(v->name, "Composite(VCR)");break;
				case 3:
					strcpy(v->name, "SVideo(VCR)");break;
			}
			return 0;
		}
		case VIDIOCSCHAN:
		{
			struct video_channel *v = arg;
			if(v->channel<0 || v->channel>3)
				return -EINVAL;
730
			mutex_lock(&pd->lock);
L
Linus Torvalds 已提交
731 732
			pms_videosource(v->channel&1);
			pms_vcrinput(v->channel>>1);
733
			mutex_unlock(&pd->lock);
L
Linus Torvalds 已提交
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
			return 0;
		}
		case VIDIOCGTUNER:
		{
			struct video_tuner *v = arg;
			if(v->tuner)
				return -EINVAL;
			strcpy(v->name, "Format");
			v->rangelow=0;
			v->rangehigh=0;
			v->flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
			switch(standard)
			{
				case 0:
					v->mode = VIDEO_MODE_AUTO;
					break;
				case 1:
					v->mode = VIDEO_MODE_NTSC;
					break;
				case 2:
					v->mode = VIDEO_MODE_PAL;
					break;
				case 3:
					v->mode = VIDEO_MODE_SECAM;
					break;
			}
			return 0;
		}
		case VIDIOCSTUNER:
		{
			struct video_tuner *v = arg;
			if(v->tuner)
				return -EINVAL;
767
			mutex_lock(&pd->lock);
L
Linus Torvalds 已提交
768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
			switch(v->mode)
			{
				case VIDEO_MODE_AUTO:
					pms_framerate(25);
					pms_secamcross(0);
					pms_format(0);
					break;
				case VIDEO_MODE_NTSC:
					pms_framerate(30);
					pms_secamcross(0);
					pms_format(1);
					break;
				case VIDEO_MODE_PAL:
					pms_framerate(25);
					pms_secamcross(0);
					pms_format(2);
					break;
				case VIDEO_MODE_SECAM:
					pms_framerate(25);
					pms_secamcross(1);
					pms_format(2);
					break;
				default:
791
					mutex_unlock(&pd->lock);
L
Linus Torvalds 已提交
792 793
					return -EINVAL;
			}
794
			mutex_unlock(&pd->lock);
L
Linus Torvalds 已提交
795 796 797 798 799 800 801 802 803 804 805 806 807
			return 0;
		}
		case VIDIOCGPICT:
		{
			struct video_picture *p = arg;
			*p = pd->picture;
			return 0;
		}
		case VIDIOCSPICT:
		{
			struct video_picture *p = arg;
			if(!((p->palette==VIDEO_PALETTE_RGB565 && p->depth==16)
			    ||(p->palette==VIDEO_PALETTE_RGB555 && p->depth==15)))
808
				return -EINVAL;
L
Linus Torvalds 已提交
809
			pd->picture= *p;
810

L
Linus Torvalds 已提交
811 812 813 814
			/*
			 *	Now load the card.
			 */

815
			mutex_lock(&pd->lock);
L
Linus Torvalds 已提交
816 817 818
			pms_brightness(p->brightness>>8);
			pms_hue(p->hue>>8);
			pms_colour(p->colour>>8);
819
			pms_contrast(p->contrast>>8);
820
			mutex_unlock(&pd->lock);
L
Linus Torvalds 已提交
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
			return 0;
		}
		case VIDIOCSWIN:
		{
			struct video_window *vw = arg;
			if(vw->flags)
				return -EINVAL;
			if(vw->clipcount)
				return -EINVAL;
			if(vw->height<16||vw->height>480)
				return -EINVAL;
			if(vw->width<16||vw->width>640)
				return -EINVAL;
			pd->width=vw->width;
			pd->height=vw->height;
836
			mutex_lock(&pd->lock);
L
Linus Torvalds 已提交
837
			pms_resolution(pd->width, pd->height);
838
			mutex_unlock(&pd->lock);			/* Ok we figured out what to use from our wide choice */
L
Linus Torvalds 已提交
839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
			return 0;
		}
		case VIDIOCGWIN:
		{
			struct video_window *vw = arg;
			memset(vw,0,sizeof(*vw));
			vw->width=pd->width;
			vw->height=pd->height;
			return 0;
		}
		case VIDIOCKEY:
			return 0;
		case VIDIOCCAPTURE:
		case VIDIOCGFBUF:
		case VIDIOCSFBUF:
		case VIDIOCGFREQ:
		case VIDIOCSFREQ:
		case VIDIOCGAUDIO:
		case VIDIOCSAUDIO:
			return -EINVAL;
		default:
			return -ENOIOCTLCMD;
	}
	return 0;
}

static int pms_ioctl(struct inode *inode, struct file *file,
		     unsigned int cmd, unsigned long arg)
{
868
	return video_usercopy(file, cmd, arg, pms_do_ioctl);
L
Linus Torvalds 已提交
869 870 871 872 873 874 875 876
}

static ssize_t pms_read(struct file *file, char __user *buf,
		    size_t count, loff_t *ppos)
{
	struct video_device *v = video_devdata(file);
	struct pms_device *pd=(struct pms_device *)v;
	int len;
877

878
	mutex_lock(&pd->lock);
L
Linus Torvalds 已提交
879
	len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count);
880
	mutex_unlock(&pd->lock);
L
Linus Torvalds 已提交
881 882 883
	return len;
}

884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
static int pms_exclusive_open(struct inode *inode, struct file *file)
{
	struct video_device *v = video_devdata(file);
	struct pms_device *pd = (struct pms_device *)v;

	return test_and_set_bit(0, &pd->in_use) ? -EBUSY : 0;
}

static int pms_exclusive_release(struct inode *inode, struct file *file)
{
	struct video_device *v = video_devdata(file);
	struct pms_device *pd = (struct pms_device *)v;

	clear_bit(0, &pd->in_use);
	return 0;
}

901
static const struct file_operations pms_fops = {
L
Linus Torvalds 已提交
902
	.owner		= THIS_MODULE,
903 904
	.open           = pms_exclusive_open,
	.release        = pms_exclusive_release,
L
Linus Torvalds 已提交
905
	.ioctl          = pms_ioctl,
906
#ifdef CONFIG_COMPAT
907
	.compat_ioctl	= v4l_compat_ioctl32,
908
#endif
L
Linus Torvalds 已提交
909 910 911 912 913 914 915 916
	.read           = pms_read,
	.llseek         = no_llseek,
};

static struct video_device pms_template=
{
	.name		= "Mediavision PMS",
	.fops           = &pms_fops,
917
	.release 	= video_device_release_empty,
L
Linus Torvalds 已提交
918 919 920 921 922 923 924 925
};

static struct pms_device pms_device;


/*
 *	Probe for and initialise the Mediavision PMS
 */
926

L
Linus Torvalds 已提交
927 928 929 930 931
static int init_mediavision(void)
{
	int id;
	int idec, decst;
	int i;
932

L
Linus Torvalds 已提交
933 934 935 936 937 938 939 940 941 942 943 944 945
	unsigned char i2c_defs[]={
		0x4C,0x30,0x00,0xE8,
		0xB6,0xE2,0x00,0x00,
		0xFF,0xFF,0x00,0x00,
		0x00,0x00,0x78,0x98,
		0x00,0x00,0x00,0x00,
		0x34,0x0A,0xF4,0xCE,
		0xE4
	};

	mem = ioremap(mem_base, 0x800);
	if (!mem)
		return -ENOMEM;
946

L
Linus Torvalds 已提交
947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
	if (!request_region(0x9A01, 1, "Mediavision PMS config"))
	{
		printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n");
		iounmap(mem);
		return -EBUSY;
	}
	if (!request_region(io_port, 3, "Mediavision PMS"))
	{
		printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port);
		release_region(0x9A01, 1);
		iounmap(mem);
		return -EBUSY;
	}
	outb(0xB8, 0x9A01);		/* Unlock */
	outb(io_port>>4, 0x9A01);	/* Set IO port */
962 963


L
Linus Torvalds 已提交
964 965
	id=mvv_read(3);
	decst=pms_i2c_stat(0x43);
966

L
Linus Torvalds 已提交
967 968 969 970 971 972
	if(decst!=-1)
		idec=2;
	else if(pms_i2c_stat(0xb9)!=-1)
		idec=3;
	else if(pms_i2c_stat(0x8b)!=-1)
		idec=1;
973
	else
L
Linus Torvalds 已提交
974 975 976 977 978 979 980 981 982 983 984 985 986
		idec=0;

	printk(KERN_INFO "PMS type is %d\n", idec);
	if(idec == 0) {
		release_region(io_port, 3);
		release_region(0x9A01, 1);
		iounmap(mem);
		return -ENODEV;
	}

	/*
	 *	Ok we have a PMS of some sort
	 */
987

L
Linus Torvalds 已提交
988
	mvv_write(0x04, mem_base>>12);	/* Set the memory area */
989

L
Linus Torvalds 已提交
990
	/* Ok now load the defaults */
991

L
Linus Torvalds 已提交
992 993 994 995 996 997 998
	for(i=0;i<0x19;i++)
	{
		if(i2c_defs[i]==0xFF)
			pms_i2c_andor(0x8A, i, 0x07,0x00);
		else
			pms_i2c_write(0x8A, i, i2c_defs[i]);
	}
999

L
Linus Torvalds 已提交
1000 1001 1002 1003 1004 1005 1006 1007
	pms_i2c_write(0xB8,0x00,0x12);
	pms_i2c_write(0xB8,0x04,0x00);
	pms_i2c_write(0xB8,0x07,0x00);
	pms_i2c_write(0xB8,0x08,0x00);
	pms_i2c_write(0xB8,0x09,0xFF);
	pms_i2c_write(0xB8,0x0A,0x00);
	pms_i2c_write(0xB8,0x0B,0x10);
	pms_i2c_write(0xB8,0x10,0x03);
1008

L
Linus Torvalds 已提交
1009 1010 1011 1012
	mvv_write(0x01, 0x00);
	mvv_write(0x05, 0xA0);
	mvv_write(0x08, 0x25);
	mvv_write(0x09, 0x00);
1013 1014
	mvv_write(0x0A, 0x20|MVVMEMORYWIDTH);

L
Linus Torvalds 已提交
1015 1016 1017 1018
	mvv_write(0x10, 0x02);
	mvv_write(0x1E, 0x0C);
	mvv_write(0x1F, 0x03);
	mvv_write(0x26, 0x06);
1019

L
Linus Torvalds 已提交
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
	mvv_write(0x2B, 0x00);
	mvv_write(0x2C, 0x20);
	mvv_write(0x2D, 0x00);
	mvv_write(0x2F, 0x70);
	mvv_write(0x32, 0x00);
	mvv_write(0x33, MVVMEMORYWIDTH);
	mvv_write(0x34, 0x00);
	mvv_write(0x35, 0x00);
	mvv_write(0x3A, 0x80);
	mvv_write(0x3B, 0x10);
	mvv_write(0x20, 0x00);
	mvv_write(0x21, 0x00);
	mvv_write(0x30, 0x22);
	return 0;
}

/*
 *	Initialization and module stuff
 */
1039

1040 1041 1042 1043 1044
#ifndef MODULE
static int enable;
module_param(enable, int, 0);
#endif

L
Linus Torvalds 已提交
1045 1046 1047
static int __init init_pms_cards(void)
{
	printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n");
1048

1049 1050
#ifndef MODULE
	if (!enable) {
1051 1052
		printk(KERN_INFO "PMS: not enabled, use pms.enable=1 to "
				 "probe\n");
1053 1054 1055 1056
		return -ENODEV;
	}
#endif

L
Linus Torvalds 已提交
1057
	data_port = io_port +1;
1058

L
Linus Torvalds 已提交
1059 1060 1061 1062 1063 1064
	if(init_mediavision())
	{
		printk(KERN_INFO "Board not found.\n");
		return -ENODEV;
	}
	memcpy(&pms_device, &pms_template, sizeof(pms_template));
1065
	mutex_init(&pms_device.lock);
L
Linus Torvalds 已提交
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
	pms_device.height=240;
	pms_device.width=320;
	pms_swsense(75);
	pms_resolution(320,240);
	return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER, video_nr);
}

module_param(io_port, int, 0);
module_param(mem_base, int, 0);
module_param(video_nr, int, 0);
MODULE_LICENSE("GPL");


static void __exit shutdown_mediavision(void)
{
	release_region(io_port,3);
	release_region(0x9A01, 1);
}

static void __exit cleanup_pms_module(void)
{
	shutdown_mediavision();
	video_unregister_device((struct video_device *)&pms_device);
	iounmap(mem);
}

module_init(init_pms_cards);
module_exit(cleanup_pms_module);