Subversion Repositories shark

Compare Revisions

Ignore whitespace Rev 424 → Rev 425

/shark/trunk/drivers/bttv/include/drivers/bttv.h
17,8 → 17,8
#include "drivers/compbttv.h"
 
#include "drivers/videodev.h"
#include "drivers/i2c.h"
#include "drivers/i2c-algo-bit.h"
#include "linux/i2c.h"
#include "linux/i2c-algo-bit.h"
 
#include "drivers/bt848.h"
#include "drivers/audiochip.h"
/shark/trunk/drivers/bttv/makefile
12,7 → 12,7
 
OBJS = gpio.o bttv-if.o bttv-cards.o bttv-driver.o fg.o
 
OTHERINCL += -I$(BASE)/drivers/bttv/include -I$(BASE)/drivers/linuxc24/include -I$(BASE)/drivers/i2c/include
OTHERINCL += -I$(BASE)/drivers/bttv/include -I$(BASE)/drivers/linuxc24/include
 
C_OPT += -D__KERNEL__
 
/shark/trunk/drivers/cm7326/saa7146_core.c
0,0 → 1,583
/*
saa7146.o - driver for generic saa7146-based hardware
 
Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#include <media/saa7146.h>
 
/* global variables */
struct list_head saa7146_devices;
struct semaphore saa7146_devices_lock;
 
static int initialized = 0;
int saa7146_num = 0;
 
unsigned int saa7146_debug = 0;
 
MODULE_PARM(saa7146_debug,"i");
MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)");
 
#if 0
static void dump_registers(struct saa7146_dev* dev)
{
int i = 0;
 
INFO((" @ %li jiffies:\n",jiffies));
for(i = 0; i <= 0x148; i+=4) {
printk("0x%03x: 0x%08x\n",i,saa7146_read(dev,i));
}
}
#endif
 
/****************************************************************************
* gpio and debi helper functions
****************************************************************************/
 
/* write "data" to the gpio-pin "pin" */
void saa7146_set_gpio(struct saa7146_dev *dev, u8 pin, u8 data)
{
u32 value = 0;
 
/* sanity check */
if(pin > 3)
return;
 
/* read old register contents */
value = saa7146_read(dev, GPIO_CTRL );
value &= ~(0xff << (8*pin));
value |= (data << (8*pin));
 
saa7146_write(dev, GPIO_CTRL, value);
}
 
/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */
int saa7146_wait_for_debi_done(struct saa7146_dev *dev)
{
int start;
 
/* wait for registers to be programmed */
start = jiffies;
while (1) {
if (saa7146_read(dev, MC2) & 2)
break;
if (jiffies-start > HZ/20) {
DEB_S(("timed out while waiting for registers getting programmed\n"));
return -ETIMEDOUT;
}
}
 
/* wait for transfer to complete */
start = jiffies;
while (1) {
if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
break;
saa7146_read(dev, MC2);
if (jiffies-start > HZ/4) {
DEB_S(("timed out while waiting for transfer completion\n"));
return -ETIMEDOUT;
}
}
 
return 0;
}
 
/****************************************************************************
* general helper functions
****************************************************************************/
 
/* this is videobuf_vmalloc_to_sg() from video-buf.c
make sure virt has been allocated with vmalloc_32(), otherwise the BUG()
may be triggered on highmem machines */
static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages)
{
struct scatterlist *sglist;
struct page *pg;
int i;
 
sglist = kmalloc(sizeof(struct scatterlist)*nr_pages, GFP_KERNEL);
if (NULL == sglist)
return NULL;
memset(sglist,0,sizeof(struct scatterlist)*nr_pages);
for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
pg = vmalloc_to_page(virt);
if (NULL == pg)
goto err;
if (PageHighMem(pg))
BUG();
sglist[i].page = pg;
sglist[i].length = PAGE_SIZE;
}
return sglist;
err:
kfree(sglist);
return NULL;
}
 
/********************************************************************************/
/* common page table functions */
 
#define SAA7146_PGTABLE_SIZE 4096
 
char *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt)
{
int pages = (length+PAGE_SIZE-1)/PAGE_SIZE;
char *mem = vmalloc_32(length);
int slen = 0;
 
if (NULL == mem) {
return NULL;
}
 
if (!(pt->slist = vmalloc_to_sg(mem, pages))) {
vfree(mem);
return NULL;
}
 
if (saa7146_pgtable_alloc(pci, pt)) {
kfree(pt->slist);
pt->slist = NULL;
vfree(mem);
return NULL;
}
slen = pci_map_sg(pci,pt->slist,pages,PCI_DMA_FROMDEVICE);
if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen)) {
return NULL;
}
 
return mem;
}
 
void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
{
if (NULL == pt->cpu)
return;
pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
pt->cpu = NULL;
if (NULL != pt->slist) {
kfree(pt->slist);
pt->slist = NULL;
}
}
 
int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
{
u32 *cpu;
dma_addr_t dma_addr;
 
cpu = pci_alloc_consistent(pci, SAA7146_PGTABLE_SIZE, &dma_addr);
if (NULL == cpu) {
return -ENOMEM;
}
pt->size = SAA7146_PGTABLE_SIZE;
pt->cpu = cpu;
pt->dma = dma_addr;
 
return 0;
}
 
int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt,
struct scatterlist *list, int sglen )
{
u32 *ptr, fill;
int nr_pages = 0;
int i,p;
 
BUG_ON( 0 == sglen);
if (list->offset > PAGE_SIZE) {
DEB_D(("offset > PAGE_SIZE. this should not happen."));
return -EINVAL;
}
/* if we have a user buffer, the first page may not be
aligned to a page boundary. */
pt->offset = list->offset;
 
ptr = pt->cpu;
for (i = 0; i < sglen; i++, list++) {
/*
printk("i:%d, adr:0x%08x, len:%d, offset:%d\n", i,sg_dma_address(list), sg_dma_len(list), list->offset);
*/
for (p = 0; p * 4096 < list->length; p++, ptr++) {
*ptr = sg_dma_address(list) + p * 4096;
nr_pages++;
}
}
 
 
/* safety; fill the page table up with the last valid page */
fill = *(ptr-1);
for(i=nr_pages;i<1024;i++) {
*ptr++ = fill;
}
 
/*
ptr = pt->cpu;
printk("offset: %d\n",pt->offset);
for(i=0;i<5;i++) {
printk("ptr1 %d: 0x%08x\n",i,ptr[i]);
}
*/
return 0;
}
 
/********************************************************************************/
/* gpio functions */
 
void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data)
{
u32 val = 0;
 
val=saa7146_read(dev,GPIO_CTRL);
val&=~(0xff << (8*(port)));
val|=(data)<<(8*(port));
saa7146_write(dev, GPIO_CTRL, val);
}
 
/********************************************************************************/
/* interrupt handler */
static irqreturn_t interrupt_hw(int irq, void *dev_id, struct pt_regs *regs)
{
struct saa7146_dev *dev = (struct saa7146_dev*)dev_id;
u32 isr = 0;
 
/* read out the interrupt status register */
isr = saa7146_read(dev, ISR);
 
/* is this our interrupt? */
if ( 0 == isr ) {
/* nope, some other device */
return IRQ_NONE;
}
 
saa7146_write(dev, ISR, isr);
 
if( 0 != (dev->ext)) {
if( 0 != (dev->ext->irq_mask & isr )) {
if( 0 != dev->ext->irq_func ) {
dev->ext->irq_func(dev, &isr);
}
isr &= ~dev->ext->irq_mask;
}
}
if (0 != (isr & (MASK_27))) {
DEB_INT(("irq: RPS0 (0x%08x).\n",isr));
if( 0 != dev->vv_data && 0 != dev->vv_callback) {
dev->vv_callback(dev,isr);
}
isr &= ~MASK_27;
}
if (0 != (isr & (MASK_28))) {
if( 0 != dev->vv_data && 0 != dev->vv_callback) {
dev->vv_callback(dev,isr);
}
isr &= ~MASK_28;
}
if (0 != (isr & (MASK_16|MASK_17))) {
u32 status = saa7146_read(dev, I2C_STATUS);
if( (0x3 == (status & 0x3)) || (0 == (status & 0x1)) ) {
IER_DISABLE(dev, MASK_16|MASK_17);
/* only wake up if we expect something */
if( 0 != dev->i2c_op ) {
u32 psr = (saa7146_read(dev, PSR) >> 16) & 0x2;
u32 ssr = (saa7146_read(dev, SSR) >> 17) & 0x1f;
DEB_I2C(("irq: i2c, status: 0x%08x, psr:0x%02x, ssr:0x%02x).\n",status,psr,ssr));
dev->i2c_op = 0;
wake_up(&dev->i2c_wq);
} else {
DEB_I2C(("unexpected irq: i2c, status: 0x%08x, isr %#x\n",status, isr));
}
} else {
DEB_I2C(("unhandled irq: i2c, status: 0x%08x, isr %#x\n",status, isr));
}
isr &= ~(MASK_16|MASK_17);
}
if( 0 != isr ) {
ERR(("warning: interrupt enabled, but not handled properly.(0x%08x)\n",isr));
ERR(("disabling interrupt source(s)!\n"));
IER_DISABLE(dev,isr);
}
return IRQ_HANDLED;
}
 
/*********************************************************************************/
/* configuration-functions */
 
static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent)
{
unsigned long adr = 0, len = 0;
struct saa7146_dev* dev = kmalloc (sizeof(struct saa7146_dev),GFP_KERNEL);
struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data;
struct saa7146_extension* ext = pci_ext->ext;
int err = 0;
if (!(dev = kmalloc (sizeof(struct saa7146_dev),GFP_KERNEL))) {
ERR(("out of memory.\n"));
return -ENOMEM;
}
 
/* clear out mem for sure */
memset(dev, 0x0, sizeof(struct saa7146_dev));
 
DEB_EE(("pci:%p\n",pci));
if (pci_enable_device(pci)) {
ERR(("pci_enable_device() failed.\n"));
err = -EIO;
goto pci_error;
}
 
/* enable bus-mastering */
pci_set_master(pci);
 
dev->pci = pci;
/* get chip-revision; this is needed to enable bug-fixes */
if( 0 > pci_read_config_dword(dev->pci, PCI_CLASS_REVISION, &dev->revision)) {
ERR(("pci_read_config_dword() failed.\n"));
err = -ENODEV;
goto pci_error;
}
dev->revision &= 0xf;
 
/* remap the memory from virtual to physical adress */
adr = pci_resource_start(pci,0);
len = pci_resource_len(pci,0);
 
if (!request_mem_region(pci_resource_start(pci,0), pci_resource_len(pci,0), "saa7146")) {
ERR(("request_mem_region() failed.\n"));
err = -ENODEV;
goto pci_error;
}
 
if (!(dev->mem = ioremap(adr,len))) {
ERR(("ioremap() failed.\n"));
err = -ENODEV;
goto ioremap_error;
}
 
/* we don't do a master reset here anymore, it screws up
some boards that don't have an i2c-eeprom for configuration
values */
/*
saa7146_write(dev, MC1, MASK_31);
*/
 
/* disable all irqs */
saa7146_write(dev, IER, 0);
 
/* shut down all dma transfers */
saa7146_write(dev, MC1, 0x00ff0000);
 
/* clear out any rps-signals pending */
saa7146_write(dev, MC2, 0xf8000000);
 
/* request an interrupt for the saa7146 */
if (request_irq(dev->pci->irq, interrupt_hw, SA_SHIRQ | SA_INTERRUPT,
dev->name, dev))
{
ERR(("request_irq() failed.\n"));
err = -ENODEV;
goto irq_error;
}
 
/* get memory for various stuff */
dev->d_rps0.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_RPS_MEM, &dev->d_rps0.dma_handle);
if( NULL == dev->d_rps0.cpu_addr ) {
err = -ENOMEM;
goto kmalloc_error_1;
}
memset(dev->d_rps0.cpu_addr, 0x0, SAA7146_RPS_MEM);
 
dev->d_rps1.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_RPS_MEM, &dev->d_rps1.dma_handle);
if( NULL == dev->d_rps1.cpu_addr ) {
err = -ENOMEM;
goto kmalloc_error_2;
}
memset(dev->d_rps1.cpu_addr, 0x0, SAA7146_RPS_MEM);
dev->d_i2c.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_RPS_MEM, &dev->d_i2c.dma_handle);
if( NULL == dev->d_i2c.cpu_addr ) {
err = -ENOMEM;
goto kmalloc_error_3;
}
memset(dev->d_i2c.cpu_addr, 0x0, SAA7146_RPS_MEM);
 
/* the rest + print status message */
/* create a nice device name */
sprintf(&dev->name[0], "saa7146 (%d)",saa7146_num);
 
INFO(("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x).\n", dev->mem, dev->revision,dev->pci->irq,dev->pci->subsystem_vendor,dev->pci->subsystem_device));
dev->ext = ext;
 
pci_set_drvdata(pci,dev);
 
init_MUTEX(&dev->lock);
dev->int_slock = SPIN_LOCK_UNLOCKED;
dev->slock = SPIN_LOCK_UNLOCKED;
 
init_MUTEX(&dev->i2c_lock);
 
dev->module = THIS_MODULE;
init_waitqueue_head(&dev->i2c_wq);
 
/* set some sane pci arbitrition values */
saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
 
if( 0 != ext->probe) {
if( 0 != ext->probe(dev) ) {
DEB_D(("ext->probe() failed for %p. skipping device.\n",dev));
err = -ENODEV;
goto probe_error;
}
}
 
if( 0 != ext->attach(dev,pci_ext) ) {
DEB_D(("ext->attach() failed for %p. skipping device.\n",dev));
err = -ENODEV;
goto attach_error;
}
 
INIT_LIST_HEAD(&dev->item);
list_add_tail(&dev->item,&saa7146_devices);
saa7146_num++;
 
err = 0;
goto out;
attach_error:
probe_error:
pci_set_drvdata(pci,NULL);
pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle);
kmalloc_error_3:
pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle);
kmalloc_error_2:
pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle);
kmalloc_error_1:
free_irq(dev->pci->irq, (void *)dev);
irq_error:
iounmap(dev->mem);
ioremap_error:
release_mem_region(adr,len);
pci_error:
kfree(dev);
out:
return err;
}
 
static void saa7146_remove_one(struct pci_dev *pdev)
{
struct saa7146_dev* dev = (struct saa7146_dev*) pci_get_drvdata(pdev);
DEB_EE(("dev:%p\n",dev));
 
dev->ext->detach(dev);
 
/* shut down all video dma transfers */
saa7146_write(dev, MC1, 0x00ff0000);
 
/* disable all irqs, release irq-routine */
saa7146_write(dev, IER, 0);
 
free_irq(dev->pci->irq, (void *)dev);
 
/* free kernel memory */
pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle);
pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle);
pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle);
 
iounmap(dev->mem);
release_mem_region(pci_resource_start(dev->pci,0), pci_resource_len(dev->pci,0));
 
list_del(&dev->item);
kfree(dev);
 
saa7146_num--;
}
 
/*********************************************************************************/
/* extension handling functions */
 
int saa7146_register_extension(struct saa7146_extension* ext)
{
DEB_EE(("ext:%p\n",ext));
 
if( 0 == initialized ) {
INIT_LIST_HEAD(&saa7146_devices);
init_MUTEX(&saa7146_devices_lock);
initialized = 1;
}
 
ext->driver.name = ext->name;
ext->driver.id_table = ext->pci_tbl;
ext->driver.probe = saa7146_init_one;
ext->driver.remove = saa7146_remove_one;
 
printk("saa7146: register extension '%s'.\n",ext->name);
return pci_module_init(&ext->driver);
}
 
int saa7146_unregister_extension(struct saa7146_extension* ext)
{
DEB_EE(("ext:%p\n",ext));
printk("saa7146: unregister extension '%s'.\n",ext->name);
pci_unregister_driver(&ext->driver);
return 0;
}
 
static int __init saa7146_init_module(void)
{
if( 0 == initialized ) {
INIT_LIST_HEAD(&saa7146_devices);
init_MUTEX(&saa7146_devices_lock);
initialized = 1;
}
return 0;
}
 
static void __exit saa7146_cleanup_module(void)
{
}
 
module_init(saa7146_init_module);
module_exit(saa7146_cleanup_module);
 
EXPORT_SYMBOL_GPL(saa7146_register_extension);
EXPORT_SYMBOL_GPL(saa7146_unregister_extension);
 
/* misc functions used by extension modules */
EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc);
EXPORT_SYMBOL_GPL(saa7146_pgtable_free);
EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single);
EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable);
EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done);
 
EXPORT_SYMBOL_GPL(saa7146_setgpio);
 
EXPORT_SYMBOL_GPL(saa7146_i2c_transfer);
EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare);
 
EXPORT_SYMBOL_GPL(saa7146_debug);
EXPORT_SYMBOL_GPL(saa7146_devices);
EXPORT_SYMBOL_GPL(saa7146_devices_lock);
 
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("driver for generic saa7146-based hardware");
MODULE_LICENSE("GPL");
/shark/trunk/drivers/cm7326/hexium_orion.c
0,0 → 1,514
/*
hexium_orion.c - v4l2 driver for the Hexium Orion frame grabber cards
 
Visit http://www.mihu.de/linux/saa7146/ and follow the link
to "hexium" for further details about this card.
Copyright (C) 2003 Michael Hunold <michael@mihu.de>
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#define DEBUG_VARIABLE debug
 
#include <media/saa7146_vv.h>
 
static int debug = 0;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "debug verbosity");
 
/* global variables */
int hexium_num = 0;
 
#define HEXIUM_HV_PCI6_ORION 1
#define HEXIUM_ORION_1SVHS_3BNC 2
#define HEXIUM_ORION_4BNC 3
 
#define HEXIUM_INPUTS 9
static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
{ 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
};
 
#define HEXIUM_AUDIOS 0
 
struct hexium_data
{
s8 adr;
u8 byte;
};
 
static struct saa7146_extension_ioctls ioctls[] = {
{ VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_S_STD, SAA7146_AFTER },
{ 0, 0 }
};
 
struct hexium
{
int type;
struct video_device video_dev;
struct i2c_adapter i2c_adapter;
int cur_input; /* current input */
};
 
/* Philips SAA7110 decoder default registers */
static u8 hexium_saa7110[53]={
/*00*/ 0x4C,0x3C,0x0D,0xEF,0xBD,0xF0,0x00,0x00,
/*08*/ 0xF8,0xF8,0x60,0x60,0x40,0x86,0x18,0x90,
/*10*/ 0x00,0x2C,0x40,0x46,0x42,0x1A,0xFF,0xDA,
/*18*/ 0xF0,0x8B,0x00,0x00,0x00,0x00,0x00,0x00,
/*20*/ 0xD9,0x17,0x40,0x41,0x80,0x41,0x80,0x4F,
/*28*/ 0xFE,0x01,0x0F,0x0F,0x03,0x01,0x81,0x03,
/*30*/ 0x44,0x75,0x01,0x8C,0x03
};
 
static struct {
struct hexium_data data[8];
} hexium_input_select[] = {
{
{ /* cvbs 1 */
{ 0x06, 0x00 },
{ 0x20, 0xD9 },
{ 0x21, 0x17 }, // 0x16,
{ 0x22, 0x40 },
{ 0x2C, 0x03 },
{ 0x30, 0x44 },
{ 0x31, 0x75 }, // ??
{ 0x21, 0x16 }, // 0x03,
}
}, {
{ /* cvbs 2 */
{ 0x06, 0x00 },
{ 0x20, 0x78 },
{ 0x21, 0x07 }, // 0x03,
{ 0x22, 0xD2 },
{ 0x2C, 0x83 },
{ 0x30, 0x60 },
{ 0x31, 0xB5 }, // ?
{ 0x21, 0x03 },
}
}, {
{ /* cvbs 3 */
{ 0x06, 0x00 },
{ 0x20, 0xBA },
{ 0x21, 0x07 }, // 0x05,
{ 0x22, 0x91 },
{ 0x2C, 0x03 },
{ 0x30, 0x60 },
{ 0x31, 0xB5 }, // ??
{ 0x21, 0x05 }, // 0x03,
}
}, {
{ /* cvbs 4 */
{ 0x06, 0x00 },
{ 0x20, 0xD8 },
{ 0x21, 0x17 }, // 0x16,
{ 0x22, 0x40 },
{ 0x2C, 0x03 },
{ 0x30, 0x44 },
{ 0x31, 0x75 }, // ??
{ 0x21, 0x16 }, // 0x03,
}
}, {
{ /* cvbs 5 */
{ 0x06, 0x00 },
{ 0x20, 0xB8 },
{ 0x21, 0x07 }, // 0x05,
{ 0x22, 0x91 },
{ 0x2C, 0x03 },
{ 0x30, 0x60 },
{ 0x31, 0xB5 }, // ??
{ 0x21, 0x05 }, // 0x03,
}
}, {
{ /* cvbs 6 */
{ 0x06, 0x00 },
{ 0x20, 0x7C },
{ 0x21, 0x07 }, // 0x03
{ 0x22, 0xD2 },
{ 0x2C, 0x83 },
{ 0x30, 0x60 },
{ 0x31, 0xB5 }, // ??
{ 0x21, 0x03 },
}
}, {
{ /* y/c 1 */
{ 0x06, 0x80 },
{ 0x20, 0x59 },
{ 0x21, 0x17 },
{ 0x22, 0x42 },
{ 0x2C, 0xA3 },
{ 0x30, 0x44 },
{ 0x31, 0x75 },
{ 0x21, 0x12 },
}
}, {
{ /* y/c 2 */
{ 0x06, 0x80 },
{ 0x20, 0x9A },
{ 0x21, 0x17 },
{ 0x22, 0xB1 },
{ 0x2C, 0x13 },
{ 0x30, 0x60 },
{ 0x31, 0xB5 },
{ 0x21, 0x14 },
}
}, {
{ /* y/c 3 */
{ 0x06, 0x80 },
{ 0x20, 0x3C },
{ 0x21, 0x27 },
{ 0x22, 0xC1 },
{ 0x2C, 0x23 },
{ 0x30, 0x44 },
{ 0x31, 0x75 },
{ 0x21, 0x21 },
}
}
};
 
static struct saa7146_standard hexium_standards[] = {
{
.name = "PAL", .id = V4L2_STD_PAL,
.v_offset = 16, .v_field = 288, .v_calc = 576,
.h_offset = 1, .h_pixels = 680, .h_calc = 680+1,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "NTSC", .id = V4L2_STD_NTSC,
.v_offset = 16, .v_field = 240, .v_calc = 480,
.h_offset = 1, .h_pixels = 640, .h_calc = 641+1,
.v_max_out = 480, .h_max_out = 640,
}, {
.name = "SECAM", .id = V4L2_STD_SECAM,
.v_offset = 16, .v_field = 288, .v_calc = 576,
.h_offset = 1, .h_pixels = 720, .h_calc = 720+1,
.v_max_out = 576, .h_max_out = 768,
}
};
 
/* this is only called for old HV-PCI6/Orion cards
without eeprom */
static int hexium_probe(struct saa7146_dev *dev)
{
struct hexium *hexium = 0;
union i2c_smbus_data data;
int err = 0;
 
DEB_EE((".\n"));
 
/* there are no hexium orion cards with revision 0 saa7146s */
if (0 == dev->revision) {
return -EFAULT;
}
 
hexium = (struct hexium *) kmalloc(sizeof(struct hexium), GFP_KERNEL);
if (NULL == hexium) {
printk("hexium_orion: hexium_probe: not enough kernel memory.\n");
return -ENOMEM;
}
memset(hexium, 0x0, sizeof(struct hexium));
 
saa7146_write(dev, DD1_INIT, 0x01000100);
saa7146_write(dev, DD1_STREAM_B, 0x00000000);
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
 
saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
DEB_S(("cannot register i2c-device. skipping.\n"));
kfree(hexium);
return -EFAULT;
}
 
/* set SAA7110 control GPIO 0 */
saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI);
/* set HWControl GPIO number 2 */
saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
 
mdelay(10);
 
/* detect newer Hexium Orion cards by subsystem ids */
if (0x17c8 == dev->pci->subsystem_vendor && 0x0101 == dev->pci->subsystem_device) {
printk("hexium_orion: device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs.\n");
/* we store the pointer in our private data field */
(struct hexium *) dev->ext_priv = hexium;
hexium->type = HEXIUM_ORION_1SVHS_3BNC;
return 0;
}
 
if (0x17c8 == dev->pci->subsystem_vendor && 0x2101 == dev->pci->subsystem_device) {
printk("hexium_orion: device is a Hexium Orion w/ 4 BNC inputs.\n");
/* we store the pointer in our private data field */
(struct hexium *) dev->ext_priv = hexium;
hexium->type = HEXIUM_ORION_4BNC;
return 0;
}
 
/* check if this is an old hexium Orion card by looking at
a saa7110 at address 0x4e */
if (0 == (err = i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, &data))) {
printk("hexium_orion: device is a Hexium HV-PCI6/Orion (old).\n");
/* we store the pointer in our private data field */
(struct hexium *) dev->ext_priv = hexium;
hexium->type = HEXIUM_HV_PCI6_ORION;
return 0;
}
 
i2c_del_adapter(&hexium->i2c_adapter);
kfree(hexium);
return -EFAULT;
}
 
/* bring hardware to a sane state. this has to be done, just in case someone
wants to capture from this device before it has been properly initialized.
the capture engine would badly fail, because no valid signal arrives on the
saa7146, thus leading to timeouts and stuff. */
static int hexium_init_done(struct saa7146_dev *dev)
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
union i2c_smbus_data data;
int i = 0;
 
DEB_D(("hexium_init_done called.\n"));
 
/* initialize the helper ics to useful values */
for (i = 0; i < sizeof(hexium_saa7110); i++) {
data.byte = hexium_saa7110[i];
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
printk("hexium_orion: failed for address 0x%02x\n", i);
}
}
 
return 0;
}
 
static int hexium_set_input(struct hexium *hexium, int input)
{
union i2c_smbus_data data;
int i = 0;
DEB_D((".\n"));
 
for (i = 0; i < 8; i++) {
int adr = hexium_input_select[input].data[i].adr;
data.byte = hexium_input_select[input].data[i].byte;
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, adr, I2C_SMBUS_BYTE_DATA, &data)) {
return -1;
}
printk("%d: 0x%02x => 0x%02x\n",input, adr,data.byte);
}
 
return 0;
}
 
static struct saa7146_ext_vv vv_data;
 
/* this function only gets called when the probing was successful */
static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
 
DEB_EE((".\n"));
 
saa7146_vv_init(dev, &vv_data);
if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) {
printk("hexium_orion: cannot register capture v4l2 device. skipping.\n");
return -1;
}
 
printk("hexium_orion: found 'hexium orion' frame grabber-%d.\n", hexium_num);
hexium_num++;
 
/* the rest */
hexium->cur_input = 0;
hexium_init_done(dev);
 
return 0;
}
 
static int hexium_detach(struct saa7146_dev *dev)
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
 
DEB_EE(("dev:%p\n", dev));
 
saa7146_unregister_device(&hexium->video_dev, dev);
saa7146_vv_release(dev);
 
hexium_num--;
 
i2c_del_adapter(&hexium->i2c_adapter);
kfree(hexium);
return 0;
}
 
static int hexium_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
{
struct saa7146_dev *dev = fh->dev;
struct hexium *hexium = (struct hexium *) dev->ext_priv;
/*
struct saa7146_vv *vv = dev->vv_data;
*/
switch (cmd) {
case VIDIOC_ENUMINPUT:
{
struct v4l2_input *i = arg;
DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
 
if (i->index < 0 || i->index >= HEXIUM_INPUTS) {
return -EINVAL;
}
 
memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
 
DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
return 0;
}
case VIDIOC_G_INPUT:
{
int *input = (int *) arg;
*input = hexium->cur_input;
 
DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
return 0;
}
case VIDIOC_S_INPUT:
{
int input = *(int *) arg;
 
if (input < 0 || input >= HEXIUM_INPUTS) {
return -EINVAL;
}
 
hexium->cur_input = input;
hexium_set_input(hexium, input);
 
return 0;
}
default:
/*
DEB_D(("hexium_ioctl() does not handle this ioctl.\n"));
*/
return -ENOIOCTLCMD;
}
return 0;
}
 
static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
{
return 0;
}
 
static struct saa7146_extension extension;
 
static struct saa7146_pci_extension_data hexium_hv_pci6 = {
.ext_priv = "Hexium HV-PCI6 / Orion",
.ext = &extension,
};
 
static struct saa7146_pci_extension_data hexium_orion_1svhs_3bnc = {
.ext_priv = "Hexium HV-PCI6 / Orion (1 SVHS/3 BNC)",
.ext = &extension,
};
 
static struct saa7146_pci_extension_data hexium_orion_4bnc = {
.ext_priv = "Hexium HV-PCI6 / Orion (4 BNC)",
.ext = &extension,
};
 
static struct pci_device_id pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x0000,
.subdevice = 0x0000,
.driver_data = (unsigned long) &hexium_hv_pci6,
},
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x17c8,
.subdevice = 0x0101,
.driver_data = (unsigned long) &hexium_orion_1svhs_3bnc,
},
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x17c8,
.subdevice = 0x2101,
.driver_data = (unsigned long) &hexium_orion_4bnc,
},
{
.vendor = 0,
}
};
 
MODULE_DEVICE_TABLE(pci, pci_tbl);
 
static struct saa7146_ext_vv vv_data = {
.inputs = HEXIUM_INPUTS,
.capabilities = 0,
.stds = &hexium_standards[0],
.num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
.std_callback = &std_callback,
.ioctls = &ioctls[0],
.ioctl = hexium_ioctl,
};
 
static struct saa7146_extension extension = {
.name = "hexium HV-PCI6/Orion",
.flags = 0, // SAA7146_USE_I2C_IRQ,
 
.pci_tbl = &pci_tbl[0],
.module = THIS_MODULE,
 
.probe = hexium_probe,
.attach = hexium_attach,
.detach = hexium_detach,
 
.irq_mask = 0,
.irq_func = NULL,
};
 
int __init hexium_init_module(void)
{
if (0 != saa7146_register_extension(&extension)) {
DEB_S(("failed to register extension.\n"));
return -ENODEV;
}
 
return 0;
}
 
void __exit hexium_cleanup_module(void)
{
saa7146_unregister_extension(&extension);
}
 
module_init(hexium_init_module);
module_exit(hexium_cleanup_module);
 
MODULE_DESCRIPTION("video4linux-2 driver for Hexium Orion frame grabber cards");
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_LICENSE("GPL");
/shark/trunk/drivers/cm7326/saa7146_video.c
0,0 → 1,1498
#include <media/saa7146_vv.h>
 
static int memory = 32;
 
MODULE_PARM(memory,"i");
MODULE_PARM_DESC(memory, "maximum memory usage for capture buffers (default: 32Mb)");
 
/* format descriptions for capture and preview */
static struct saa7146_format formats[] = {
{
.name = "RGB-8 (3-3-2)",
.pixelformat = V4L2_PIX_FMT_RGB332,
.trans = RGB08_COMPOSED,
.depth = 8,
.flags = 0,
}, {
.name = "RGB-16 (5/B-6/G-5/R)",
.pixelformat = V4L2_PIX_FMT_RGB565,
.trans = RGB16_COMPOSED,
.depth = 16,
.flags = 0,
}, {
.name = "RGB-24 (B-G-R)",
.pixelformat = V4L2_PIX_FMT_BGR24,
.trans = RGB24_COMPOSED,
.depth = 24,
.flags = 0,
}, {
.name = "RGB-32 (B-G-R)",
.pixelformat = V4L2_PIX_FMT_BGR32,
.trans = RGB32_COMPOSED,
.depth = 32,
.flags = 0,
}, {
.name = "Greyscale-8",
.pixelformat = V4L2_PIX_FMT_GREY,
.trans = Y8,
.depth = 8,
.flags = 0,
}, {
.name = "YUV 4:2:2 planar (Y-Cb-Cr)",
.pixelformat = V4L2_PIX_FMT_YUV422P,
.trans = YUV422_DECOMPOSED,
.depth = 16,
.flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
}, {
.name = "YVU 4:2:0 planar (Y-Cb-Cr)",
.pixelformat = V4L2_PIX_FMT_YVU420,
.trans = YUV420_DECOMPOSED,
.depth = 12,
.flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
}, {
.name = "YUV 4:2:0 planar (Y-Cb-Cr)",
.pixelformat = V4L2_PIX_FMT_YUV420,
.trans = YUV420_DECOMPOSED,
.depth = 12,
.flags = FORMAT_IS_PLANAR,
}, {
.name = "YUV 4:2:2 (U-Y-V-Y)",
.pixelformat = V4L2_PIX_FMT_UYVY,
.trans = YUV422_COMPOSED,
.depth = 16,
.flags = 0,
}
};
 
/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps.
due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped
(like V4L2_PIX_FMT_YUYV) ... 8-( */
static int NUM_FORMATS = sizeof(formats)/sizeof(struct saa7146_format);
 
struct saa7146_format* format_by_fourcc(struct saa7146_dev *dev, int fourcc)
{
int i, j = NUM_FORMATS;
for (i = 0; i < j; i++) {
if (formats[i].pixelformat == fourcc) {
return formats+i;
}
}
DEB_D(("unknown pixelformat:'%4.4s'\n",(char *)&fourcc));
return NULL;
}
 
static int g_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
{
struct saa7146_dev *dev = fh->dev;
DEB_EE(("dev:%p, fh:%p\n",dev,fh));
 
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
f->fmt.pix = fh->video_fmt;
return 0;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
f->fmt.win = fh->ov.win;
return 0;
case V4L2_BUF_TYPE_VBI_CAPTURE:
{
f->fmt.vbi = fh->vbi_fmt;
return 0;
}
default:
DEB_D(("invalid format type '%d'.\n",f->type));
return -EINVAL;
}
}
 
static int try_win(struct saa7146_dev *dev, struct v4l2_window *win)
{
struct saa7146_vv *vv = dev->vv_data;
enum v4l2_field field;
int maxw, maxh;
 
DEB_EE(("dev:%p\n",dev));
 
if (NULL == vv->ov_fb.base) {
DEB_D(("no fb base set.\n"));
return -EINVAL;
}
if (NULL == vv->ov_fmt) {
DEB_D(("no fb fmt set.\n"));
return -EINVAL;
}
if (win->w.width < 48 || win->w.height < 32) {
DEB_D(("min width/height. (%d,%d)\n",win->w.width,win->w.height));
return -EINVAL;
}
if (win->clipcount > 16) {
DEB_D(("clipcount too big.\n"));
return -EINVAL;
}
 
field = win->field;
maxw = vv->standard->h_max_out;
maxh = vv->standard->v_max_out;
 
if (V4L2_FIELD_ANY == field) {
field = (win->w.height > maxh/2)
? V4L2_FIELD_INTERLACED
: V4L2_FIELD_TOP;
}
switch (field) {
case V4L2_FIELD_TOP:
case V4L2_FIELD_BOTTOM:
case V4L2_FIELD_ALTERNATE:
maxh = maxh / 2;
break;
case V4L2_FIELD_INTERLACED:
break;
default: {
DEB_D(("no known field mode '%d'.\n",field));
return -EINVAL;
}
}
 
win->field = field;
if (win->w.width > maxw)
win->w.width = maxw;
if (win->w.height > maxh)
win->w.height = maxh;
 
return 0;
}
 
static int try_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
int err;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
{
struct saa7146_format *fmt;
enum v4l2_field field;
int maxw, maxh;
int calc_bpl;
DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh));
 
fmt = format_by_fourcc(dev,f->fmt.pix.pixelformat);
if (NULL == fmt) {
return -EINVAL;
}
 
