/*
Media Vision Pro Movie Studio
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/isa.h>
#include <asm/io.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-device.h>
MODULE_LICENSE("GPL");
MODULE_VERSION("0.0.5");
#define MOTOROLA 1
#define PHILIPS2 2 /* SAA7191 */
#define PHILIPS1 3
#define MVVMEMORYWIDTH 0x40 /* 512 bytes */
struct i2c_info {
u8 slave;
u8 sub;
u8 data;
u8 hits;
};
struct pms {
struct v4l2_device v4l2_dev;
struct video_device vdev;
struct v4l2_ctrl_handler hdl;
int height;
int width;
int depth;
int input;
struct mutex lock;
int i2c_count;
struct i2c_info i2cinfo[64];
int decoder;
int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
v4l2_std_id std;
int io;
int data;
void __iomem *mem;
};
/*
* I/O ports and Shared Memory
*/
static int io_port = 0x250;
module_param(io_port, int, 0);
static int mem_base = 0xc8000;
module_param(mem_base, int, 0);
static int video_nr = -1;
module_param(video_nr, int, 0);
static inline void mvv_write(struct pms *dev, u8 index, u8 value)
{
outw(index | (value << 8), dev->io);
}
static inline u8 mvv_read(struct pms *dev, u8 index)
{
outb(index, dev->io);
return inb(dev->data);
}
static int pms_i2c_stat(struct pms *dev, u8 slave)
{
int counter = 0;
int i;
outb(0x28, dev->io);
while ((inb(dev->data) & 0x01) == 0)
if (counter++ == 256)
break;
while ((inb(dev->data) & 0x01) != 0)
if (counter++ == 256)
break;
outb(slave, dev->io);
counter = 0;
while ((inb(dev->data) & 0x01) == 0)
if (counter++ == 256)
break;
while ((inb(dev->data) & 0x01) != 0)
if (counter++ == 256)
break;
for (i = 0; i < 12; i++) {
char st = inb(dev->data);
if ((st & 2) != 0)
return -1;
if ((st & 1) == 0)
break;
}
outb(0x29, dev->io);
return inb(dev->data);
}
static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data)
{
int skip = 0;
int count;
int i;
for (i = 0; i < dev->i2c_count; i++) {
if ((dev->i2cinfo[i].slave == slave) &&
(dev->i2cinfo[i].sub == sub)) {
if (dev->i2cinfo[i].data == data)
skip = 1;
dev->i2cinfo[i].data = data;
i = dev->i2c_count + 1;
}
}
if (i == dev->i2c_count && dev->i2c_count < 64) {
dev->i2cinfo[dev->i2c_count].slave = slave;
dev->i2cinfo[dev->i2c_count].sub = sub;
dev->i2cinfo[dev->i2c_count].data = data;
dev->i2c_count++;
}
if (skip)
return 0;
mvv_write(dev, 0x29, sub);
mvv_write(dev, 0x2A, data);
mvv_write(dev, 0x28, slave);
outb(0x28, dev->io);
count = 0;
while ((inb(dev->data) & 1) == 0)
if (count > 255)
break;
while ((inb(dev->data) & 1) != 0)
if (count > 255)
break;
count = inb(dev->data);
if (count & 2)
return -1;
return count;
}
static int pms_i2c_read(struct pms *dev, int slave, int sub)
{
int i;
for (i = 0; i < dev->i2c_count; i++) {
if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub)
return dev->i2cinfo[i].data;
}
return 0;
}
static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or)
{
u8 tmp;
tmp = pms_i2c_read(dev, slave, sub);
tmp = (tmp & and) | or;
pms_i2c_write(dev, slave, sub, tmp);
}
/*
* Control functions
*/
static void pms_videosource(struct pms *dev, short source)
{
switch (dev->decoder) {
case MOTOROLA:
break;
case PHILIPS2:
pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0);
break;
case PHILIPS1:
break;
}
mvv_write(dev, 0x2E, 0x31);
/* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30);
But could not make this work correctly. Only Composite input
worked for me. */
}
static void pms_hue(struct pms *dev, short hue)
{
switch (dev->decoder) {
case MOTOROLA:
pms_i2c_write(dev, 0x8a, 0x00, hue);
break;
case PHILIPS2:
pms_i2c_write(dev, 0x8a, 0x07, hue);
break;
case PHILIPS1:
pms_i2c_write(dev, 0x42, 0x07, hue);
break;
}
}
static void pms_saturation(struct pms *dev, short sat)
{
switch (dev->decoder) {
case MOTOROLA:
pms_i2c_write(dev, 0x8a, 0x00, sat);
break;
case PHILIPS1:
pms_i2c_write(dev, 0x42, 0x12, sat);
break;
}
}
static void pms_contrast(struct pms *dev, short contrast)
{
switch (dev->decoder) {
case MOTOROLA:
pms_i2c_write(dev, 0x8a, 0x00, contrast);
break;
case PHILIPS1:
pms_i2c_write(dev, 0x42, 0x13, contrast);
break;
}
}
static void pms_brightness(struct pms *dev, short brightness)
{
switch (dev->decoder) {
case MOTOROLA:
pms_i2c_write(dev, 0x8a, 0x00, brightness);
pms_i2c_write(dev, 0x8a, 0x00, brightness);
pms_i2c_write(dev, 0x8a, 0x00, brightness);
break;
case PHILIPS1:
pms_i2c_write(dev, 0x42, 0x19, brightness);
break;
}
}
static void pms_format(struct pms *dev, short format)
{
int target;
dev->standard = format;
if (dev->decoder == PHILIPS1)
target = 0x42;
else if (dev->decoder == PHILIPS2)
target = 0x8a;
else
return;
switch (format) {
case 0: /* Auto */
pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80);
break;
case 1: /* NTSC */
pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40);
break;
case 2: /* PAL */
pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
break;
case 3: /* SECAM */
pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01);
pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
break;
}
}
#ifdef FOR_FUTURE_EXPANSION
/*
* These features of the PMS card are not currently exposes. They
* could become a private v4l ioctl for PMSCONFIG or somesuch if
* people need it. We also don't yet use the PMS interrupt.
*/
static void pms_hstart(struct pms *dev, short start)
{
switch (dev->decoder) {
case PHILIPS1:
pms_i2c_write(dev, 0x8a, 0x05, start);
pms_i2c_write(dev, 0x8a, 0x18, start);
break;
case PHILIPS2:
pms_i2c_write(dev, 0x42, 0x05, start);
pms_i2c_write(dev, 0x42, 0x18, start);
break;
}
}
/*
* Bandpass filters
*/
static void pms_bandpass(struct pms *dev, short pass)
{
if (dev->decoder == PHILIPS2)
pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4);
else if (dev->decoder == PHILIPS1)
pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4);
}
static void pms_antisnow(struct pms *dev, short snow)
{
if (dev->decoder == PHILIPS2)
pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2);
else if (dev->decoder == PHILIPS1)
pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2);
}
static void pms_sharpness(struct pms *dev, short sharp)
{
if (dev->decoder == PHILIPS2)
pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03);
else if (dev->decoder == PHILIPS1)
pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03);
}
static void pms_chromaagc(struct pms *dev, short agc)
{
if (dev->decoder == PHILIPS2)
pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5);
else if (dev->decoder == PHILIPS1)
pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5);
}
static void pms_vertnoise(struct pms *dev, short noise)
{
if (dev->decoder == PHILIPS2)
pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3);
else if (dev->decoder == PHILIPS1)
pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3);
}
static void pms_forcecolour(struct pms *dev, short colour)
{
if (dev->decoder == PHILIPS2)
pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7);
else if (dev->decoder == PHILIPS1)
pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7);
}
static void pms_antigamma(struct pms *dev, short gamma)
{
if (dev->decoder == PHILIPS2)
pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7);
else if (dev->decoder == PHILIPS1)
pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7);
}
static v