2008-07-25 12:46:10 +04:00
/*
bt8xx GPIO abuser
Copyright ( C ) 2008 Michael Buesch < mb @ bu3sch . de >
Please do _only_ contact the people listed _above_ with issues related to this driver .
All the other people listed below are not related to this driver . Their names
are only here , because this driver is derived from the bt848 driver .
Derived from the bt848 driver :
Copyright ( C ) 1996 , 97 , 98 Ralph Metzler
& Marcus Metzler
( c ) 1999 - 2002 Gerd Knorr
some v4l2 code lines are taken from Justin ' s bttv2 driver which is
( c ) 2000 Justin Schoeman
V4L1 removal from :
( c ) 2005 - 2006 Nickolay V . Shmyrev
Fixes to be fully V4L2 compliant by
( c ) 2006 Mauro Carvalho Chehab
Cropping and overscan support
Copyright ( C ) 2005 , 2006 Michael H . Schimek
Sponsored by OPQ Systems AB
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 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/spinlock.h>
2009-09-23 03:46:37 +04:00
# include <linux/gpio.h>
2008-07-25 12:46:10 +04:00
/* Steal the hardware definitions from the bttv driver. */
# include "../media/video/bt8xx/bt848.h"
# define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */
struct bt8xxgpio {
spinlock_t lock ;
void __iomem * mmio ;
struct pci_dev * pdev ;
struct gpio_chip gpio ;
# ifdef CONFIG_PM
u32 saved_outen ;
u32 saved_data ;
# endif
} ;
# define bgwrite(dat, adr) writel((dat), bg->mmio+(adr))
# define bgread(adr) readl(bg->mmio+(adr))
static int modparam_gpiobase = - 1 /* dynamic */ ;
module_param_named ( gpiobase , modparam_gpiobase , int , 0444 ) ;
MODULE_PARM_DESC ( gpiobase , " The GPIO number base. -1 means dynamic, which is the default. " ) ;
static int bt8xxgpio_gpio_direction_input ( struct gpio_chip * gpio , unsigned nr )
{
struct bt8xxgpio * bg = container_of ( gpio , struct bt8xxgpio , gpio ) ;
unsigned long flags ;
u32 outen , data ;
spin_lock_irqsave ( & bg - > lock , flags ) ;
data = bgread ( BT848_GPIO_DATA ) ;
data & = ~ ( 1 < < nr ) ;
bgwrite ( data , BT848_GPIO_DATA ) ;
outen = bgread ( BT848_GPIO_OUT_EN ) ;
outen & = ~ ( 1 < < nr ) ;
bgwrite ( outen , BT848_GPIO_OUT_EN ) ;
spin_unlock_irqrestore ( & bg - > lock , flags ) ;
return 0 ;
}
static int bt8xxgpio_gpio_get ( struct gpio_chip * gpio , unsigned nr )
{
struct bt8xxgpio * bg = container_of ( gpio , struct bt8xxgpio , gpio ) ;
unsigned long flags ;
u32 val ;
spin_lock_irqsave ( & bg - > lock , flags ) ;
val = bgread ( BT848_GPIO_DATA ) ;
spin_unlock_irqrestore ( & bg - > lock , flags ) ;
return ! ! ( val & ( 1 < < nr ) ) ;
}
static int bt8xxgpio_gpio_direction_output ( struct gpio_chip * gpio ,
unsigned nr , int val )
{
struct bt8xxgpio * bg = container_of ( gpio , struct bt8xxgpio , gpio ) ;
unsigned long flags ;
u32 outen , data ;
spin_lock_irqsave ( & bg - > lock , flags ) ;
outen = bgread ( BT848_GPIO_OUT_EN ) ;
outen | = ( 1 < < nr ) ;
bgwrite ( outen , BT848_GPIO_OUT_EN ) ;
data = bgread ( BT848_GPIO_DATA ) ;
if ( val )
data | = ( 1 < < nr ) ;
else
data & = ~ ( 1 < < nr ) ;
bgwrite ( data , BT848_GPIO_DATA ) ;
spin_unlock_irqrestore ( & bg - > lock , flags ) ;
return 0 ;
}
static void bt8xxgpio_gpio_set ( struct gpio_chip * gpio ,
unsigned nr , int val )
{
struct bt8xxgpio * bg = container_of ( gpio , struct bt8xxgpio , gpio ) ;
unsigned long flags ;
u32 data ;
spin_lock_irqsave ( & bg - > lock , flags ) ;
data = bgread ( BT848_GPIO_DATA ) ;
if ( val )
data | = ( 1 < < nr ) ;
else
data & = ~ ( 1 < < nr ) ;
bgwrite ( data , BT848_GPIO_DATA ) ;
spin_unlock_irqrestore ( & bg - > lock , flags ) ;
}
static void bt8xxgpio_gpio_setup ( struct bt8xxgpio * bg )
{
struct gpio_chip * c = & bg - > gpio ;
2009-03-25 02:38:23 +03:00
c - > label = dev_name ( & bg - > pdev - > dev ) ;
2008-07-25 12:46:10 +04:00
c - > owner = THIS_MODULE ;
c - > direction_input = bt8xxgpio_gpio_direction_input ;
c - > get = bt8xxgpio_gpio_get ;
c - > direction_output = bt8xxgpio_gpio_direction_output ;
c - > set = bt8xxgpio_gpio_set ;
c - > dbg_show = NULL ;
c - > base = modparam_gpiobase ;
c - > ngpio = BT8XXGPIO_NR_GPIOS ;
c - > can_sleep = 0 ;
}
static int bt8xxgpio_probe ( struct pci_dev * dev ,
const struct pci_device_id * pci_id )
{
struct bt8xxgpio * bg ;
int err ;
bg = kzalloc ( sizeof ( * bg ) , GFP_KERNEL ) ;
if ( ! bg )
return - ENOMEM ;
bg - > pdev = dev ;
spin_lock_init ( & bg - > lock ) ;
err = pci_enable_device ( dev ) ;
if ( err ) {
printk ( KERN_ERR " bt8xxgpio: Can't enable device. \n " ) ;
goto err_freebg ;
}
if ( ! request_mem_region ( pci_resource_start ( dev , 0 ) ,
pci_resource_len ( dev , 0 ) ,
" bt8xxgpio " ) ) {
printk ( KERN_WARNING " bt8xxgpio: Can't request iomem (0x%llx). \n " ,
( unsigned long long ) pci_resource_start ( dev , 0 ) ) ;
err = - EBUSY ;
goto err_disable ;
}
pci_set_master ( dev ) ;
pci_set_drvdata ( dev , bg ) ;
bg - > mmio = ioremap ( pci_resource_start ( dev , 0 ) , 0x1000 ) ;
if ( ! bg - > mmio ) {
printk ( KERN_ERR " bt8xxgpio: ioremap() failed \n " ) ;
err = - EIO ;
goto err_release_mem ;
}
/* Disable interrupts */
bgwrite ( 0 , BT848_INT_MASK ) ;
/* gpio init */
bgwrite ( 0 , BT848_GPIO_DMA_CTL ) ;
bgwrite ( 0 , BT848_GPIO_REG_INP ) ;
bgwrite ( 0 , BT848_GPIO_OUT_EN ) ;
bt8xxgpio_gpio_setup ( bg ) ;
err = gpiochip_add ( & bg - > gpio ) ;
if ( err ) {
printk ( KERN_ERR " bt8xxgpio: Failed to register GPIOs \n " ) ;
goto err_release_mem ;
}
printk ( KERN_INFO " bt8xxgpio: Abusing BT8xx card for GPIOs %d to %d \n " ,
bg - > gpio . base , bg - > gpio . base + BT8XXGPIO_NR_GPIOS - 1 ) ;
return 0 ;
err_release_mem :
release_mem_region ( pci_resource_start ( dev , 0 ) ,
pci_resource_len ( dev , 0 ) ) ;
pci_set_drvdata ( dev , NULL ) ;
err_disable :
pci_disable_device ( dev ) ;
err_freebg :
kfree ( bg ) ;
return err ;
}
static void bt8xxgpio_remove ( struct pci_dev * pdev )
{
struct bt8xxgpio * bg = pci_get_drvdata ( pdev ) ;
gpiochip_remove ( & bg - > gpio ) ;
bgwrite ( 0 , BT848_INT_MASK ) ;
bgwrite ( ~ 0x0 , BT848_INT_STAT ) ;
bgwrite ( 0x0 , BT848_GPIO_OUT_EN ) ;
iounmap ( bg - > mmio ) ;
release_mem_region ( pci_resource_start ( pdev , 0 ) ,
pci_resource_len ( pdev , 0 ) ) ;
pci_disable_device ( pdev ) ;
pci_set_drvdata ( pdev , NULL ) ;
kfree ( bg ) ;
}
# ifdef CONFIG_PM
static int bt8xxgpio_suspend ( struct pci_dev * pdev , pm_message_t state )
{
struct bt8xxgpio * bg = pci_get_drvdata ( pdev ) ;
unsigned long flags ;
spin_lock_irqsave ( & bg - > lock , flags ) ;
bg - > saved_outen = bgread ( BT848_GPIO_OUT_EN ) ;
bg - > saved_data = bgread ( BT848_GPIO_DATA ) ;
bgwrite ( 0 , BT848_INT_MASK ) ;
bgwrite ( ~ 0x0 , BT848_INT_STAT ) ;
bgwrite ( 0x0 , BT848_GPIO_OUT_EN ) ;
spin_unlock_irqrestore ( & bg - > lock , flags ) ;
pci_save_state ( pdev ) ;
pci_disable_device ( pdev ) ;
pci_set_power_state ( pdev , pci_choose_state ( pdev , state ) ) ;
return 0 ;
}
static int bt8xxgpio_resume ( struct pci_dev * pdev )
{
struct bt8xxgpio * bg = pci_get_drvdata ( pdev ) ;
unsigned long flags ;
int err ;
pci_set_power_state ( pdev , 0 ) ;
err = pci_enable_device ( pdev ) ;
if ( err )
return err ;
pci_restore_state ( pdev ) ;
spin_lock_irqsave ( & bg - > lock , flags ) ;
bgwrite ( 0 , BT848_INT_MASK ) ;
bgwrite ( 0 , BT848_GPIO_DMA_CTL ) ;
bgwrite ( 0 , BT848_GPIO_REG_INP ) ;
bgwrite ( bg - > saved_outen , BT848_GPIO_OUT_EN ) ;
bgwrite ( bg - > saved_data & bg - > saved_outen ,
BT848_GPIO_DATA ) ;
spin_unlock_irqrestore ( & bg - > lock , flags ) ;
return 0 ;
}
# else
# define bt8xxgpio_suspend NULL
# define bt8xxgpio_resume NULL
# endif /* CONFIG_PM */
static struct pci_device_id bt8xxgpio_pci_tbl [ ] = {
{ PCI_DEVICE ( PCI_VENDOR_ID_BROOKTREE , PCI_DEVICE_ID_BT848 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_BROOKTREE , PCI_DEVICE_ID_BT849 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_BROOKTREE , PCI_DEVICE_ID_BT878 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_BROOKTREE , PCI_DEVICE_ID_BT879 ) } ,
{ 0 , } ,
} ;
MODULE_DEVICE_TABLE ( pci , bt8xxgpio_pci_tbl ) ;
static struct pci_driver bt8xxgpio_pci_driver = {
. name = " bt8xxgpio " ,
. id_table = bt8xxgpio_pci_tbl ,
. probe = bt8xxgpio_probe ,
. remove = bt8xxgpio_remove ,
. suspend = bt8xxgpio_suspend ,
. resume = bt8xxgpio_resume ,
} ;
2009-08-23 04:01:10 +04:00
static int __init bt8xxgpio_init ( void )
2008-07-25 12:46:10 +04:00
{
return pci_register_driver ( & bt8xxgpio_pci_driver ) ;
}
module_init ( bt8xxgpio_init )
2009-08-23 04:01:10 +04:00
static void __exit bt8xxgpio_exit ( void )
2008-07-25 12:46:10 +04:00
{
pci_unregister_driver ( & bt8xxgpio_pci_driver ) ;
}
module_exit ( bt8xxgpio_exit )
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Michael Buesch " ) ;
MODULE_DESCRIPTION ( " Abuse a BT8xx framegrabber card as generic GPIO card " ) ;