field = f->fmt.pix.field;
maxw = vv->standard->h_max_out;
maxh = vv->standard->v_max_out;
if (V4L2_FIELD_ANY == field) {
field = (f->fmt.pix.height > maxh/2)
? V4L2_FIELD_INTERLACED
: V4L2_FIELD_BOTTOM;
}
switch (field) {
case V4L2_FIELD_ALTERNATE: {
vv->last_field = V4L2_FIELD_TOP;
maxh = maxh / 2;
break;
}
case V4L2_FIELD_TOP:
case V4L2_FIELD_BOTTOM:
vv->last_field = V4L2_FIELD_INTERLACED;
maxh = maxh / 2;
break;
case V4L2_FIELD_INTERLACED:
vv->last_field = V4L2_FIELD_INTERLACED;
break;
default: {
DEB_D(("no known field mode '%d'.\n",field));
return -EINVAL;
}
}
 
f->fmt.pix.field = field;
if (f->fmt.pix.width > maxw)
f->fmt.pix.width = maxw;
if (f->fmt.pix.height > maxh)
f->fmt.pix.height = maxh;
 
calc_bpl = (f->fmt.pix.width * fmt->depth)/8;
 
if (f->fmt.pix.bytesperline < calc_bpl)
f->fmt.pix.bytesperline = calc_bpl;
if (f->fmt.pix.bytesperline > (2*PAGE_SIZE * fmt->depth)/8) /* arbitrary constraint */
f->fmt.pix.bytesperline = calc_bpl;
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
DEB_D(("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",f->fmt.pix.width,f->fmt.pix.height,f->fmt.pix.bytesperline,f->fmt.pix.sizeimage));
 
return 0;
}
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh));
err = try_win(dev,&f->fmt.win);
if (0 != err) {
return err;
}
return 0;
default:
DEB_EE(("unknown format type '%d'\n",f->type));
return -EINVAL;
}
}
 
int saa7146_start_preview(struct saa7146_fh *fh)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
int ret = 0, err = 0;
 
DEB_EE(("dev:%p, fh:%p\n",dev,fh));
 
/* check if we have overlay informations */
if( NULL == fh->ov.fh ) {
DEB_D(("no overlay data available. try S_FMT first.\n"));
return -EAGAIN;
}
 
/* check if overlay is running */
if( 0 != vv->ov_data ) {
if( fh != vv->ov_data->fh ) {
DEB_D(("overlay is running in another open.\n"));
return -EAGAIN;
}
DEB_D(("overlay is already active.\n"));
return 0;
}
if( 0 != vv->streaming ) {
DEB_D(("streaming capture is active.\n"));
return -EBUSY;
}
err = try_win(dev,&fh->ov.win);
if (0 != err) {
return err;
}
vv->ov_data = &fh->ov;
 
DEB_D(("%dx%d+%d+%d %s field=%s\n",
fh->ov.win.w.width,fh->ov.win.w.height,
fh->ov.win.w.left,fh->ov.win.w.top,
vv->ov_fmt->name,v4l2_field_names[fh->ov.win.field]));
if (0 != (ret = saa7146_enable_overlay(fh))) {
vv->ov_data = NULL;
DEB_D(("enabling overlay failed: %d\n",ret));
return ret;
}
 
return 0;
}
 
int saa7146_stop_preview(struct saa7146_fh *fh)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
DEB_EE(("dev:%p, fh:%p\n",dev,fh));
 
/* check if overlay is running */
if( 0 == vv->ov_data ) {
DEB_D(("overlay is not active.\n"));
return 0;
}
 
if( fh != vv->ov_data->fh ) {
DEB_D(("overlay is active, but for another open.\n"));
return 0;
}
 
vv->ov_data = NULL;
saa7146_disable_overlay(fh);
 
return 0;
}
 
static int s_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
 
unsigned long flags;
int err;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh));
if( fh == vv->streaming ) {
DEB_EE(("streaming capture is active"));
return -EAGAIN;
}
err = try_fmt(fh,f);
if (0 != err)
return err;
fh->video_fmt = f->fmt.pix;
DEB_EE(("set to pixelformat '%4.4s'\n",(char *)&fh->video_fmt.pixelformat));
return 0;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh));
err = try_win(dev,&f->fmt.win);
if (0 != err)
return err;
down(&dev->lock);
fh->ov.win = f->fmt.win;
fh->ov.nclips = f->fmt.win.clipcount;
if (fh->ov.nclips > 16)
fh->ov.nclips = 16;
if (copy_from_user(fh->ov.clips,f->fmt.win.clips,sizeof(struct v4l2_clip)*fh->ov.nclips)) {
up(&dev->lock);
return -EFAULT;
}
/* fh->ov.fh is used to indicate that we have valid overlay informations, too */
fh->ov.fh = fh;
 
/* check if we have an active overlay */
if( vv->ov_data != NULL ) {
if( fh == vv->ov_data->fh) {
spin_lock_irqsave(&dev->slock,flags);
saa7146_stop_preview(fh);
saa7146_start_preview(fh);
spin_unlock_irqrestore(&dev->slock,flags);
}
}
up(&dev->lock);
return 0;
default:
DEB_D(("unknown format type '%d'\n",f->type));
return -EINVAL;
}
}
 
/********************************************************************************/
/* device controls */
 
static struct v4l2_queryctrl controls[] = {
{
id: V4L2_CID_BRIGHTNESS,
name: "Brightness",
minimum: 0,
maximum: 255,
step: 1,
default_value: 128,
type: V4L2_CTRL_TYPE_INTEGER,
},{
id: V4L2_CID_CONTRAST,
name: "Contrast",
minimum: 0,
maximum: 127,
step: 1,
default_value: 64,
type: V4L2_CTRL_TYPE_INTEGER,
},{
id: V4L2_CID_SATURATION,
name: "Saturation",
minimum: 0,
maximum: 127,
step: 1,
default_value: 64,
type: V4L2_CTRL_TYPE_INTEGER,
},{
id: V4L2_CID_VFLIP,
name: "Vertical flip",
minimum: 0,
maximum: 1,
type: V4L2_CTRL_TYPE_BOOLEAN,
},{
id: V4L2_CID_HFLIP,
name: "Horizontal flip",
minimum: 0,
maximum: 1,
type: V4L2_CTRL_TYPE_BOOLEAN,
},
};
static int NUM_CONTROLS = sizeof(controls)/sizeof(struct v4l2_queryctrl);
 
#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 0)
 
static struct v4l2_queryctrl* ctrl_by_id(int id)
{
int i;
for (i = 0; i < NUM_CONTROLS; i++)
if (controls[i].id == id)
return controls+i;
return NULL;
}
 
static int get_control(struct saa7146_fh *fh, struct v4l2_control *c)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
 
const struct v4l2_queryctrl* ctrl;
u32 value = 0;
 
ctrl = ctrl_by_id(c->id);
if (NULL == ctrl)
return -EINVAL;
switch (c->id) {
case V4L2_CID_BRIGHTNESS:
value = saa7146_read(dev, BCS_CTRL);
c->value = 0xff & (value >> 24);
DEB_D(("V4L2_CID_BRIGHTNESS: %d\n",c->value));
break;
case V4L2_CID_CONTRAST:
value = saa7146_read(dev, BCS_CTRL);
c->value = 0x7f & (value >> 16);
DEB_D(("V4L2_CID_CONTRAST: %d\n",c->value));
break;
case V4L2_CID_SATURATION:
value = saa7146_read(dev, BCS_CTRL);
c->value = 0x7f & (value >> 0);
DEB_D(("V4L2_CID_SATURATION: %d\n",c->value));
break;
case V4L2_CID_VFLIP:
c->value = vv->vflip;
DEB_D(("V4L2_CID_VFLIP: %d\n",c->value));
break;
case V4L2_CID_HFLIP:
c->value = vv->hflip;
DEB_D(("V4L2_CID_HFLIP: %d\n",c->value));
break;
default:
return -EINVAL;
}
 
return 0;
}
 
static int set_control(struct saa7146_fh *fh, struct v4l2_control *c)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
 
const struct v4l2_queryctrl* ctrl;
unsigned long flags;
int restart_overlay = 0;
 
ctrl = ctrl_by_id(c->id);
if (NULL == ctrl) {
DEB_D(("unknown control %d\n",c->id));
return -EINVAL;
}
switch (ctrl->type) {
case V4L2_CTRL_TYPE_BOOLEAN:
case V4L2_CTRL_TYPE_MENU:
case V4L2_CTRL_TYPE_INTEGER:
if (c->value < ctrl->minimum)
c->value = ctrl->minimum;
if (c->value > ctrl->maximum)
c->value = ctrl->maximum;
break;
default:
/* nothing */;
};
 
switch (c->id) {
case V4L2_CID_BRIGHTNESS: {
u32 value = saa7146_read(dev, BCS_CTRL);
value &= 0x00ffffff;
value |= (c->value << 24);
saa7146_write(dev, BCS_CTRL, value);
saa7146_write(dev, MC2, MASK_22 | MASK_06 );
break;
}
case V4L2_CID_CONTRAST: {
u32 value = saa7146_read(dev, BCS_CTRL);
value &= 0xff00ffff;
value |= (c->value << 16);
saa7146_write(dev, BCS_CTRL, value);
saa7146_write(dev, MC2, MASK_22 | MASK_06 );
break;
}
case V4L2_CID_SATURATION: {
u32 value = saa7146_read(dev, BCS_CTRL);
value &= 0xffffff00;
value |= (c->value << 0);
saa7146_write(dev, BCS_CTRL, value);
saa7146_write(dev, MC2, MASK_22 | MASK_06 );
break;
}
case V4L2_CID_HFLIP:
/* fixme: we can supfhrt changing VFLIP and HFLIP here... */
if( 0 != vv->streaming ) {
DEB_D(("V4L2_CID_HFLIP while active capture.\n"));
return -EINVAL;
}
vv->hflip = c->value;
restart_overlay = 1;
break;
case V4L2_CID_VFLIP:
if( 0 != vv->streaming ) {
DEB_D(("V4L2_CID_VFLIP while active capture.\n"));
return -EINVAL;
}
vv->vflip = c->value;
restart_overlay = 1;
break;
default: {
return -EINVAL;
}
}
if( 0 != restart_overlay ) {
if( 0 != vv->ov_data ) {
if( fh == vv->ov_data->fh ) {
spin_lock_irqsave(&dev->slock,flags);
saa7146_stop_preview(fh);
saa7146_start_preview(fh);
spin_unlock_irqrestore(&dev->slock,flags);
}
}
}
return 0;
}
 
/********************************************************************************/
/* common pagetable functions */
 
static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf)
{
struct pci_dev *pci = dev->pci;
struct scatterlist *list = buf->vb.dma.sglist;
int length = buf->vb.dma.sglen;
struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
 
DEB_EE(("dev:%p, buf:%p, sg_len:%d\n",dev,buf,length));
 
if( 0 != IS_PLANAR(sfmt->trans)) {
struct saa7146_pgtable *pt1 = &buf->pt[0];
struct saa7146_pgtable *pt2 = &buf->pt[1];
struct saa7146_pgtable *pt3 = &buf->pt[2];
u32 *ptr1, *ptr2, *ptr3;
u32 fill;
 
int size = buf->fmt->width*buf->fmt->height;
int i,p,m1,m2,m3,o1,o2;
 
switch( sfmt->depth ) {
case 12: {
/* create some offsets inside the page table */
m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
m2 = ((size+(size/4)+PAGE_SIZE)/PAGE_SIZE)-1;
m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
o1 = size%PAGE_SIZE;
o2 = (size+(size/4))%PAGE_SIZE;
DEB_CAP(("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",size,m1,m2,m3,o1,o2));
break;
}
case 16: {
/* create some offsets inside the page table */
m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
m2 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1;
o1 = size%PAGE_SIZE;
o2 = (size+(size/2))%PAGE_SIZE;
DEB_CAP(("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",size,m1,m2,m3,o1,o2));
break;
}
default: {
return -1;
}
}
ptr1 = pt1->cpu;
ptr2 = pt2->cpu;
ptr3 = pt3->cpu;
 
/* walk all pages, copy all page addresses to ptr1 */
for (i = 0; i < length; i++, list++) {
for (p = 0; p * 4096 < list->length; p++, ptr1++) {
*ptr1 = sg_dma_address(list) - list->offset;
}
}
/*
ptr1 = pt1->cpu;
for(j=0;j<40;j++) {
printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
}
*/
 
/* if we have a user buffer, the first page may not be
aligned to a page boundary. */
pt1->offset = buf->vb.dma.sglist->offset;
pt2->offset = pt1->offset+o1;
pt3->offset = pt1->offset+o2;
/* create video-dma2 page table */
ptr1 = pt1->cpu;
for(i = m1; i <= m2 ; i++, ptr2++) {
*ptr2 = ptr1[i];
}
fill = *(ptr2-1);
for(;i<1024;i++,ptr2++) {
*ptr2 = fill;
}
/* create video-dma3 page table */
ptr1 = pt1->cpu;
for(i = m2; i <= m3; i++,ptr3++) {
*ptr3 = ptr1[i];
}
fill = *(ptr3-1);
for(;i<1024;i++,ptr3++) {
*ptr3 = fill;
}
/* finally: finish up video-dma1 page table */
ptr1 = pt1->cpu+m1;
fill = pt1->cpu[m1];
for(i=m1;i<1024;i++,ptr1++) {
*ptr1 = fill;
}
/*
ptr1 = pt1->cpu;
ptr2 = pt2->cpu;
ptr3 = pt3->cpu;
for(j=0;j<40;j++) {
printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
}
for(j=0;j<40;j++) {
printk("ptr2 %d: 0x%08x\n",j,ptr2[j]);
}
for(j=0;j<40;j++) {
printk("ptr3 %d: 0x%08x\n",j,ptr3[j]);
}
*/
} else {
struct saa7146_pgtable *pt = &buf->pt[0];
return saa7146_pgtable_build_single(pci, pt, list, length);
}
 
return 0;
}
 
 
/********************************************************************************/
/* file operations */
 
static int video_begin(struct saa7146_fh *fh)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_format *fmt = NULL;
unsigned long flags;
unsigned int resource;
int ret = 0;
 
DEB_EE(("dev:%p, fh:%p\n",dev,fh));
 
if( fh == vv->streaming ) {
DEB_S(("already capturing.\n"));
return -EBUSY;
}
if( vv->streaming != 0 ) {
DEB_S(("already capturing, but in another open.\n"));
return -EBUSY;
}
fmt = format_by_fourcc(dev,fh->video_fmt.pixelformat);
/* we need to have a valid format set here */
BUG_ON(NULL == fmt);
 
if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
} else {
resource = RESOURCE_DMA1_HPS;
}
 
ret = saa7146_res_get(fh, resource);
if (0 == ret) {
DEB_S(("cannot get capture resource %d\n",resource));
return -EBUSY;
}
spin_lock_irqsave(&dev->slock,flags);
 
/* clear out beginning of streaming bit (rps register 0)*/
saa7146_write(dev, MC2, MASK_27 );
 
/* enable rps0 irqs */
IER_ENABLE(dev, MASK_27);
 
vv->streaming = fh;
spin_unlock_irqrestore(&dev->slock,flags);
return 0;
}
 
static int video_end(struct saa7146_fh *fh, struct file *file)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_format *fmt = NULL;
unsigned long flags;
unsigned int resource;
u32 dmas = 0;
DEB_EE(("dev:%p, fh:%p\n",dev,fh));
 
if( vv->streaming != fh ) {
DEB_S(("not capturing.\n"));
return -EINVAL;
}
 
fmt = format_by_fourcc(dev,fh->video_fmt.pixelformat);
/* we need to have a valid format set here */
BUG_ON(NULL == fmt);
 
if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
dmas = 0x00700000;
} else {
resource = RESOURCE_DMA1_HPS;
dmas = 0x00100000;
}
saa7146_res_free(fh, resource);
 
spin_lock_irqsave(&dev->slock,flags);
 
/* disable rps0 */
saa7146_write(dev, MC1, MASK_28);
 
/* disable rps0 irqs */
IER_DISABLE(dev, MASK_27);
 
/* shut down all used video dma transfers */
saa7146_write(dev, MC1, dmas);
 
vv->streaming = NULL;
 
spin_unlock_irqrestore(&dev->slock, flags);
return 0;
}
 
/*
* This function is _not_ called directly, but from
* video_generic_ioctl (and maybe others). userspace
* copying is done already, arg is a kernel pointer.
*/
 
int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
 
unsigned long flags;
int err = 0, result = 0, ee = 0;
 
struct saa7146_use_ops *ops;
struct videobuf_queue *q;
 
/* check if extension handles the command */
for(ee = 0; dev->ext_vv_data->ioctls[ee].flags != 0; ee++) {
if( cmd == dev->ext_vv_data->ioctls[ee].cmd )
break;
}
if( 0 != (dev->ext_vv_data->ioctls[ee].flags & SAA7146_EXCLUSIVE) ) {
DEB_D(("extension handles ioctl exclusive.\n"));
result = dev->ext_vv_data->ioctl(fh, cmd, arg);
return result;
}
if( 0 != (dev->ext_vv_data->ioctls[ee].flags & SAA7146_BEFORE) ) {
DEB_D(("extension handles ioctl before.\n"));
result = dev->ext_vv_data->ioctl(fh, cmd, arg);
if( -EAGAIN != result ) {
return result;
}
}
/* fixme: add handle "after" case (is it still needed?) */
 
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
ops = &saa7146_video_uops;
q = &fh->video_q;
break;
}
case V4L2_BUF_TYPE_VBI_CAPTURE: {
ops = &saa7146_vbi_uops;
q = &fh->vbi_q;
break;
}
default:
BUG();
return 0;
}
 
switch (cmd) {
case VIDIOC_QUERYCAP:
{
struct v4l2_capability *cap = arg;
memset(cap,0,sizeof(*cap));
 
DEB_EE(("VIDIOC_QUERYCAP\n"));
strcpy(cap->driver, "saa7146 v4l2");
strlcpy(cap->card, dev->ext->name, sizeof(cap->card));
sprintf(cap->bus_info,"PCI:%s",dev->pci->slot_name);
cap->version = SAA7146_VERSION_CODE;
cap->capabilities =
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_VIDEO_OVERLAY |
V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING;
cap->capabilities |= dev->ext_vv_data->capabilities;
return 0;
}
case VIDIOC_G_FBUF:
{
struct v4l2_framebuffer *fb = arg;
 
DEB_EE(("VIDIOC_G_FBUF\n"));
 
*fb = vv->ov_fb;
fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
return 0;
}
case VIDIOC_S_FBUF:
{
struct v4l2_framebuffer *fb = arg;
struct saa7146_format *fmt;
struct saa7146_fh *ov_fh = NULL;
int restart_overlay = 0;
 
DEB_EE(("VIDIOC_S_FBUF\n"));
/*
if(!capable(CAP_SYS_ADMIN)) { // && !capable(CAP_SYS_RAWIO)) {
DEB_D(("VIDIOC_S_FBUF: not CAP_SYS_ADMIN or CAP_SYS_RAWIO.\n"));
return -EPERM;
}
*/
 
/* check args */
fmt = format_by_fourcc(dev,fb->fmt.pixelformat);
if (NULL == fmt) {
return -EINVAL;
}
/* planar formats are not allowed for overlay video, clipping and video dma would clash */
if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
DEB_S(("planar pixelformat '%4.4s' not allowed for overlay\n",(char *)&fmt->pixelformat));
}
down(&dev->lock);
if( vv->ov_data != NULL ) {
ov_fh = vv->ov_data->fh;
saa7146_stop_preview(ov_fh);
restart_overlay = 1;
}
 
/* ok, accept it */
vv->ov_fb = *fb;
vv->ov_fmt = fmt;
if (0 == vv->ov_fb.fmt.bytesperline)
vv->ov_fb.fmt.bytesperline =
vv->ov_fb.fmt.width*fmt->depth/8;
 
if( 0 != restart_overlay ) {
saa7146_start_preview(ov_fh);
}
 
up(&dev->lock);
 
return 0;
}
case VIDIOC_ENUM_FMT:
{
struct v4l2_fmtdesc *f = arg;
int index;
 
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OVERLAY: {
index = f->index;
if (index < 0 || index >= NUM_FORMATS) {
return -EINVAL;
}
memset(f,0,sizeof(*f));
f->index = index;
strlcpy(f->description,formats[index].name,sizeof(f->description));
f->pixelformat = formats[index].pixelformat;
break;
}
default:
return -EINVAL;
}
 
DEB_EE(("VIDIOC_ENUM_FMT: type:%d, index:%d\n",f->type,f->index));
return 0;
}
case VIDIOC_QUERYCTRL:
{
const struct v4l2_queryctrl *ctrl;
struct v4l2_queryctrl *c = arg;
 
if ((c->id < V4L2_CID_BASE ||
c->id >= V4L2_CID_LASTP1) &&
(c->id < V4L2_CID_PRIVATE_BASE ||
c->id >= V4L2_CID_PRIVATE_LASTP1))
return -EINVAL;
ctrl = ctrl_by_id(c->id);
if( NULL == ctrl ) {
return -EINVAL;
/*
c->flags = V4L2_CTRL_FLAG_DISABLED;
return 0;
*/
}
 
DEB_EE(("VIDIOC_QUERYCTRL: id:%d\n",c->id));
*c = *ctrl;
return 0;
}
case VIDIOC_G_CTRL: {
DEB_EE(("VIDIOC_G_CTRL\n"));
return get_control(fh,arg);
}
case VIDIOC_S_CTRL:
/* FIXME: remove when videodev2.h update is in kernel */
#ifdef VIDIOC_S_CTRL_OLD
case VIDIOC_S_CTRL_OLD:
#endif
{
DEB_EE(("VIDIOC_S_CTRL\n"));
down(&dev->lock);
err = set_control(fh,arg);
up(&dev->lock);
return err;
}
case VIDIOC_G_PARM:
{
struct v4l2_streamparm *parm = arg;
if( parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ) {
return -EINVAL;
}
memset(&parm->parm.capture,0,sizeof(struct v4l2_captureparm));
parm->parm.capture.readbuffers = 1;
// fixme: only for PAL!
parm->parm.capture.timeperframe.numerator = 1;
parm->parm.capture.timeperframe.denominator = 25;
return 0;
}
case VIDIOC_G_FMT:
{
struct v4l2_format *f = arg;
DEB_EE(("VIDIOC_G_FMT\n"));
return g_fmt(fh,f);
}
case VIDIOC_S_FMT:
{
struct v4l2_format *f = arg;
DEB_EE(("VIDIOC_S_FMT\n"));
return s_fmt(fh,f);
}
case VIDIOC_TRY_FMT:
{
struct v4l2_format *f = arg;
DEB_EE(("VIDIOC_TRY_FMT\n"));
return try_fmt(fh,f);
}
case VIDIOC_G_STD:
{
v4l2_std_id *id = arg;
DEB_EE(("VIDIOC_G_STD\n"));
*id = vv->standard->id;
return 0;
}
/* the saa7146 supfhrts (used in conjunction with the saa7111a for example)
PAL / NTSC / SECAM. if your hardware does not (or does more)
-- override this function in your extension */
case VIDIOC_ENUMSTD:
{
struct v4l2_standard *e = arg;
if (e->index < 0 )
return -EINVAL;
if( e->index < dev->ext_vv_data->num_stds ) {
DEB_EE(("VIDIOC_ENUMSTD: index:%d\n",e->index));
v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name);
return 0;
}
return -EINVAL;
}
case VIDIOC_S_STD:
{
v4l2_std_id *id = arg;
int i;
int restart_overlay = 0;
int found = 0;
struct saa7146_fh *ov_fh = NULL;
DEB_EE(("VIDIOC_S_STD\n"));
 
if( 0 != vv->streaming ) {
return -EBUSY;
}
 
DEB_D(("before getting lock...\n"));
down(&dev->lock);
DEB_D(("got lock\n"));
if( vv->ov_data != NULL ) {
ov_fh = vv->ov_data->fh;
saa7146_stop_preview(ov_fh);
restart_overlay = 1;
}
 
for(i = 0; i < dev->ext_vv_data->num_stds; i++)
if (*id & dev->ext_vv_data->stds[i].id)
break;
if (i != dev->ext_vv_data->num_stds) {
vv->standard = &dev->ext_vv_data->stds[i];
if( NULL != dev->ext_vv_data->std_callback )
dev->ext_vv_data->std_callback(dev, vv->standard);
found = 1;
}
 
if( 0 != restart_overlay ) {
saa7146_start_preview(ov_fh);
}
up(&dev->lock);
 
if( 0 == found ) {
DEB_EE(("VIDIOC_S_STD: standard not found.\n"));
return -EINVAL;
}
 
DEB_EE(("VIDIOC_S_STD: set to standard to '%s'\n",vv->standard->name));
return 0;
}
case VIDIOC_OVERLAY:
/* FIXME: remove when videodev2.h update is in kernel */
#ifdef VIDIOC_OVERLAY_OLD
case VIDIOC_OVERLAY_OLD:
#endif
{
int on = *(int *)arg;
int err = 0;
 
if( NULL == vv->ov_fmt && on != 0 ) {
DEB_D(("VIDIOC_OVERLAY: no framebuffer informations. call S_FBUF first!\n"));
return -EAGAIN;
}
 
DEB_D(("VIDIOC_OVERLAY on:%d\n",on));
if( 0 != on ) {
if( vv->ov_data != NULL ) {
if( fh != vv->ov_data->fh) {
DEB_D(("overlay already active in another open\n"));
return -EAGAIN;
}
}
 
if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
DEB_D(("cannot get overlay resources\n"));
return -EBUSY;
}
 
spin_lock_irqsave(&dev->slock,flags);
err = saa7146_start_preview(fh);
spin_unlock_irqrestore(&dev->slock,flags);
return err;
}
if( vv->ov_data != NULL ) {
if( fh != vv->ov_data->fh) {
DEB_D(("overlay is active, but in another open\n"));
return -EAGAIN;
}
}
spin_lock_irqsave(&dev->slock,flags);
err = saa7146_stop_preview(fh);
spin_unlock_irqrestore(&dev->slock,flags);
/* free resources */
saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
return err;
}
case VIDIOC_REQBUFS: {
struct v4l2_requestbuffers *req = arg;
DEB_D(("VIDIOC_REQBUFS, type:%d\n",req->type));
return videobuf_reqbufs(file,q,req);
}
case VIDIOC_QUERYBUF: {
struct v4l2_buffer *buf = arg;
DEB_D(("VIDIOC_QUERYBUF, type:%d, offset:%d\n",buf->type,buf->m.offset));
return videobuf_querybuf(q,buf);
}
case VIDIOC_QBUF: {
struct v4l2_buffer *buf = arg;
int ret = 0;
ret = videobuf_qbuf(file,q,buf);
DEB_D(("VIDIOC_QBUF: ret:%d, index:%d\n",ret,buf->index));
return ret;
}
case VIDIOC_DQBUF: {
struct v4l2_buffer *buf = arg;
int ret = 0;
ret = videobuf_dqbuf(file,q,buf);
DEB_D(("VIDIOC_DQBUF: ret:%d, index:%d\n",ret,buf->index));
return ret;
}
case VIDIOC_STREAMON: {
int *type = arg;
DEB_D(("VIDIOC_STREAMON, type:%d\n",*type));
if( fh == vv->streaming ) {
DEB_D(("already capturing.\n"));
return 0;
}
 
err = video_begin(fh);
if( 0 != err) {
return err;
}
err = videobuf_streamon(file,q);
return err;
}
case VIDIOC_STREAMOFF: {
int *type = arg;
 
DEB_D(("VIDIOC_STREAMOFF, type:%d\n",*type));
 
if( fh != vv->streaming ) {
DEB_D(("this open is not capturing.\n"));
return -EINVAL;
}
 
err = videobuf_streamoff(file,q);
video_end(fh, file);
return err;
}
case VIDIOCGMBUF:
{
struct video_mbuf *mbuf = arg;
struct videobuf_queue *q;
int i;
 
/* fixme: number of capture buffers and sizes for v4l apps */
int gbuffers = 2;
int gbufsize = 768*576*4;
DEB_D(("VIDIOCGMBUF \n"));
 
q = &fh->video_q;
down(&q->lock);
err = videobuf_mmap_setup(file,q,gbuffers,gbufsize,
V4L2_MEMORY_MMAP);
if (err < 0) {
up(&q->lock);
return err;
}
memset(mbuf,0,sizeof(*mbuf));
mbuf->frames = gbuffers;
mbuf->size = gbuffers * gbufsize;
for (i = 0; i < gbuffers; i++)
mbuf->offsets[i] = i * gbufsize;
up(&q->lock);
return 0;
}
default:
return v4l_compat_translate_ioctl(inode,file,cmd,arg,
saa7146_video_do_ioctl);
}
return 0;
}
 
/*********************************************************************************/
/* buffer handling functions */
 
static int buffer_activate (struct saa7146_dev *dev,
struct saa7146_buf *buf,
struct saa7146_buf *next)
{
struct saa7146_vv *vv = dev->vv_data;
 
buf->vb.state = STATE_ACTIVE;
saa7146_set_capture(dev,buf,next);
mod_timer(&vv->video_q.timeout, jiffies+BUFFER_TIMEOUT);
return 0;
}
 
static int buffer_prepare(struct file *file, struct videobuf_buffer *vb, enum v4l2_field field)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
int size,err = 0;
 
DEB_CAP(("vbuf:%p\n",vb));
 
/* sanity checks */
if (fh->video_fmt.width < 48 ||
fh->video_fmt.height < 32 ||
fh->video_fmt.width > vv->standard->h_max_out ||
fh->video_fmt.height > vv->standard->v_max_out) {
DEB_D(("w (%d) / h (%d) out of bounds.\n",fh->video_fmt.width,fh->video_fmt.height));
return -EINVAL;
}
 
size = fh->video_fmt.sizeimage;
if (0 != buf->vb.baddr && buf->vb.bsize < size) {
DEB_D(("size mismatch.\n"));
return -EINVAL;
}
DEB_CAP(("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
fh->video_fmt.width,fh->video_fmt.height,size,v4l2_field_names[fh->video_fmt.field]));
if (buf->vb.width != fh->video_fmt.width ||
buf->vb.bytesperline != fh->video_fmt.bytesperline ||
buf->vb.height != fh->video_fmt.height ||
buf->vb.size != size ||
buf->vb.field != field ||
buf->vb.field != fh->video_fmt.field ||
buf->fmt != &fh->video_fmt) {
saa7146_dma_free(dev,buf);
}
 
if (STATE_NEEDS_INIT == buf->vb.state) {
struct saa7146_format *sfmt;
buf->vb.bytesperline = fh->video_fmt.bytesperline;
buf->vb.width = fh->video_fmt.width;
buf->vb.height = fh->video_fmt.height;
buf->vb.size = size;
buf->vb.field = field;
buf->fmt = &fh->video_fmt;
buf->vb.field = fh->video_fmt.field;
sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
if( 0 != IS_PLANAR(sfmt->trans)) {
saa7146_pgtable_free(dev->pci, &buf->pt[0]);
saa7146_pgtable_free(dev->pci, &buf->pt[1]);
saa7146_pgtable_free(dev->pci, &buf->pt[2]);
 
saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
saa7146_pgtable_alloc(dev->pci, &buf->pt[1]);
saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
} else {
saa7146_pgtable_free(dev->pci, &buf->pt[0]);
saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
}
err = videobuf_iolock(dev->pci,&buf->vb, &vv->ov_fb);
if (err)
goto oops;
err = saa7146_pgtable_build(dev,buf);
if (err)
goto oops;
}
buf->vb.state = STATE_PREPARED;
buf->activate = buffer_activate;
 
return 0;
 
oops:
DEB_D(("error out.\n"));
saa7146_dma_free(dev,buf);
 
return err;
}
 
static int buffer_setup(struct file *file, unsigned int *count, unsigned int *size)
{
struct saa7146_fh *fh = file->private_data;
 
if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS)
*count = MAX_SAA7146_CAPTURE_BUFFERS;
 
*size = fh->video_fmt.sizeimage;
 
/* check if we exceed the "memory" parameter */
if( (*count * *size) > (memory*1048576) ) {
*count = (memory*1048576) / *size;
}
DEB_CAP(("%d buffers, %d bytes each.\n",*count,*size));
 
return 0;
}
 
static void buffer_queue(struct file *file, struct videobuf_buffer *vb)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
DEB_CAP(("vbuf:%p\n",vb));
saa7146_buffer_queue(fh->dev,&vv->video_q,buf);
}
 
 
static void buffer_release(struct file *file, struct videobuf_buffer *vb)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
DEB_CAP(("vbuf:%p\n",vb));
saa7146_dma_free(dev,buf);
}
 
static struct videobuf_queue_ops video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
 
/********************************************************************************/
/* file operations */
 
static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
{
INIT_LIST_HEAD(&vv->video_q.queue);
 
init_timer(&vv->video_q.timeout);
vv->video_q.timeout.function = saa7146_buffer_timeout;
vv->video_q.timeout.data = (unsigned long)(&vv->video_q);
vv->video_q.dev = dev;
 
/* set some default values */
vv->standard = &dev->ext_vv_data->stds[0];
 
/* FIXME: what's this? */
vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A;
vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A;
}
 
 
static int video_open(struct saa7146_dev *dev, struct file *file)
{
struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
struct saa7146_format *sfmt;
 
fh->video_fmt.width = 384;
fh->video_fmt.height = 288;
fh->video_fmt.pixelformat = V4L2_PIX_FMT_BGR24;
fh->video_fmt.bytesperline = 0;
fh->video_fmt.field = V4L2_FIELD_ANY;
sfmt = format_by_fourcc(dev,fh->video_fmt.pixelformat);
fh->video_fmt.sizeimage = (fh->video_fmt.width * fh->video_fmt.height * sfmt->depth)/8;
 
videobuf_queue_init(&fh->video_q, &video_qops,
dev->pci, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct saa7146_buf));
 
init_MUTEX(&fh->video_q.lock);
 
return 0;
}
 
 
static void video_close(struct saa7146_dev *dev, struct file *file)
{
struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
struct saa7146_vv *vv = dev->vv_data;
unsigned long flags;
if( 0 != vv->ov_data ) {
if( fh == vv->ov_data->fh ) {
spin_lock_irqsave(&dev->slock,flags);
saa7146_stop_preview(fh);
spin_unlock_irqrestore(&dev->slock,flags);
}
}
if( fh == vv->streaming ) {
video_end(fh, file);
}
}
 
 
static void video_irq_done(struct saa7146_dev *dev, unsigned long st)
{
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_dmaqueue *q = &vv->video_q;
spin_lock(&dev->slock);
DEB_CAP(("called.\n"));
 
/* only finish the buffer if we have one... */
if( NULL != q->curr ) {
saa7146_buffer_finish(dev,q,STATE_DONE);
}
saa7146_buffer_next(dev,q,0);
 
spin_unlock(&dev->slock);
}
 
static ssize_t video_read(struct file *file, char *data, size_t count, loff_t *ppos)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
ssize_t ret = 0;
int restart_overlay = 0;
struct saa7146_fh *ov_fh = NULL;
 
DEB_EE(("called.\n"));
 
/* fixme: should we allow read() captures while streaming capture? */
if( 0 != vv->streaming ) {
DEB_S(("already capturing.\n"));
return -EBUSY;
}
 
