/*
Hardware driver for Intel i810 LCD/TV
Copyright 2002 Lars Gustavsson <lars@textalk.se>
----------------------------------------------------------
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <linux/miscdevice.h>
#include <linux/smp_lock.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include "i810tv.h"
/*
* core module and version information
*/
#define TV_VERSION "0.1.0"
#define TV_MODULE_NAME "i810_tv"
#define TV_DRIVER_NAME TV_MODULE_NAME " hardware driver " TV_VERSION
#define IO_CTRL 0x5000
#define IO_CTRL_LEN 0x1000
#define CLKPWR_CTRL 0x6000
#define CLKPWR_LEN 0x1000
#define TV_ADDR 0x60000
#define TV_LEN 0x1000
#define TV_MINOR 42
static __u8 *tv_mem, *io_mem, *clk_mem;
static struct semaphore tv_open_sem;
static char * print_bin( int b ) {
static char bin[33];
int i;
for (i=0;i<32;i++) {
bin[i] = b&0x80000000?'1':'0';
b <<= 1;
}
bin[32]='\0';
return bin;
}
static inline void outlong( __u8 *base, int adr, unsigned int val ) {
*((unsigned int *) ( base + adr )) = val;
// writel(tv_mem+adr,val);
printk("i810 set 0x%08x=%s\n", adr, print_bin(val) );
}
static inline unsigned int readlong( __u8 *base, int adr ) {
return *((unsigned int *)(base+adr));
// return readl(tv_mem+adr);
}
static int tv_dev_open (struct inode *inode, struct file *filp)
{
return 0;
}
static int tv_dev_release (struct inode *inode, struct file *filp)
{
return 0;
}
static int tv_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
static struct file_operations tv_chrdev_ops = {
owner: THIS_MODULE,
open: tv_dev_open,
release: tv_dev_release,
ioctl: tv_ioctl,
};
static struct miscdevice tv_miscdev = {
TV_MINOR,
TV_MODULE_NAME,
&tv_chrdev_ops,
};
static int __init tv_init_one (struct pci_dev *dev)
{
unsigned long cadr;
int rc, i;
u8 hw_status;
rc = misc_register (&tv_miscdev);
if (rc) {
printk ("cannot register misc device\n");
goto err_out;
}
cadr = dev->resource[1].start;
printk("0x%08x\n", cadr );
tv_mem = ioremap (cadr + TV_ADDR, TV_LEN);
if (tv_mem == NULL) {
printk ("cannot ioremap TV Memory\n");
rc = -EBUSY;
goto err_out_free_miscdev;
}
io_mem = ioremap_nocache(cadr + IO_CTRL, IO_CTRL_LEN );
if (io_mem == NULL) {
iounmap(tv_mem);
printk ("cannot ioremap io ctrl memory\n");
rc = -EBUSY;
goto err_out_free_miscdev;
}
clk_mem = ioremap_nocache(cadr + CLKPWR_CTRL, CLKPWR_LEN );
if (clk_mem == NULL) {
iounmap(tv_mem);
iounmap(io_mem);
printk ("cannot ioremap io ctrl memory\n");
rc = -EBUSY;
goto err_out_free_miscdev;
}
for(i=0;i<0x20;i+=4)
printk("0x600%02x: 0x%08x\n", i, readlong(tv_mem,i));
printk("---- clock ----\n");
for(i=0;i<=0x14;i+=4)
printk("0x60%02x: 0x%08x\n", i, readlong(clk_mem,i));
printk("0x%08x\n", readlong(clk_mem,0x14));
return 0;
err_out_free_map:
iounmap (tv_mem);
err_out_free_miscdev:
misc_deregister (&tv_miscdev);
err_out:
return rc;
}
/*
* Data for PCI driver interface
*
* This data only exists for exporting the supported
* PCI ids via MODULE_DEVICE_TABLE. We do not actually
* register a pci_driver, because someone else might one day
* want to register another driver on the same PCI id.
*/
static struct pci_device_id tv_pci_tbl[] __initdata = {
{ 0x8086, 0x7121, PCI_ANY_ID, PCI_ANY_ID, },
{ 0x8086, 0x7123, PCI_ANY_ID, PCI_ANY_ID, },
{ 0x8086, 0x7125, PCI_ANY_ID, PCI_ANY_ID, },
{ 0, },
};
MODULE_DEVICE_TABLE (pci, tv_pci_tbl);
MODULE_AUTHOR("Lars Gustavsson");
MODULE_DESCRIPTION("Intel i810 LCD/TV driver");
MODULE_LICENSE("GPL");
static int __init tv_init (void)
{
int rc;
struct pci_dev *pdev;
init_MUTEX (&tv_open_sem);
pci_for_each_dev(pdev) {
if (pci_match_device (tv_pci_tbl, pdev) != NULL)
goto match;
}
return -ENODEV;
match:
rc = tv_init_one (pdev);
if (rc)
return rc;
printk ("i810 LCD/TV driver loaded\n");
return 0;
}
static void __exit tv_cleanup (void)
{
misc_deregister (&tv_miscdev);
iounmap (tv_mem);
}
static int tv_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
unsigned int minor = MINOR(inode->i_rdev);
int retval = 0;
unsigned int memdata, tmpval;
switch ( cmd ) {
case TV_SET_BORDER:
memdata=readlong(tv_mem,0x18);
if (arg)
memdata |= 0x80;
else
memdata &= 0xffffff7f;
outlong(tv_mem,0x18,memdata);
break;
case TV_GET_BORDER:
memdata=readlong(tv_mem,0x18) & 0x80;
if (copy_to_user((int *) arg, &memdata, sizeof(int)))
return -EFAULT;
break;
case TV_SET_CENTER:
memdata=readlong(tv_mem,0x18);
if (arg)
memdata |= 0x20000000;
else
memdata &= 0xdfffffff;
outlong(tv_mem,0x18,memdata);
break;
case TV_GET_CENTER:
memdata=readlong(tv_mem,0x18) & 0x20000000;
if (copy_to_user((int *) arg, &memdata, sizeof(int)))
return -EFAULT;
break;
case TV_SET_VGAOUT:
if (arg) {
// Turn vga output on
outlong(io_mem,0,0);
outlong(clk_mem,0x14,3);
} else {
// Turn off VGA output
outlong(io_mem,0,0);
outlong(clk_mem,0x14,2);
}
break;
case TV_SET_HOR_TOT:
if (!arg)
return -EFAULT;
memdata = ((arg & 0xfff) - 1) << 16;
tmpval = readlong(tv_mem,0x00) & 0xf000ffff;
outlong( tv_mem, 0x00, memdata|tmpval );
tmpval = readlong(tv_mem,0x04) & 0xf000ffff;
outlong( tv_mem, 0x04, memdata|tmpval );
printk("horz: 0x%08x\n",memdata|tmpval);
break;
case TV_SET_VER_TOT:
if (!arg)
return -EFAULT;
memdata = ((arg & 0xfff) - 1) << 16;
tmpval = readlong(tv_mem,0x0c) & 0xf000ffff;
outlong( tv_mem, 0x0c, memdata|tmpval );
tmpval = readlong(tv_mem,0x10) & 0xf000ffff;
outlong( tv_mem, 0x10, memdata|tmpval );
printk("vert: 0x%08x\n",memdata|tmpval);
break;
case TV_GET_VGAOUT:
memdata=readlong(clk_mem,0x14)&1;
if (copy_to_user((int *) arg, &memdata, sizeof(int)))
return -EFAULT;
break;
case TV_SET_TVOUT:
if (arg) {
// LCD/TV on
outlong(tv_mem,0x00,0x03a7031f);
outlong(tv_mem,0x04,0x03a7031f);
outlong(tv_mem,0x08,0x04a30363);
outlong(tv_mem,0x0c,0x03430257);
outlong(tv_mem,0x10,0x03430257);
outlong(tv_mem,0x14,0x0290028d);
outlong(tv_mem,0x18,0xa0000007);
outlong(clk_mem,0x00,0x00030013);
outlong(clk_mem,0x04,0x00100053);
outlong(clk_mem,0x08,0x0011007b);
outlong(clk_mem,0x0c,0x0001000a);
outlong(clk_mem,0x10,0x30404040);
} else {
outlong(tv_mem,0x18,0x00000000);
outlong(clk_mem,0x00,0x00030013);
outlong(clk_mem,0x04,0x00100053);
outlong(clk_mem,0x08,0x0011007b);
outlong(clk_mem,0x0c,0x00030013);
outlong(clk_mem,0x10,0x40404040);
}
break;
case TV_GET_VSYNC_START:
memdata=readlong(tv_mem,0x14)&0x00000fff;
if (copy_to_user((int *) arg, &memdata, sizeof(int)))
return -EFAULT;
break;
case TV_SET_VSYNC_START:
memdata=readlong(tv_mem,0x14)&0xfffff000;
memdata |= (arg & 0xfff);
outlong(tv_mem,0x14,memdata);
break;
case TV_GET_VSYNC_END:
memdata=(readlong(tv_mem,0x14)>>16)&0x00000fff;
if (copy_to_user((int *) arg, &memdata, sizeof(int)))
return -EFAULT;
break;
case TV_SET_VSYNC_END:
memdata=readlong(tv_mem,0x14)&0xf000ffff;
memdata |= ((arg&0xfff)<<16);
outlong(tv_mem,0x14,memdata);
break;
case TV_GET_HSYNC_START:
memdata=readlong(tv_mem,0x08)&0x00000fff;
if (copy_to_user((int *) arg, &memdata, sizeof(int)))