2008-04-28 02:15:38 -07:00
/*
* linux / drivers / video / am200epd . c - - Platform device for AM200 EPD kit
*
* Copyright ( C ) 2008 , Jaya Kumar
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file COPYING in the main directory of this archive for
* more details .
*
* Layout is based on skeletonfb . c by James Simmons and Geert Uytterhoeven .
*
* This work was made possible by help and equipment support from E - Ink
* Corporation . http : //support.eink.com/community
*
* This driver is written to be used with the Metronome display controller .
* on the AM200 EPD prototype kit / development kit with an E - Ink 800 x600
* Vizplex EPD on a Gumstix board using the Lyre interface board .
*
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/fb.h>
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/list.h>
# include <linux/uaccess.h>
# include <linux/irq.h>
# include <video/metronomefb.h>
2008-08-05 16:14:15 +01:00
# include <mach/pxa-regs.h>
2008-04-28 02:15:38 -07:00
/* register offsets for gpio control */
# define LED_GPIO_PIN 51
# define STDBY_GPIO_PIN 48
# define RST_GPIO_PIN 49
# define RDY_GPIO_PIN 32
# define ERR_GPIO_PIN 17
# define PCBPWR_GPIO_PIN 16
# define AF_SEL_GPIO_N 0x3
# define GAFR0_U_OFFSET(pin) ((pin - 16) * 2)
# define GAFR1_L_OFFSET(pin) ((pin - 32) * 2)
# define GAFR1_U_OFFSET(pin) ((pin - 48) * 2)
# define GPDR1_OFFSET(pin) (pin - 32)
# define GPCR1_OFFSET(pin) (pin - 32)
# define GPSR1_OFFSET(pin) (pin - 32)
# define GPCR0_OFFSET(pin) (pin)
# define GPSR0_OFFSET(pin) (pin)
static void am200_set_gpio_output ( int pin , int val )
{
u8 index ;
index = pin > > 4 ;
switch ( index ) {
case 1 :
if ( val )
GPSR0 | = ( 1 < < GPSR0_OFFSET ( pin ) ) ;
else
GPCR0 | = ( 1 < < GPCR0_OFFSET ( pin ) ) ;
break ;
case 2 :
break ;
case 3 :
if ( val )
GPSR1 | = ( 1 < < GPSR1_OFFSET ( pin ) ) ;
else
GPCR1 | = ( 1 < < GPCR1_OFFSET ( pin ) ) ;
break ;
default :
printk ( KERN_ERR " unimplemented \n " ) ;
}
}
static void __devinit am200_init_gpio_pin ( int pin , int dir )
{
u8 index ;
/* dir 0 is output, 1 is input
- do 2 things here :
- set gpio alternate function to standard gpio
- set gpio direction to input or output */
index = pin > > 4 ;
switch ( index ) {
case 1 :
GAFR0_U & = ~ ( AF_SEL_GPIO_N < < GAFR0_U_OFFSET ( pin ) ) ;
if ( dir )
GPDR0 & = ~ ( 1 < < pin ) ;
else
GPDR0 | = ( 1 < < pin ) ;
break ;
case 2 :
GAFR1_L & = ~ ( AF_SEL_GPIO_N < < GAFR1_L_OFFSET ( pin ) ) ;
if ( dir )
GPDR1 & = ~ ( 1 < < GPDR1_OFFSET ( pin ) ) ;
else
GPDR1 | = ( 1 < < GPDR1_OFFSET ( pin ) ) ;
break ;
case 3 :
GAFR1_U & = ~ ( AF_SEL_GPIO_N < < GAFR1_U_OFFSET ( pin ) ) ;
if ( dir )
GPDR1 & = ~ ( 1 < < GPDR1_OFFSET ( pin ) ) ;
else
GPDR1 | = ( 1 < < GPDR1_OFFSET ( pin ) ) ;
break ;
default :
printk ( KERN_ERR " unimplemented \n " ) ;
}
}
static void am200_init_gpio_regs ( struct metronomefb_par * par )
{
am200_init_gpio_pin ( LED_GPIO_PIN , 0 ) ;
am200_set_gpio_output ( LED_GPIO_PIN , 0 ) ;
am200_init_gpio_pin ( STDBY_GPIO_PIN , 0 ) ;
am200_set_gpio_output ( STDBY_GPIO_PIN , 0 ) ;
am200_init_gpio_pin ( RST_GPIO_PIN , 0 ) ;
am200_set_gpio_output ( RST_GPIO_PIN , 0 ) ;
am200_init_gpio_pin ( RDY_GPIO_PIN , 1 ) ;
am200_init_gpio_pin ( ERR_GPIO_PIN , 1 ) ;
am200_init_gpio_pin ( PCBPWR_GPIO_PIN , 0 ) ;
am200_set_gpio_output ( PCBPWR_GPIO_PIN , 0 ) ;
}
static void am200_disable_lcd_controller ( struct metronomefb_par * par )
{
LCSR = 0xffffffff ; /* Clear LCD Status Register */
LCCR0 | = LCCR0_DIS ; /* Disable LCD Controller */
/* we reset and just wait for things to settle */
msleep ( 200 ) ;
}
static void am200_enable_lcd_controller ( struct metronomefb_par * par )
{
LCSR = 0xffffffff ;
FDADR0 = par - > metromem_desc_dma ;
LCCR0 | = LCCR0_ENB ;
}
static void am200_init_lcdc_regs ( struct metronomefb_par * par )
{
/* here we do:
- disable the lcd controller
- setup lcd control registers
- setup dma descriptor
- reenable lcd controller
*/
/* disable the lcd controller */
am200_disable_lcd_controller ( par ) ;
/* setup lcd control registers */
LCCR0 = LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_PAS
| LCCR0_QDM | LCCR0_BM | LCCR0_OUM ;
LCCR1 = ( par - > info - > var . xres / 2 - 1 ) /* pixels per line */
| ( 27 < < 10 ) /* hsync pulse width - 1 */
| ( 33 < < 16 ) /* eol pixel count */
| ( 33 < < 24 ) ; /* bol pixel count */
LCCR2 = ( par - > info - > var . yres - 1 ) /* lines per panel */
| ( 24 < < 10 ) /* vsync pulse width - 1 */
| ( 2 < < 16 ) /* eof pixel count */
| ( 0 < < 24 ) ; /* bof pixel count */
LCCR3 = 2 /* pixel clock divisor */
| ( 24 < < 8 ) /* AC Bias pin freq */
| LCCR3_16BPP /* BPP */
| LCCR3_PCP ; /* PCP falling edge */
}
static void am200_post_dma_setup ( struct metronomefb_par * par )
{
par - > metromem_desc - > mFDADR0 = par - > metromem_desc_dma ;
par - > metromem_desc - > mFSADR0 = par - > metromem_dma ;
par - > metromem_desc - > mFIDR0 = 0 ;
par - > metromem_desc - > mLDCMD0 = par - > info - > var . xres
* par - > info - > var . yres ;
am200_enable_lcd_controller ( par ) ;
}
static void am200_free_irq ( struct fb_info * info )
{
free_irq ( IRQ_GPIO ( RDY_GPIO_PIN ) , info ) ;
}
static irqreturn_t am200_handle_irq ( int irq , void * dev_id )
{
struct fb_info * info = dev_id ;
struct metronomefb_par * par = info - > par ;
wake_up_interruptible ( & par - > waitq ) ;
return IRQ_HANDLED ;
}
static int am200_setup_irq ( struct fb_info * info )
{
int retval ;
retval = request_irq ( IRQ_GPIO ( RDY_GPIO_PIN ) , am200_handle_irq ,
IRQF_DISABLED , " AM200 " , info ) ;
if ( retval ) {
printk ( KERN_ERR " am200epd: request_irq failed: %d \n " , retval ) ;
return retval ;
}
2008-07-27 04:23:31 +01:00
return set_irq_type ( IRQ_GPIO ( RDY_GPIO_PIN ) , IRQ_TYPE_EDGE_FALLING ) ;
2008-04-28 02:15:38 -07:00
}
static void am200_set_rst ( struct metronomefb_par * par , int state )
{
am200_set_gpio_output ( RST_GPIO_PIN , state ) ;
}
static void am200_set_stdby ( struct metronomefb_par * par , int state )
{
am200_set_gpio_output ( STDBY_GPIO_PIN , state ) ;
}
static int am200_wait_event ( struct metronomefb_par * par )
{
return wait_event_timeout ( par - > waitq , ( GPLR1 & 0x01 ) , HZ ) ;
}
static int am200_wait_event_intr ( struct metronomefb_par * par )
{
return wait_event_interruptible_timeout ( par - > waitq , ( GPLR1 & 0x01 ) , HZ ) ;
}
static struct metronome_board am200_board = {
. owner = THIS_MODULE ,
. free_irq = am200_free_irq ,
. setup_irq = am200_setup_irq ,
. init_gpio_regs = am200_init_gpio_regs ,
. init_lcdc_regs = am200_init_lcdc_regs ,
. post_dma_setup = am200_post_dma_setup ,
. set_rst = am200_set_rst ,
. set_stdby = am200_set_stdby ,
. met_wait_event = am200_wait_event ,
. met_wait_event_intr = am200_wait_event_intr ,
} ;
static struct platform_device * am200_device ;
static int __init am200_init ( void )
{
int ret ;
/* request our platform independent driver */
request_module ( " metronomefb " ) ;
am200_device = platform_device_alloc ( " metronomefb " , - 1 ) ;
if ( ! am200_device )
return - ENOMEM ;
platform_device_add_data ( am200_device , & am200_board ,
sizeof ( am200_board ) ) ;
/* this _add binds metronomefb to am200. metronomefb refcounts am200 */
ret = platform_device_add ( am200_device ) ;
if ( ret )
platform_device_put ( am200_device ) ;
return ret ;
}
static void __exit am200_exit ( void )
{
platform_device_unregister ( am200_device ) ;
}
module_init ( am200_init ) ;
module_exit ( am200_exit ) ;
MODULE_DESCRIPTION ( " board driver for am200 metronome epd kit " ) ;
MODULE_AUTHOR ( " Jaya Kumar " ) ;
MODULE_LICENSE ( " GPL " ) ;