/* stop any active overlay */
if( vv->ov_data != NULL ) {
ov_fh = vv->ov_data->fh;
saa7146_stop_preview(ov_fh);
saa7146_res_free(ov_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
restart_overlay = 1;
}
 
ret = video_begin(fh);
if( 0 != ret) {
goto out;
}
 
ret = videobuf_read_one(file,&fh->video_q , data, count, ppos);
video_end(fh, file);
 
out:
/* restart overlay if it was active before */
if( 0 != restart_overlay ) {
if (0 == saa7146_res_get(ov_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
DEB_D(("cannot get overlay resources again!\n"));
BUG();
}
saa7146_start_preview(ov_fh);
}
 
return ret;
}
 
struct saa7146_use_ops saa7146_video_uops = {
.init = video_init,
.open = video_open,
.release = video_close,
.irq_done = video_irq_done,
.read = video_read,
};
/shark/trunk/drivers/cm7326/saa7146_fops.c
0,0 → 1,578
#include <media/saa7146_vv.h>
 
#define BOARD_CAN_DO_VBI(dev) (dev->revision != 0 && dev->vv_data->vbi_minor != -1)
 
/****************************************************************************/
/* resource management functions, shamelessly stolen from saa7134 driver */
 
int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
if (fh->resources & bit)
/* have it already allocated */
return 1;
 
/* is it free? */
DEB_D(("getting lock...\n"));
down(&dev->lock);
DEB_D(("got lock\n"));
if (vv->resources & bit) {
DEB_D(("locked! vv->resources:0x%02x, we want:0x%02x\n",vv->resources,bit));
/* no, someone else uses it */
up(&dev->lock);
return 0;
}
/* it's free, grab it */
fh->resources |= bit;
vv->resources |= bit;
DEB_D(("res: get %d\n",bit));
up(&dev->lock);
return 1;
}
 
int saa7146_res_check(struct saa7146_fh *fh, unsigned int bit)
{
return (fh->resources & bit);
}
 
int saa7146_res_locked(struct saa7146_dev *dev, unsigned int bit)
{
struct saa7146_vv *vv = dev->vv_data;
return (vv->resources & bit);
}
 
void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
 
if ((fh->resources & bits) != bits)
BUG();
 
DEB_D(("getting lock...\n"));
down(&dev->lock);
DEB_D(("got lock\n"));
fh->resources &= ~bits;
vv->resources &= ~bits;
DEB_D(("res: put %d\n",bits));
up(&dev->lock);
}
 
 
/********************************************************************************/
/* common dma functions */
 
void saa7146_dma_free(struct saa7146_dev *dev,struct saa7146_buf *buf)
{
DEB_EE(("dev:%p, buf:%p\n",dev,buf));
 
if (in_interrupt())
BUG();
 
videobuf_waiton(&buf->vb,0,0);
videobuf_dma_pci_unmap(dev->pci, &buf->vb.dma);
videobuf_dma_free(&buf->vb.dma);
buf->vb.state = STATE_NEEDS_INIT;
}
 
 
/********************************************************************************/
/* common buffer functions */
 
int saa7146_buffer_queue(struct saa7146_dev *dev,
struct saa7146_dmaqueue *q,
struct saa7146_buf *buf)
{
#if DEBUG_SPINLOCKS
BUG_ON(!spin_is_locked(&dev->slock));
#endif
DEB_EE(("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf));
 
if( NULL == q ) {
ERR(("internal error: fatal NULL pointer for q.\n"));
return 0;
}
 
if (NULL == q->curr) {
q->curr = buf;
DEB_D(("immediately activating buffer %p\n", buf));
buf->activate(dev,buf,NULL);
} else {
list_add_tail(&buf->vb.queue,&q->queue);
buf->vb.state = STATE_QUEUED;
DEB_D(("adding buffer %p to queue. (active buffer present)\n", buf));
}
return 0;
}
 
void saa7146_buffer_finish(struct saa7146_dev *dev,
struct saa7146_dmaqueue *q,
int state)
{
#if DEBUG_SPINLOCKS
BUG_ON(!spin_is_locked(&dev->slock));
#endif
if( NULL == q->curr ) {
ERR(("internal error: fatal NULL pointer for q->curr.\n"));
return;
}
 
DEB_EE(("dev:%p, dmaq:%p, state:%d\n", dev, q, state));
DEB_EE(("q->curr:%p\n",q->curr));
 
/* finish current buffer */
if (NULL == q->curr) {
DEB_D(("aiii. no current buffer\n"));
return;
}
q->curr->vb.state = state;
do_gettimeofday(&q->curr->vb.ts);
wake_up(&q->curr->vb.done);
 
q->curr = NULL;
}
 
void saa7146_buffer_next(struct saa7146_dev *dev,
struct saa7146_dmaqueue *q, int vbi)
{
struct saa7146_buf *buf,*next = NULL;
 
if( NULL == q ) {
ERR(("internal error: fatal NULL pointer for q.\n"));
return;
}
 
DEB_INT(("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi));
 
#if DEBUG_SPINLOCKS
BUG_ON(!spin_is_locked(&dev->slock));
#endif
if (!list_empty(&q->queue)) {
/* activate next one from queue */
buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue);
list_del(&buf->vb.queue);
if (!list_empty(&q->queue))
next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
q->curr = buf;
DEB_INT(("next buffer: buf:%p, prev:%p, next:%p\n", buf, q->queue.prev,q->queue.next));
buf->activate(dev,buf,next);
} else {
DEB_INT(("no next buffer. stopping.\n"));
if( 0 != vbi ) {
/* turn off video-dma3 */
saa7146_write(dev,MC1, MASK_20);
} else {
/* nothing to do -- just prevent next video-dma1 transfer
by lowering the protection address */
 
// fixme: fix this for vflip != 0
 
saa7146_write(dev, PROT_ADDR1, 0);
saa7146_write(dev, MC2, (MASK_02|MASK_18));
 
/* write the address of the rps-program */
saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
/* turn on rps */
saa7146_write(dev, MC1, (MASK_12 | MASK_28));
/*
printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
*/
}
del_timer(&q->timeout);
}
}
 
void saa7146_buffer_timeout(unsigned long data)
{
struct saa7146_dmaqueue *q = (struct saa7146_dmaqueue*)data;
struct saa7146_dev *dev = q->dev;
unsigned long flags;
 
DEB_EE(("dev:%p, dmaq:%p\n", dev, q));
 
spin_lock_irqsave(&dev->slock,flags);
if (q->curr) {
DEB_D(("timeout on %p\n", q->curr));
saa7146_buffer_finish(dev,q,STATE_ERROR);
}
 
/* we don't restart the transfer here like other drivers do. when
a streaming capture is disabled, the timeout function will be
called for the current buffer. if we activate the next buffer now,
we mess up our capture logic. if a timeout occurs on another buffer,
then something is seriously broken before, so no need to buffer the
next capture IMHO... */
/*
saa7146_buffer_next(dev,q);
*/
spin_unlock_irqrestore(&dev->slock,flags);
}
 
/********************************************************************************/
/* file operations */
 
static int fops_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct saa7146_dev *h = NULL, *dev = NULL;
struct list_head *list;
struct saa7146_fh *fh = NULL;
int result = 0;
 
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
DEB_EE(("inode:%p, file:%p, minor:%d\n",inode,file,minor));
 
if (down_interruptible(&saa7146_devices_lock))
return -ERESTARTSYS;
 
list_for_each(list,&saa7146_devices) {
h = list_entry(list, struct saa7146_dev, item);
if( NULL == h->vv_data ) {
DEB_D(("device %p has not registered video devices.\n",h));
continue;
}
DEB_D(("trying: %p @ major %d,%d\n",h,h->vv_data->video_minor,h->vv_data->vbi_minor));
 
if (h->vv_data->video_minor == minor) {
dev = h;
}
if (h->vv_data->vbi_minor == minor) {
type = V4L2_BUF_TYPE_VBI_CAPTURE;
dev = h;
}
}
if (NULL == dev) {
DEB_S(("no such video device.\n"));
result = -ENODEV;
goto out;
}
 
DEB_D(("using: %p\n",dev));
 
/* check if an extension is registered */
if( NULL == dev->ext ) {
DEB_S(("no extension registered for this device.\n"));
result = -ENODEV;
goto out;
}
 
/* allocate per open data */
fh = kmalloc(sizeof(*fh),GFP_KERNEL);
if (NULL == fh) {
DEB_S(("cannot allocate memory for per open data.\n"));
result = -ENOMEM;
goto out;
}
memset(fh,0,sizeof(*fh));
file->private_data = fh;
fh->dev = dev;
fh->type = type;
 
if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
DEB_S(("initializing vbi...\n"));
result = saa7146_vbi_uops.open(dev,file);
} else {
DEB_S(("initializing video...\n"));
result = saa7146_video_uops.open(dev,file);
}
if (0 != result) {
goto out;
}
 
if( 0 == try_module_get(dev->ext->module)) {
result = -EINVAL;
goto out;
}
 
result = 0;
out:
if( fh != 0 && result != 0 ) {
kfree(fh);
file->private_data = NULL;
}
up(&saa7146_devices_lock);
return result;
}
 
static int fops_release(struct inode *inode, struct file *file)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
 
DEB_EE(("inode:%p, file:%p\n",inode,file));
 
if (down_interruptible(&saa7146_devices_lock))
return -ERESTARTSYS;
 
if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
saa7146_vbi_uops.release(dev,file);
} else {
saa7146_video_uops.release(dev,file);
}
 
module_put(dev->ext->module);
file->private_data = NULL;
kfree(fh);
 
up(&saa7146_devices_lock);
 
return 0;
}
 
int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg);
static int fops_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
/*
DEB_EE(("inode:%p, file:%p, cmd:%d, arg:%li\n",inode, file, cmd, arg));
*/
return video_usercopy(inode, file, cmd, arg, saa7146_video_do_ioctl);
}
 
static int fops_mmap(struct file *file, struct vm_area_struct * vma)
{
struct saa7146_fh *fh = file->private_data;
struct videobuf_queue *q;
 
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",file, vma));
q = &fh->video_q;
break;
}
case V4L2_BUF_TYPE_VBI_CAPTURE: {
DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",file, vma));
q = &fh->vbi_q;
break;
}
default:
BUG();
return 0;
}
return videobuf_mmap_mapper(vma,q);
}
 
static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
{
struct saa7146_fh *fh = file->private_data;
struct videobuf_buffer *buf = NULL;
struct videobuf_queue *q;
 
DEB_EE(("file:%p, poll:%p\n",file, wait));
 
if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
if( 0 == fh->vbi_q.streaming )
return videobuf_poll_stream(file, &fh->vbi_q, wait);
q = &fh->vbi_q;
} else {
DEB_D(("using video queue.\n"));
q = &fh->video_q;
}
 
if (!list_empty(&q->stream))
buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
 
if (!buf) {
DEB_D(("buf == NULL!\n"));
return POLLERR;
}
 
poll_wait(file, &buf->done, wait);
if (buf->state == STATE_DONE || buf->state == STATE_ERROR) {
DEB_D(("poll succeeded!\n"));
return POLLIN|POLLRDNORM;
}
 
DEB_D(("nothing to poll for, buf->state:%d\n",buf->state));
return 0;
}
 
static ssize_t fops_read(struct file *file, char *data, size_t count, loff_t *ppos)
{
struct saa7146_fh *fh = file->private_data;
 
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
// DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", file, data, (unsigned long)count));
return saa7146_video_uops.read(file,data,count,ppos);
}
case V4L2_BUF_TYPE_VBI_CAPTURE: {
// DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", file, data, (unsigned long)count));
return saa7146_vbi_uops.read(file,data,count,ppos);
}
break;
default:
BUG();
return 0;
}
}
 
static struct file_operations video_fops =
{
.owner = THIS_MODULE,
.open = fops_open,
.release = fops_release,
.read = fops_read,
.poll = fops_poll,
.mmap = fops_mmap,
.ioctl = fops_ioctl,
.llseek = no_llseek,
};
 
void vv_callback(struct saa7146_dev *dev, unsigned long status)
{
u32 isr = status;
DEB_INT(("dev:%p, isr:0x%08x\n",dev,(u32)status));
 
if (0 != (isr & (MASK_27))) {
DEB_INT(("irq: RPS0 (0x%08x).\n",isr));
saa7146_video_uops.irq_done(dev,isr);
}
 
if (0 != (isr & (MASK_28))) {
u32 mc2 = saa7146_read(dev, MC2);
if( 0 != (mc2 & MASK_15)) {
DEB_INT(("irq: RPS1 vbi workaround (0x%08x).\n",isr));
wake_up(&dev->vv_data->vbi_wq);
saa7146_write(dev,MC2, MASK_31);
return;
}
DEB_INT(("irq: RPS1 (0x%08x).\n",isr));
saa7146_vbi_uops.irq_done(dev,isr);
}
}
 
static struct video_device device_template =
{
.hardware = VID_HARDWARE_SAA7146,
.fops = &video_fops,
.minor = -1,
};
 
int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
{
struct saa7146_vv *vv = kmalloc (sizeof(struct saa7146_vv),GFP_KERNEL);
if( NULL == vv ) {
ERR(("out of memory. aborting.\n"));
return -1;
}
memset(vv, 0x0, sizeof(*vv));
 
DEB_EE(("dev:%p\n",dev));
 
/* set default values for video parts of the saa7146 */
saa7146_write(dev, BCS_CTRL, 0x80400040);
 
/* enable video-port pins */
saa7146_write(dev, MC1, (MASK_10 | MASK_26));
 
/* save per-device extension data (one extension can
handle different devices that might need different
configuration data) */
dev->ext_vv_data = ext_vv;
vv->video_minor = -1;
vv->vbi_minor = -1;
 
vv->d_clipping.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, &vv->d_clipping.dma_handle);
if( NULL == vv->d_clipping.cpu_addr ) {
ERR(("out of memory. aborting.\n"));
kfree(vv);
return -1;
}
memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM);
 
saa7146_video_uops.init(dev,vv);
saa7146_vbi_uops.init(dev,vv);
dev->vv_data = vv;
dev->vv_callback = &vv_callback;
 
return 0;
}
 
int saa7146_vv_release(struct saa7146_dev* dev)
{
struct saa7146_vv *vv = dev->vv_data;
 
DEB_EE(("dev:%p\n",dev));
pci_free_consistent(dev->pci, SAA7146_RPS_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
kfree(vv);
dev->vv_data = NULL;
dev->vv_callback = NULL;
return 0;
}
 
int saa7146_register_device(struct video_device *vid, struct saa7146_dev* dev, char *name, int type)
{
struct saa7146_vv *vv = dev->vv_data;
 
DEB_EE(("dev:%p, name:'%s', type:%d\n",dev,name,type));
*vid = device_template;
strlcpy(vid->name, name, sizeof(vid->name));
vid->priv = dev;
 
// fixme: -1 should be an insmod parameter *for the extension* (like "video_nr");
if (video_register_device(vid,type,-1) < 0) {
ERR(("cannot register v4l2 device. skipping.\n"));
return -1;
}
 
if( VFL_TYPE_GRABBER == type ) {
vv->video_minor = vid->minor;
INFO(("%s: registered device video%d [v4l2]\n", dev->name,vid->minor & 0x1f));
} else {
vv->vbi_minor = vid->minor;
INFO(("%s: registered device vbi%d [v4l2]\n", dev->name,vid->minor & 0x1f));
}
 
return 0;
}
 
int saa7146_unregister_device(struct video_device *vid, struct saa7146_dev* dev)
{
struct saa7146_vv *vv = dev->vv_data;
DEB_EE(("dev:%p\n",dev));
 
if( VFL_TYPE_GRABBER == vid->type ) {
vv->video_minor = -1;
} else {
vv->vbi_minor = -1;
}
video_unregister_device(vid);
 
return 0;
}
 
static int __init saa7146_vv_init_module(void)
{
return 0;
}
 
 
static void __exit saa7146_vv_cleanup_module(void)
{
}
 
module_init(saa7146_vv_init_module);
module_exit(saa7146_vv_cleanup_module);
 
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
MODULE_LICENSE("GPL");
/shark/trunk/drivers/cm7326/saa7111.c
0,0 → 1,460
/*
saa7111 - Philips SAA7111A video decoder driver version 0.0.3
 
Copyright (C) 1998 Dave Perks <dperks@ibm.net>
 
Slight changes for video timing and attachment output by
Wolfgang Scherr <scherr@net4you.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/sched.h>
 
#include <linux/videodev.h>
#include <linux/version.h>
#include <linux/i2c.h>
 
#include <linux/video_decoder.h>
 
#define DEBUG(x) /* Debug driver */
 
/* ----------------------------------------------------------------------- */
 
struct saa7111 {
struct i2c_client *client;
int addr;
struct semaphore lock;
unsigned char reg[32];
 
int norm;
int input;
int enable;
int bright;
int contrast;
int hue;
int sat;
};
 
static unsigned short normal_i2c[] = { 0x24, 0x25, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
 
I2C_CLIENT_INSMOD;
 
static struct i2c_client client_template;
/* ----------------------------------------------------------------------- */
static int saa7111_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind)
{
int i;
struct saa7111 *decoder;
struct i2c_client *client;
 
/* who wrote this? init[] is used for i2c_master_send() which expects an array that
will be used for the 'buf' part of an i2c message unchanged. so, the first byte
needs to be the subaddress to start with, then follow the data bytes... */
static const unsigned char init[] = {
0x00, /* start address */
0x00, /* 00 - ID byte */
0x00, /* 01 - reserved */
 
/*front end */
0xd0, /* 02 - FUSE=3, GUDL=2, MODE=0 */
0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */
0x00, /* 04 - GAI1=256 */
0x00, /* 05 - GAI2=256 */
 
/* decoder */
0xf3, /* 06 - HSB at 13(50Hz) / 17(60Hz) pixels after end of last line */
0x13, /* 07 - HSS at 113(50Hz) / 117(60Hz) pixels after end of last line */
0xc8, /* 08 - AUFD=1, FSEL=1, EXFIL=0, VTRC=1, HPLL=0, VNOI=0 */
0x01, /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0, UPTCV=0, APER=1 */
0x80, /* 0a - BRIG=128 */
0x47, /* 0b - CONT=1.109 */
0x40, /* 0c - SATN=1.0 */
0x00, /* 0d - HUE=0 */
0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */
0x00, /* 0f - reserved */
0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */
0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, OEYC=1, OEHV=1, VIPB=0, COLO=0 */
0x00, /* 12 - output control 2 */
0x00, /* 13 - output control 3 */
0x00, /* 14 - reserved */
0x00, /* 15 - VBI */
0x00, /* 16 - VBI */
0x00, /* 17 - VBI */
};
client = kmalloc(sizeof(*client), GFP_KERNEL);
if(client == NULL)
return -ENOMEM;
client_template.adapter = adap;
client_template.addr = addr;
memcpy(client, &client_template, sizeof(*client));
 
decoder = kmalloc(sizeof(*decoder), GFP_KERNEL);
if (decoder == NULL)
{
kfree(client);
return -ENOMEM;
}
 
memset(decoder, 0, sizeof(*decoder));
decoder->client = client;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
client->data = decoder;
#else
i2c_set_clientdata(client, decoder);
#endif
decoder->addr = addr;
decoder->norm = VIDEO_MODE_NTSC;
decoder->input = 0;
decoder->enable = 1;
decoder->bright = 32768;
decoder->contrast = 32768;
decoder->hue = 32768;
decoder->sat = 32768;
 
i = i2c_master_send(client, init, sizeof(init));
if (i < 0) {
printk(KERN_ERR "%s_attach: init status %d\n",
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
client->name, i);
#else
client->dev.name, i);
#endif
} else {
printk(KERN_INFO "%s_attach: chip version %x @ 0x%08x\n",
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
client->name, i2c_smbus_read_byte_data(client, 0x00) >> 4,addr);
#else
client->dev.name, i2c_smbus_read_byte_data(client, 0x00) >> 4,addr);
#endif
}
 
init_MUTEX(&decoder->lock);
i2c_attach_client(client);
MOD_INC_USE_COUNT;
return 0;
}
static int saa7111_probe(struct i2c_adapter *adap)
{
/* probing unknown devices on any Matrox i2c-bus takes ages due to the
slow bit banging algorithm used. because of the fact a saa7111(a)
is *never* present on a Matrox gfx card, we can skip such adapters
here */
if( 0 != (adap->id & I2C_HW_B_G400)) {
return -ENODEV;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
printk("saa7111: probing %s i2c adapter [id=0x%x]\n",
adap->name,adap->id);
#else
printk("saa7111: probing %s i2c adapter [id=0x%x]\n",
adap->dev.name,adap->id);
#endif
return i2c_probe(adap, &addr_data, saa7111_attach);
}
 
static int saa7111_detach(struct i2c_client *client)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
struct saa7111 *decoder = client->data;
#else
struct saa7111 *decoder = i2c_get_clientdata(client);
#endif
i2c_detach_client(client);
kfree(decoder);
kfree(client);
MOD_DEC_USE_COUNT;
return 0;
}
 
static int saa7111_command(struct i2c_client *client, unsigned int cmd,
void *arg)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
struct saa7111 *decoder = client->data;
#else
struct saa7111 *decoder = i2c_get_clientdata(client);
#endif
switch (cmd) {
 
#if defined(DECODER_DUMP)
case DECODER_DUMP:
{
int i;
 
for (i = 0; i < 32; i += 16) {
int j;
 
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
printk("KERN_DEBUG %s: %03x", client->name,
#else
printk("KERN_DEBUG %s: %03x", client->dev.name,
#endif
i);
for (j = 0; j < 16; ++j) {
printk(" %02x",
i2c_smbus_read_byte_data(client,
i + j));
}
printk("\n");
}
}
break;
#endif /* defined(DECODER_DUMP) */
case DECODER_GET_CAPABILITIES:
{
struct video_decoder_capability *cap = arg;
 
cap->flags
= VIDEO_DECODER_PAL
| VIDEO_DECODER_NTSC
| VIDEO_DECODER_AUTO | VIDEO_DECODER_CCIR;
cap->inputs = 8;
cap->outputs = 1;
}
break;
case DECODER_GET_STATUS:
{
int *iarg = arg;
int status;
int res;
 
status = i2c_smbus_read_byte_data(client, 0x1f);
res = 0;
if ((status & (1 << 6)) == 0) {
res |= DECODER_STATUS_GOOD;
}
switch (decoder->norm) {
case VIDEO_MODE_NTSC:
res |= DECODER_STATUS_NTSC;
break;
case VIDEO_MODE_PAL:
res |= DECODER_STATUS_PAL;
break;
default:
case VIDEO_MODE_AUTO:
if ((status & (1 << 5)) != 0) {
res |= DECODER_STATUS_NTSC;
} else {
res |= DECODER_STATUS_PAL;
}
break;
}
if ((status & (1 << 0)) != 0) {
res |= DECODER_STATUS_COLOR;
}
*iarg = res;
}
break;
 
case DECODER_SET_NORM:
{
int *iarg = arg;
 
switch (*iarg) {
 
case VIDEO_MODE_NTSC:
i2c_smbus_write_byte_data(client, 0x08,
(decoder->
reg[0x08] & 0x3f) | 0x40);
break;
 
case VIDEO_MODE_PAL:
i2c_smbus_write_byte_data(client, 0x08,
(decoder->
reg[0x08] & 0x3f) | 0x00);
break;
 
case VIDEO_MODE_AUTO:
i2c_smbus_write_byte_data(client, 0x08,
(decoder->
reg[0x08] & 0x3f) | 0x80);
break;
 
default:
return -EINVAL;
 
}
decoder->norm = *iarg;
}
break;
 
case DECODER_SET_INPUT:
{
int *iarg = arg;
 
if (*iarg < 0 || *iarg > 7) {
return -EINVAL;
}
 
if (decoder->input != *iarg) {
decoder->input = *iarg;
/* select mode */
i2c_smbus_write_byte_data(client, 0x02,
(decoder->
reg[0x02] & 0xf8) |
decoder->input);
/* bypass chrominance trap for modes 4..7 */
i2c_smbus_write_byte_data(client, 0x09,
(decoder->
reg[0x09] & 0x7f) |
((decoder->input >
3) ? 0x80 : 0));
}
}
break;
 
case DECODER_SET_OUTPUT:
{
int *iarg = arg;
 
/* not much choice of outputs */
if (*iarg != 0) {
return -EINVAL;
}
}
break;
 
case DECODER_ENABLE_OUTPUT:
{
int *iarg = arg;
int enable = (*iarg != 0);
 
if (decoder->enable != enable) {
decoder->enable = enable;
 
// RJ: If output should be disabled (for playing videos), we also need a open PLL.
// The input is set to 0 (where no input source is connected), although this
// is not necessary.
//
// If output should be enabled, we have to reverse the above.
 
if (decoder->enable) {
i2c_smbus_write_byte_data(client, 0x02,
(decoder->
reg[0x02] & 0xf8) |
decoder->input);
i2c_smbus_write_byte_data(client, 0x08,
(decoder->
reg[0x08] & 0xfb));
i2c_smbus_write_byte_data(client, 0x11,
(decoder->
reg[0x11] & 0xf3) |
0x0c);
} else {
i2c_smbus_write_byte_data(client, 0x02,
(decoder->
reg[0x02] & 0xf8));
i2c_smbus_write_byte_data(client, 0x08,
(decoder->
reg[0x08] & 0xfb) |
0x04);
i2c_smbus_write_byte_data(client, 0x11,
(decoder->
reg[0x11] & 0xf3));
}
}
}
break;
 
case DECODER_SET_PICTURE:
{
struct video_picture *pic = arg;
 
if (decoder->bright != pic->brightness) {
/* We want 0 to 255 we get 0-65535 */
decoder->bright = pic->brightness;
i2c_smbus_write_byte_data(client, 0x0a,
decoder->bright >> 8);
}
if (decoder->contrast != pic->contrast) {
/* We want 0 to 127 we get 0-65535 */
decoder->contrast = pic->contrast;
i2c_smbus_write_byte_data(client, 0x0b,
decoder->contrast >> 9);
}
if (decoder->sat != pic->colour) {
/* We want 0 to 127 we get 0-65535 */
decoder->sat = pic->colour;
i2c_smbus_write_byte_data(client, 0x0c,
decoder->sat >> 9);
}
if (decoder->hue != pic->hue) {
/* We want -128 to 127 we get 0-65535 */
decoder->hue = pic->hue;
i2c_smbus_write_byte_data(client, 0x0d,
(decoder->hue - 32768) >> 8);
}
}
break;
 
default:
return -EINVAL;
}
 
return 0;
}
 
/* ----------------------------------------------------------------------- */
 
static struct i2c_driver i2c_driver_saa7111 = {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,54)
.owner = THIS_MODULE,
#endif
.name = "saa7111", /* name */
.id = I2C_DRIVERID_SAA7111A, /* ID */
.flags = I2C_DF_NOTIFY,
.attach_adapter = saa7111_probe,
.detach_client = saa7111_detach,
.command = saa7111_command
};
 
static struct i2c_client client_template = {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
.name = "saa7111_client",
#else
.dev = {
.name = "saa7111_client",
},
#endif
.id = -1,
.driver = &i2c_driver_saa7111
};
 
static int saa7111_init(void)
{
return i2c_add_driver(&i2c_driver_saa7111);
}
 
static void saa7111_exit(void)
{
i2c_del_driver(&i2c_driver_saa7111);
}
 
module_init(saa7111_init);
module_exit(saa7111_exit);
MODULE_LICENSE("GPL");
/shark/trunk/drivers/cm7326/cm7326.c
0,0 → 1,487
/*
cm7326.c - v4l2 driver for pc104+ framegrabber module
http://www.rtdusa.com/PC104/UM/video/cm7326.htm
Copyright (C) 2002 Miguel Freitas
Copyright (C) 2003 Michael Hunold <michael@mihu.de>
 
Visit http://www.mihu.de/linux/saa7146
for further details about this card.
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#define DEBUG_VARIABLE debug
#include "drivers/saa7146_vv.h"
 
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
#define KBUILD_MODNAME cm7326
#endif
 
/* module parameters */
static int debug = 0;
MODULE_PARM(debug,"i");
MODULE_PARM_DESC(debug, "debug verbosity");
 
/* global variables */
static int cm7326_num = 0;
 
/* we simply use the two video-interfaces (aka port a and port b) */
#define CM7326_INPUTS 6
static struct v4l2_input cm7326_inputs[CM7326_INPUTS] =
{
{ 0, "VID1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 1, "VID2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 2, "VID3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 3, "VID4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 4, "VID5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 5, "VID6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
};
 
#define CM7326_AUDIOS 0
 
/* you can demand that your extension get's called for any v4l2-ioctl. here,
we want to be called exclusively when the ioctls VIDIOC_S_INPUT and
VIDIOC_ENUMINPUT get called by an user application */
static struct saa7146_extension_ioctls ioctls[] =
{
{ VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_S_STD, SAA7146_AFTER },
{ 0, 0 }
};
 
struct cm7326
{
struct video_device video_dev;
struct video_device vbi_dev;
 
struct i2c_adapter i2c_adapter;
 
int cur_input; /* current input */
};
 
static unsigned char saa7110_initseq[] = {
0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF0, 0x00, 0x00,
0xF8, 0xF8, 0x60, 0x60, 0x00, 0x06, 0x18, 0x90,
0x00, 0x2C, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA,
0xF0, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xD9, 0x17, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F,
0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x81, 0x03,
0x40, 0x75, 0x01, 0x8C, 0x03
};
 
static int saa7110_init(struct cm7326 *cm7326)
{
union i2c_smbus_data data;
int ret = 0;
int i = 0;
for (i = 0; i < sizeof(saa7110_initseq); i++) {
data.byte = saa7110_initseq[i];
if (0 != i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
DEB_D(("failed for address 0x%02x\n", i));
return -EFAULT;
}
}
 
data.byte = 0x16;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x21, I2C_SMBUS_BYTE_DATA, &data);
 
/* fixme: unclear why there are two writes to register 0x0d here ... */
data.byte = 0x04;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x0D, I2C_SMBUS_BYTE_DATA, &data);
data.byte = 0x06;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x0D, I2C_SMBUS_BYTE_DATA, &data);
if (0 != ret) {
DEB_S(("writing to saa7110 failed.\n"));
return -EFAULT;
}
 
return 0;
}
 
static const unsigned char input_modes[9][8] = {
/* mode 0 */ { 0x00, 0xD9, 0x17, 0x40, 0x03, 0x44, 0x75, 0x16 },
/* mode 1 */ { 0x00, 0xD8, 0x17, 0x40, 0x03, 0x44, 0x75, 0x16 },
/* mode 2 */ { 0x00, 0xBA, 0x07, 0x91, 0x03, 0x60, 0xB5, 0x05 },
/* mode 3 */ { 0x00, 0xB8, 0x07, 0x91, 0x03, 0x60, 0xB5, 0x05 },
/* mode 4 */ { 0x00, 0x7C, 0x07, 0xD2, 0x83, 0x60, 0xB5, 0x03 },
/* mode 5 */ { 0x00, 0x78, 0x07, 0xD2, 0x83, 0x60, 0xB5, 0x03 },
/* mode 6 */ { 0x80, 0x59, 0x17, 0x42, 0xA3, 0x44, 0x75, 0x12 },
/* mode 7 */ { 0x80, 0x9A, 0x17, 0xB1, 0x13, 0x60, 0xB5, 0x14 },
/* mode 8 */ { 0x80, 0x3C, 0x27, 0xC1, 0x23, 0x44, 0x75, 0x21 }
};
 
static int saa7110_selmux(struct cm7326 *cm7326, int chan)
{
const unsigned char* ptr = input_modes[chan];
union i2c_smbus_data data;
int ret = 0;
data.byte = ptr[0]; /* Luminance control */
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x06, I2C_SMBUS_BYTE_DATA, &data);
 
data.byte = ptr[1]; /* Analog Control #1 */
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x20, I2C_SMBUS_BYTE_DATA, &data);
 
data.byte = ptr[2]; /* Analog Control #2 */
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x21, I2C_SMBUS_BYTE_DATA, &data);
 
data.byte = ptr[3]; /* Mixer Control #1 */
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x22, I2C_SMBUS_BYTE_DATA, &data);
 
data.byte = ptr[4]; /* Mixer Control #2 */
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x2c, I2C_SMBUS_BYTE_DATA, &data);
 
data.byte = ptr[5]; /* ADCs gain control */
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x30, I2C_SMBUS_BYTE_DATA, &data);
 
data.byte = ptr[6]; /* Mixer Control #3 */
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x31, I2C_SMBUS_BYTE_DATA, &data);
 
/* fixme: unclear why there are two writes analog control #2 above and here */
data.byte = ptr[7]; /* Analog Control #2 */
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x21, I2C_SMBUS_BYTE_DATA, &data);
 
if (0 != ret) {
DEB_S(("writing to saa7110 failed.\n"));
return -EFAULT;
}
 
return 0;
}
 
static int cm7326_probe(struct saa7146_dev *dev)
{
struct cm7326 *cm7326 = NULL;
union i2c_smbus_data data;
int err = 0;
 
DEB_D(("cm7326_probe called.\n"));
 
cm7326 = (struct cm7326*)kmalloc(sizeof(struct cm7326), GFP_KERNEL);
if( NULL == cm7326 ) {
printk("cm7326: cm7326_probe: not enough kernel memory.\n");
return -ENOMEM;
}
memset(cm7326, 0x0, sizeof(struct cm7326));
 
saa7146_i2c_adapter_prepare(dev, &cm7326->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
if(i2c_add_adapter(&cm7326->i2c_adapter) < 0) {
DEB_S(("cannot register i2c-device. skipping.\n"));
kfree(cm7326);
return -EFAULT;
}
 
/* have a look if a saa7110 is present */
if (0 != (err = i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, &data))) {
DEB_D(("cm7326_probe failed for this device.\n"));
i2c_del_adapter(&cm7326->i2c_adapter);
kfree(cm7326);
return -ENODEV;
}
 
DEB_D(("cm7326_probe succeeded for this device.\n"));
 
/* we store the pointer in our private data field */
(struct cm7326*)dev->ext_priv = cm7326;
 
return 0;
}
 
/* bring hardware to a sane state. this has to be done, just in case someone
wants to capture from this device before it has been properly initialized.
the capture engine would badly fail, because no valid signal arrives on the
saa7146, thus leading to timeouts and stuff. */
static int cm7326_init_done(struct saa7146_dev *dev)
{
struct cm7326* cm7326 = (struct cm7326*)dev->ext_priv;
int ret = 0;
DEB_D(("cm7326_init_done called.\n"));
 
/* initialize the helper ics to useful values */
if (0 != (ret = saa7110_init(cm7326))) {
DEB_S(("initialization of saa7110 failed\n"));
return -EFAULT;
}
/* the rest for saa7146: you should definitely set some basic values
for the input-port handling of the saa7146. */
/* some stuff is done via variables */
saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A);
 
/* some stuff is done via direct write to the registers */
 
/* this is ugly, but because of the fact that this is completely
hardware dependend, it should be done directly... */
saa7146_write(dev, DD1_STREAM_B, 0x00000000);
saa7146_write(dev, DD1_INIT, 0x02000200);
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
 
return 0;
}
 
static struct saa7146_ext_vv vv_data;
 
/* this function only gets called when the probing was successful */
static int cm7326_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
{
struct cm7326* cm7326 = (struct cm7326*)dev->ext_priv;
DEB_D(("cm7326_attach called.\n"));
 
/* checking for i2c-devices can be omitted here, because we
already did this in "cm7326_vl42_probe" */
 
saa7146_vv_init(dev,&vv_data);
if( 0 != saa7146_register_device(&cm7326->video_dev, dev, "cm7326", VFL_TYPE_GRABBER)) {
ERR(("cannot register capture v4l2 device. skipping.\n"));
return -1;
}
 
printk("cm7326: found 'cm7326'-%d.\n",cm7326_num);
cm7326_num++;
 
/* the rest */
cm7326->cur_input = 0;
return cm7326_init_done(dev);
}
 
static int cm7326_detach(struct saa7146_dev *dev)
{
struct cm7326* cm7326 = (struct cm7326*)dev->ext_priv;
DEB_EE(("dev:%p\n",dev));
 
saa7146_unregister_device(&cm7326->video_dev,dev);
saa7146_vv_release(dev);
 
cm7326_num--;
 
i2c_del_adapter(&cm7326->i2c_adapter);
kfree(cm7326);
return 0;
}
 
static int cm7326_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
{
struct saa7146_dev *dev = fh->dev;
struct cm7326* card = (struct cm7326*)dev->ext_priv;
 
switch(cmd)
{
case VIDIOC_G_INPUT:
{
int* input = (int *)arg;
*input = card->cur_input;
 
DEB_D(("VIDIOC_G_INPUT %d.\n",*input));
return 0;
}
case VIDIOC_S_INPUT:
{
int input = *(int *)arg;
int source = 0, sync = 0;
int i;
static int saa7110_inputs[6] = {5,4,3,2,1,0};
if (input < 0 || input >= CM7326_INPUTS) {
DEB_D(("v4l2_ioctl: VIDIOC_S_INPUT: invalid input %d.\n",input));
return -EINVAL;
}
 
DEB_D(("v4l2_ioctl: VIDIOC_S_INPUT %d.\n",input));
 
source = SAA7146_HPS_SOURCE_PORT_A;
sync = SAA7146_HPS_SYNC_PORT_A;
card->cur_input = input;
 
/* switch video in saa7110 */
saa7146_set_hps_source_and_sync(dev, source, sync);
i = saa7110_inputs[input];
return saa7110_selmux(card, i);
}
case VIDIOC_ENUMINPUT:
{
struct v4l2_input *i = arg;
/* sanity check to satisfy xawtv */
if( i->index < 0 || i->index >= CM7326_INPUTS) {
DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT: invalid input %d.\n",i->index));
return -EINVAL;
}
memcpy(i, &cm7326_inputs[i->index], sizeof(struct v4l2_input));
 
DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n",i->index));
return 0;
}
default:
DEB_D(("v4l2_ioctl does not handle this ioctl.\n"));
return -ENOIOCTLCMD;
}
return 0;
}
 
static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
{
struct cm7326* cm7326 = (struct cm7326*)dev->ext_priv;
union i2c_smbus_data data;
int ret = 0;
data.byte = 0x00;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x06, I2C_SMBUS_BYTE_DATA, &data);
 
switch (std->id) {
case V4L2_STD_NTSC:
{
data.byte = 0x06;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x0D, I2C_SMBUS_BYTE_DATA, &data);
data.byte = 0x2C;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x11, I2C_SMBUS_BYTE_DATA, &data);
data.byte = 0x81;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x30, I2C_SMBUS_BYTE_DATA, &data);
data.byte = 0xDF;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x2A, I2C_SMBUS_BYTE_DATA, &data);
break;
}
case V4L2_STD_PAL:
{
data.byte = 0x06;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x0D, I2C_SMBUS_BYTE_DATA, &data);
data.byte = 0x59;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x11, I2C_SMBUS_BYTE_DATA, &data);
data.byte = 0x9A;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x2E, I2C_SMBUS_BYTE_DATA, &data);
break;
}
case V4L2_STD_SECAM:
{
data.byte = 0x07;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x0D, I2C_SMBUS_BYTE_DATA, &data);
data.byte = 0x59;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x11, I2C_SMBUS_BYTE_DATA, &data);
data.byte = 0x9A;
ret += i2c_smbus_xfer(&cm7326->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, 0x2E, I2C_SMBUS_BYTE_DATA, &data);
break;
}
default:
{
DEB_S(("invalid standard.\n"));
return -EFAULT;
}
}
 
if (0 != ret) {
DEB_S(("writing to saa7110 failed.\n"));
return -EFAULT;
}
 
return 0;
}
 
static struct saa7146_standard standard[] = {
{
.name = "PAL", .id = V4L2_STD_PAL,
.v_offset = 0x17, .v_field = 288, .v_calc = 576,
.h_offset = 0x14, .h_pixels = 680, .h_calc = 680+1,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "NTSC", .id = V4L2_STD_NTSC,
.v_offset = 0x14, .v_field = 240, .v_calc = 480,
.h_offset = 0x00, .h_pixels = 640, .h_calc = 640+1,
.v_max_out = 480, .h_max_out = 640,
}, {
.name = "SECAM", .id = V4L2_STD_SECAM,
.v_offset = 0x14, .v_field = 288, .v_calc = 576,
.h_offset = 0x14, .h_pixels = 720, .h_calc = 720+1,
.v_max_out = 576, .h_max_out = 768,
}
};
 
static struct saa7146_extension extension;
 
static struct saa7146_pci_extension_data cm7326 = {
.ext_priv = "cm7326",
.ext = &extension,
};
 
static struct pci_device_id pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x1435,
.subdevice = 0x3303,
.driver_data = (unsigned long)&cm7326,
}, {
.vendor = 0,
}
};
 
static struct saa7146_ext_vv vv_data = {
.inputs = CM7326_INPUTS,
.capabilities = 0,
.stds = &standard[0],
.num_stds = sizeof(standard)/sizeof(struct saa7146_standard),
.std_callback = &std_callback,
.ioctls = &ioctls[0],
.ioctl = cm7326_ioctl,
};
 
static struct saa7146_extension extension = {
.name = "cm7326",
.flags = SAA7146_USE_I2C_IRQ,
.pci_tbl = &pci_tbl[0],
.module = THIS_MODULE,
 
.probe = cm7326_probe,
.attach = cm7326_attach,
.detach = cm7326_detach,
 
.irq_mask = 0,
.irq_func = NULL,
};
 
 
int __init cm7326_init_module(void)
{
int ret = 0;
 
ret = saa7146_register_extension(&extension);
if (0 != ret) {
DEB_S(("failed to register extension.\n"));
}
return ret;
}
 
void __exit cm7326_cleanup_module(void)
{
saa7146_unregister_extension(&extension);
}
 
module_init(cm7326_init_module);
module_exit(cm7326_cleanup_module);
 
MODULE_AUTHOR("Miguel Freitas, Michael Hunold");
MODULE_DESCRIPTION("CM7326 frame grabber v4l2-driver");
MODULE_LICENSE("GPL");
 
/shark/trunk/drivers/cm7326/saa7146_i2c.c
0,0 → 1,433
#include <linux/version.h>
#include <media/saa7146_vv.h>
 
/* helper function */
static void my_wait(struct saa7146_dev *dev, long ms)
{
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout((((ms+10)/10)*HZ)/1000);
}
 
u32 saa7146_i2c_func(struct i2c_adapter *adapter)
{
//fm DEB_I2C(("'%s'.\n", adapter->name));
 
return I2C_FUNC_I2C
| I2C_FUNC_SMBUS_QUICK
| I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE
| I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
}
 
/* this function returns the status-register of our i2c-device */
static inline u32 saa7146_i2c_status(struct saa7146_dev *dev)
{
u32 iicsta = saa7146_read(dev, I2C_STATUS);
/*
DEB_I2C(("status: 0x%08x\n",iicsta));
*/
return iicsta;
}
 
/* this function runs through the i2c-messages and prepares the data to be
sent through the saa7146. have a look at the specifications p. 122 ff
to understand this. it returns the number of u32s to send, or -1
in case of an error. */
static int saa7146_i2c_msg_prepare(const struct i2c_msg m[], int num, u32 *op)
{
int h1, h2;
int i, j, addr;
int mem = 0, op_count = 0;
 
/* first determine size of needed memory */
for(i = 0; i < num; i++) {
mem += m[i].len + 1;
}
 
/* worst case: we need one u32 for three bytes to be send
plus one extra byte to address the device */
mem = 1 + ((mem-1) / 3);
 
/* we assume that op points to a memory of at least SAA7146_I2C_MEM bytes
size. if we exceed this limit... */
if ( (4*mem) > SAA7146_I2C_MEM ) {
//fm DEB_I2C(("cannot prepare i2c-message.\n"));
return -ENOMEM;
}
 
/* be careful: clear out the i2c-mem first */
memset(op,0,sizeof(u32)*mem);
 
/* loop through all messages */
for(i = 0; i < num; i++) {
 
/* insert the address of the i2c-slave.
note: we get 7 bit i2c-addresses,
so we have to perform a translation */
addr = (m[i].addr*2) + ( (0 != (m[i].flags & I2C_M_RD)) ? 1 : 0);
h1 = op_count/3; h2 = op_count%3;
op[h1] |= ( (u8)addr << ((3-h2)*8));
op[h1] |= (SAA7146_I2C_START << ((3-h2)*2));
op_count++;
 
/* loop through all bytes of message i */
for(j = 0; j < m[i].len; j++) {
/* insert the data bytes */
h1 = op_count/3; h2 = op_count%3;
op[h1] |= ( (u32)((u8)m[i].buf[j]) << ((3-h2)*8));
op[h1] |= ( SAA7146_I2C_CONT << ((3-h2)*2));
op_count++;
}
}
 
/* have a look at the last byte inserted:
if it was: ...CONT change it to ...STOP */
h1 = (op_count-1)/3; h2 = (op_count-1)%3;
if ( SAA7146_I2C_CONT == (0x3 & (op[h1] >> ((3-h2)*2))) ) {
op[h1] &= ~(0x2 << ((3-h2)*2));
op[h1] |= (SAA7146_I2C_STOP << ((3-h2)*2));
}
 
/* return the number of u32s to send */
return mem;
}
 
/* this functions loops through all i2c-messages. normally, it should determine
which bytes were read through the adapter and write them back to the corresponding
i2c-message. but instead, we simply write back all bytes.
fixme: this could be improved. */
static int saa7146_i2c_msg_cleanup(const struct i2c_msg m[], int num, u32 *op)
{
int i, j;
int op_count = 0;
 
/* loop through all messages */
for(i = 0; i < num; i++) {
 
op_count++;
 
/* loop throgh all bytes of message i */
for(j = 0; j < m[i].len; j++) {
/* write back all bytes that could have been read */
m[i].buf[j] = (op[op_count/3] >> ((3-(op_count%3))*8));
op_count++;
}
}
return 0;
}
 
/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */
static int saa7146_i2c_reset(struct saa7146_dev *dev)
{
/* get current status */
u32 status = saa7146_i2c_status(dev);
/* clear registers for sure */
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, I2C_TRANSFER, 0);
 
/* check if any operation is still in progress */
if ( 0 != ( status & SAA7146_I2C_BUSY) ) {
 
/* yes, kill ongoing operation */
DEB_I2C(("busy_state detected.\n"));
 
/* set "ABORT-OPERATION"-bit (bit 7)*/
saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
my_wait(dev,SAA7146_I2C_DELAY);
 
/* clear all error-bits pending; this is needed because p.123, note 1 */
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
my_wait(dev,SAA7146_I2C_DELAY);
}
 
/* check if any error is (still) present. (this can be necessary because p.123, note 1) */
status = saa7146_i2c_status(dev);
 
if ( dev->i2c_bitrate != status ) {
 
DEB_I2C(("error_state detected. status:0x%08x\n",status));
 
/* Repeat the abort operation. This seems to be necessary
after serious protocol errors caused by e.g. the SAA7740 */
saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
my_wait(dev,SAA7146_I2C_DELAY);
 
/* clear all error-bits pending */
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
my_wait(dev,SAA7146_I2C_DELAY);
 
/* the data sheet says it might be necessary to clear the status
twice after an abort */
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
my_wait(dev,SAA7146_I2C_DELAY);
}
 
/* if any error is still present, a fatal error has occured ... */
status = saa7146_i2c_status(dev);
if ( dev->i2c_bitrate != status ) {
DEB_I2C(("fatal error. status:0x%08x\n",status));
return -1;
}
 
return 0;
}
 
/* this functions writes out the data-byte 'dword' to the i2c-device.
it returns 0 if ok, -1 if the transfer failed, -2 if the transfer
failed badly (e.g. address error) */
static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_delay)
{
u32 status = 0, mc2 = 0;
int trial = 0;
unsigned long timeout;
 
/* write out i2c-command */
DEB_I2C(("before: 0x%08x (status: 0x%08x), %d\n",*dword,saa7146_read(dev, I2C_STATUS), dev->i2c_op));
 
if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) {
 
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, I2C_TRANSFER, *dword);
 
dev->i2c_op = 1;
IER_ENABLE(dev, MASK_16|MASK_17);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
 
wait_event_interruptible(dev->i2c_wq, dev->i2c_op == 0);
if (signal_pending (current)) {
/* a signal arrived */
return -ERESTARTSYS;
}
status = saa7146_read(dev, I2C_STATUS);
} else {
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, I2C_TRANSFER, *dword);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
 
/* do not poll for i2c-status before upload is complete */
timeout = jiffies + HZ/100 + 1; /* 10ms */
while(1) {
mc2 = (saa7146_read(dev, MC2) & 0x1);
if( 0 != mc2 ) {
break;
}
if (time_after(jiffies,timeout)) {
printk(KERN_WARNING "saa7146_i2c_writeout: timed out waiting for MC2\n");
return -EIO;
}
}
/* wait until we get a transfer done or error */
timeout = jiffies + HZ/100 + 1; /* 10ms */
while(1) {
/**
* first read usually delivers bogus results...
*/
saa7146_i2c_status(dev);
status = saa7146_i2c_status(dev);
if ((status & 0x3) != 1)
break;
if (time_after(jiffies,timeout)) {
/* this is normal when probing the bus
* (no answer from nonexisistant device...)
*/
DEB_I2C(("saa7146_i2c_writeout: timed out waiting for end of xfer\n"));
return -EIO;
}
if ((++trial < 20) && short_delay)
udelay(10);
else
my_wait(dev,1);
}
}
 
/* give a detailed status report */
if ( 0 != (status & SAA7146_I2C_ERR)) {
 
if( 0 != (status & SAA7146_I2C_SPERR) ) {
DEB_I2C(("error due to invalid start/stop condition.\n"));
}
if( 0 != (status & SAA7146_I2C_DTERR) ) {
DEB_I2C(("error in data transmission.\n"));
}
if( 0 != (status & SAA7146_I2C_DRERR) ) {
DEB_I2C(("error when receiving data.\n"));
}
if( 0 != (status & SAA7146_I2C_AL) ) {
DEB_I2C(("error because arbitration lost.\n"));
}
 
/* we handle address-errors here */
if( 0 != (status & SAA7146_I2C_APERR) ) {
DEB_I2C(("error in address phase.\n"));
return -EREMOTEIO;
}
 
return -EIO;
}
 
/* read back data, just in case we were reading ... */
*dword = saa7146_read(dev, I2C_TRANSFER);
 
DEB_I2C(("after: 0x%08x\n",*dword));
return 0;
}
 
int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg msgs[], int num, int retries)
{
int i = 0, count = 0;
u32* buffer = dev->d_i2c.cpu_addr;
int err = 0;
int address_err = 0;
int short_delay = 0;
if (down_interruptible (&dev->i2c_lock))
return -ERESTARTSYS;
 
for(i=0;i<num;i++) {
DEB_I2C(("msg:%d/%d\n",i+1,num));
}
/* prepare the message(s), get number of u32s to transfer */
count = saa7146_i2c_msg_prepare(msgs, num, buffer);
if ( 0 > count ) {
err = -1;
goto out;
}
 
if ( count > 3 || 0 != (SAA7146_I2C_SHORT_DELAY & dev->ext->flags) )
short_delay = 1;
 
do {
/* reset the i2c-device if necessary */
err = saa7146_i2c_reset(dev);
if ( 0 > err ) {
DEB_I2C(("could not reset i2c-device.\n"));
goto out;
}
 
/* write out the u32s one after another */
for(i = 0; i < count; i++) {
err = saa7146_i2c_writeout(dev, &buffer[i], short_delay);
if ( 0 != err) {
/* this one is unsatisfying: some i2c slaves on some
dvb cards don't acknowledge correctly, so the saa7146
thinks that an address error occured. in that case, the
transaction should be retrying, even if an address error
occured. analog saa7146 based cards extensively rely on
i2c address probing, however, and address errors indicate that a
device is really *not* there. retrying in that case
increases the time the device needs to probe greatly, so
it should be avoided. because of the fact, that only
analog based cards use irq based i2c transactions (for dvb
cards, this screwes up other interrupt sources), we bail out
completely for analog cards after an address error and trust
the saa7146 address error detection. */
if ( -EREMOTEIO == err ) {
if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) {
goto out;
}
address_err++;
}
DEB_I2C(("error while sending message(s). starting again.\n"));
break;
}
}
if( 0 == err ) {
err = num;
break;
}
/* delay a bit before retrying */
my_wait(dev, 10);
} while (err != num && retries--);
 
/* if every retry had an address error, exit right away */
if (address_err == retries) {
goto out;
}
/* if any things had to be read, get the results */
if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) {
DEB_I2C(("could not cleanup i2c-message.\n"));
err = -1;
goto out;
}
 
/* return the number of delivered messages */
DEB_I2C(("transmission successful. (msg:%d).\n",err));
out:
/* another bug in revision 0: the i2c-registers get uploaded randomly by other
uploads, so we better clear them out before continueing */
if( 0 == dev->revision ) {
u32 zero = 0;
saa7146_i2c_reset(dev);
if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) {
INFO(("revision 0 error. this should never happen.\n"));
}
}
 
up(&dev->i2c_lock);
return err;
}
 
/* utility functions */
static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg msg[], int num)
{
struct saa7146_dev* dev = i2c_get_adapdata(adapter);
 
/* use helper function to transfer data */
return saa7146_i2c_transfer(dev, msg, num, adapter->retries);
}
 
 
/*****************************************************************************/
/* i2c-adapter helper functions */
#include <linux/i2c-id.h>
 
/* exported algorithm data */
static struct i2c_algorithm saa7146_algo = {
.name = "saa7146 i2c algorithm",
.id = I2C_ALGO_SAA7146,
.master_xfer = saa7146_i2c_xfer,
.functionality = saa7146_i2c_func,
};
 
int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate)
{
DEB_EE(("bitrate: 0x%08x\n",bitrate));
/* enable i2c-port pins */
saa7146_write(dev, MC1, (MASK_08 | MASK_24));
 
dev->i2c_bitrate = bitrate;
saa7146_i2c_reset(dev);
 
if( NULL != i2c_adapter ) {
memset(i2c_adapter,0,sizeof(struct i2c_adapter));
strcpy(i2c_adapter->name, dev->name);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
i2c_adapter->data = dev;
#else
i2c_set_adapdata(i2c_adapter,dev);
#endif
i2c_adapter->algo = &saa7146_algo;
i2c_adapter->algo_data = NULL;
i2c_adapter->id = I2C_ALGO_SAA7146;
i2c_adapter->timeout = SAA7146_I2C_TIMEOUT;
i2c_adapter->retries = SAA7146_I2C_RETRIES;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
#else
i2c_adapter->class = I2C_ADAP_CLASS_TV_ANALOG;
#endif
}
return 0;
}
/shark/trunk/drivers/cm7326/tea6420.c
0,0 → 1,243
/*
tea6420.o - i2c-driver for the tea6420 by SGS Thomson
 
Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
 
The tea6420 is a bus controlled audio-matrix with 5 stereo inputs,
4 stereo outputs and gain control for each output.
It is cascadable, i.e. it can be found at the adresses 0x98
and 0x9a on the i2c-bus.
For detailed informations download the specifications directly
from SGS Thomson at http://www.st.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/init.h>
 
#include "tea6420.h"
 
static int debug = 0; /* insmod parameter */
MODULE_PARM(debug,"i");
#define dprintk if (debug) printk
 
/* addresses to scan, found only at 0x4c and/or 0x4d (7-Bit) */
static unsigned short normal_i2c[] = {I2C_TEA6420_1, I2C_TEA6420_2, I2C_CLIENT_END};
static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
 
/* magic definition of all other variables and things */
I2C_CLIENT_INSMOD;
 
static struct i2c_driver driver;
 
/* unique ID allocation */
static int tea6420_id = 0;
 
/* make a connection between the input 'i' and the output 'o'
with gain 'g' for the tea6420-client 'client' (note: i = 6 means 'mute') */
static int tea6420_switch(struct i2c_client *client, int i, int o, int g)
{
u8 byte = 0;
int result = 0;
dprintk("tea6420.o: tea6420_switch: adr:0x%02x, i:%d, o:%d, g:%d\n",client->addr,i,o,g);
 
/* check if the paramters are valid */
if ( i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g%2 != 0 )
return -1;
 
byte = ((o-1)<<5);
byte |= (i-1);
 
/* to understand this, have a look at the tea6420-specs (p.5) */
switch(g) {
case 0:
byte |= (3<<3);
break;
case 2:
byte |= (2<<3);
break;
case 4:
byte |= (1<<3);
break;
case 6:
break;
}
 
/* fixme?: 1 != ... => 0 != */
if ( 0 != (result = i2c_smbus_write_byte(client,byte))) {
printk("tea6402:%d\n",result);
dprintk(KERN_ERR "tea6420.o: could not switch, result:%d\n",result);
return -EFAULT;
}
return 0;
}
 
/* this function is called by i2c_probe */
static int tea6420_detect(struct i2c_adapter *adapter, int address, unsigned short flags, int kind)
{
struct i2c_client *client;
int err = 0, i = 0;
 
/* let's see whether this adapter can support what we need */
if ( 0 == i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) {
return 0;
}
 
/* allocate memory for client structure */
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (0 == client) {
return -ENOMEM;
}
memset(client, 0, sizeof(struct i2c_client));
/* fill client structure */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
sprintf(client->name,"tea6420 (0x%02x)", address);
#else
sprintf(client->dev.name,"tea6420 (0x%02x)", address);
#endif
client->id = tea6420_id++;
client->flags = 0;
client->addr = address;
client->adapter = adapter;
client->driver = &driver;
 
/* tell the i2c layer a new client has arrived */
if (0 != (err = i2c_attach_client(client))) {
kfree(client);
return err;
}
 
/* set initial values: set "mute"-input to all outputs at gain 0 */
err = 0;
for(i = 1; i < 5; i++) {
err += tea6420_switch(client, 6, i, 0);
}
if( 0 != err) {
printk("tea6420.o: could not initialize chipset. continuing anyway.\n");
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
printk("tea6420.o: detected @ 0x%02x on adapter %s\n",address,&client->adapter->name[0]);
#else
printk("tea6420.o: detected @ 0x%02x on adapter %s\n",address,&client->adapter->dev.name[0]);
#endif
return 0;
}
 
static int tea6420_attach(struct i2c_adapter *adapter)
{
/* let's see whether this is a know adapter we can attach to */
if( adapter->id != I2C_ALGO_SAA7146 ) {
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
dprintk("tea6420.o: refusing to probe on unknown adapter [name='%s',id=0x%x]\n",adapter->name,adapter->id);
#else
dprintk("tea6420.o: refusing to probe on unknown adapter [name='%s',id=0x%x]\n",adapter->dev.name,adapter->id);
#endif
return -ENODEV;
}
 
return i2c_probe(adapter,&addr_data,&tea6420_detect);
}
 
static int tea6420_detach(struct i2c_client *client)
{
int err = 0;
 
if ( 0 != (err = i2c_detach_client(client))) {
printk("tea6420.o: Client deregistration failed, client not detached.\n");
return err;
}
kfree(client);
 
return 0;
}
 
static int tea6420_command(struct i2c_client *client, unsigned int cmd, void* arg)
{
struct tea6420_multiplex *a = (struct tea6420_multiplex*)arg;
int result = 0;
 
switch (cmd) {
case TEA6420_SWITCH: {
result = tea6420_switch(client,a->in,a->out,a->gain);
break;
}
default: {
return -ENOIOCTLCMD;
}
}
 
if ( 0 != result )
return result;
return 0;
}
static void tea6420_inc_use(struct i2c_client *client)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
 
static void tea6420_dec_use(struct i2c_client *client)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
 
static struct i2c_driver driver = {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,54)
.owner = THIS_MODULE,
#endif
.name = "tea6420 driver",
.id = I2C_DRIVERID_TEA6420,
.flags = I2C_DF_NOTIFY,
.attach_adapter = tea6420_attach,
.detach_client = tea6420_detach,
.command = tea6420_command,
.inc_use = tea6420_inc_use,
.dec_use = tea6420_dec_use,
};
 
static int tea6420_init_module(void)
{
i2c_add_driver(&driver);
return 0;
}
 
static void tea6420_cleanup_module(void)
{
i2c_del_driver(&driver);
}
 
module_init(tea6420_init_module);
module_exit(tea6420_cleanup_module);
 
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("tea6420 driver");
MODULE_LICENSE("GPL");
/shark/trunk/drivers/cm7326/mxb.c
0,0 → 1,1092
/*
mxb.c - v4l2 driver for the Multimedia eXtension Board
Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
 
Visit http://www.mihu.de/linux/saa7146/mxb/
for further details about this card.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#define DEBUG_VARIABLE debug
 
#include <media/saa7146_vv.h>
#include <linux/video_decoder.h> /* for saa7111a */
 
#include "mxb.h"
#include "tea6415c.h"
#include "tea6420.h"
#include "tda9840.h"
#include <media/tuner.h>
 
#define I2C_SAA7111A 0x24
 
/* All unused bytes are reserverd. */
#define SAA711X_CHIP_VERSION 0x00
#define SAA711X_ANALOG_INPUT_CONTROL_1 0x02
#define SAA711X_ANALOG_INPUT_CONTROL_2 0x03
#define SAA711X_ANALOG_INPUT_CONTROL_3 0x04
#define SAA711X_ANALOG_INPUT_CONTROL_4 0x05
#define SAA711X_HORIZONTAL_SYNC_START 0x06
#define SAA711X_HORIZONTAL_SYNC_STOP 0x07
#define SAA711X_SYNC_CONTROL 0x08
#define SAA711X_LUMINANCE_CONTROL 0x09
#define SAA711X_LUMINANCE_BRIGHTNESS 0x0A
#define SAA711X_LUMINANCE_CONTRAST 0x0B
#define SAA711X_CHROMA_SATURATION 0x0C
#define SAA711X_CHROMA_HUE_CONTROL 0x0D
#define SAA711X_CHROMA_CONTROL 0x0E
#define SAA711X_FORMAT_DELAY_CONTROL 0x10
#define SAA711X_OUTPUT_CONTROL_1 0x11
#define SAA711X_OUTPUT_CONTROL_2 0x12
#define SAA711X_OUTPUT_CONTROL_3 0x13
#define SAA711X_V_GATE_1_START 0x15
#define SAA711X_V_GATE_1_STOP 0x16
#define SAA711X_V_GATE_1_MSB 0x17
#define SAA711X_TEXT_SLICER_STATUS 0x1A
#define SAA711X_DECODED_BYTES_OF_TS_1 0x1B
#define SAA711X_DECODED_BYTES_OF_TS_2 0x1C
#define SAA711X_STATUS_BYTE 0x1F
 
#define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0)
 
/* global variable */
static int mxb_num = 0;
 
/* initial frequence the tuner will be tuned to.
in verden (lower saxony, germany) 4148 is a
channel called "phoenix" */
static int freq = 4148;
MODULE_PARM(freq,"i");
MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup");
 
static int debug = 0;
MODULE_PARM(debug,"i");
MODULE_PARM_DESC(debug, "debug verbosity");
 
#define MXB_INPUTS 4
enum { TUNER, AUX1, AUX3, AUX3_YC };
 
static struct v4l2_input mxb_inputs[MXB_INPUTS] = {
{ TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
};
 
/* this array holds the information, which port of the saa7146 each
input actually uses. the mxb uses port 0 for every input */
static struct {
int hps_source;
int hps_sync;
} input_port_selection[MXB_INPUTS] = {
{ SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
{ SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
{ SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
{ SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
};
 
/* this array holds the information of the audio source (mxb_audios),
which has to be switched corresponding to the video source (mxb_channels) */
static int video_audio_connect[MXB_INPUTS] =
{ 0, 1, 3, 3 };
 
/* these are the necessary input-output-pins for bringing one audio source
(see above) to the CD-output */
static struct tea6420_multiplex TEA6420_cd[MXB_AUDIOS+1][2] =
{
{{1,1,0},{1,1,0}}, /* Tuner */
{{5,1,0},{6,1,0}}, /* AUX 1 */
{{4,1,0},{6,1,0}}, /* AUX 2 */
{{3,1,0},{6,1,0}}, /* AUX 3 */
{{1,1,0},{3,1,0}}, /* Radio */
{{1,1,0},{2,1,0}}, /* CD-Rom */
{{6,1,0},{6,1,0}} /* Mute */
};
 
/* these are the necessary input-output-pins for bringing one audio source
(see above) to the line-output */
static struct tea6420_multiplex TEA6420_line[MXB_AUDIOS+1][2] =
{
{{2,3,0},{1,2,0}},
{{5,3,0},{6,2,0}},
{{4,3,0},{6,2,0}},
{{3,3,0},{6,2,0}},
{{2,3,0},{3,2,0}},
{{2,3,0},{2,2,0}},
{{6,3,0},{6,2,0}} /* Mute */
};
 
#define MAXCONTROLS 1
static struct v4l2_queryctrl mxb_controls[] = {
{ V4L2_CID_AUDIO_MUTE, V4L2_CTRL_TYPE_BOOLEAN, "Mute", 0, 1, 1, 0, 0 },
};
 
static struct saa7146_extension_ioctls ioctls[] = {
{ VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_QUERYCTRL, SAA7146_BEFORE },
{ VIDIOC_G_CTRL, SAA7146_BEFORE },
{ VIDIOC_S_CTRL, SAA7146_BEFORE },
{ VIDIOC_G_TUNER, SAA7146_EXCLUSIVE },
{ VIDIOC_S_TUNER, SAA7146_EXCLUSIVE },
{ VIDIOC_G_FREQUENCY, SAA7146_EXCLUSIVE },
{ VIDIOC_S_FREQUENCY, SAA7146_EXCLUSIVE },
{ VIDIOC_G_AUDIO, SAA7146_EXCLUSIVE },
{ VIDIOC_S_AUDIO, SAA7146_EXCLUSIVE },
{ MXB_S_AUDIO_CD, SAA7146_EXCLUSIVE }, /* custom control */
{ MXB_S_AUDIO_LINE, SAA7146_EXCLUSIVE }, /* custom control */
{ 0, 0 }
};
 
struct mxb
{
struct video_device video_dev;
struct video_device vbi_dev;
 
struct i2c_adapter i2c_adapter;
 
struct i2c_client* saa7111a;
struct i2c_client* tda9840;
struct i2c_client* tea6415c;
struct i2c_client* tuner;
struct i2c_client* tea6420_1;
struct i2c_client* tea6420_2;
 
int cur_mode; /* current audio mode (mono, stereo, ...) */
int cur_input; /* current input */
int cur_freq; /* current frequency the tuner is tuned to */
int cur_mute; /* current mute status */
};
 
static struct saa7146_extension extension;
 
static int mxb_vbi_bypass(struct saa7146_dev* dev)
{
struct mxb* mxb = (struct mxb*)dev->ext_priv;
s32 byte = 0x0;
int result = 0;
 
DEB_EE(("dev:%p\n",dev));
 
/* switch bypass in saa7111a, this should be done in the
saa7111a driver of course... */
if ( -1 == (result = i2c_smbus_read_byte_data(mxb->saa7111a, SAA711X_OUTPUT_CONTROL_3))) {
DEB_D(("could not read from saa7111a.\n"));
return -EFAULT;
}
byte = result;
byte &= 0xf0;
byte |= 0x0a;
 
if ( 0 != (result = i2c_smbus_write_byte_data(mxb->saa7111a, SAA711X_OUTPUT_CONTROL_3, byte))) {
DEB_D(("could not write to saa7111a.\n"));
return -EFAULT;
}
return 0;
}
 
static int mxb_probe(struct saa7146_dev* dev)
{
struct mxb* mxb = 0;
int i = 0;
 
request_module("tuner");
request_module("tea6420");
request_module("tea6415c");
request_module("tda9840");
request_module("saa7111");
 
mxb = (struct mxb*)kmalloc(sizeof(struct mxb), GFP_KERNEL);
if( NULL == mxb ) {
DEB_D(("not enough kernel memory.\n"));
return -ENOMEM;
}
memset(mxb, 0x0, sizeof(struct mxb));
 
saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
if(i2c_add_adapter(&mxb->i2c_adapter) < 0) {
DEB_S(("cannot register i2c-device. skipping.\n"));
kfree(mxb);
return -EFAULT;
}
 
/* loop through all i2c-devices on the bus and look who is there */
for(i = 0; i < I2C_CLIENT_MAX; i++) {
if( NULL == mxb->i2c_adapter.clients[i] ) {
continue;
}
if( I2C_TEA6420_1 == mxb->i2c_adapter.clients[i]->addr )
mxb->tea6420_1 = mxb->i2c_adapter.clients[i];
if( I2C_TEA6420_2 == mxb->i2c_adapter.clients[i]->addr )
mxb->tea6420_2 = mxb->i2c_adapter.clients[i];
if( I2C_TEA6415C_2 == mxb->i2c_adapter.clients[i]->addr )
mxb->tea6415c = mxb->i2c_adapter.clients[i];
if( I2C_TDA9840 == mxb->i2c_adapter.clients[i]->addr )
mxb->tda9840 = mxb->i2c_adapter.clients[i];
if( I2C_SAA7111A == mxb->i2c_adapter.clients[i]->addr )
mxb->saa7111a = mxb->i2c_adapter.clients[i];
if( 0x60 == mxb->i2c_adapter.clients[i]->addr )
mxb->tuner = mxb->i2c_adapter.clients[i];
}
 
/* check if all devices are present */
if( 0 == mxb->tea6420_1 || 0 == mxb->tea6420_2 || 0 == mxb->tea6415c
|| 0 == mxb->tda9840 || 0 == mxb->saa7111a || 0 == mxb->tuner ) {
 
printk("mxb: did not find all i2c devices. are you sure you\n");
printk("mxb: insmod'ed tea6420, tea6415c, saa7111, tea6415c and tuner?\n");
i2c_del_adapter(&mxb->i2c_adapter);
kfree(mxb);
return -ENODEV;
}
 
/* all devices are present, probe was successful */
 
/* we store the pointer in our private data field */
(struct mxb*)dev->ext_priv = mxb;
 
return 0;
}
 
/* some init data for the saa7740, the so-called 'sound arena module'.
there are no specs available, so we simply use some init values */
static struct {
int length;
char data[9];
} mxb_saa7740_init[] = {
{ 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } },
{ 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } },
{ 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } },
{ 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } },
{ 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } },
{ 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } },
{ 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } },
{ 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } },
{ 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } },
{ 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } },
{ 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } },
{ 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } },
{ 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } },
{ 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } },
{ 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } },
{ 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } },
{ 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } },
{ 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } },
{ 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } },
{ 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } },
{ 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } },
{ 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } },
{ 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } },
{ 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } },
{ 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } },
{ 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } },
{ 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } },
{ 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } },
{ 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } },
{ 3, { 0x48, 0x00, 0x01 } },
{ 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
{ 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
{ 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
{ 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
{ 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
{ 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
{ 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
{ 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
{ 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
{ 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
{ 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
{ 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
{ 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
{ 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
{ 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
{ 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
{ 3, { 0x80, 0xb3, 0x0a } },
{-1, { 0} }
};
 
static unsigned char mxb_saa7111_init[25] = {
0x00,
0x00, /* 00 - ID byte */
0x00, /* 01 - reserved */
 
/*front end */
0xd8, /* 02 - FUSE=x, GUDL=x, MODE=x */
0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */
0x00, /* 04 - GAI1=256 */
0x00, /* 05 - GAI2=256 */
 
/* decoder */
0xf0, /* 06 - HSB at xx(50Hz) / xx(60Hz) pixels after end of last line */
0x30, /* 07 - HSS at xx(50Hz) / xx(60Hz) pixels after end of last line */
0xa8, /* 08 - AUFD=x, FSEL=x, EXFIL=x, VTRC=x, HPLL=x, VNOI=x */
0x02, /* 09 - BYPS=x, PREF=x, BPSS=x, VBLB=x, UPTCV=x, APER=x */
0x80, /* 0a - BRIG=128 */
0x47, /* 0b - CONT=1.109 */
0x40, /* 0c - SATN=1.0 */
0x00, /* 0d - HUE=0 */
0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */
0x00, /* 0f - reserved */
0xd0, /* 10 - OFTS=x, HDEL=x, VRLN=x, YDEL=x */
0x8c, /* 11 - GPSW=x, CM99=x, FECO=x, COMPO=x, OEYC=1, OEHV=1, VIPB=0, COLO=0 */
0x80, /* 12 - xx output control 2 */
0x30, /* 13 - xx output control 3 */
0x00, /* 14 - reserved */
0x15, /* 15 - VBI */
0x04, /* 16 - VBI */
0x00, /* 17 - VBI */
};
 
/* bring hardware to a sane state. this has to be done, just in case someone
wants to capture from this device before it has been properly initialized.
the capture engine would badly fail, because no valid signal arrives on the
saa7146, thus leading to timeouts and stuff. */
static int mxb_init_done(struct saa7146_dev* dev)
{
struct mxb* mxb = (struct mxb*)dev->ext_priv;
struct i2c_msg msg;
 
int i = 0, err = 0;
struct tea6415c_multiplex vm;
 
/* write configuration to saa7111a */
i = i2c_master_send(mxb->saa7111a, mxb_saa7111_init, sizeof(mxb_saa7111_init));
if (i < 0) {
printk("failed to initialize saa7111a. this should never happen.\n");
}
 
/* select tuner-output on saa7111a */
i = 0;
mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_INPUT, &i);
// i = VIDEO_MODE_PAL;
// mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_NORM, &i);
 
mxb_vbi_bypass(dev);
 
/* select a tuner type */
i = 5;
mxb->tuner->driver->command(mxb->tuner,TUNER_SET_TYPE, &i);
/* mute audio on tea6420s */
mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[6][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[6][1]);
mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_cd[6][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_cd[6][1]);
 
/* switch to tuner-channel on tea6415c*/
vm.out = 17;
vm.in = 3;
mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm);
 
/* select tuner-output on multicable on tea6415c*/
vm.in = 3;
vm.out = 13;
mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm);
/* tune in some frequency on tuner */
mxb->tuner->driver->command(mxb->tuner, VIDIOCSFREQ, &freq);
 
/* the rest for mxb */
mxb->cur_input = 0;
mxb->cur_freq = freq;
mxb->cur_mute = 1;
 
mxb->cur_mode = V4L2_TUNER_MODE_STEREO;
mxb->tda9840->driver->command(mxb->tda9840, TDA9840_SWITCH, &mxb->cur_mode);
/* check if the saa7740 (aka 'sound arena module') is present
on the mxb. if so, we must initialize it. due to lack of
informations about the saa7740, the values were reverse
engineered. */
msg.addr = 0x1b;
msg.flags = 0;
msg.len = mxb_saa7740_init[0].length;
msg.buf = &mxb_saa7740_init[0].data[0];
 
if( 1 == (err = i2c_transfer(&mxb->i2c_adapter, &msg, 1))) {
/* the sound arena module is a pos, that's probably the reason
philips refuses to hand out a datasheet for the saa7740...
it seems to screw up the i2c bus, so we disable fast irq
based i2c transactions here and rely on the slow and safe
polling method ... */
extension.flags &= ~SAA7146_USE_I2C_IRQ;
for(i = 1;;i++) {
if( -1 == mxb_saa7740_init[i].length ) {
break;
}
 
msg.len = mxb_saa7740_init[i].length;
msg.buf = &mxb_saa7740_init[i].data[0];
if( 1 != (err = i2c_transfer(&mxb->i2c_adapter, &msg, 1))) {
DEB_D(("failed to initialize 'sound arena module'.\n"));
goto err;
}
}
INFO(("'sound arena module' detected.\n"));
}
err:
/* the rest for saa7146: you should definitely set some basic values
for the input-port handling of the saa7146. */
 
/* ext->saa has been filled by the core driver */
/* some stuff is done via variables */
saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source, input_port_selection[mxb->cur_input].hps_sync);
 
/* some stuff is done via direct write to the registers */
 
/* this is ugly, but because of the fact that this is completely
hardware dependend, it should be done directly... */
saa7146_write(dev, DD1_STREAM_B, 0x00000000);
saa7146_write(dev, DD1_INIT, 0x02000200);
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
 
return 0;
}
 
/* interrupt-handler. this gets called when irq_mask is != 0.
it must clear the interrupt-bits in irq_mask it has handled */
/*
void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask)
{
struct mxb* mxb = (struct mxb*)dev->ext_priv;
}
*/
 
static struct saa7146_ext_vv vv_data;
 
/* this function only gets called when the probing was successful */
static int mxb_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
{
struct mxb* mxb = (struct mxb*)dev->ext_priv;
DEB_EE(("dev:%p\n",dev));
 
/* checking for i2c-devices can be omitted here, because we
already did this in "mxb_vl42_probe" */
 
saa7146_vv_init(dev,&vv_data);
if( 0 != saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
ERR(("cannot register capture v4l2 device. skipping.\n"));
return -1;
}
/* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
if( 0 != MXB_BOARD_CAN_DO_VBI(dev)) {
if( 0 != saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) {
ERR(("cannot register vbi v4l2 device. skipping.\n"));
}
}
 
i2c_use_client(mxb->tea6420_1);
i2c_use_client(mxb->tea6420_2);
i2c_use_client(mxb->tea6415c);
i2c_use_client(mxb->tda9840);
i2c_use_client(mxb->saa7111a);
i2c_use_client(mxb->tuner);
 
printk("mxb: found 'Multimedia eXtension Board'-%d.\n",mxb_num);
 
mxb_num++;
mxb_init_done(dev);
return 0;
}
 
static int mxb_detach(struct saa7146_dev* dev)
{
struct mxb* mxb = (struct mxb*)dev->ext_priv;
 
DEB_EE(("dev:%p\n",dev));
 
i2c_release_client(mxb->tea6420_1);
i2c_release_client(mxb->tea6420_2);
i2c_release_client(mxb->tea6415c);
i2c_release_client(mxb->tda9840);
i2c_release_client(mxb->saa7111a);
i2c_release_client(mxb->tuner);
 
saa7146_unregister_device(&mxb->video_dev,dev);
if( 0 != MXB_BOARD_CAN_DO_VBI(dev)) {
saa7146_unregister_device(&mxb->vbi_dev,dev);
}
saa7146_vv_release(dev);
 
mxb_num--;
 
i2c_del_adapter(&mxb->i2c_adapter);
kfree(mxb);
 
return 0;
}
 
/* hack: this should go into saa711x */
static int saa7111_set_gpio(struct saa7146_dev *dev, int bl)
{
struct mxb* mxb = (struct mxb*)dev->ext_priv;
s32 byte = 0x0;
int result = 0;
DEB_EE(("dev:%p\n",dev));
 
/* get the old register contents */
if ( -1 == (byte = i2c_smbus_read_byte_data(mxb->saa7111a, SAA711X_OUTPUT_CONTROL_1))) {
DEB_D(("could not read from saa711x\n"));
return -EFAULT;
}
if( 0 == bl ) {
byte &= 0x7f;
} else {
byte |= 0x80;
}
 
/* write register contents back */
if ( 0 != (result = i2c_smbus_write_byte_data(mxb->saa7111a, SAA711X_OUTPUT_CONTROL_1, byte))) {
DEB_D(("could not write to saa711x\n"));
return -EFAULT;
}
 
return 0;
}
 
static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
{
struct saa7146_dev *dev = fh->dev;
struct mxb* mxb = (struct mxb*)dev->ext_priv;
struct saa7146_vv *vv = dev->vv_data;
switch(cmd) {
case VIDIOC_ENUMINPUT:
{
struct v4l2_input *i = arg;
DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index));
if( i->index < 0 || i->index >= MXB_INPUTS) {
return -EINVAL;
}
memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
 
return 0;
}
/* the saa7146 provides some controls (brightness, contrast, saturation)
which gets registered *after* this function. because of this we have
to return with a value != 0 even if the function succeded.. */
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *qc = arg;
int i;
 
for (i = MAXCONTROLS - 1; i >= 0; i--) {
if (mxb_controls[i].id == qc->id) {
*qc = mxb_controls[i];
DEB_D(("VIDIOC_QUERYCTRL %d.\n",qc->id));
return 0;
}
}
return -EAGAIN;
}
case VIDIOC_G_CTRL:
{
struct v4l2_control *vc = arg;
int i;
 
for (i = MAXCONTROLS - 1; i >= 0; i--) {
if (mxb_controls[i].id == vc->id) {
break;
}
}
if( i < 0 ) {
return -EAGAIN;
}
switch (vc->id ) {
case V4L2_CID_AUDIO_MUTE: {
vc->value = mxb->cur_mute;
DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n",vc->value));
return 0;
}
}
DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n",vc->value));
return 0;
}
 
case VIDIOC_S_CTRL:
{
struct v4l2_control *vc = arg;
int i = 0;
for (i = MAXCONTROLS - 1; i >= 0; i--) {
if (mxb_controls[i].id == vc->id) {
break;
}
}
if( i < 0 ) {
return -EAGAIN;
}
switch (vc->id ) {
case V4L2_CID_AUDIO_MUTE: {
mxb->cur_mute = vc->value;
if( 0 == vc->value ) {
/* switch the audio-source */
mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][1]);
} else {
mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[6][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[6][1]);
}
DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n",vc->value));
break;
}
}
return 0;
}
case VIDIOC_G_INPUT:
{
int *input = (int *)arg;
*input = mxb->cur_input;
 
DEB_EE(("VIDIOC_G_INPUT %d.\n",*input));
return 0;
}
case VIDIOC_S_INPUT:
{
int input = *(int *)arg;
struct tea6415c_multiplex vm;
int i = 0;
 
DEB_EE(("VIDIOC_S_INPUT %d.\n",input));
 
if (input < 0 || input >= MXB_INPUTS) {
return -EINVAL;
}
/* fixme: locke das setzen des inputs mit hilfe des mutexes
down(&dev->lock);
video_mux(dev,*i);
up(&dev->lock);
*/
/* fixme: check if streaming capture
if ( 0 != dev->streaming ) {
DEB_D(("VIDIOC_S_INPUT illegal while streaming.\n"));
return -EPERM;
}
*/
mxb->cur_input = input;
saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, input_port_selection[input].hps_sync);
/* prepare switching of tea6415c and saa7111a;
have a look at the 'background'-file for further informations */
switch( input ) {
case TUNER:
{
i = 0;
vm.in = 3;
vm.out = 17;
if ( 0 != mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm)) {
printk("VIDIOC_S_INPUT: could not address tea6415c #1\n");
return -EFAULT;
}
/* connect tuner-output always to multicable */
vm.in = 3;
vm.out = 13;
break;
}
case AUX3_YC:
{
/* nothing to be done here. aux3_yc is
directly connected to the saa711a */
i = 5;
break;
}
case AUX3:
{
/* nothing to be done here. aux3 is
directly connected to the saa711a */
i = 1;
break;
}
case AUX1:
{
i = 0;
vm.in = 1;
vm.out = 17;
break;
}
}
 
/* switch video in tea6415c only if necessary */
switch( input ) {
case TUNER:
case AUX1:
{
if ( 0 != mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm)) {
printk("VIDIOC_S_INPUT: could not address tea6415c #3\n");
return -EFAULT;
}
break;
}
default:
{
break;
}
}
/* switch video in saa7111a */
if ( 0 != mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_INPUT, &i)) {
printk("VIDIOC_S_INPUT: could not address saa7111a #1.\n");
}
 
/* switch the audio-source only if necessary */
if( 0 == mxb->cur_mute ) {
mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][1]);
}
 
return 0;
}
case VIDIOC_G_TUNER:
{
struct v4l2_tuner *t = arg;
int byte = 0;
 
if( 0 != t->index ) {
DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index));
return -EINVAL;
}
 
DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index));
 
memset(t,0,sizeof(*t));
strcpy(t->name, "Television");
 
t->type = V4L2_TUNER_ANALOG_TV;
t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */
t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */
/* FIXME: add the real signal strength here */
t->signal = 0xffff;
t->afc = 0;
 
byte = mxb->tda9840->driver->command(mxb->tda9840,TDA9840_DETECT, NULL);
t->audmode = mxb->cur_mode;
if( byte < 0 ) {
t->rxsubchans = V4L2_TUNER_SUB_MONO;
} else {
switch(byte) {
case TDA9840_MONO_DETECT: {
t->rxsubchans = V4L2_TUNER_SUB_MONO;
DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_MONO.\n"));
break;
}
case TDA9840_DUAL_DETECT: {
t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_LANG1.\n"));
break;
}
case TDA9840_STEREO_DETECT: {
t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_STEREO.\n"));
break;
}
default: { /* TDA9840_INCORRECT_DETECT */
t->rxsubchans = V4L2_TUNER_MODE_MONO;
DEB_D(("VIDIOC_G_TUNER: TDA9840_INCORRECT_DETECT => V4L2_TUNER_MODE_MONO\n"));
break;
}
}
}
 
return 0;
}
case VIDIOC_S_TUNER:
{
struct v4l2_tuner *t = arg;
int result = 0;
int byte = 0;
if( 0 != t->index ) {
DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n",t->index));
return -EINVAL;
}
switch(t->audmode) {
case V4L2_TUNER_MODE_STEREO: {
mxb->cur_mode = V4L2_TUNER_MODE_STEREO;
byte = TDA9840_SET_STEREO;
DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n"));
break;
}
case V4L2_TUNER_MODE_LANG1: {
mxb->cur_mode = V4L2_TUNER_MODE_LANG1;
byte = TDA9840_SET_LANG1;
DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n"));
break;
}
case V4L2_TUNER_MODE_LANG2: {
mxb->cur_mode = V4L2_TUNER_MODE_LANG2;
byte = TDA9840_SET_LANG2;
DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n"));
break;
}
default: { /* case V4L2_TUNER_MODE_MONO: {*/
mxb->cur_mode = V4L2_TUNER_MODE_MONO;
byte = TDA9840_SET_MONO;
DEB_D(("VIDIOC_S_TUNER: TDA9840_SET_MONO\n"));
break;
}
}
 
if( 0 != (result = mxb->tda9840->driver->command(mxb->tda9840, TDA9840_SWITCH, &byte))) {
printk("VIDIOC_S_TUNER error. result:%d, byte:%d\n",result,byte);
}
return 0;
}
case VIDIOC_G_FREQUENCY:
{
struct v4l2_frequency *f = arg;
 
if(0 != mxb->cur_input) {
DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",mxb->cur_input));
return -EINVAL;
}
 
memset(f,0,sizeof(*f));
f->type = V4L2_TUNER_ANALOG_TV;
f->frequency = mxb->cur_freq;
 
DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq));
return 0;
}
case VIDIOC_S_FREQUENCY:
{
struct v4l2_frequency *f = arg;
int t_locked = 0;
int v_byte = 0;
 
if (0 != f->tuner)
return -EINVAL;
 
if (V4L2_TUNER_ANALOG_TV != f->type)
return -EINVAL;
if(0 != mxb->cur_input) {
DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n",mxb->cur_input));
return -EINVAL;
}
 
DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n",f->frequency));
 
mxb->cur_freq = f->frequency;
 
/* tune in desired frequency */
mxb->tuner->driver->command(mxb->tuner, VIDIOCSFREQ, &mxb->cur_freq);
 
/* check if pll of tuner & saa7111a is locked */
// mxb->tuner->driver->command(mxb->tuner,TUNER_IS_LOCKED, &t_locked);
mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_GET_STATUS, &v_byte);
 
/* not locked -- anything to do here ? */
if( 0 == t_locked || 0 == (v_byte & DECODER_STATUS_GOOD)) {
}
 
/* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */
spin_lock(&dev->slock);
vv->vbi_fieldcount = 0;
spin_unlock(&dev->slock);
 
return 0;
}
case MXB_S_AUDIO_CD:
{
int i = *(int*)arg;
if( i < 0 || i >= MXB_AUDIOS ) {
DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n",i));
return -EINVAL;
}
DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n",i));
 
mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_cd[i][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_cd[i][1]);
 
return 0;
}
case MXB_S_AUDIO_LINE:
{
int i = *(int*)arg;
if( i < 0 || i >= MXB_AUDIOS ) {
DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n",i));
return -EINVAL;
}
DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n",i));
mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[i][0]);
mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[i][1]);
 
return 0;
}
case VIDIOC_G_AUDIO:
{
struct v4l2_audio *a = arg;
 
if( a->index < 0 || a->index > MXB_INPUTS ) {
DEB_D(("VIDIOC_G_AUDIO %d out of range.\n",a->index));
return -EINVAL;
}
DEB_EE(("VIDIOC_G_AUDIO %d.\n",a->index));
memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio));
return 0;
}
case VIDIOC_S_AUDIO:
{
struct v4l2_audio *a = arg;
DEB_D(("VIDIOC_S_AUDIO %d.\n",a->index));
return 0;
}
default:
/*
DEB2(printk("does not handle this ioctl.\n"));
*/
return -ENOIOCTLCMD;
}
return 0;
}
 
static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
{
if(V4L2_STD_PAL_I == std->id ) {
DEB_D(("VIDIOC_S_STD: setting mxb for PAL_I.\n"));
/* set the 7146 gpio register -- I don't know what this does exactly */
saa7146_write(dev, GPIO_CTRL, 0x00404050);
/* unset the 7111 gpio register -- I don't know what this does exactly */
saa7111_set_gpio(dev,0);
} else {
DEB_D(("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM.\n"));
/* set the 7146 gpio register -- I don't know what this does exactly */
saa7146_write(dev, GPIO_CTRL, 0x00404050);
/* set the 7111 gpio register -- I don't know what this does exactly */
saa7111_set_gpio(dev,1);
}
return 0;
}
 
static struct saa7146_standard standard[] = {
{
.name = "PAL-BG", .id = V4L2_STD_PAL_BG,
.v_offset = 0x17, .v_field = 288, .v_calc = 576,
.h_offset = 0x14, .h_pixels = 680, .h_calc = 680+1,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "PAL-I", .id = V4L2_STD_PAL_I,
.v_offset = 0x17, .v_field = 288, .v_calc = 576,
.h_offset = 0x14, .h_pixels = 680, .h_calc = 680+1,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "NTSC", .id = V4L2_STD_NTSC,
.v_offset = 0x16, .v_field = 240, .v_calc = 480,
.h_offset = 0x06, .h_pixels = 708, .h_calc = 708+1,
.v_max_out = 480, .h_max_out = 640,
}, {
.name = "SECAM", .id = V4L2_STD_SECAM,
.v_offset = 0x14, .v_field = 288, .v_calc = 576,
.h_offset = 0x14, .h_pixels = 720, .h_calc = 720+1,
.v_max_out = 576, .h_max_out = 768,
}
};
 
static struct saa7146_pci_extension_data mxb = {
.ext_priv = "Multimedia eXtension Board",
.ext = &extension,
};
 
static struct pci_device_id pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x0000,
.subdevice = 0x0000,
.driver_data = (unsigned long)&mxb,
}, {
.vendor = 0,
}
};
 
MODULE_DEVICE_TABLE(pci, pci_tbl);
 
static struct saa7146_ext_vv vv_data = {
.inputs = MXB_INPUTS,
.capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE,
.stds = &standard[0],
.num_stds = sizeof(standard)/sizeof(struct saa7146_standard),
.std_callback = &std_callback,
.ioctls = &ioctls[0],
.ioctl = mxb_ioctl,
};
 
static struct saa7146_extension extension = {
.name = MXB_IDENTIFIER,
.flags = SAA7146_USE_I2C_IRQ,
.pci_tbl = &pci_tbl[0],
.module = THIS_MODULE,
 
.probe = mxb_probe,
.attach = mxb_attach,
.detach = mxb_detach,
 
.irq_mask = 0,
.irq_func = NULL,
};
 
int __init mxb_init_module(void)
{
if( 0 != saa7146_register_extension(&extension)) {
DEB_S(("failed to register extension.\n"));
return -ENODEV;
}
return 0;
}
 
void __exit mxb_cleanup_module(void)
{
saa7146_unregister_extension(&extension);
}
 
module_init(mxb_init_module);
module_exit(mxb_cleanup_module);
 
MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'");
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_LICENSE("GPL");
/shark/trunk/drivers/cm7326/saa7146_vbi.c
0,0 → 1,503
#include <media/saa7146_vv.h>
 
static int vbi_pixel_to_capture = 720 * 2;
 
static int vbi_workaround(struct saa7146_dev *dev)
{
struct saa7146_vv *vv = dev->vv_data;
 
u32 *cpu;
dma_addr_t dma_addr;
int count = 0;
int i;
 
DECLARE_WAITQUEUE(wait, current);
DEB_VBI(("dev:%p\n",dev));
/* once again, a bug in the saa7146: the brs acquisition
is buggy and especially the BXO-counter does not work
as specified. there is this workaround, but please
don't let me explain it. ;-) */
 
cpu = pci_alloc_consistent(dev->pci, 4096, &dma_addr);
if (NULL == cpu)
return -ENOMEM;
 
/* setup some basic programming, just for the workaround */
saa7146_write(dev, BASE_EVEN3, dma_addr);
saa7146_write(dev, BASE_ODD3, dma_addr+vbi_pixel_to_capture);
saa7146_write(dev, PROT_ADDR3, dma_addr+4096);
saa7146_write(dev, PITCH3, vbi_pixel_to_capture);
saa7146_write(dev, BASE_PAGE3, 0x0);
saa7146_write(dev, NUM_LINE_BYTE3, (2<<16)|((vbi_pixel_to_capture)<<0));
saa7146_write(dev, MC2, MASK_04|MASK_20);
 
/* load brs-control register */
WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
/* BXO = 1h, BRS to outbound */
WRITE_RPS1(0xc000008c);
/* wait for vbi_a or vbi_b*/
if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
DEB_D(("...using port b\n"));
WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B);
WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B);
/*
WRITE_RPS1(CMD_PAUSE | MASK_09);
*/
} else {
DEB_D(("...using port a\n"));
WRITE_RPS1(CMD_PAUSE | MASK_10);
}
/* upload brs */
WRITE_RPS1(CMD_UPLOAD | MASK_08);
/* load brs-control register */
WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
/* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */
WRITE_RPS1(((1728-(vbi_pixel_to_capture)) << 7) | MASK_19);
/* wait for brs_done */
WRITE_RPS1(CMD_PAUSE | MASK_08);
/* upload brs */
WRITE_RPS1(CMD_UPLOAD | MASK_08);
/* load video-dma3 NumLines3 and NumBytes3 */
WRITE_RPS1(CMD_WR_REG | (1 << 8) | (NUM_LINE_BYTE3/4));
/* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */
WRITE_RPS1((2 << 16) | (vbi_pixel_to_capture));
/* load brs-control register */
WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
/* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */
WRITE_RPS1((540 << 7) | (5 << 19)); // 5 == vbi_start
/* wait for brs_done */
WRITE_RPS1(CMD_PAUSE | MASK_08);
/* upload brs and video-dma3*/
WRITE_RPS1(CMD_UPLOAD | MASK_08 | MASK_04);
/* load mc2 register: enable dma3 */
WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC1/4));
WRITE_RPS1(MASK_20 | MASK_04);
/* generate interrupt */
WRITE_RPS1(CMD_INTERRUPT);
/* stop rps1 */
WRITE_RPS1(CMD_STOP);
/* we have to do the workaround twice to be sure that
everything is ok */
for(i = 0; i < 2; i++) {
 
/* indicate to the irq handler that we do the workaround */
saa7146_write(dev, MC2, MASK_31|MASK_15);
 
saa7146_write(dev, NUM_LINE_BYTE3, (1<<16)|(2<<0));
saa7146_write(dev, MC2, MASK_04|MASK_20);
/* enable rps1 irqs */
IER_ENABLE(dev,MASK_28);
 
/* prepare to wait to be woken up by the irq-handler */
add_wait_queue(&vv->vbi_wq, &wait);
current->state = TASK_INTERRUPTIBLE;
 
/* start rps1 to enable workaround */
saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
saa7146_write(dev, MC1, (MASK_13 | MASK_29));
schedule();
 
DEB_VBI(("brs bug workaround %d/1.\n",i));
remove_wait_queue(&vv->vbi_wq, &wait);
current->state = TASK_RUNNING;
 
/* disable rps1 irqs */
IER_DISABLE(dev,MASK_28);
 
/* stop video-dma3 */
saa7146_write(dev, MC1, MASK_20);
 
if(signal_pending(current)) {
DEB_VBI(("aborted (rps:0x%08x).\n",saa7146_read(dev,RPS_ADDR1)));
 
/* stop rps1 for sure */
saa7146_write(dev, MC1, MASK_29);
 
pci_free_consistent(dev->pci, 4096, cpu, dma_addr);
return -EINTR;
}
}
 
pci_free_consistent(dev->pci, 4096, cpu, dma_addr);
return 0;
}
 
void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
{
struct saa7146_vv *vv = dev->vv_data;
 
struct saa7146_video_dma vdma3;
 
int count = 0;
unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
 
/*
vdma3.base_even = 0xc8000000+2560*70;
vdma3.base_odd = 0xc8000000;
vdma3.prot_addr = 0xc8000000+2560*164;
vdma3.pitch = 2560;
vdma3.base_page = 0;
vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above!
*/
vdma3.base_even = buf->pt[2].offset;
vdma3.base_odd = buf->pt[2].offset + 16 * vbi_pixel_to_capture;
vdma3.prot_addr = buf->pt[2].offset + 16 * 2 * vbi_pixel_to_capture;
vdma3.pitch = vbi_pixel_to_capture;
vdma3.base_page = buf->pt[2].dma | ME1;
vdma3.num_line_byte = (16 << 16) | vbi_pixel_to_capture;
 
saa7146_write_out_dma(dev, 3, &vdma3);
 
/* write beginning of rps-program */
count = 0;
 
/* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */
 
/* we don't wait here for the first field anymore. this is different from the video
capture and might cause that the first buffer is only half filled (with only
one field). but since this is some sort of streaming data, this is not that negative.
but by doing this, we can use the whole engine from video-buf.c... */
/*
WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait);
WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait);
*/
/* set bit 1 */
WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC2/4));
WRITE_RPS1(MASK_28 | MASK_12);
/* turn on video-dma3 */
WRITE_RPS1(CMD_WR_REG_MASK | (MC1/4));
WRITE_RPS1(MASK_04 | MASK_20); /* => mask */
WRITE_RPS1(MASK_04 | MASK_20); /* => values */
/* wait for o_fid_a/b / e_fid_a/b toggle */
WRITE_RPS1(CMD_PAUSE | o_wait);
WRITE_RPS1(CMD_PAUSE | e_wait);
 
/* generate interrupt */
WRITE_RPS1(CMD_INTERRUPT);
 
/* stop */
WRITE_RPS1(CMD_STOP);
 
/* enable rps1 irqs */
IER_ENABLE(dev, MASK_28);
 
/* write the address of the rps-program */
saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
 
/* turn on rps */
saa7146_write(dev, MC1, (MASK_13 | MASK_29));
}
 
static int buffer_activate(struct saa7146_dev *dev,
struct saa7146_buf *buf,
struct saa7146_buf *next)
{
struct saa7146_vv *vv = dev->vv_data;
buf->vb.state = STATE_ACTIVE;
 
DEB_VBI(("dev:%p, buf:%p, next:%p\n",dev,buf,next));
saa7146_set_vbi_capture(dev,buf,next);
 
mod_timer(&vv->vbi_q.timeout, jiffies+BUFFER_TIMEOUT);
return 0;
}
 
static int buffer_prepare(struct file *file, struct videobuf_buffer *vb,enum v4l2_field field)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
 
int err = 0;
int lines, llength, size;
 
lines = 16 * 2 ; /* 2 fields */
llength = vbi_pixel_to_capture;
size = lines * llength;
 
DEB_VBI(("vb:%p\n",vb));
if (0 != buf->vb.baddr && buf->vb.bsize < size) {
DEB_VBI(("size mismatch.\n"));
return -EINVAL;
}
if (buf->vb.size != size)
saa7146_dma_free(dev,buf);
 
if (STATE_NEEDS_INIT == buf->vb.state) {
buf->vb.width = llength;
buf->vb.height = lines;
buf->vb.size = size;
buf->vb.field = field; // FIXME: check this
 
saa7146_pgtable_free(dev->pci, &buf->pt[2]);
saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
 
err = videobuf_iolock(dev->pci,&buf->vb, NULL);
if (err)
goto oops;
err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2], buf->vb.dma.sglist, buf->vb.dma.sglen);
if (0 != err)
return err;
}
buf->vb.state = STATE_PREPARED;
buf->activate = buffer_activate;
 
return 0;
 
oops:
DEB_VBI(("error out.\n"));
saa7146_dma_free(dev,buf);
 
return err;
}
 
static int buffer_setup(struct file *file, unsigned int *count, unsigned int *size)
{
int llength,lines;
lines = 16 * 2 ; /* 2 fields */
llength = vbi_pixel_to_capture;
 
*size = lines * llength;
*count = 2;
 
DEB_VBI(("count:%d, size:%d\n",*count,*size));
 
return 0;
}
 
static void buffer_queue(struct file *file, struct videobuf_buffer *vb)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
DEB_VBI(("vb:%p\n",vb));
saa7146_buffer_queue(dev,&vv->vbi_q,buf);
}
 
static void buffer_release(struct file *file, struct videobuf_buffer *vb)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
DEB_VBI(("vb:%p\n",vb));
saa7146_dma_free(dev,buf);
}
 
static struct videobuf_queue_ops vbi_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
 
/* ------------------------------------------------------------------ */
 
static void vbi_stop(struct saa7146_fh *fh, struct file *file)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
unsigned long flags;
DEB_VBI(("dev:%p, fh:%p\n",dev, fh));
 
spin_lock_irqsave(&dev->slock,flags);
 
/* disable rps1 */
saa7146_write(dev, MC1, MASK_29);
 
/* disable rps1 irqs */
IER_DISABLE(dev, MASK_28);
 
/* shut down dma 3 transfers */
saa7146_write(dev, MC1, MASK_20);
 
if (vv->vbi_q.curr) {
saa7146_buffer_finish(dev,&vv->vbi_q,STATE_DONE);
}
 
videobuf_queue_cancel(file,&fh->vbi_q);
 
vv->vbi_streaming = NULL;
 
del_timer(&vv->vbi_q.timeout);
del_timer(&fh->vbi_read_timeout);
 
spin_unlock_irqrestore(&dev->slock, flags);
}
 
static void vbi_read_timeout(unsigned long data)
{
struct file *file = (struct file*)data;
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
DEB_VBI(("dev:%p, fh:%p\n",dev, fh));
 
vbi_stop(fh, file);
}
 
static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
{
DEB_VBI(("dev:%p\n",dev));
 
INIT_LIST_HEAD(&vv->vbi_q.queue);
 
init_timer(&vv->vbi_q.timeout);
vv->vbi_q.timeout.function = saa7146_buffer_timeout;
vv->vbi_q.timeout.data = (unsigned long)(&vv->vbi_q);
vv->vbi_q.dev = dev;
 
init_waitqueue_head(&vv->vbi_wq);
}
 
static int vbi_open(struct saa7146_dev *dev, struct file *file)
{
struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
int ret = 0;
DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
 
ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS);
if (0 == ret) {
DEB_S(("cannot get vbi RESOURCE_DMA3_BRS resource\n"));
return -EBUSY;
}
 
/* adjust arbitrition control for video dma 3 */
arbtr_ctrl &= ~0x1f0000;
arbtr_ctrl |= 0x1d0000;
saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
saa7146_write(dev, MC2, (MASK_04|MASK_20));
memset(&fh->vbi_fmt,0,sizeof(fh->vbi_fmt));
 
fh->vbi_fmt.sampling_rate = 27000000;
fh->vbi_fmt.offset = 248; /* todo */
fh->vbi_fmt.samples_per_line = vbi_pixel_to_capture;
fh->vbi_fmt.sample_format = V4L2_PIX_FMT_GREY;
 
/* fixme: this only works for PAL */
fh->vbi_fmt.start[0] = 5;
fh->vbi_fmt.count[0] = 16;
fh->vbi_fmt.start[1] = 312;
fh->vbi_fmt.count[1] = 16;
 
videobuf_queue_init(&fh->vbi_q, &vbi_qops,
dev->pci, &dev->slock,
V4L2_BUF_TYPE_VBI_CAPTURE,
V4L2_FIELD_SEQ_TB, // FIXME: does this really work?
sizeof(struct saa7146_buf));
init_MUTEX(&fh->vbi_q.lock);
 
init_timer(&fh->vbi_read_timeout);
fh->vbi_read_timeout.function = vbi_read_timeout;
fh->vbi_read_timeout.data = (unsigned long)file;
 
/* initialize the brs */
if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
saa7146_write(dev, BRS_CTRL, MASK_30|MASK_29 | (7 << 19));
} else {
saa7146_write(dev, BRS_CTRL, 0x00000001);
 
if (0 != (ret = vbi_workaround(dev))) {
DEB_VBI(("vbi workaround failed!\n"));
/* return ret;*/
}
}
 
/* upload brs register */
saa7146_write(dev, MC2, (MASK_08|MASK_24));
return 0;
}
 
static void vbi_close(struct saa7146_dev *dev, struct file *file)
{
struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
struct saa7146_vv *vv = dev->vv_data;
DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
 
if( fh == vv->vbi_streaming ) {
vbi_stop(fh, file);
}
saa7146_res_free(fh, RESOURCE_DMA3_BRS);
}
 
static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
{
struct saa7146_vv *vv = dev->vv_data;
spin_lock(&dev->slock);
 
if (vv->vbi_q.curr) {
DEB_VBI(("dev:%p, curr:%p\n",dev,vv->vbi_q.curr));
/* this must be += 2, one count for each field */
vv->vbi_fieldcount+=2;
vv->vbi_q.curr->vb.field_count = vv->vbi_fieldcount;
saa7146_buffer_finish(dev,&vv->vbi_q,STATE_DONE);
} else {
DEB_VBI(("dev:%p\n",dev));
}
saa7146_buffer_next(dev,&vv->vbi_q,1);
 
spin_unlock(&dev->slock);
}
 
static ssize_t vbi_read(struct file *file, char *data, size_t count, loff_t *ppos)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
ssize_t ret = 0;
 
DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
if( NULL == vv->vbi_streaming ) {
// fixme: check if dma3 is available
// fixme: activate vbi engine here if necessary. (really?)
vv->vbi_streaming = fh;
}
 
if( fh != vv->vbi_streaming ) {
DEB_VBI(("open %p is already using vbi capture.",vv->vbi_streaming));
return -EBUSY;
}
 
mod_timer(&fh->vbi_read_timeout, jiffies+BUFFER_TIMEOUT);
ret = videobuf_read_stream(file, &fh->vbi_q, data, count, ppos, 1);
/*
printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3));
printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3));
printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3));
printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3));
printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3));
printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3));
printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL));
*/
return ret;
}
 
struct saa7146_use_ops saa7146_vbi_uops = {
.init = vbi_init,
.open = vbi_open,
.release = vbi_close,
.irq_done = vbi_irq_done,
.read = vbi_read,
};
/shark/trunk/drivers/cm7326/hexium_gemini.c
0,0 → 1,550
/*
hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards
Visit http://www.mihu.de/linux/saa7146/ and follow the link
to "hexium" for further details about this card.
Copyright (C) 2003 Michael Hunold <michael@mihu.de>
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#define DEBUG_VARIABLE debug
 
#include <media/saa7146_vv.h>
 
static int debug = 0;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "debug verbosity");
 
/* global variables */
static int hexium_num = 0;
 
#define HEXIUM_GEMUINI 4
#define HEXIUM_GEMUINI_DUAL 5
 
#define HEXIUM_INPUTS 9
static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
{ 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
};
 
#define HEXIUM_AUDIOS 0
 
struct hexium_data
{
s8 adr;
u8 byte;
};
 
static struct saa7146_extension_ioctls ioctls[] = {
{ VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_QUERYCTRL, SAA7146_BEFORE },
{ VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_S_STD, SAA7146_AFTER },
{ VIDIOC_G_CTRL, SAA7146_BEFORE },
{ VIDIOC_S_CTRL, SAA7146_BEFORE },
{ 0, 0 }
};
 
#define HEXIUM_CONTROLS 1
static struct v4l2_queryctrl hexium_controls[] = {
{ V4L2_CID_PRIVATE_BASE, V4L2_CTRL_TYPE_BOOLEAN, "B/W", 0, 1, 1, 0, 0 },
};
 
#define HEXIUM_GEMUINI_V_1_0 1
#define HEXIUM_GEMUINI_DUAL_V_1_0 2
 
struct hexium
{
int type;
struct video_device video_dev;
struct i2c_adapter i2c_adapter;
int cur_input; /* current input */
v4l2_std_id cur_std; /* current standard */
int cur_bw; /* current black/white status */
};
 
/* Samsung KS0127B decoder default registers */
static u8 hexium_ks0127b[0x100]={
/*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10,
/*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06,
/*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00,
/*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22,
/*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00,
/*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80,
/*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00,
/*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
 
static struct hexium_data hexium_pal[] = {
{ 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
};
 
static struct hexium_data hexium_pal_bw[] = {
{ 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
};
 
static struct hexium_data hexium_ntsc[] = {
{ 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
};
 
static struct hexium_data hexium_ntsc_bw[] = {
{ 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
};
 
static struct hexium_data hexium_secam[] = {
{ 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
};
 
static struct hexium_data hexium_input_select[] = {
{ 0x02, 0x60 },
{ 0x02, 0x64 },
{ 0x02, 0x61 },
{ 0x02, 0x65 },
{ 0x02, 0x62 },
{ 0x02, 0x66 },
{ 0x02, 0x68 },
{ 0x02, 0x69 },
{ 0x02, 0x6A },
};
 
/* fixme: h_offset = 0 for Hexium Gemini Dual */
static struct saa7146_standard hexium_standards[] = {
{
.name = "PAL", .id = V4L2_STD_PAL,
.v_offset = 28, .v_field = 288, .v_calc = 576,
.h_offset = 1, .h_pixels = 680, .h_calc = 680+1,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "NTSC", .id = V4L2_STD_NTSC,
.v_offset = 28, .v_field = 240, .v_calc = 480,
.h_offset = 1, .h_pixels = 640, .h_calc = 641+1,
.v_max_out = 480, .h_max_out = 640,
}, {
.name = "SECAM", .id = V4L2_STD_SECAM,
.v_offset = 28, .v_field = 288, .v_calc = 576,
.h_offset = 1, .h_pixels = 720, .h_calc = 720+1,
.v_max_out = 576, .h_max_out = 768,
}
};
 
/* bring hardware to a sane state. this has to be done, just in case someone
wants to capture from this device before it has been properly initialized.
the capture engine would badly fail, because no valid signal arrives on the
saa7146, thus leading to timeouts and stuff. */
static int hexium_init_done(struct saa7146_dev *dev)
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
union i2c_smbus_data data;
int i = 0;
 
DEB_D(("hexium_init_done called.\n"));
 
/* initialize the helper ics to useful values */
for (i = 0; i < sizeof(hexium_ks0127b); i++) {
data.byte = hexium_ks0127b[i];
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
printk("hexium_gemini: hexium_init_done() failed for address 0x%02x\n", i);
}
}
 
return 0;
}
 
static int hexium_set_input(struct hexium *hexium, int input)
{
union i2c_smbus_data data;
 
DEB_D((".\n"));
 
data.byte = hexium_input_select[input].byte;
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) {
return -1;
}
 
return 0;
}
 
static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
{
union i2c_smbus_data data;
int i = 0;
 
DEB_D((".\n"));
 
while (vdec[i].adr != -1) {
data.byte = vdec[i].byte;
if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) {
printk("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n", i);
return -1;
}
i++;
}
return 0;
}
 
static struct saa7146_ext_vv vv_data;
 
/* this function only gets called when the probing was successful */
static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
 
DEB_EE((".\n"));
 
hexium = (struct hexium *) kmalloc(sizeof(struct hexium), GFP_KERNEL);
if (NULL == hexium) {
printk("hexium_gemini: not enough kernel memory in hexium_attach().\n");
return -ENOMEM;
}
memset(hexium, 0x0, sizeof(struct hexium));
(struct hexium *) dev->ext_priv = hexium;
 
/* enable i2c-port pins */
saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
 
saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
DEB_S(("cannot register i2c-device. skipping.\n"));
kfree(hexium);
return -EFAULT;
}
 
/* set HWControl GPIO number 2 */
saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
 
saa7146_write(dev, DD1_INIT, 0x07000700);
saa7146_write(dev, DD1_STREAM_B, 0x00000000);
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
 
/* the rest */
hexium->cur_input = 0;
hexium_init_done(dev);
 
hexium_set_standard(hexium, hexium_pal);
hexium->cur_std = V4L2_STD_PAL;
 
hexium_set_input(hexium, 0);
hexium->cur_input = 0;
 
saa7146_vv_init(dev, &vv_data);
if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER)) {
printk("hexium_gemini: cannot register capture v4l2 device. skipping.\n");
return -1;
}
 
printk("hexium_gemini: found 'hexium gemini' frame grabber-%d.\n", hexium_num);
hexium_num++;
 
return 0;
}
 
static int hexium_detach(struct saa7146_dev *dev)
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
 
DEB_EE(("dev:%p\n", dev));
 
saa7146_unregister_device(&hexium->video_dev, dev);
saa7146_vv_release(dev);
 
hexium_num--;
 
i2c_del_adapter(&hexium->i2c_adapter);
kfree(hexium);
return 0;
}
 
static int hexium_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
{
struct saa7146_dev *dev = fh->dev;
struct hexium *hexium = (struct hexium *) dev->ext_priv;
/*
struct saa7146_vv *vv = dev->vv_data;
*/
switch (cmd) {
case VIDIOC_ENUMINPUT:
{
struct v4l2_input *i = arg;
DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
 
if (i->index < 0 || i->index >= HEXIUM_INPUTS) {
return -EINVAL;
}
 
memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
 
DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
return 0;
}
case VIDIOC_G_INPUT:
{
int *input = (int *) arg;
*input = hexium->cur_input;
 
DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
return 0;
}
case VIDIOC_S_INPUT:
{
int input = *(int *) arg;
 
DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
 
if (input < 0 || input >= HEXIUM_INPUTS) {
return -EINVAL;
}
 
hexium->cur_input = input;
hexium_set_input(hexium, input);
 
return 0;
}
/* the saa7146 provides some controls (brightness, contrast, saturation)
which gets registered *after* this function. because of this we have
to return with a value != 0 even if the function succeded.. */
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *qc = arg;
int i;
 
for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
if (hexium_controls[i].id == qc->id) {
*qc = hexium_controls[i];
DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
return 0;
}
}
return -EAGAIN;
}
case VIDIOC_G_CTRL:
{
struct v4l2_control *vc = arg;
int i;
 
for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
if (hexium_controls[i].id == vc->id) {
break;
}
}
 
if (i < 0) {
return -EAGAIN;
}
 
switch (vc->id) {
case V4L2_CID_PRIVATE_BASE:{
vc->value = hexium->cur_bw;
DEB_D(("VIDIOC_G_CTRL BW:%d.\n", vc->value));
return 0;
}
}
return -EINVAL;
}
 
case VIDIOC_S_CTRL:
{
struct v4l2_control *vc = arg;
int i = 0;
 
for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
if (hexium_controls[i].id == vc->id) {
break;
}
}
 
if (i < 0) {
return -EAGAIN;
}
 
switch (vc->id) {
case V4L2_CID_PRIVATE_BASE:{
hexium->cur_bw = vc->value;
break;
}
}
 
DEB_D(("VIDIOC_S_CTRL BW:%d.\n", hexium->cur_bw));
 
if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
hexium_set_standard(hexium, hexium_pal);
return 0;
}
if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
hexium_set_standard(hexium, hexium_ntsc);
return 0;
}
if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
hexium_set_standard(hexium, hexium_secam);
return 0;
}
if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
hexium_set_standard(hexium, hexium_pal_bw);
return 0;
}
if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
hexium_set_standard(hexium, hexium_ntsc_bw);
return 0;
}
if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
/* fixme: is there no bw secam mode? */
return -EINVAL;
}
 
return -EINVAL;
}
default:
/*
DEB_D(("hexium_ioctl() does not handle this ioctl.\n"));
*/
return -ENOIOCTLCMD;
}
return 0;
}
 
static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
 
if (V4L2_STD_PAL == std->id) {
hexium_set_standard(hexium, hexium_pal);
hexium->cur_std = V4L2_STD_PAL;
return 0;
} else if (V4L2_STD_NTSC == std->id) {
hexium_set_standard(hexium, hexium_ntsc);
hexium->cur_std = V4L2_STD_NTSC;
return 0;
} else if (V4L2_STD_SECAM == std->id) {
hexium_set_standard(hexium, hexium_secam);
hexium->cur_std = V4L2_STD_SECAM;
return 0;
}
 
return -1;
}
 
static struct saa7146_extension hexium_extension;
 
static struct saa7146_pci_extension_data hexium_gemini_4bnc = {
.ext_priv = "Hexium Gemini (4 BNC)",
.ext = &hexium_extension,
};
 
static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = {
.ext_priv = "Hexium Gemini Dual (4 BNC)",
.ext = &hexium_extension,
};
 
static struct pci_device_id pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x17c8,
.subdevice = 0x2401,
.driver_data = (unsigned long) &hexium_gemini_4bnc,
},
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x17c8,
.subdevice = 0x2402,
.driver_data = (unsigned long) &hexium_gemini_dual_4bnc,
},
{
.vendor = 0,
}
};
 
MODULE_DEVICE_TABLE(pci, pci_tbl);
 
static struct saa7146_ext_vv vv_data = {
.inputs = HEXIUM_INPUTS,
.capabilities = 0,
.stds = &hexium_standards[0],
.num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
.std_callback = &std_callback,
.ioctls = &ioctls[0],
.ioctl = hexium_ioctl,
};
 
static struct saa7146_extension hexium_extension = {
.name = "hexium gemini",
.flags = SAA7146_USE_I2C_IRQ,
 
.pci_tbl = &pci_tbl[0],
.module = THIS_MODULE,
 
.attach = hexium_attach,
.detach = hexium_detach,
 
.irq_mask = 0,
.irq_func = NULL,
};
 
static int __init hexium_init_module(void)
{
if (0 != saa7146_register_extension(&hexium_extension)) {
DEB_S(("failed to register extension.\n"));
return -ENODEV;
}
 
return 0;
}
 
static void __exit hexium_cleanup_module(void)
{
saa7146_unregister_extension(&hexium_extension);
}
 
module_init(hexium_init_module);
module_exit(hexium_cleanup_module);
 
MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards");
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_LICENSE("GPL");
/shark/trunk/drivers/cm7326/saa7146_vv_ksyms.c
0,0 → 1,15
#include <linux/module.h>
#include <media/saa7146_vv.h>
 
EXPORT_SYMBOL_GPL(saa7146_vbi_uops);
EXPORT_SYMBOL_GPL(saa7146_video_uops);
 
EXPORT_SYMBOL_GPL(saa7146_start_preview);
EXPORT_SYMBOL_GPL(saa7146_stop_preview);
 
EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync);
EXPORT_SYMBOL_GPL(saa7146_register_device);
EXPORT_SYMBOL_GPL(saa7146_unregister_device);
 
EXPORT_SYMBOL_GPL(saa7146_vv_init);
EXPORT_SYMBOL_GPL(saa7146_vv_release);
/shark/trunk/drivers/cm7326/dpc7146.c
0,0 → 1,394
/*
dpc7146.c - v4l2 driver for the dpc7146 demonstration board
Copyright (C) 2000-2003 Michael Hunold <michael@mihu.de>
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#define DEBUG_VARIABLE debug
 
#include <media/saa7146_vv.h>
#include <linux/video_decoder.h> /* for saa7111a */
 
#define I2C_SAA7111A 0x24
 
/* All unused bytes are reserverd. */
#define SAA711X_CHIP_VERSION 0x00
#define SAA711X_ANALOG_INPUT_CONTROL_1 0x02
#define SAA711X_ANALOG_INPUT_CONTROL_2 0x03
#define SAA711X_ANALOG_INPUT_CONTROL_3 0x04
#define SAA711X_ANALOG_INPUT_CONTROL_4 0x05
#define SAA711X_HORIZONTAL_SYNC_START 0x06
#define SAA711X_HORIZONTAL_SYNC_STOP 0x07
#define SAA711X_SYNC_CONTROL 0x08
#define SAA711X_LUMINANCE_CONTROL 0x09
#define SAA711X_LUMINANCE_BRIGHTNESS 0x0A
#define SAA711X_LUMINANCE_CONTRAST 0x0B
#define SAA711X_CHROMA_SATURATION 0x0C
#define SAA711X_CHROMA_HUE_CONTROL 0x0D
#define SAA711X_CHROMA_CONTROL 0x0E
#define SAA711X_FORMAT_DELAY_CONTROL 0x10
#define SAA711X_OUTPUT_CONTROL_1 0x11
#define SAA711X_OUTPUT_CONTROL_2 0x12
#define SAA711X_OUTPUT_CONTROL_3 0x13
#define SAA711X_V_GATE_1_START 0x15
#define SAA711X_V_GATE_1_STOP 0x16
#define SAA711X_V_GATE_1_MSB 0x17
#define SAA711X_TEXT_SLICER_STATUS 0x1A
#define SAA711X_DECODED_BYTES_OF_TS_1 0x1B
#define SAA711X_DECODED_BYTES_OF_TS_2 0x1C
#define SAA711X_STATUS_BYTE 0x1F
 
#define DPC_BOARD_CAN_DO_VBI(dev) (dev->revision != 0)
 
static int debug = 0;
MODULE_PARM(debug,"i");
MODULE_PARM_DESC(debug, "debug verbosity");
 
/* global variables */
int dpc_num = 0;
 
#define DPC_INPUTS 2
static struct v4l2_input dpc_inputs[DPC_INPUTS] = {
{ 0, "Port A", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
{ 1, "Port B", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
};
 
#define DPC_AUDIOS 0
 
static struct saa7146_extension_ioctls ioctls[] = {
{ VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
{ VIDIOC_S_STD, SAA7146_AFTER },
{ 0, 0 }
};
 
struct dpc
{
struct video_device video_dev;
struct video_device vbi_dev;
 
struct i2c_adapter i2c_adapter;
struct i2c_client *saa7111a;
int cur_input; /* current input */
};
 
/* fixme: add vbi stuff here */
static int dpc_probe(struct saa7146_dev* dev)
{
struct dpc* dpc = 0;
int i = 0;
 
dpc = (struct dpc*)kmalloc(sizeof(struct dpc), GFP_KERNEL);
if( NULL == dpc ) {
printk("dpc_v4l2.o: dpc_probe: not enough kernel memory.\n");
return -ENOMEM;
}
memset(dpc, 0x0, sizeof(struct dpc));
 
saa7146_i2c_adapter_prepare(dev, &dpc->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
if(i2c_add_adapter(&dpc->i2c_adapter) < 0) {
DEB_S(("cannot register i2c-device. skipping.\n"));
kfree(dpc);
return -EFAULT;
}
 
/* loop through all i2c-devices on the bus and look who is there */
for(i = 0; i < I2C_CLIENT_MAX; i++) {
if( NULL == dpc->i2c_adapter.clients[i] ) {
continue;
}
if( I2C_SAA7111A == dpc->i2c_adapter.clients[i]->addr )
dpc->saa7111a = dpc->i2c_adapter.clients[i];
}
 
/* check if all devices are present */
if( 0 == dpc->saa7111a ) {
DEB_D(("dpc_v4l2.o: dpc_attach failed for this device.\n"));
kfree(dpc);
return -ENODEV;
}
/* all devices are present, probe was successful */
DEB_D(("dpc_v4l2.o: dpc_probe succeeded for this device.\n"));
 
/* we store the pointer in our private data field */
(struct dpc*)dev->ext_priv = dpc;
 
return 0;
}
 
/* bring hardware to a sane state. this has to be done, just in case someone
wants to capture from this device before it has been properly initialized.
the capture engine would badly fail, because no valid signal arrives on the
saa7146, thus leading to timeouts and stuff. */
static int dpc_init_done(struct saa7146_dev* dev)
{
struct dpc* dpc = (struct dpc*)dev->ext_priv;
 
DEB_D(("dpc_v4l2.o: dpc_init_done called.\n"));
 
/* initialize the helper ics to useful values */
i2c_smbus_write_byte_data(dpc->saa7111a, 0x00, 0x11);
 
i2c_smbus_write_byte_data(dpc->saa7111a, 0x02, 0xc0);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x03, 0x30);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x04, 0x00);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x05, 0x00);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x06, 0xde);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x07, 0xad);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x08, 0xa8);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x09, 0x00);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x0a, 0x80);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x0b, 0x47);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x0c, 0x40);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x0d, 0x00);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x0e, 0x03);
 
i2c_smbus_write_byte_data(dpc->saa7111a, 0x10, 0xd0);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x11, 0x1c);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x12, 0xc1);
i2c_smbus_write_byte_data(dpc->saa7111a, 0x13, 0x30);
 
i2c_smbus_write_byte_data(dpc->saa7111a, 0x1f, 0x81);
 
return 0;
}
 
static struct saa7146_ext_vv vv_data;
 
/* this function only gets called when the probing was successful */
static int dpc_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
{
struct dpc* dpc = (struct dpc*)dev->ext_priv;
DEB_D(("dpc_v4l2.o: dpc_attach called.\n"));
 
/* checking for i2c-devices can be omitted here, because we
already did this in "dpc_vl42_probe" */
 
saa7146_vv_init(dev,&vv_data);
if( 0 != saa7146_register_device(&dpc->video_dev, dev, "dpc", VFL_TYPE_GRABBER)) {
ERR(("cannot register capture v4l2 device. skipping.\n"));
return -1;
}
/* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
if( 0 != DPC_BOARD_CAN_DO_VBI(dev)) {
if( 0 != saa7146_register_device(&dpc->vbi_dev, dev, "dpc", VFL_TYPE_VBI)) {
ERR(("cannot register vbi v4l2 device. skipping.\n"));
}
}
 
i2c_use_client(dpc->saa7111a);
 
printk("dpc: found 'dpc7146 demonstration board'-%d.\n",dpc_num);
dpc_num++;
/* the rest */
dpc->cur_input = 0;
dpc_init_done(dev);
return 0;
}
 
static int dpc_detach(struct saa7146_dev* dev)
{
struct dpc* dpc = (struct dpc*)dev->ext_priv;
DEB_EE(("dev:%p\n",dev));
 
i2c_release_client(dpc->saa7111a);
 
saa7146_unregister_device(&dpc->video_dev,dev);
if( 0 != DPC_BOARD_CAN_DO_VBI(dev)) {
saa7146_unregister_device(&dpc->vbi_dev,dev);
}
saa7146_vv_release(dev);
 
dpc_num--;
 
i2c_del_adapter(&dpc->i2c_adapter);
kfree(dpc);
return 0;
}
 
#ifdef axa
int dpc_vbi_bypass(struct saa7146_dev* dev)
{
struct dpc* dpc = (struct dpc*)dev->ext_priv;
int i = 1;
 
/* switch bypass in saa7111a */
if ( 0 != dpc->saa7111a->driver->command(dpc->saa7111a,SAA711X_VBI_BYPASS, &i)) {
printk("dpc_v4l2.o: VBI_BYPASS: could not address saa7111a.\n");
return -1;
}
 
return 0;
}
#endif
 
static int dpc_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
{
struct saa7146_dev *dev = fh->dev;
struct dpc* dpc = (struct dpc*)dev->ext_priv;
/*
struct saa7146_vv *vv = dev->vv_data;
*/
switch(cmd)
{
case VIDIOC_ENUMINPUT:
{
struct v4l2_input *i = arg;
DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index));
if( i->index < 0 || i->index >= DPC_INPUTS) {
return -EINVAL;
}
memcpy(i, &dpc_inputs[i->index], sizeof(struct v4l2_input));
 
DEB_D(("dpc_v4l2.o: v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n",i->index));
return 0;
}
case VIDIOC_G_INPUT:
{
int *input = (int *)arg;
*input = dpc->cur_input;
 
DEB_D(("dpc_v4l2.o: VIDIOC_G_INPUT: %d\n",*input));
return 0;
}
case VIDIOC_S_INPUT:
{
int input = *(int *)arg;
 
if (input < 0 || input >= DPC_INPUTS) {
return -EINVAL;
}
dpc->cur_input = input;
 
/* fixme: switch input here, switch audio, too! */
// saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, input_port_selection[input].hps_sync);
printk("dpc_v4l2.o: VIDIOC_S_INPUT: fixme switch input.\n");
return 0;
}
default:
/*
DEB_D(("dpc_v4l2.o: v4l2_ioctl does not handle this ioctl.\n"));
*/
return -ENOIOCTLCMD;
}
return 0;
}
 
static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
{
return 0;
}
 
static struct saa7146_standard standard[] = {
{
.name = "PAL", .id = V4L2_STD_PAL,
.v_offset = 0x17, .v_field = 288, .v_calc = 576,
.h_offset = 0x14, .h_pixels = 680, .h_calc = 680+1,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "NTSC", .id = V4L2_STD_NTSC,
.v_offset = 0x16, .v_field = 240, .v_calc = 480,
.h_offset = 0x06, .h_pixels = 708, .h_calc = 708+1,
.v_max_out = 480, .h_max_out = 640,
}, {
.name = "SECAM", .id = V4L2_STD_SECAM,
.v_offset = 0x14, .v_field = 288, .v_calc = 576,
.h_offset = 0x14, .h_pixels = 720, .h_calc = 720+1,
.v_max_out = 576, .h_max_out = 768,
}
};
 
static struct saa7146_extension extension;
 
static struct saa7146_pci_extension_data dpc = {
.ext_priv = "Multimedia eXtension Board",
.ext = &extension,
};
 
static struct pci_device_id pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x0000,
.subdevice = 0x0000,
.driver_data = (unsigned long)&dpc,
}, {
.vendor = 0,
}
};
 
MODULE_DEVICE_TABLE(pci, pci_tbl);
 
static struct saa7146_ext_vv vv_data = {
.inputs = DPC_INPUTS,
.capabilities = V4L2_CAP_VBI_CAPTURE,
.stds = &standard[0],
.num_stds = sizeof(standard)/sizeof(struct saa7146_standard),
.std_callback = &std_callback,
.ioctls = &ioctls[0],
.ioctl = dpc_ioctl,
};
 
static struct saa7146_extension extension = {
.name = "dpc7146 demonstration board",
.flags = SAA7146_USE_I2C_IRQ,
.pci_tbl = &pci_tbl[0],
.module = THIS_MODULE,
 
.probe = dpc_probe,
.attach = dpc_attach,
.detach = dpc_detach,
 
.irq_mask = 0,
.irq_func = NULL,
};
 
int __init dpc_init_module(void)
{
if( 0 != saa7146_register_extension(&extension)) {
DEB_S(("failed to register extension.\n"));
return -ENODEV;
}
return 0;
}
 
void __exit dpc_cleanup_module(void)
{
saa7146_unregister_extension(&extension);
}
 
module_init(dpc_init_module);
module_exit(dpc_cleanup_module);
 
MODULE_DESCRIPTION("video4linux-2 driver for the 'dpc7146 demonstration board'");
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_LICENSE("GPL");
/shark/trunk/drivers/cm7326/saa7146_hlp.c
0,0 → 1,1057
#include <media/saa7146_vv.h>
 
#define my_min(type,x,y) \
({ type __x = (x), __y = (y); __x < __y ? __x: __y; })
#define my_max(type,x,y) \
({ type __x = (x), __y = (y); __x > __y ? __x: __y; })
 
static void calculate_output_format_register(struct saa7146_dev* saa, u32 palette, u32* clip_format)
{
/* clear out the necessary bits */
*clip_format &= 0x0000ffff;
/* set these bits new */
*clip_format |= (( ((palette&0xf00)>>8) << 30) | ((palette&0x00f) << 24) | (((palette&0x0f0)>>4) << 16));
}
 
static void calculate_bcs_ctrl_register(struct saa7146_dev *dev, int brightness, int contrast, int colour, u32 *bcs_ctrl)
{
*bcs_ctrl = ((brightness << 24) | (contrast << 16) | (colour << 0));
}
 
static void calculate_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync, u32* hps_ctrl)
{
*hps_ctrl &= ~(MASK_30 | MASK_31 | MASK_28);
*hps_ctrl |= (source << 30) | (sync << 28);
}
 
static void calculate_hxo_and_hyo(struct saa7146_vv *vv, u32* hps_h_scale, u32* hps_ctrl)
{
int hyo = 0, hxo = 0;
 
hyo = vv->standard->v_offset;
hxo = vv->standard->h_offset;
*hps_h_scale &= ~(MASK_B0 | 0xf00);
*hps_h_scale |= (hxo << 0);
 
*hps_ctrl &= ~(MASK_W0 | MASK_B2);
*hps_ctrl |= (hyo << 12);
}
 
/* helper functions for the calculation of the horizontal- and vertical
scaling registers, clip-format-register etc ...
these functions take pointers to the (most-likely read-out
original-values) and manipulate them according to the requested
changes.
*/
 
/* hps_coeff used for CXY and CXUV; scale 1/1 -> scale 1/64 */
static struct {
u16 hps_coeff;
u16 weight_sum;
} hps_h_coeff_tab [] = {
{0x00, 2}, {0x02, 4}, {0x00, 4}, {0x06, 8}, {0x02, 8},
{0x08, 8}, {0x00, 8}, {0x1E, 16}, {0x0E, 8}, {0x26, 8},
{0x06, 8}, {0x42, 8}, {0x02, 8}, {0x80, 8}, {0x00, 8},
{0xFE, 16}, {0xFE, 8}, {0x7E, 8}, {0x7E, 8}, {0x3E, 8},
{0x3E, 8}, {0x1E, 8}, {0x1E, 8}, {0x0E, 8}, {0x0E, 8},
{0x06, 8}, {0x06, 8}, {0x02, 8}, {0x02, 8}, {0x00, 8},
{0x00, 8}, {0xFE, 16}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
{0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
{0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
{0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0x7E, 8},
{0x7E, 8}, {0x3E, 8}, {0x3E, 8}, {0x1E, 8}, {0x1E, 8},
{0x0E, 8}, {0x0E, 8}, {0x06, 8}, {0x06, 8}, {0x02, 8},
{0x02, 8}, {0x00, 8}, {0x00, 8}, {0xFE, 16}
};
 
/* table of attenuation values for horizontal scaling */
u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0};
 
/* calculate horizontal scale registers */
static int calculate_h_scale_registers(struct saa7146_dev *dev,
int in_x, int out_x, int flip_lr,
u32* hps_ctrl, u32* hps_v_gain, u32* hps_h_prescale, u32* hps_h_scale)
{
/* horizontal prescaler */
u32 dcgx = 0, xpsc = 0, xacm = 0, cxy = 0, cxuv = 0;
/* horizontal scaler */
u32 xim = 0, xp = 0, xsci =0;
/* vertical scale & gain */
u32 pfuv = 0;
 
/* helper variables */
u32 h_atten = 0, i = 0;
 
if ( 0 == out_x ) {
return -EINVAL;
}
/* mask out vanity-bit */
*hps_ctrl &= ~MASK_29;
/* calculate prescale-(xspc)-value: [n .. 1/2) : 1
[1/2 .. 1/3) : 2
[1/3 .. 1/4) : 3
... */
if (in_x > out_x) {
xpsc = in_x / out_x;
}
else {
/* zooming */
xpsc = 1;
}
/* if flip_lr-bit is set, number of pixels after
horizontal prescaling must be < 384 */
if ( 0 != flip_lr ) {
/* set vanity bit */
*hps_ctrl |= MASK_29;
while (in_x / xpsc >= 384 )
xpsc++;
}
/* if zooming is wanted, number of pixels after
horizontal prescaling must be < 768 */
else {
while ( in_x / xpsc >= 768 )
xpsc++;
}
/* maximum prescale is 64 (p.69) */
if ( xpsc > 64 )
xpsc = 64;
 
/* keep xacm clear*/
xacm = 0;
/* set horizontal filter parameters (CXY = CXUV) */
cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff;
cxuv = cxy;
/* calculate and set horizontal fine scale (xsci) */
/* bypass the horizontal scaler ? */
if ( (in_x == out_x) && ( 1 == xpsc ) )
xsci = 0x400;
else
xsci = ( (1024 * in_x) / (out_x * xpsc) ) + xpsc;
 
/* set start phase for horizontal fine scale (xp) to 0 */
xp = 0;
/* set xim, if we bypass the horizontal scaler */
if ( 0x400 == xsci )
xim = 1;
else
xim = 0;
/* if the prescaler is bypassed, enable horizontal
accumulation mode (xacm) and clear dcgx */
if( 1 == xpsc ) {
xacm = 1;
dcgx = 0;
} else {
xacm = 0;
/* get best match in the table of attenuations
for horizontal scaling */
h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum;
for (i = 0; h_attenuation[i] != 0; i++) {
if (h_attenuation[i] >= h_atten)
break;
}
dcgx = i;
}
 
/* the horizontal scaling increment controls the UV filter
to reduce the bandwith to improve the display quality,
so set it ... */
if ( xsci == 0x400)
pfuv = 0x00;
else if ( xsci < 0x600)
pfuv = 0x01;
else if ( xsci < 0x680)
pfuv = 0x11;
else if ( xsci < 0x700)
pfuv = 0x22;
else
pfuv = 0x33;
 
*hps_v_gain &= MASK_W0|MASK_B2;
*hps_v_gain |= (pfuv << 24);
 
*hps_h_scale &= ~(MASK_W1 | 0xf000);
*hps_h_scale |= (xim << 31) | (xp << 24) | (xsci << 12);
 
*hps_h_prescale |= (dcgx << 27) | ((xpsc-1) << 18) | (xacm << 17) | (cxy << 8) | (cxuv << 0);
 
return 0;
}
 
static struct {
u16 hps_coeff;
u16 weight_sum;
} hps_v_coeff_tab [] = {
{0x0100, 2}, {0x0102, 4}, {0x0300, 4}, {0x0106, 8}, {0x0502, 8},
{0x0708, 8}, {0x0F00, 8}, {0x011E, 16}, {0x110E, 16}, {0x1926, 16},
{0x3906, 16}, {0x3D42, 16}, {0x7D02, 16}, {0x7F80, 16}, {0xFF00, 16},
{0x01FE, 32}, {0x01FE, 32}, {0x817E, 32}, {0x817E, 32}, {0xC13E, 32},
{0xC13E, 32}, {0xE11E, 32}, {0xE11E, 32}, {0xF10E, 32}, {0xF10E, 32},
{0xF906, 32}, {0xF906, 32}, {0xFD02, 32}, {0xFD02, 32}, {0xFF00, 32},
{0xFF00, 32}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
{0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
{0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
{0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x817E, 64},
{0x817E, 64}, {0xC13E, 64}, {0xC13E, 64}, {0xE11E, 64}, {0xE11E, 64},
{0xF10E, 64}, {0xF10E, 64}, {0xF906, 64}, {0xF906, 64}, {0xFD02, 64},
{0xFD02, 64}, {0xFF00, 64}, {0xFF00, 64}, {0x01FE, 128}
};
 
/* table of attenuation values for vertical scaling */
u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0};
 
/* calculate vertical scale registers */
static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field field,
int in_y, int out_y, u32* hps_v_scale, u32* hps_v_gain)
{
int lpi = 0;
/* vertical scaling */
u32 yacm = 0, ysci = 0, yacl = 0, ypo = 0, ype = 0;
/* vertical scale & gain */
u32 dcgy = 0, cya_cyb = 0;
/* helper variables */
u32 v_atten = 0, i = 0;
 
/* error, if vertical zooming */
if ( in_y < out_y ) {
return -EINVAL;
}
 
/* linear phase interpolation may be used
if scaling is between 1 and 1/2 (both fields used)
or scaling is between 1/2 and 1/4 (if only one field is used) */
 
if (V4L2_FIELD_HAS_BOTH(field)) {
if( 2*out_y >= in_y) {
lpi = 1;
}
} else if (field == V4L2_FIELD_TOP
|| field == V4L2_FIELD_ALTERNATE
|| field == V4L2_FIELD_BOTTOM) {
if( 4*out_y >= in_y ) {
lpi = 1;
}
out_y *= 2;
}
if( 0 != lpi ) {
 
yacm = 0;
yacl = 0;
cya_cyb = 0x00ff;
/* calculate scaling increment */
if ( in_y > out_y )
ysci = ((1024 * in_y) / (out_y + 1)) - 1024;
else
ysci = 0;
 
dcgy = 0;
 
/* calculate ype and ypo */
ype = ysci / 16;
ypo = ype + (ysci / 64);
} else {
yacm = 1;
 
/* calculate scaling increment */
ysci = (((10 * 1024 * (in_y - out_y - 1)) / in_y) + 9) / 10;
 
/* calculate ype and ypo */
ypo = ype = ((ysci + 15) / 16);
 
/* the sequence length interval (yacl) has to be set according
to the prescale value, e.g. [n .. 1/2) : 0
[1/2 .. 1/3) : 1
[1/3 .. 1/4) : 2
... */
if ( ysci < 512) {
yacl = 0;
} else {
yacl = ( ysci / (1024 - ysci) );
}
 
/* get filter coefficients for cya, cyb from table hps_v_coeff_tab */
cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff;
 
/* get best match in the table of attenuations for vertical scaling */
v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum;
 
for (i = 0; v_attenuation[i] != 0; i++) {
if (v_attenuation[i] >= v_atten)
break;
}
dcgy = i;
}
 
/* ypo and ype swapped in spec ? */
*hps_v_scale |= (yacm << 31) | (ysci << 21) | (yacl << 15) | (ypo << 8 ) | (ype << 1);
 
*hps_v_gain &= ~(MASK_W0|MASK_B2);
*hps_v_gain |= (dcgy << 16) | (cya_cyb << 0);
 
return 0;
}
 
/* simple bubble-sort algorithm with duplicate elimination */
static int sort_and_eliminate(u32* values, int* count)
{
int low = 0, high = 0, top = 0, temp = 0;
int cur = 0, next = 0;
/* sanity checks */
if( (0 > *count) || (NULL == values) ) {
return -EINVAL;
}
/* bubble sort the first ´count´ items of the array ´values´ */
for( top = *count; top > 0; top--) {
for( low = 0, high = 1; high < top; low++, high++) {
if( values[low] > values[high] ) {
temp = values[low];
values[low] = values[high];
values[high] = temp;
}
}
}
 
/* remove duplicate items */
for( cur = 0, next = 1; next < *count; next++) {
if( values[cur] != values[next])
values[++cur] = values[next];
}
*count = cur + 1;
return 0;
}
 
static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct saa7146_fh *fh,
struct saa7146_video_dma *vdma2, u32* clip_format, u32* arbtr_ctrl, enum v4l2_field field)
{
struct saa7146_vv *vv = dev->vv_data;
u32 *clipping = vv->d_clipping.cpu_addr;
 
int width = fh->ov.win.w.width;
int height = fh->ov.win.w.height;
int clipcount = fh->ov.nclips;
u32 line_list[32];
u32 pixel_list[32];
int numdwords = 0;
 
int i = 0, j = 0;
int cnt_line = 0, cnt_pixel = 0;
 
int x[32], y[32], w[32], h[32];
 
/* clear out memory */
memset(&line_list[0], 0x00, sizeof(u32)*32);
memset(&pixel_list[0], 0x00, sizeof(u32)*32);
memset(clipping, 0x00, SAA7146_CLIPPING_MEM);
 
/* fill the line and pixel-lists */
for(i = 0; i < clipcount; i++) {
int l = 0, r = 0, t = 0, b = 0;
x[i] = fh->ov.clips[i].c.left;
y[i] = fh->ov.clips[i].c.top;
w[i] = fh->ov.clips[i].c.width;
h[i] = fh->ov.clips[i].c.height;
if( w[i] < 0) {
x[i] += w[i]; w[i] = -w[i];
}
if( h[i] < 0) {
y[i] += h[i]; h[i] = -h[i];
}
if( x[i] < 0) {
w[i] += x[i]; x[i] = 0;
}
if( y[i] < 0) {
h[i] += y[i]; y[i] = 0;
}
if( 0 != vv->vflip ) {
y[i] = height - y[i] - h[i];
}
 
l = x[i];
r = x[i]+w[i];
t = y[i];
b = y[i]+h[i];
/* insert left/right coordinates */
pixel_list[ 2*i ] = my_min(int, l, width);
pixel_list[(2*i)+1] = my_min(int, r, width);
/* insert top/bottom coordinates */
line_list[ 2*i ] = my_min(int, t, height);
line_list[(2*i)+1] = my_min(int, b, height);
}
 
/* sort and eliminate lists */
cnt_line = cnt_pixel = 2*clipcount;
sort_and_eliminate( &pixel_list[0], &cnt_pixel );
sort_and_eliminate( &line_list[0], &cnt_line );
 
/* calculate the number of used u32s */
numdwords = my_max(int, (cnt_line+1), (cnt_pixel+1))*2;
numdwords = my_max(int, 4, numdwords);
numdwords = my_min(int, 64, numdwords);
 
/* fill up cliptable */
for(i = 0; i < cnt_pixel; i++) {
clipping[2*i] |= (pixel_list[i] << 16);
}
for(i = 0; i < cnt_line; i++) {
clipping[(2*i)+1] |= (line_list[i] << 16);
}
 
/* fill up cliptable with the display infos */
for(j = 0; j < clipcount; j++) {
 
for(i = 0; i < cnt_pixel; i++) {
 
if( x[j] < 0)
x[j] = 0;
 
if( pixel_list[i] < (x[j] + w[j])) {
if ( pixel_list[i] >= x[j] ) {
clipping[2*i] |= (1 << j);
}
}
}
for(i = 0; i < cnt_line; i++) {
 
if( y[j] < 0)
y[j] = 0;
 
if( line_list[i] < (y[j] + h[j]) ) {
 
if( line_list[i] >= y[j] ) {
clipping[(2*i)+1] |= (1 << j);
}
}
}
}
 
/* adjust arbitration control register */
*arbtr_ctrl &= 0xffff00ff;
*arbtr_ctrl |= 0x00001c00;
vdma2->base_even = vv->d_clipping.dma_handle;
vdma2->base_odd = vv->d_clipping.dma_handle;
vdma2->prot_addr = vv->d_clipping.dma_handle+((sizeof(u32))*(numdwords));
vdma2->base_page = 0x04;
vdma2->pitch = 0x00;
vdma2->num_line_byte = (0 << 16 | (sizeof(u32))*(numdwords-1) );
 
/* set clipping-mode. this depends on the field(s) used */
*clip_format &= 0xfffffff7;
if (V4L2_FIELD_HAS_BOTH(field)) {
*clip_format |= 0x00000008;
} else {
*clip_format |= 0x00000000;
}
}
 
/* disable clipping */
static void saa7146_disable_clipping(struct saa7146_dev *dev)
{
u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
 
/* mask out relevant bits (=lower word)*/
clip_format &= MASK_W1;
 
/* upload clipping-registers*/
saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format);
saa7146_write(dev, MC2, (MASK_05 | MASK_21));
/* disable video dma2 */
saa7146_write(dev, MC1, MASK_21);
}
 
static void saa7146_set_clipping_rect(struct saa7146_fh *fh)
{
struct saa7146_dev *dev = fh->dev;
enum v4l2_field field = fh->ov.win.field;
struct saa7146_video_dma vdma2;
u32 clip_format;
u32 arbtr_ctrl;
/* check clipcount, disable clipping if clipcount == 0*/
if( fh->ov.nclips == 0 ) {
saa7146_disable_clipping(dev);
return;
}
 
clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
 
calculate_clipping_registers_rect(dev, fh, &vdma2, &clip_format, &arbtr_ctrl, field);
 
/* set clipping format */
clip_format &= 0xffff0008;
clip_format |= (SAA7146_CLIPPING_RECT << 4);
 
/* prepare video dma2 */
saa7146_write(dev, BASE_EVEN2, vdma2.base_even);
saa7146_write(dev, BASE_ODD2, vdma2.base_odd);
saa7146_write(dev, PROT_ADDR2, vdma2.prot_addr);
saa7146_write(dev, BASE_PAGE2, vdma2.base_page);
saa7146_write(dev, PITCH2, vdma2.pitch);
saa7146_write(dev, NUM_LINE_BYTE2, vdma2.num_line_byte);
/* prepare the rest */
saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format);
saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
 
/* upload clip_control-register, clipping-registers, enable video dma2 */
saa7146_write(dev, MC2, (MASK_05 | MASK_21 | MASK_03 | MASK_19));
saa7146_write(dev, MC1, (MASK_05 | MASK_21));
}
 
static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, enum v4l2_field field)
{
struct saa7146_vv *vv = dev->vv_data;
 
int source = vv->current_hps_source;
int sync = vv->current_hps_sync;
 
u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0;
 
/* set vertical scale */
hps_v_scale = 0; /* all bits get set by the function-call */
hps_v_gain = 0; /* fixme: saa7146_read(dev, HPS_V_GAIN);*/
calculate_v_scale_registers(dev, field, vv->standard->v_calc, height, &hps_v_scale, &hps_v_gain);
 
/* set horizontal scale */
hps_ctrl = 0;
hps_h_prescale = 0; /* all bits get set in the function */
hps_h_scale = 0;
calculate_h_scale_registers(dev, vv->standard->h_calc, width, vv->hflip, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale);
 
/* set hyo and hxo */
calculate_hxo_and_hyo(vv, &hps_h_scale, &hps_ctrl);
calculate_hps_source_and_sync(dev, source, sync, &hps_ctrl);
/* write out new register contents */
saa7146_write(dev, HPS_V_SCALE, hps_v_scale);
saa7146_write(dev, HPS_V_GAIN, hps_v_gain);
saa7146_write(dev, HPS_CTRL, hps_ctrl);
saa7146_write(dev, HPS_H_PRESCALE,hps_h_prescale);
saa7146_write(dev, HPS_H_SCALE, hps_h_scale);
/* upload shadow-ram registers */
saa7146_write(dev, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) );
}
 
/* calculate the new memory offsets for a desired position */
static void saa7146_set_position(struct saa7146_dev *dev, int w_x, int w_y, int w_height, enum v4l2_field field)
{
struct saa7146_vv *vv = dev->vv_data;
 
int b_depth = vv->ov_fmt->depth;
int b_bpl = vv->ov_fb.fmt.bytesperline;
u32 base = (u32)vv->ov_fb.base;
struct saa7146_video_dma vdma1;
 
/* calculate memory offsets for picture, look if we shall top-down-flip */
vdma1.pitch = 2*b_bpl;
if ( 0 == vv->vflip ) {
vdma1.base_even = (u32)base + (w_y * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
vdma1.base_odd = vdma1.base_even + (vdma1.pitch / 2);
vdma1.prot_addr = vdma1.base_even + (w_height * (vdma1.pitch / 2));
}
else {
vdma1.base_even = (u32)base + ((w_y+w_height) * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
vdma1.base_odd = vdma1.base_even - (vdma1.pitch / 2);
vdma1.prot_addr = vdma1.base_odd - (w_height * (vdma1.pitch / 2));
}
if (V4L2_FIELD_HAS_BOTH(field)) {
} else if (field == V4L2_FIELD_ALTERNATE) {
/* fixme */
vdma1.base_odd = vdma1.prot_addr;
vdma1.pitch /= 2;
} else if (field == V4L2_FIELD_TOP) {
vdma1.base_odd = vdma1.prot_addr;
vdma1.pitch /= 2;
} else if (field == V4L2_FIELD_BOTTOM) {
vdma1.base_odd = vdma1.base_even;
vdma1.base_even = vdma1.prot_addr;
vdma1.pitch /= 2;
}
 
if ( 0 != vv->vflip ) {
vdma1.pitch *= -1;
}
vdma1.base_page = 0;
vdma1.num_line_byte = (vv->standard->v_field<<16)+vv->standard->h_pixels;
 
saa7146_write_out_dma(dev, 1, &vdma1);
}
 
static void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long palette)
{
u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
/* call helper function */
calculate_output_format_register(dev,palette,&clip_format);
 
/* update the hps registers */
saa7146_write(dev, CLIP_FORMAT_CTRL, clip_format);
saa7146_write(dev, MC2, (MASK_05 | MASK_21));
}
 
void saa7146_set_picture_prop(struct saa7146_dev *dev, int brightness, int contrast, int colour)
{
u32 bcs_ctrl = 0;
calculate_bcs_ctrl_register(dev, brightness, contrast, colour, &bcs_ctrl);
saa7146_write(dev, BCS_CTRL, bcs_ctrl);
/* update the bcs register */
saa7146_write(dev, MC2, (MASK_06 | MASK_22));
}
 
 
/* select input-source */
void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync)
{
struct saa7146_vv *vv = dev->vv_data;
u32 hps_ctrl = 0;
 
/* read old state */
hps_ctrl = saa7146_read(dev, HPS_CTRL);
 
hps_ctrl &= ~( MASK_31 | MASK_30 | MASK_28 );
hps_ctrl |= (source << 30) | (sync << 28);
 
/* write back & upload register */
saa7146_write(dev, HPS_CTRL, hps_ctrl);
saa7146_write(dev, MC2, (MASK_05 | MASK_21));
vv->current_hps_source = source;
vv->current_hps_sync = sync;
}
 
int saa7146_enable_overlay(struct saa7146_fh *fh)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
 
saa7146_set_window(dev, fh->ov.win.w.width, fh->ov.win.w.height, fh->ov.win.field);
saa7146_set_position(dev, fh->ov.win.w.left, fh->ov.win.w.top, fh->ov.win.w.height, fh->ov.win.field);
saa7146_set_output_format(dev, vv->ov_fmt->trans);
saa7146_set_clipping_rect(fh);
 
/* enable video dma1 */
saa7146_write(dev, MC1, (MASK_06 | MASK_22));
return 0;
}
 
void saa7146_disable_overlay(struct saa7146_fh *fh)
{
struct saa7146_dev *dev = fh->dev;
 
/* disable clipping + video dma1 */
saa7146_disable_clipping(dev);
saa7146_write(dev, MC1, MASK_22);
}
 
void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma)
{
int where = 0;
if( which < 1 || which > 3) {
return;
}
/* calculate starting address */
where = (which-1)*0x18;
 
saa7146_write(dev, where, vdma->base_odd);
saa7146_write(dev, where+0x04, vdma->base_even);
saa7146_write(dev, where+0x08, vdma->prot_addr);
saa7146_write(dev, where+0x0c, vdma->pitch);
saa7146_write(dev, where+0x10, vdma->base_page);
saa7146_write(dev, where+0x14, vdma->num_line_byte);
/* upload */
saa7146_write(dev, MC2, (MASK_02<<(which-1))|(MASK_18<<(which-1)));
/*
printk("vdma%d.base_even: 0x%08x\n", which,vdma->base_even);
printk("vdma%d.base_odd: 0x%08x\n", which,vdma->base_odd);
printk("vdma%d.prot_addr: 0x%08x\n", which,vdma->prot_addr);
printk("vdma%d.base_page: 0x%08x\n", which,vdma->base_page);
printk("vdma%d.pitch: 0x%08x\n", which,vdma->pitch);
printk("vdma%d.num_line_byte: 0x%08x\n", which,vdma->num_line_byte);
*/
}
 
static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa7146_buf *buf)
{
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_video_dma vdma1;
 
struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
 
int width = buf->fmt->width;
int height = buf->fmt->height;
int bytesperline = buf->fmt->bytesperline;
enum v4l2_field field = buf->fmt->field;
 
int depth = sfmt->depth;
 
DEB_CAP(("[size=%dx%d,fields=%s]\n",
width,height,v4l2_field_names[field]));
 
if( bytesperline != 0) {
vdma1.pitch = bytesperline*2;
} else {
vdma1.pitch = (width*depth*2)/8;
}
vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels);
vdma1.base_page = buf->pt[0].dma | ME1;
if( 0 != vv->vflip ) {
vdma1.prot_addr = buf->pt[0].offset;
vdma1.base_even = buf->pt[0].offset+(vdma1.pitch/2)*height;
vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
} else {
vdma1.base_even = buf->pt[0].offset;
vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
vdma1.prot_addr = buf->pt[0].offset+(vdma1.pitch/2)*height;
}
 
if (V4L2_FIELD_HAS_BOTH(field)) {
} else if (field == V4L2_FIELD_ALTERNATE) {
/* fixme */
if ( vv->last_field == V4L2_FIELD_TOP ) {
vdma1.base_odd = vdma1.prot_addr;
vdma1.pitch /= 2;
} else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
vdma1.base_odd = vdma1.base_even;
vdma1.base_even = vdma1.prot_addr;
vdma1.pitch /= 2;
}
} else if (field == V4L2_FIELD_TOP) {
vdma1.base_odd = vdma1.prot_addr;
vdma1.pitch /= 2;
} else if (field == V4L2_FIELD_BOTTOM) {
vdma1.base_odd = vdma1.base_even;
vdma1.base_even = vdma1.prot_addr;
vdma1.pitch /= 2;
}
 
if( 0 != vv->vflip ) {
vdma1.pitch *= -1;
}
 
saa7146_write_out_dma(dev, 1, &vdma1);
return 0;
}
 
static int calc_planar_422(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3)
{
int height = buf->fmt->height;
int width = buf->fmt->width;
 
vdma2->pitch = width;
vdma3->pitch = width;
 
/* fixme: look at bytesperline! */
 
if( 0 != vv->vflip ) {
vdma2->prot_addr = buf->pt[1].offset;
vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[1].offset;
vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2);
 
vdma3->prot_addr = buf->pt[2].offset;
vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[2].offset;
vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2);
} else {
vdma3->base_even = buf->pt[2].offset;
vdma3->base_odd = vdma3->base_even + (vdma3->pitch/2);
vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset;
vdma2->base_even = buf->pt[1].offset;
vdma2->base_odd = vdma2->base_even + (vdma2->pitch/2);
vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset;
}
 
return 0;
}
 
static int calc_planar_420(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3)
{
int height = buf->fmt->height;
int width = buf->fmt->width;
 
vdma2->pitch = width/2;
vdma3->pitch = width/2;
 
if( 0 != vv->vflip ) {
vdma2->prot_addr = buf->pt[2].offset;
vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[2].offset;
vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2);
 
vdma3->prot_addr = buf->pt[1].offset;
vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[1].offset;
vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2);
 
} else {
vdma3->base_even = buf->pt[2].offset;
vdma3->base_odd = vdma3->base_even + (vdma3->pitch);
vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset;
 
vdma2->base_even = buf->pt[1].offset;
vdma2->base_odd = vdma2->base_even + (vdma2->pitch);
vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset;
}
return 0;
}
 
 
static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa7146_buf *buf)
{
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_video_dma vdma1;
struct saa7146_video_dma vdma2;
struct saa7146_video_dma vdma3;
 
struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
 
int width = buf->fmt->width;
int height = buf->fmt->height;
enum v4l2_field field = buf->fmt->field;
 
BUG_ON(0 == buf->pt[0].dma);
BUG_ON(0 == buf->pt[1].dma);
BUG_ON(0 == buf->pt[2].dma);
 
DEB_CAP(("[size=%dx%d,fields=%s]\n",
width,height,v4l2_field_names[field]));
 
/* fixme: look at bytesperline! */
 
/* fixme: what happens for user space buffers here?. The offsets are
most likely wrong, this version here only works for page-aligned
buffers, modifications to the pagetable-functions are necessary...*/
 
vdma1.pitch = width*2;
vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels);
vdma1.base_page = buf->pt[0].dma | ME1;
 
if( 0 != vv->vflip ) {
vdma1.prot_addr = buf->pt[0].offset;
vdma1.base_even = ((vdma1.pitch/2)*height)+buf->pt[0].offset;
vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
} else {
vdma1.base_even = buf->pt[0].offset;
vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
vdma1.prot_addr = (vdma1.pitch/2)*height+buf->pt[0].offset;
}
 
vdma2.num_line_byte = 0; /* unused */
vdma2.base_page = buf->pt[1].dma | ME1;
 
vdma3.num_line_byte = 0; /* unused */
vdma3.base_page = buf->pt[2].dma | ME1;
 
switch( sfmt->depth ) {
case 12: {
calc_planar_420(vv,buf,&vdma2,&vdma3);
break;
}
case 16: {
calc_planar_422(vv,buf,&vdma2,&vdma3);
break;
}
default: {
return -1;
}
}
 
if (V4L2_FIELD_HAS_BOTH(field)) {
} else if (field == V4L2_FIELD_ALTERNATE) {
/* fixme */
vdma1.base_odd = vdma1.prot_addr;
vdma1.pitch /= 2;
vdma2.base_odd = vdma2.prot_addr;
vdma2.pitch /= 2;
vdma3.base_odd = vdma3.prot_addr;
vdma3.pitch /= 2;
} else if (field == V4L2_FIELD_TOP) {
vdma1.base_odd = vdma1.prot_addr;
vdma1.pitch /= 2;
vdma2.base_odd = vdma2.prot_addr;
vdma2.pitch /= 2;
vdma3.base_odd = vdma3.prot_addr;
vdma3.pitch /= 2;
} else if (field == V4L2_FIELD_BOTTOM) {
vdma1.base_odd = vdma1.base_even;
vdma1.base_even = vdma1.prot_addr;
vdma1.pitch /= 2;
vdma2.base_odd = vdma2.base_even;
vdma2.base_even = vdma2.prot_addr;
vdma2.pitch /= 2;
vdma3.base_odd = vdma3.base_even;
vdma3.base_even = vdma3.prot_addr;
vdma3.pitch /= 2;
}
 
if( 0 != vv->vflip ) {
vdma1.pitch *= -1;
vdma2.pitch *= -1;
vdma3.pitch *= -1;
}
 
saa7146_write_out_dma(dev, 1, &vdma1);
if( (sfmt->flags & FORMAT_BYTE_SWAP) != 0 ) {
saa7146_write_out_dma(dev, 3, &vdma2);
saa7146_write_out_dma(dev, 2, &vdma3);
} else {
saa7146_write_out_dma(dev, 2, &vdma2);
saa7146_write_out_dma(dev, 3, &vdma3);
}
return 0;
}
 
static void program_capture_engine(struct saa7146_dev *dev, int planar)
{
struct saa7146_vv *vv = dev->vv_data;
int count = 0;
 
unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
 
/* wait for o_fid_a/b / e_fid_a/b toggle only if rps register 0 is not set*/
WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | o_wait);
WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | e_wait);
 
/* set rps register 0 */
WRITE_RPS0(CMD_WR_REG | (1 << 8) | (MC2/4));
WRITE_RPS0(MASK_27 | MASK_11);
/* turn on video-dma1 */
WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
WRITE_RPS0(MASK_06 | MASK_22); /* => mask */
WRITE_RPS0(MASK_06 | MASK_22); /* => values */
if( 0 != planar ) {
/* turn on video-dma2 */
WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
WRITE_RPS0(MASK_05 | MASK_21); /* => mask */
WRITE_RPS0(MASK_05 | MASK_21); /* => values */
 
/* turn on video-dma3 */
WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
WRITE_RPS0(MASK_04 | MASK_20); /* => mask */
WRITE_RPS0(MASK_04 | MASK_20); /* => values */
}
/* wait for o_fid_a/b / e_fid_a/b toggle */
if ( vv->last_field == V4L2_FIELD_INTERLACED ) {
WRITE_RPS0(CMD_PAUSE | o_wait);
WRITE_RPS0(CMD_PAUSE | e_wait);
} else if ( vv->last_field == V4L2_FIELD_TOP ) {
WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
WRITE_RPS0(CMD_PAUSE | o_wait);
} else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
WRITE_RPS0(CMD_PAUSE | e_wait);
}
 
/* turn off video-dma1 */
WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
WRITE_RPS0(MASK_22 | MASK_06); /* => mask */
WRITE_RPS0(MASK_22); /* => values */
if( 0 != planar ) {
/* turn off video-dma2 */
WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
WRITE_RPS0(MASK_05 | MASK_21); /* => mask */
WRITE_RPS0(MASK_21); /* => values */
 
/* turn off video-dma3 */
WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
WRITE_RPS0(MASK_04 | MASK_20); /* => mask */
WRITE_RPS0(MASK_20); /* => values */
}
 
/* generate interrupt */
WRITE_RPS0(CMD_INTERRUPT);
 
/* stop */
WRITE_RPS0(CMD_STOP);
}
 
void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
{
struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
struct saa7146_vv *vv = dev->vv_data;
u32 vdma1_prot_addr;
DEB_CAP(("buf:%p, next:%p\n",buf,next));
 
vdma1_prot_addr = saa7146_read(dev, PROT_ADDR1);
if( 0 == vdma1_prot_addr ) {
/* clear out beginning of streaming bit (rps register 0)*/
DEB_CAP(("forcing sync to new frame\n"));
saa7146_write(dev, MC2, MASK_27 );
}
 
saa7146_set_window(dev, buf->fmt->width, buf->fmt->height, buf->fmt->field);
saa7146_set_output_format(dev, sfmt->trans);
 
if ( vv->last_field == V4L2_FIELD_INTERLACED ) {
} else if ( vv->last_field == V4L2_FIELD_TOP ) {
vv->last_field = V4L2_FIELD_BOTTOM;
} else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
vv->last_field = V4L2_FIELD_TOP;
}
 
if( 0 != IS_PLANAR(sfmt->trans)) {
calculate_video_dma_grab_planar(dev, buf);
program_capture_engine(dev,1);
} else {
calculate_video_dma_grab_packed(dev, buf);
program_capture_engine(dev,0);
}
/*
printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
printk("vdma%d => vptr : 0x%08x\n", 1,saa7146_read(dev,PCI_VDP1));
*/
 
/* write the address of the rps-program */
saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
 
/* turn on rps */
saa7146_write(dev, MC1, (MASK_12 | MASK_28));
}
 
/shark/trunk/drivers/cm7326/tea6415c.c
0,0 → 1,266
/*
tea6415c.h - i2c-driver for the tea6415c by SGS Thomson
 
Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
 
The tea6415c is a bus controlled video-matrix-switch
with 8 inputs and 6 outputs.
It is cascadable, i.e. it can be found at the addresses
0x43 and 0x03 on the i2c-bus.
For detailed informations download the specifications directly
from SGS Thomson at http://www.st.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License vs published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mvss Ave, Cambridge, MA 02139, USA.
*/
 
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include "tea6415c.h"
 
static int debug = 0; /* insmod parameter */
MODULE_PARM(debug,"i");
#define dprintk if (debug) printk
 
#define TEA6415C_NUM_INPUTS 8
#define TEA6415C_NUM_OUTPUTS 6
 
/* addresses to scan, found only at 0x03 and/or 0x43 (7-bit) */
static unsigned short normal_i2c[] = {I2C_TEA6415C_1, I2C_TEA6415C_2, I2C_CLIENT_END};
static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
 
/* magic definition of all other variables and things */
I2C_CLIENT_INSMOD;
 
static struct i2c_driver driver;
 
/* unique ID allocation */
static int tea6415c_id = 0;
 
/* this function is called by i2c_probe */
static int tea6415c_detect(struct i2c_adapter *adapter, int address, unsigned short flags, int kind)
{
struct i2c_client *client = 0;
int err = 0;
 
/* let's see whether this adapter can support what we need */
if ( 0 == i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) {
return 0;
}
 
/* allocate memory for client structure */
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (0 == client) {
return -ENOMEM;
}
memset(client, 0, sizeof(struct i2c_client));
 
/* fill client structure */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
sprintf(client->name,"tea6415c (0x%02x)", address);
#else
sprintf(client->dev.name,"tea6415c (0x%02x)", address);
#endif
client->id = tea6415c_id++;
client->flags = 0;
client->addr = address;
client->adapter = adapter;
client->driver = &driver;
 
/* tell the i2c layer a new client has arrived */
if (0 != (err = i2c_attach_client(client))) {
kfree(client);
return err;
}
 
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
printk("tea6415c.o: detected @ 0x%02x on adapter %s\n",address,&client->adapter->name[0]);
#else
printk("tea6415c.o: detected @ 0x%02x on adapter %s\n",address,&client->adapter->dev.name[0]);
#endif
 
return 0;
}
 
static int tea6415c_attach(struct i2c_adapter *adapter)
{
/* let's see whether this is a know adapter we can attach to */
if( adapter->id != I2C_ALGO_SAA7146 ) {
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
dprintk("tea6415c.o: refusing to probe on unknown adapter [name='%s',id=0x%x]\n",adapter->name,adapter->id);
#else
dprintk("tea6415c.o: refusing to probe on unknown adapter [name='%s',id=0x%x]\n",adapter->dev.name,adapter->id);
#endif
return -ENODEV;
}
 
return i2c_probe(adapter,&addr_data,&tea6415c_detect);
}
 
static int tea6415c_detach(struct i2c_client *client)
{
int err = 0;
 
if ( 0 != (err = i2c_detach_client(client))) {
printk("tea6415c.o: Client deregistration failed, client not detached.\n");
return err;
}
kfree(client);
 
return 0;
}
 
/* makes a connection between the input-pin 'i' and the output-pin 'o'
for the tea6415c-client 'client' */
static int tea6415c_switch(struct i2c_client *client, int i, int o)
{
u8 byte = 0;
dprintk("tea6415c.o: tea6415c_switch: adr:0x%02x, i:%d, o:%d\n", client->addr, i, o);
/* check if the pins are valid */
if ( 0 == (( 1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i ) &&
(18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o )))
return -1;
 
/* to understand this, have a look at the tea6415c-specs (p.5) */
switch(o) {
case 18:
byte = 0x00;
break;
case 14:
byte = 0x20;
break;
case 16:
byte = 0x10;
break;
case 17:
byte = 0x08;
break;
case 15:
byte = 0x18;
break;
case 13:
byte = 0x28;
break;
};
switch(i) {
case 5:
byte |= 0x00;
break;
case 8:
byte |= 0x04;
break;
case 3:
byte |= 0x02;
break;
case 20:
byte |= 0x06;
break;
case 6:
byte |= 0x01;
break;
case 10:
byte |= 0x05;
break;
case 1:
byte |= 0x03;
break;
case 11:
byte |= 0x07;
break;
};
 
if ( 0 != i2c_smbus_write_byte(client,byte)) {
dprintk("tea6415c.o: tea6415c_switch: could not write to tea6415c\n");
return -1;
}
 
return 0;
}
 
static int tea6415c_command(struct i2c_client *client, unsigned int cmd, void* arg)
{
struct tea6415c_multiplex *v = (struct tea6415c_multiplex*)arg;
int result = 0;
 
switch (cmd) {
case TEA6415C_SWITCH: {
result = tea6415c_switch(client,v->in,v->out);
break;
}
default: {
return -ENOIOCTLCMD;
}
}
 
if ( 0 != result )
return result;
return 0;
}
static void tea6415c_inc_use(struct i2c_client *client)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
 
static void tea6415c_dec_use(struct i2c_client *client)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
 
static struct i2c_driver driver = {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,54)
.owner = THIS_MODULE,
#endif
.name = "tea6415c driver",
.id = I2C_DRIVERID_TEA6415C,
.flags = I2C_DF_NOTIFY,
.attach_adapter = tea6415c_attach,
.detach_client = tea6415c_detach,
.command = tea6415c_command,
.inc_use = tea6415c_inc_use,
.dec_use = tea6415c_dec_use,
};
 
static int tea6415c_init_module(void)
{
i2c_add_driver(&driver);
return 0;
}
 
static void tea6415c_cleanup_module(void)
{
i2c_del_driver(&driver);
}
 
module_init(tea6415c_init_module);
module_exit(tea6415c_cleanup_module);
 
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("tea6415c driver");
MODULE_LICENSE("GPL");
 
/shark/trunk/drivers/cm7326/makefile
0,0 → 1,23
# CM7326 - Driver for pc104+ framegrabber
 
ifndef BASE
BASE=../..
endif
 
include $(BASE)/config/config.mk
 
LIBRARY = cm7326
 
OBJS_PATH = $(BASE)/drivers/cm7326
 
OBJS = cm7326.o
 
OTHERINCL += -I$(BASE)/drivers/cm7326/include -I$(BASE)/drivers/linuxc26/include -I$(BASE)/drivers/i2c/include
 
C_OPT += -D__KERNEL__
 
include $(BASE)/config/lib.mk
 
clean::
rm -f $(OBJS)
 
/shark/trunk/drivers/cm7326/tda9840.c
0,0 → 1,316
/*
tda9840.h - i2c-driver for the tda9840 by SGS Thomson
 
Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
 
The tda9840 is a stereo/dual sound processor with digital
identification. It can be found at address 0x42 on the i2c-bus.
 
For detailed informations download the specifications directly
from SGS Thomson at http://www.st.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/init.h>
 
#include "tda9840.h"
 
static int debug = 0; /* insmod parameter */
MODULE_PARM(debug,"i");
#define dprintk if (debug) printk
 
#define SWITCH 0x00
#define LEVEL_ADJUST 0x02
#define STEREO_ADJUST 0x03
#define TEST 0x04
 
/* addresses to scan, found only at 0x42 (7-Bit) */
static unsigned short normal_i2c[] = {I2C_TDA9840, I2C_CLIENT_END};
static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
 
/* magic definition of all other variables and things */
I2C_CLIENT_INSMOD;
 
/* unique ID allocation */
static int tda9840_id = 0;
 
static struct i2c_driver driver;
 
static int tda9840_command(struct i2c_client *client, unsigned int cmd, void* arg)
{
int result = 0;
 
switch (cmd) {
case TDA9840_SWITCH:
{
int byte = *(int*)arg;
 
dprintk("tda9840.o: TDA9840_SWITCH: 0x%02x\n",byte);
 
if ( byte != TDA9840_SET_MONO
&& byte != TDA9840_SET_MUTE
&& byte != TDA9840_SET_STEREO
&& byte != TDA9840_SET_LANG1
&& byte != TDA9840_SET_LANG2
&& byte != TDA9840_SET_BOTH
&& byte != TDA9840_SET_BOTH_R
&& byte != TDA9840_SET_EXTERNAL ) {
return -EINVAL;
}
if ( 0 != (result = i2c_smbus_write_byte_data(client, SWITCH, byte))) {
printk("tda9840.o: TDA9840_SWITCH error.\n");
return -EFAULT;
}
return 0;
}
 
case TDA9840_LEVEL_ADJUST:
{
int byte = *(int*)arg;
 
dprintk("tda9840.o: TDA9840_LEVEL_ADJUST: %d\n",byte);
 
/* check for correct range */
if ( byte > 25 || byte < -20 )
return -EINVAL;
/* calculate actual value to set, see specs, page 18 */
byte /= 5;
if ( 0 < byte )
byte += 0x8;
else
byte = -byte;
 
if ( 0 != (result = i2c_smbus_write_byte_data(client, LEVEL_ADJUST, byte))) {
printk("tda9840.o: TDA9840_LEVEL_ADJUST error.\n");
return -EFAULT;
}
return 0;
}
 
case TDA9840_STEREO_ADJUST:
{
int byte = *(int*)arg;
 
dprintk("tda9840.o: TDA9840_STEREO_ADJUST: %d\n",byte);
 
/* check for correct range */
if ( byte > 25 || byte < -24 )
return -EINVAL;
/* calculate actual value to set */
byte /= 5;
if ( 0 < byte )
byte += 0x20;
else
byte = -byte;
 
if ( 0 != (result = i2c_smbus_write_byte_data(client, STEREO_ADJUST, byte))) {
printk("tda9840.o: TDA9840_STEREO_ADJUST error.\n");
return -EFAULT;
}
return 0;
}
 
case TDA9840_DETECT:
{
int byte = 0x0;
 
if ( -1 == (byte = i2c_smbus_read_byte_data(client, STEREO_ADJUST))) {
printk("tda9840.o: TDA9840_DETECT error while reading.\n");
return -EFAULT;
}
 
if( 0 != (byte & 0x80)) {
dprintk("tda9840.o: TDA9840_DETECT, register contents invalid.\n");
return -EFAULT;
}
 
dprintk("tda9840.o: TDA9840_DETECT, result: 0x%02x (original byte)\n",byte);
 
return ((byte & 0x60) >> 5);
}
 
case TDA9840_TEST:
{
int byte = *(int*)arg;
 
dprintk("tda9840.o: TDA9840_TEST: 0x%02x\n",byte);
 
/* mask out irrelevant bits */
byte &= 0x3;
 
if ( 0 != (result = i2c_smbus_write_byte_data(client, TEST, byte))) {
printk("tda9840.o: TDA9840_TEST error.\n");
return -EFAULT;
}
return 0;
}
 
default:
return -ENOIOCTLCMD;
}
 
return 0;
}
 
static int tda9840_detect(struct i2c_adapter *adapter, int address, unsigned short flags, int kind)
{
struct i2c_client *client;
int result = 0;
 
int byte = 0x0;
/* let's see whether this adapter can support what we need */
if ( 0 == i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA|I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) {
return 0;
}
 
/* allocate memory for client structure */
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (0 == client) {
printk("tda9840.o: not enough kernel memory.\n");
return -ENOMEM;
}
memset(client, 0, sizeof(struct i2c_client));
/* fill client structure */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
sprintf(client->name,"tda9840 (0x%02x)", address);
#else
sprintf(client->dev.name,"tda9840 (0x%02x)", address);
#endif
client->id = tda9840_id++;
client->flags = 0;
client->addr = address;
client->adapter = adapter;
client->driver = &driver;
 
/* tell the i2c layer a new client has arrived */
if (0 != (result = i2c_attach_client(client))) {
kfree(client);
return result;
}
 
/* set initial values for level & stereo - adjustment, mode */
byte = 0;
if ( 0 != (result = tda9840_command(client, TDA9840_LEVEL_ADJUST, &byte))) {
printk("tda9840.o: could not initialize ic #1. continuing anyway. (result:%d)\n",result);
}
if ( 0 != (result = tda9840_command(client, TDA9840_STEREO_ADJUST, &byte))) {
printk("tda9840.o: could not initialize ic #2. continuing anyway. (result:%d)\n",result);
}
 
byte = TDA9840_SET_MONO;
if ( 0 != (result = tda9840_command(client, TDA9840_SWITCH, &byte))) {
printk("tda9840.o: could not initialize ic #3. continuing anyway. (result:%d)\n",result);
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
printk("tda9840.o: detected @ 0x%02x on adapter %s\n",address,&client->adapter->name[0]);
#else
printk("tda9840.o: detected @ 0x%02x on adapter %s\n",address,&client->adapter->dev.name[0]);
#endif
return 0;
}
 
static int tda9840_attach(struct i2c_adapter *adapter)
{
/* let's see whether this is a know adapter we can attach to */
if( adapter->id != I2C_ALGO_SAA7146 ) {
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
dprintk("tda9840.o: refusing to probe on unknown adapter [name='%s',id=0x%x]\n",adapter->name,adapter->id);
#else
dprintk("tda9840.o: refusing to probe on unknown adapter [name='%s',id=0x%x]\n",adapter->dev.name,adapter->id);
#endif
return -ENODEV;
}
 
return i2c_probe(adapter,&addr_data,&tda9840_detect);
}
 
static int tda9840_detach(struct i2c_client *client)
{
int err = 0;
 
if ( 0 != (err = i2c_detach_client(client))) {
printk("tda9840.o: Client deregistration failed, client not detached.\n");
return err;
}
kfree(client);
 
return 0;
}
 
static void tda9840_inc_use(struct i2c_client *client)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
 
static void tda9840_dec_use(struct i2c_client *client)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
 
static struct i2c_driver driver = {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,54)
.owner = THIS_MODULE,
#endif
.name = "tda9840 driver",
.id = I2C_DRIVERID_TDA9840,
.flags = I2C_DF_NOTIFY,
.attach_adapter = tda9840_attach,
.detach_client = tda9840_detach,
.command = tda9840_command,
.inc_use = tda9840_inc_use,
.dec_use = tda9840_dec_use,
};
 
static int tda9840_init_module(void)
{
i2c_add_driver(&driver);
return 0;
}
 
static void tda9840_cleanup_module(void)
{
i2c_del_driver(&driver);
}
 
module_init(tda9840_init_module);
module_exit(tda9840_cleanup_module);
 
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("tda9840 driver");
MODULE_LICENSE("GPL");
 
/shark/trunk/drivers/cm7326/tuner.c
0,0 → 1,1007
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/i2c.h>
#include <linux/types.h>
#include <linux/videodev.h>
#include <linux/init.h>
 
#include <media/tuner.h>
#include <media/audiochip.h>
 
/* Addresses to scan */
static unsigned short normal_i2c[] = {I2C_CLIENT_END};
static unsigned short normal_i2c_range[] = {0x60,0x6f,I2C_CLIENT_END};
I2C_CLIENT_INSMOD;
 
/* insmod options */
static int debug = 0;
static int type = -1;
static int addr = 0;
static char *pal = "b";
static int tv_range[2] = { 44, 958 };
static int radio_range[2] = { 65, 108 };
MODULE_PARM(debug,"i");
MODULE_PARM(type,"i");
MODULE_PARM(addr,"i");
MODULE_PARM(tv_range,"2i");
MODULE_PARM(radio_range,"2i");
MODULE_PARM(pal,"s");
 
#define optimize_vco 1
 
MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
MODULE_LICENSE("GPL");
 
static int this_adap;
#define dprintk if (debug) printk
 
struct tuner
{
int type; /* chip type */
int freq; /* keep track of the current settings */
int std;
 
int radio;
int mode; /* current norm for multi-norm tuners */
int xogc; // only for MT2032
};
 
static struct i2c_driver driver;
static struct i2c_client client_template;
 
/* ---------------------------------------------------------------------- */
 
/* tv standard selection for Temic 4046 FM5
this value takes the low bits of control byte 2
from datasheet Rev.01, Feb.00
standard BG I L L2 D
picture IF 38.9 38.9 38.9 33.95 38.9
sound 1 33.4 32.9 32.4 40.45 32.4
sound 2 33.16
NICAM 33.05 32.348 33.05 33.05
*/
#define TEMIC_SET_PAL_I 0x05
#define TEMIC_SET_PAL_DK 0x09
#define TEMIC_SET_PAL_L 0x0a // SECAM ?
#define TEMIC_SET_PAL_L2 0x0b // change IF !
#define TEMIC_SET_PAL_BG 0x0c
 
/* tv tuner system standard selection for Philips FQ1216ME
this value takes the low bits of control byte 2
from datasheet "1999 Nov 16" (supersedes "1999 Mar 23")
standard BG DK I L L`
picture carrier 38.90 38.90 38.90 38.90 33.95
colour 34.47 34.47 34.47 34.47 38.38
sound 1 33.40 32.40 32.90 32.40 40.45
sound 2 33.16 - - - -
NICAM 33.05 33.05 32.35 33.05 39.80
*/
#define PHILIPS_SET_PAL_I 0x01 /* Bit 2 always zero !*/
#define PHILIPS_SET_PAL_BGDK 0x09
#define PHILIPS_SET_PAL_L2 0x0a
#define PHILIPS_SET_PAL_L 0x0b
 
/* system switching for Philips FI1216MF MK2
from datasheet "1996 Jul 09",
standard BG L L'
picture carrier 38.90 38.90 33.95
colour 34.47 34.37 38.38
sound 1 33.40 32.40 40.45
sound 2 33.16 - -
NICAM 33.05 33.05 39.80
*/
#define PHILIPS_MF_SET_BG 0x01 /* Bit 2 must be zero, Bit 3 is system output */
#define PHILIPS_MF_SET_PAL_L 0x03 // France
#define PHILIPS_MF_SET_PAL_L2 0x02 // L'
 
 
/* ---------------------------------------------------------------------- */
 
struct tunertype
{
char *name;
unsigned char Vendor;
unsigned char Type;
unsigned short thresh1; /* band switch VHF_LO <=> VHF_HI */
unsigned short thresh2; /* band switch VHF_HI <=> UHF */
unsigned char VHF_L;
unsigned char VHF_H;
unsigned char UHF;
unsigned char config;
unsigned short IFPCoff; /* 622.4=16*38.90 MHz PAL,
732 =16*45.75 NTSCi,
940 =58.75 NTSC-Japan */
};
 
/*
* The floats in the tuner struct are computed at compile time
* by gcc and cast back to integers. Thus we don't violate the
* "no float in kernel" rule.
*/
static struct tunertype tuners[] = {
{ "Temic PAL (4002 FH5)", TEMIC, PAL,
16*140.25,16*463.25,0x02,0x04,0x01,0x8e,623},
{ "Philips PAL_I (FI1246 and compatibles)", Philips, PAL_I,
16*140.25,16*463.25,0xa0,0x90,0x30,0x8e,623},
{ "Philips NTSC (FI1236,FM1236 and compatibles)", Philips, NTSC,
16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,732},
{ "Philips (SECAM+PAL_BG) (FI1216MF, FM1216MF, FR1216MF)", Philips, SECAM,
16*168.25,16*447.25,0xA7,0x97,0x37,0x8e,623},
 
{ "NoTuner", NoTuner, NOTUNER,
0,0,0x00,0x00,0x00,0x00,0x00},
{ "Philips PAL_BG (FI1216 and compatibles)", Philips, PAL,
16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,623},
{ "Temic NTSC (4032 FY5)", TEMIC, NTSC,
16*157.25,16*463.25,0x02,0x04,0x01,0x8e,732},
{ "Temic PAL_I (4062 FY5)", TEMIC, PAL_I,
16*170.00,16*450.00,0x02,0x04,0x01,0x8e,623},
 
{ "Temic NTSC (4036 FY5)", TEMIC, NTSC,
16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,732},
{ "Alps HSBH1", TEMIC, NTSC,
16*137.25,16*385.25,0x01,0x02,0x08,0x8e,732},
{ "Alps TSBE1",TEMIC,PAL,
16*137.25,16*385.25,0x01,0x02,0x08,0x8e,732},
{ "Alps TSBB5", Alps, PAL_I, /* tested (UK UHF) with Modulartech MM205 */
16*133.25,16*351.25,0x01,0x02,0x08,0x8e,632},
 
{ "Alps TSBE5", Alps, PAL, /* untested - data sheet guess. Only IF differs. */
16*133.25,16*351.25,0x01,0x02,0x08,0x8e,622},
{ "Alps TSBC5", Alps, PAL, /* untested - data sheet guess. Only IF differs. */
16*133.25,16*351.25,0x01,0x02,0x08,0x8e,608},
{ "Temic PAL_BG (4006FH5)", TEMIC, PAL,
16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
{ "Alps TSCH6",Alps,NTSC,
16*137.25,16*385.25,0x14,0x12,0x11,0x8e,732},
 
{ "Temic PAL_DK (4016 FY5)",TEMIC,PAL,
16*168.25,16*456.25,0xa0,0x90,0x30,0x8e,623},
{ "Philips NTSC_M (MK2)",Philips,NTSC,
16*160.00,16*454.00,0xa0,0x90,0x30,0x8e,732},
{ "Temic PAL_I (4066 FY5)", TEMIC, PAL_I,
16*169.00, 16*454.00, 0xa0,0x90,0x30,0x8e,623},
{ "Temic PAL* auto (4006 FN5)", TEMIC, PAL,
16*169.00, 16*454.00, 0xa0,0x90,0x30,0x8e,623},
 
{ "Temic PAL_BG (4009 FR5) or PAL_I (4069 FR5)", TEMIC, PAL,
16*141.00, 16*464.00, 0xa0,0x90,0x30,0x8e,623},
{ "Temic NTSC (4039 FR5)", TEMIC, NTSC,
16*158.00, 16*453.00, 0xa0,0x90,0x30,0x8e,732},
{ "Temic PAL/SECAM multi (4046 FM5)", TEMIC, PAL,
16*169.00, 16*454.00, 0xa0,0x90,0x30,0x8e,623},
{ "Philips PAL_DK (FI1256 and compatibles)", Philips, PAL,
16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
 
{ "Philips PAL/SECAM multi (FQ1216ME)", Philips, PAL,
16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
{ "LG PAL_I+FM (TAPC-I001D)", LGINNOTEK, PAL_I,
16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
{ "LG PAL_I (TAPC-I701D)", LGINNOTEK, PAL_I,
16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
{ "LG NTSC+FM (TPI8NSR01F)", LGINNOTEK, NTSC,
16*210.00,16*497.00,0xa0,0x90,0x30,0x8e,732},
 
{ "LG PAL_BG+FM (TPI8PSB01D)", LGINNOTEK, PAL,
16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
{ "LG PAL_BG (TPI8PSB11D)", LGINNOTEK, PAL,
16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
{ "Temic PAL* auto + FM (4009 FN5)", TEMIC, PAL,
16*141.00, 16*464.00, 0xa0,0x90,0x30,0x8e,623},
{ "SHARP NTSC_JP (2U5JF5540)", SHARP, NTSC, /* 940=16*58.75 NTSC@Japan */
16*137.25,16*317.25,0x01,0x02,0x08,0x8e,732 }, // Corrected to NTSC=732 (was:940)
 
{ "Samsung PAL TCPM9091PD27", Samsung, PAL, /* from sourceforge v3tv */
16*169,16*464,0xA0,0x90,0x30,0x8e,623},
{ "MT2032 universal", Microtune,PAL|NTSC,
0,0,0,0,0,0,0},
{ "Temic PAL_BG (4106 FH5)", TEMIC, PAL,
16*141.00, 16*464.00, 0xa0,0x90,0x30,0x8e,623},
{ "Temic PAL_DK/SECAM_L (4012 FY5)", TEMIC, PAL,
16*140.25, 16*463.25, 0x02,0x04,0x01,0x8e,623},
 
{ "Temic NTSC (4136 FY5)", TEMIC, NTSC,
16*158.00, 16*453.00, 0xa0,0x90,0x30,0x8e,732},
{ "LG PAL (newer TAPC series)", LGINNOTEK, PAL,
16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,623},
{ "Philips PAL/SECAM multi (FM1216ME MK3)", Philips, PAL,
16*160.00,16*442.00,0x01,0x02,0x04,0x8e,623 },
{ "LG NTSC (newer TAPC series)", LGINNOTEK, NTSC,
16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,732},
};
#define TUNERS (sizeof(tuners)/sizeof(struct tunertype))
 
/* ---------------------------------------------------------------------- */
 
static int tuner_getstatus(struct i2c_client *c)
{
unsigned char byte;
 
struct tuner *t = (struct tuner*)c->data;
 
if (t->type == TUNER_MT2032)
return 0;
 
if (1 != i2c_master_recv(c,&byte,1))
return 0;
return byte;
}
 
#define TUNER_POR 0x80
#define TUNER_FL 0x40
#define TUNER_MODE 0x38
#define TUNER_AFC 0x07
 
#define TUNER_STEREO 0x10 /* radio mode */
#define TUNER_SIGNAL 0x07 /* radio mode */
 
static int tuner_signal(struct i2c_client *c)
{
return (tuner_getstatus(c) & TUNER_SIGNAL)<<13;
}
 
static int tuner_stereo(struct i2c_client *c)
{
return (tuner_getstatus (c) & TUNER_STEREO);
}
 
#if 0 /* unused */
static int tuner_islocked (struct i2c_client *c)
{
return (tuner_getstatus (c) & TUNER_FL);
}
 
static int tuner_afcstatus (struct i2c_client *c)
{
return (tuner_getstatus (c) & TUNER_AFC) - 2;
}
 
static int tuner_mode (struct i2c_client *c)
{
return (tuner_getstatus (c) & TUNER_MODE) >> 3;
}
#endif
 
// Initalization as described in "MT203x Programming Procedures", Rev 1.2, Feb.2001
static int mt2032_init(struct i2c_client *c)
{
unsigned char buf[21];
int ret,xogc,xok=0;
struct tuner *t = (struct tuner*)c->data;
 
buf[0]=0;
ret=i2c_master_send(c,buf,1);
i2c_master_recv(c,buf,21);
 
printk("MT2032: Companycode=%02x%02x Part=%02x Revision=%02x\n",
buf[0x11],buf[0x12],buf[0x13],buf[0x14]);
 
if(debug) {
int i;
printk("MT2032 hexdump:\n");
for(i=0;i<21;i++) {
printk(" %02x",buf[i]);
if(((i+1)%8)==0) printk(" ");
if(((i+1)%16)==0) printk("\n ");
}
printk("\n ");
}
// Look for MT2032 id:
// part= 0x04(MT2032), 0x06(MT2030), 0x07(MT2040)
if((buf[0x11] != 0x4d) || (buf[0x12] != 0x54) || (buf[0x13] != 0x04)) {
printk("not a MT2032.\n");
return 0;
}
 
 
// Initialize Registers per spec.
buf[1]=2; // Index to register 2
buf[2]=0xff;
buf[3]=0x0f;
buf[4]=0x1f;
ret=i2c_master_send(c,buf+1,4);
 
buf[5]=6; // Index register 6
buf[6]=0xe4;
buf[7]=0x8f;
buf[8]=0xc3;
buf[9]=0x4e;
buf[10]=0xec;
ret=i2c_master_send(c,buf+5,6);
 
buf[12]=13; // Index register 13
buf[13]=0x32;
ret=i2c_master_send(c,buf+12,2);
 
// Adjust XOGC (register 7), wait for XOK
xogc=7;
do {
dprintk("mt2032: xogc = 0x%02x\n",xogc&0x07);
mdelay(10);
buf[0]=0x0e;
i2c_master_send(c,buf,1);
i2c_master_recv(c,buf,1);
xok=buf[0]&0x01;
dprintk("mt2032: xok = 0x%02x\n",xok);
if (xok == 1) break;
 
xogc--;
dprintk("mt2032: xogc = 0x%02x\n",xogc&0x07);
if (xogc == 3) {
xogc=4; // min. 4 per spec
break;
}
buf[0]=0x07;
buf[1]=0x88 + xogc;
ret=i2c_master_send(c,buf,2);
if (ret!=2)
printk("mt2032_init failed with %d\n",ret);
} while (xok != 1 );
t->xogc=xogc;
 
return(1);
}
 
 
// IsSpurInBand()?
static int mt2032_spurcheck(int f1, int f2, int spectrum_from,int spectrum_to)
{
int n1=1,n2,f;
 
f1=f1/1000; //scale to kHz to avoid 32bit overflows
f2=f2/1000;
spectrum_from/=1000;
spectrum_to/=1000;
 
dprintk("spurcheck f1=%d f2=%d from=%d to=%d\n",f1,f2,spectrum_from,spectrum_to);
 
do {
n2=-n1;
f=n1*(f1-f2);
do {
n2--;
f=f-f2;
dprintk(" spurtest n1=%d n2=%d ftest=%d\n",n1,n2,f);
 
if( (f>spectrum_from) && (f<spectrum_to))
printk("mt2032 spurcheck triggered: %d\n",n1);
} while ( (f>(f2-spectrum_to)) || (n2>-5));
n1++;
} while (n1<5);
 
return 1;
}
 
static int mt2032_compute_freq(int rfin, int if1, int if2, int spectrum_from,
int spectrum_to, unsigned char *buf, int *ret_sel, int xogc) //all in Hz
{
int fref,lo1,lo1n,lo1a,s,sel,lo1freq, desired_lo1,
desired_lo2,lo2,lo2n,lo2a,lo2num,lo2freq;
 
fref= 5250 *1000; //5.25MHz
desired_lo1=rfin+if1;
 
lo1=(2*(desired_lo1/1000)+(fref/1000)) / (2*fref/1000);
lo1n=lo1/8;
lo1a=lo1-(lo1n*8);
 
s=rfin/1000/1000+1090;
 
if(optimize_vco) {
if(s>1890) sel=0;
else if(s>1720) sel=1;
else if(s>1530) sel=2;
else if(s>1370) sel=3;
else sel=4; // >1090
}
else {
if(s>1790) sel=0; // <1958
else if(s>1617) sel=1;
else if(s>1449) sel=2;
else if(s>1291) sel=3;
else sel=4; // >1090
}
*ret_sel=sel;
 
lo1freq=(lo1a+8*lo1n)*fref;
 
dprintk("mt2032: rfin=%d lo1=%d lo1n=%d lo1a=%d sel=%d, lo1freq=%d\n",
rfin,lo1,lo1n,lo1a,sel,lo1freq);
 
desired_lo2=lo1freq-rfin-if2;
lo2=(desired_lo2)/fref;
lo2n=lo2/8;
lo2a=lo2-(lo2n*8);
lo2num=((desired_lo2/1000)%(fref/1000))* 3780/(fref/1000); //scale to fit in 32bit arith
lo2freq=(lo2a+8*lo2n)*fref + lo2num*(fref/1000)/3780*1000;
 
dprintk("mt2032: rfin=%d lo2=%d lo2n=%d lo2a=%d num=%d lo2freq=%d\n",
rfin,lo2,lo2n,lo2a,lo2num,lo2freq);
 
if(lo1a<0 || lo1a>7 || lo1n<17 ||lo1n>48 || lo2a<0 ||lo2a >7 ||lo2n<17 || lo2n>30) {
printk("mt2032: frequency parameters out of range: %d %d %d %d\n",
lo1a, lo1n, lo2a,lo2n);
return(-1);
}
 
mt2032_spurcheck(lo1freq, desired_lo2, spectrum_from, spectrum_to);
// should recalculate lo1 (one step up/down)
 
// set up MT2032 register map for transfer over i2c
buf[0]=lo1n-1;
buf[1]=lo1a | (sel<<4);
buf[2]=0x86; // LOGC
buf[3]=0x0f; //reserved
buf[4]=0x1f;
buf[5]=(lo2n-1) | (lo2a<<5);
if(rfin >400*1000*1000)
buf[6]=0xe4;
else
buf[6]=0xf4; // set PKEN per rev 1.2
buf[7]=8+xogc;
buf[8]=0xc3; //reserved
buf[9]=0x4e; //reserved
buf[10]=0xec; //reserved
buf[11]=(lo2num&0xff);
buf[12]=(lo2num>>8) |0x80; // Lo2RST
 
return 0;
}
 
static int mt2032_check_lo_lock(struct i2c_client *c)
{
int try,lock=0;
unsigned char buf[2];
for(try=0;try<10;try++) {
buf[0]=0x0e;
i2c_master_send(c,buf,1);
i2c_master_recv(c,buf,1);
dprintk("mt2032 Reg.E=0x%02x\n",buf[0]);
lock=buf[0] &0x06;
if (lock==6)
break;
dprintk("mt2032: pll wait 1ms for lock (0x%2x)\n",buf[0]);
udelay(1000);
}
return lock;
}
 
static int mt2032_optimize_vco(struct i2c_client *c,int sel,int lock)
{
unsigned char buf[2];
int tad1;
 
buf[0]=0x0f;
i2c_master_send(c,buf,1);
i2c_master_recv(c,buf,1);
dprintk("mt2032 Reg.F=0x%02x\n",buf[0]);
tad1=buf[0]&0x07;
 
if(tad1 ==0) return lock;
if(tad1 ==1) return lock;
 
if(tad1==2) {
if(sel==0)
return lock;
else sel--;
}
else {
if(sel<4)
sel++;
else
return lock;
}
 
dprintk("mt2032 optimize_vco: sel=%d\n",sel);
 
buf[0]=0x0f;
buf[1]=sel;
i2c_master_send(c,buf,2);
lock=mt2032_check_lo_lock(c);
return lock;
}
 
 
static void mt2032_set_if_freq(struct i2c_client *c,int rfin, int if1, int if2, int from, int to)
{
unsigned char buf[21];
int lint_try,ret,sel,lock=0;
struct tuner *t = (struct tuner*)c->data;
 
dprintk("mt2032_set_if_freq rfin=%d if1=%d if2=%d from=%d to=%d\n",rfin,if1,if2,from,to);
 
buf[0]=0;
ret=i2c_master_send(c,buf,1);
i2c_master_recv(c,buf,21);
 
buf[0]=0;
ret=mt2032_compute_freq(rfin,if1,if2,from,to,&buf[1],&sel,t->xogc);
if (ret<0)
return;
 
// send only the relevant registers per Rev. 1.2
buf[0]=0;
ret=i2c_master_send(c,buf,4);
buf[5]=5;
ret=i2c_master_send(c,buf+5,4);
buf[11]=11;
ret=i2c_master_send(c,buf+11,3);
if(ret!=3)
printk("mt2032_set_if_freq failed with %d\n",ret);
 
// wait for PLLs to lock (per manual), retry LINT if not.
for(lint_try=0; lint_try<2; lint_try++) {
lock=mt2032_check_lo_lock(c);
if(optimize_vco)
lock=mt2032_optimize_vco(c,sel,lock);
if(lock==6) break;
printk("mt2032: re-init PLLs by LINT\n");
buf[0]=7;
buf[1]=0x80 +8+t->xogc; // set LINT to re-init PLLs
i2c_master_send(c,buf,2);
mdelay(10);
buf[1]=8+t->xogc;
i2c_master_send(c,buf,2);
}
 
if (lock!=6)
printk("MT2032 Fatal Error: PLLs didn't lock.\n");
 
buf[0]=2;
buf[1]=0x20; // LOGC for optimal phase noise
ret=i2c_master_send(c,buf,2);
if (ret!=2)
printk("mt2032_set_if_freq2 failed with %d\n",ret);
}
 
 
static void mt2032_set_tv_freq(struct i2c_client *c, int freq, int norm)
{
int if2,from,to;
 
// signal bandwidth and picture carrier
if(norm==VIDEO_MODE_NTSC) {
from=40750*1000;
to=46750*1000;
if2=45750*1000;
}
else { // Pal
from=32900*1000;
to=39900*1000;
if2=38900*1000;
}
 
mt2032_set_if_freq(c,freq* 1000*1000/16, 1090*1000*1000, if2, from, to);
}
 
 
// Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz
static void set_tv_freq(struct i2c_client *c, int freq)
{
u8 config;
u16 div;
struct tunertype *tun;
struct tuner *t = c->data;
unsigned char buffer[4];
int rc;
 
if (t->type == -1) {
printk("tuner: tuner type not set\n");
return;
}
if (t->type == TUNER_MT2032) {
mt2032_set_tv_freq(c,freq,t->mode);
return;
}
 
if (freq < tv_range[0]*16 || freq > tv_range[1]*16) {
/* FIXME: better do that chip-specific, but
right now we don't have that in the config
struct and this way is still better than no
check at all */
printk("tuner: TV freq (%d.%02d) out of range (%d-%d)\n",
freq/16,freq%16*100/16,tv_range[0],tv_range[1]);
return;
}
 
tun=&tuners[t->type];
if (freq < tun->thresh1)
config = tun->VHF_L;
else if (freq < tun->thresh2)
config = tun->VHF_H;
else
config = tun->UHF;
 
 
/* tv norm specific stuff for multi-norm tuners */
switch (t->type) {
case TUNER_PHILIPS_SECAM: // FI1216MF
/* 0x01 -> ??? no change ??? */
/* 0x02 -> PAL BDGHI / SECAM L */
/* 0x04 -> ??? PAL others / SECAM others ??? */
config &= ~0x02;
if (t->mode == VIDEO_MODE_SECAM)
config |= 0x02;
break;
 
case TUNER_TEMIC_4046FM5:
config &= ~0x0f;
switch (pal[0]) {
case 'i':
case 'I':
config |= TEMIC_SET_PAL_I;
break;
case 'd':
case 'D':
config |= TEMIC_SET_PAL_DK;
break;
case 'l':
case 'L':
config |= TEMIC_SET_PAL_L;
break;
case 'b':
case 'B':
case 'g':
case 'G':
default:
config |= TEMIC_SET_PAL_BG;
break;
}
break;
 
case TUNER_PHILIPS_FQ1216ME:
config &= ~0x0f;
switch (pal[0]) {
case 'i':
case 'I':
config |= PHILIPS_SET_PAL_I;
break;
case 'l':
case 'L':
config |= PHILIPS_SET_PAL_L;
break;
case 'd':
case 'D':
case 'b':
case 'B':
case 'g':
case 'G':
config |= PHILIPS_SET_PAL_BGDK;
break;
}
break;
}
 
/*
* Philips FI1216MK2 remark from specification :
* for channel selection involving band switching, and to ensure
* smooth tuning to the desired channel without causing
* unnecessary charge pump action, it is recommended to consider
* the difference between wanted channel frequency and the
* current channel frequency. Unnecessary charge pump action
* will result in very low tuning voltage which may drive the
* oscillator to extreme conditions.
*
* Progfou: specification says to send config data before
* frequency in case (wanted frequency < current frequency).
*/
 
div=freq + tun->IFPCoff;
if (t->type == TUNER_PHILIPS_SECAM && freq < t->freq) {
buffer[0] = tun->config;
buffer[1] = config;
buffer[2] = (div>>8) & 0x7f;
buffer[3] = div & 0xff;
} else {
buffer[0] = (div>>8) & 0x7f;
buffer[1] = div & 0xff;
buffer[2] = tun->config;
buffer[3] = config;
}
dprintk("tuner: tv 0x%02x 0x%02x 0x%02x 0x%02x\n",
buffer[0],buffer[1],buffer[2],buffer[3]);
 
if (4 != (rc = i2c_master_send(c,buffer,4)))
printk("tuner: i2c i/o error: rc == %d (should be 4)\n",rc);
 
}
 
static void mt2032_set_radio_freq(struct i2c_client *c,int freq)
{
int if2;
 
if2=10700*1000; // 10.7MHz FM intermediate frequency
 
// per Manual for FM tuning: first if center freq. 1085 MHz
mt2032_set_if_freq(c,freq* 1000*1000/16, 1085*1000*1000,if2,if2,if2);
}
 
static void set_radio_freq(struct i2c_client *c, int freq)
{
struct tunertype *tun;
struct tuner *t = (struct tuner*)c->data;
unsigned char buffer[4];
int rc,div;
 
if (freq < radio_range[0]*16 || freq > radio_range[1]*16) {
printk("tuner: radio freq (%d.%02d) out of range (%d-%d)\n",
freq/16,freq%16*100/16,
radio_range[0],radio_range[1]);
return;
}
if (t->type == -1) {
printk("tuner: tuner type not set\n");
return;
}
 
if (t->type == TUNER_MT2032) {
mt2032_set_radio_freq(c,freq);
return;
}
 
tun=&tuners[t->type];
div = freq + (int)(16*10.7);
buffer[0] = (div>>8) & 0x7f;
buffer[1] = div & 0xff;
buffer[2] = tun->config;
switch (t->type) {
case TUNER_PHILIPS_FM1216ME_MK3:
buffer[3] = 0x19;
break;
default:
buffer[3] = 0xa4;
break;
}
 
dprintk("tuner: radio 0x%02x 0x%02x 0x%02x 0x%02x\n",
buffer[0],buffer[1],buffer[2],buffer[3]);
 
if (4 != (rc = i2c_master_send(c,buffer,4)))
printk("tuner: i2c i/o error: rc == %d (should be 4)\n",rc);
}
 
/* ---------------------------------------------------------------------- */
 
static int tuner_attach(struct i2c_adapter *adap, int addr,
unsigned short flags, int kind)
{
struct tuner *t;
struct i2c_client *client;
 
if (this_adap > 0)
return -1;
this_adap++;
client_template.adapter = adap;
client_template.addr = addr;
 
printk("tuner: chip found @ 0x%x\n", addr<<1);
 
if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)))
return -ENOMEM;
memcpy(client,&client_template,sizeof(struct i2c_client));
client->data = t = kmalloc(sizeof(struct tuner),GFP_KERNEL);
if (NULL == t) {
kfree(client);
return -ENOMEM;
}
memset(t,0,sizeof(struct tuner));
if (type >= 0 && type < TUNERS) {
t->type = type;
printk("tuner(bttv): type forced to %d (%s) [insmod]\n",t->type,tuners[t->type].name);
strncpy(client->name, tuners[t->type].name, sizeof(client->name));
} else {
t->type = -1;
}
i2c_attach_client(client);
if (t->type == TUNER_MT2032)
mt2032_init(client);
 
MOD_INC_USE_COUNT;
return 0;
}
 
static int tuner_probe(struct i2c_adapter *adap)
{
int rc;
 
if (0 != addr) {
normal_i2c_range[0] = addr;
normal_i2c_range[1] = addr;
}
this_adap = 0;
switch (adap->id) {
case I2C_ALGO_BIT | I2C_HW_B_BT848:
case I2C_ALGO_BIT | I2C_HW_B_RIVA:
case I2C_ALGO_SAA7134:
case I2C_ALGO_SAA7146:
printk("tuner: probing %s i2c adapter [id=0x%x]\n",
adap->name,adap->id);
rc = i2c_probe(adap, &addr_data, tuner_attach);
break;
default:
printk("tuner: ignoring %s i2c adapter [id=0x%x]\n",
adap->name,adap->id);
rc = 0;
/* nothing */
}
return rc;
}
 
static int tuner_detach(struct i2c_client *client)
{
struct tuner *t = (struct tuner*)client->data;
 
i2c_detach_client(client);
kfree(t);
kfree(client);
MOD_DEC_USE_COUNT;
return 0;
}
 
static int
tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
struct tuner *t = (struct tuner*)client->data;
int *iarg = (int*)arg;
#if 0
__u16 *sarg = (__u16*)arg;
#endif
 
switch (cmd) {
 
/* --- configuration --- */
case TUNER_SET_TYPE:
if (t->type != -1) {
printk("tuner: type already set (%d)\n",t->type);
return 0;
}
if (*iarg < 0 || *iarg >= TUNERS)
return 0;
t->type = *iarg;
printk("tuner: type set to %d (%s)\n",
t->type,tuners[t->type].name);
strncpy(client->name, tuners[t->type].name, sizeof(client->name));
if (t->type == TUNER_MT2032)
mt2032_init(client);
break;
case AUDC_SET_RADIO:
t->radio = 1;
break;
/* --- v4l ioctls --- */
/* take care: bttv does userspace copying, we'll get a
kernel pointer here... */
case VIDIOCSCHAN:
{
struct video_channel *vc = arg;
 
t->radio = 0;
t->mode = vc->norm;
if (t->freq)
set_tv_freq(client,t->freq);
return 0;
}
case VIDIOCSFREQ:
{
unsigned long *v = arg;
 
if (t->radio) {
dprintk("tuner: radio freq set to %d.%02d\n",
(*iarg)/16,(*iarg)%16*100/16);
set_radio_freq(client,*v);
} else {
dprintk("tuner: tv freq set to %d.%02d\n",
(*iarg)/16,(*iarg)%16*100/16);
set_tv_freq(client,*v);
}
t->freq = *v;
return 0;
}
case VIDIOCGTUNER:
{
struct video_tuner *vt = arg;
 
if (t->radio)
vt->signal = tuner_signal(client);
return 0;
}
case VIDIOCGAUDIO:
{
struct video_audio *va = arg;
if (t->radio)
va->mode = (tuner_stereo(client) ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO);
return 0;
}
#if 0
/* --- old, obsolete interface --- */
case TUNER_SET_TVFREQ:
dprintk("tuner: tv freq set to %d.%02d\n",
(*iarg)/16,(*iarg)%16*100/16);
set_tv_freq(client,*iarg);
t->radio = 0;
t->freq = *iarg;
break;
 
case TUNER_SET_RADIOFREQ:
dprintk("tuner: radio freq set to %d.%02d\n",
(*iarg)/16,(*iarg)%16*100/16);
set_radio_freq(client,*iarg);
t->radio = 1;
t->freq = *iarg;
break;
case TUNER_SET_MODE:
if (t->type != TUNER_PHILIPS_SECAM) {
dprintk("tuner: trying to change mode for other than TUNER_PHILIPS_SECAM\n");
} else {
int mode=(*sarg==VIDEO_MODE_SECAM)?1:0;
dprintk("tuner: mode set to %d\n", *sarg);
t->mode = mode;
set_tv_freq(client,t->freq);
}
break;
#endif
default:
/* nothing */
break;
}
return 0;
}
 
/* ----------------------------------------------------------------------- */
 
static struct i2c_driver driver = {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,54)
.owner = THIS_MODULE,
#endif
.name = "i2c TV tuner driver",
.id = I2C_DRIVERID_TUNER,
.flags = I2C_DF_NOTIFY,
.attach_adapter = tuner_probe,
.detach_client = tuner_detach,
.command = tuner_command,
};
static struct i2c_client client_template =
{
.name = "(tuner unset)",
.flags = I2C_CLIENT_ALLOW_USE,
.driver = &driver,
};
 
static int tuner_init_module(void)
{
i2c_add_driver(&driver);
return 0;
}
 
static void tuner_cleanup_module(void)
{
i2c_del_driver(&driver);
}
 
module_init(tuner_init_module);
module_exit(tuner_cleanup_module);
 
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* ---------------------------------------------------------------------------
* Local variables:
* c-basic-offset: 8
* End:
*/
/shark/trunk/drivers/makefile
1,5 → 1,5
 
dirs := $(filter-out CVS cvs makefile linuxc24, $(wildcard *))
dirs := $(filter-out CVS cvs bttv makefile linuxc24 linuxc26, $(wildcard *))
p_all := $(addprefix prefixall_, $(dirs))
p_install := $(addprefix prefixinstall_, $(dirs))
p_clean := $(addprefix prefixclean_, $(dirs))
/shark/trunk/drivers/i2c/makefile
10,7 → 10,7
 
OBJS_PATH = $(BASE)/drivers/i2c
 
OBJS = i2c-core.o
OBJS = i2c-core.o algos/i2c-algo-bit.o
 
OTHERINCL += -I$(BASE)/drivers/linuxc26/include