2009-09-22 16:47:03 -07:00
/*
* MSM MDDI Transport
*
* Copyright ( C ) 2007 Google Incorporated
* Copyright ( C ) 2007 QUALCOMM Incorporated
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* 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 .
*
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/dma-mapping.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
# include <linux/delay.h>
# include <linux/spinlock.h>
# include <linux/clk.h>
# include <linux/io.h>
2009-11-11 14:26:51 -08:00
# include <linux/sched.h>
2009-09-22 16:47:03 -07:00
# include <mach/msm_iomap.h>
# include <mach/irqs.h>
# include <mach/board.h>
# include <mach/msm_fb.h>
# include "mddi_hw.h"
# define FLAG_DISABLE_HIBERNATION 0x0001
# define FLAG_HAVE_CAPS 0x0002
# define FLAG_HAS_VSYNC_IRQ 0x0004
# define FLAG_HAVE_STATUS 0x0008
# define CMD_GET_CLIENT_CAP 0x0601
# define CMD_GET_CLIENT_STATUS 0x0602
union mddi_rev {
unsigned char raw [ MDDI_REV_BUFFER_SIZE ] ;
struct mddi_rev_packet hdr ;
struct mddi_client_status status ;
struct mddi_client_caps caps ;
struct mddi_register_access reg ;
} ;
struct reg_read_info {
struct completion done ;
uint32_t reg ;
uint32_t status ;
uint32_t result ;
} ;
struct mddi_info {
uint16_t flags ;
uint16_t version ;
char __iomem * base ;
int irq ;
struct clk * clk ;
struct msm_mddi_client_data client_data ;
/* buffer for rev encap packets */
void * rev_data ;
dma_addr_t rev_addr ;
struct mddi_llentry * reg_write_data ;
dma_addr_t reg_write_addr ;
struct mddi_llentry * reg_read_data ;
dma_addr_t reg_read_addr ;
size_t rev_data_curr ;
spinlock_t int_lock ;
uint32_t int_enable ;
uint32_t got_int ;
wait_queue_head_t int_wait ;
struct mutex reg_write_lock ;
struct mutex reg_read_lock ;
struct reg_read_info * reg_read ;
struct mddi_client_caps caps ;
struct mddi_client_status status ;
void ( * power_client ) ( struct msm_mddi_client_data * , int ) ;
/* client device published to bind us to the
* appropriate mddi_client driver
*/
char client_name [ 20 ] ;
struct platform_device client_pdev ;
} ;
static void mddi_init_rev_encap ( struct mddi_info * mddi ) ;
# define mddi_readl(r) readl(mddi->base + (MDDI_##r))
# define mddi_writel(v, r) writel((v), mddi->base + (MDDI_##r))
void mddi_activate_link ( struct msm_mddi_client_data * cdata )
{
struct mddi_info * mddi = container_of ( cdata , struct mddi_info ,
client_data ) ;
mddi_writel ( MDDI_CMD_LINK_ACTIVE , CMD ) ;
}
static void mddi_handle_link_list_done ( struct mddi_info * mddi )
{
}
static void mddi_reset_rev_encap_ptr ( struct mddi_info * mddi )
{
printk ( KERN_INFO " mddi: resetting rev ptr \n " ) ;
mddi - > rev_data_curr = 0 ;
mddi_writel ( mddi - > rev_addr , REV_PTR ) ;
mddi_writel ( mddi - > rev_addr , REV_PTR ) ;
mddi_writel ( MDDI_CMD_FORCE_NEW_REV_PTR , CMD ) ;
}
static void mddi_handle_rev_data ( struct mddi_info * mddi , union mddi_rev * rev )
{
int i ;
struct reg_read_info * ri ;
if ( ( rev - > hdr . length < = MDDI_REV_BUFFER_SIZE - 2 ) & &
( rev - > hdr . length > = sizeof ( struct mddi_rev_packet ) - 2 ) ) {
switch ( rev - > hdr . type ) {
case TYPE_CLIENT_CAPS :
memcpy ( & mddi - > caps , & rev - > caps ,
sizeof ( struct mddi_client_caps ) ) ;
mddi - > flags | = FLAG_HAVE_CAPS ;
wake_up ( & mddi - > int_wait ) ;
break ;
case TYPE_CLIENT_STATUS :
memcpy ( & mddi - > status , & rev - > status ,
sizeof ( struct mddi_client_status ) ) ;
mddi - > flags | = FLAG_HAVE_STATUS ;
wake_up ( & mddi - > int_wait ) ;
break ;
case TYPE_REGISTER_ACCESS :
ri = mddi - > reg_read ;
if ( ri = = 0 ) {
printk ( KERN_INFO " rev: got reg %x = %x without "
" pending read \n " ,
rev - > reg . register_address ,
rev - > reg . register_data_list ) ;
break ;
}
if ( ri - > reg ! = rev - > reg . register_address ) {
printk ( KERN_INFO " rev: got reg %x = %x for "
" wrong register, expected "
" %x \n " ,
rev - > reg . register_address ,
rev - > reg . register_data_list , ri - > reg ) ;
break ;
}
mddi - > reg_read = NULL ;
ri - > status = 0 ;
ri - > result = rev - > reg . register_data_list ;
complete ( & ri - > done ) ;
break ;
default :
printk ( KERN_INFO " rev: unknown reverse packet: "
" len=%04x type=%04x CURR_REV_PTR=%x \n " ,
rev - > hdr . length , rev - > hdr . type ,
mddi_readl ( CURR_REV_PTR ) ) ;
for ( i = 0 ; i < rev - > hdr . length + 2 ; i + + ) {
if ( ( i % 16 ) = = 0 )
printk ( KERN_INFO " \n " ) ;
printk ( KERN_INFO " %02x " , rev - > raw [ i ] ) ;
}
printk ( KERN_INFO " \n " ) ;
mddi_reset_rev_encap_ptr ( mddi ) ;
}
} else {
printk ( KERN_INFO " bad rev length, %d, CURR_REV_PTR %x \n " ,
rev - > hdr . length , mddi_readl ( CURR_REV_PTR ) ) ;
mddi_reset_rev_encap_ptr ( mddi ) ;
}
}
static void mddi_wait_interrupt ( struct mddi_info * mddi , uint32_t intmask ) ;
static void mddi_handle_rev_data_avail ( struct mddi_info * mddi )
{
union mddi_rev * rev = mddi - > rev_data ;
uint32_t rev_data_count ;
uint32_t rev_crc_err_count ;
int i ;
struct reg_read_info * ri ;
size_t prev_offset ;
uint16_t length ;
union mddi_rev * crev = mddi - > rev_data + mddi - > rev_data_curr ;
/* clear the interrupt */
mddi_writel ( MDDI_INT_REV_DATA_AVAIL , INT ) ;
rev_data_count = mddi_readl ( REV_PKT_CNT ) ;
rev_crc_err_count = mddi_readl ( REV_CRC_ERR ) ;
if ( rev_data_count > 1 )
printk ( KERN_INFO " rev_data_count %d \n " , rev_data_count ) ;
if ( rev_crc_err_count ) {
printk ( KERN_INFO " rev_crc_err_count %d, INT %x \n " ,
rev_crc_err_count , mddi_readl ( INT ) ) ;
ri = mddi - > reg_read ;
if ( ri = = 0 ) {
printk ( KERN_INFO " rev: got crc error without pending "
" read \n " ) ;
} else {
mddi - > reg_read = NULL ;
ri - > status = - EIO ;
ri - > result = - 1 ;
complete ( & ri - > done ) ;
}
}
if ( rev_data_count = = 0 )
return ;
prev_offset = mddi - > rev_data_curr ;
length = * ( ( uint8_t * ) mddi - > rev_data + mddi - > rev_data_curr ) ;
mddi - > rev_data_curr + + ;
if ( mddi - > rev_data_curr = = MDDI_REV_BUFFER_SIZE )
mddi - > rev_data_curr = 0 ;
length + = * ( ( uint8_t * ) mddi - > rev_data + mddi - > rev_data_curr ) < < 8 ;
mddi - > rev_data_curr + = 1 + length ;
if ( mddi - > rev_data_curr > = MDDI_REV_BUFFER_SIZE )
mddi - > rev_data_curr =
mddi - > rev_data_curr % MDDI_REV_BUFFER_SIZE ;
if ( length > MDDI_REV_BUFFER_SIZE - 2 ) {
printk ( KERN_INFO " mddi: rev data length greater than buffer "
" size \n " ) ;
mddi_reset_rev_encap_ptr ( mddi ) ;
return ;
}
if ( prev_offset + 2 + length > = MDDI_REV_BUFFER_SIZE ) {
union mddi_rev tmprev ;
size_t rem = MDDI_REV_BUFFER_SIZE - prev_offset ;
memcpy ( & tmprev . raw [ 0 ] , mddi - > rev_data + prev_offset , rem ) ;
memcpy ( & tmprev . raw [ rem ] , mddi - > rev_data , 2 + length - rem ) ;
mddi_handle_rev_data ( mddi , & tmprev ) ;
} else {
mddi_handle_rev_data ( mddi , crev ) ;
}
if ( prev_offset < MDDI_REV_BUFFER_SIZE / 2 & &
mddi - > rev_data_curr > = MDDI_REV_BUFFER_SIZE / 2 ) {
mddi_writel ( mddi - > rev_addr , REV_PTR ) ;
}
}
static irqreturn_t mddi_isr ( int irq , void * data )
{
struct msm_mddi_client_data * cdata = data ;
struct mddi_info * mddi = container_of ( cdata , struct mddi_info ,
client_data ) ;
uint32_t active , status ;
spin_lock ( & mddi - > int_lock ) ;
active = mddi_readl ( INT ) ;
status = mddi_readl ( STAT ) ;
mddi_writel ( active , INT ) ;
/* ignore any interrupts we have disabled */
active & = mddi - > int_enable ;
mddi - > got_int | = active ;
wake_up ( & mddi - > int_wait ) ;
if ( active & MDDI_INT_PRI_LINK_LIST_DONE ) {
mddi - > int_enable & = ( ~ MDDI_INT_PRI_LINK_LIST_DONE ) ;
mddi_handle_link_list_done ( mddi ) ;
}
if ( active & MDDI_INT_REV_DATA_AVAIL )
mddi_handle_rev_data_avail ( mddi ) ;
if ( active & ~ MDDI_INT_NEED_CLEAR )
mddi - > int_enable & = ~ ( active & ~ MDDI_INT_NEED_CLEAR ) ;
if ( active & MDDI_INT_LINK_ACTIVE ) {
mddi - > int_enable & = ( ~ MDDI_INT_LINK_ACTIVE ) ;
mddi - > int_enable | = MDDI_INT_IN_HIBERNATION ;
}
if ( active & MDDI_INT_IN_HIBERNATION ) {
mddi - > int_enable & = ( ~ MDDI_INT_IN_HIBERNATION ) ;
mddi - > int_enable | = MDDI_INT_LINK_ACTIVE ;
}
mddi_writel ( mddi - > int_enable , INTEN ) ;
spin_unlock ( & mddi - > int_lock ) ;
return IRQ_HANDLED ;
}
static long mddi_wait_interrupt_timeout ( struct mddi_info * mddi ,
uint32_t intmask , int timeout )
{
unsigned long irq_flags ;
spin_lock_irqsave ( & mddi - > int_lock , irq_flags ) ;
mddi - > got_int & = ~ intmask ;
mddi - > int_enable | = intmask ;
mddi_writel ( mddi - > int_enable , INTEN ) ;
spin_unlock_irqrestore ( & mddi - > int_lock , irq_flags ) ;
return wait_event_timeout ( mddi - > int_wait , mddi - > got_int & intmask ,
timeout ) ;
}
static void mddi_wait_interrupt ( struct mddi_info * mddi , uint32_t intmask )
{
if ( mddi_wait_interrupt_timeout ( mddi , intmask , HZ / 10 ) = = 0 )
printk ( KERN_INFO KERN_ERR " mddi_wait_interrupt %d, timeout "
" waiting for %x, INT = %x, STAT = %x gotint = %x \n " ,
current - > pid , intmask , mddi_readl ( INT ) , mddi_readl ( STAT ) ,
mddi - > got_int ) ;
}
static void mddi_init_rev_encap ( struct mddi_info * mddi )
{
memset ( mddi - > rev_data , 0xee , MDDI_REV_BUFFER_SIZE ) ;
mddi_writel ( mddi - > rev_addr , REV_PTR ) ;
mddi_writel ( MDDI_CMD_FORCE_NEW_REV_PTR , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
}
void mddi_set_auto_hibernate ( struct msm_mddi_client_data * cdata , int on )
{
struct mddi_info * mddi = container_of ( cdata , struct mddi_info ,
client_data ) ;
mddi_writel ( MDDI_CMD_POWERDOWN , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_IN_HIBERNATION ) ;
mddi_writel ( MDDI_CMD_HIBERNATE | ! ! on , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
}
static uint16_t mddi_init_registers ( struct mddi_info * mddi )
{
mddi_writel ( 0x0001 , VERSION ) ;
mddi_writel ( MDDI_HOST_BYTES_PER_SUBFRAME , BPS ) ;
mddi_writel ( 0x0003 , SPM ) ; /* subframes per media */
mddi_writel ( 0x0005 , TA1_LEN ) ;
mddi_writel ( MDDI_HOST_TA2_LEN , TA2_LEN ) ;
mddi_writel ( 0x0096 , DRIVE_HI ) ;
/* 0x32 normal, 0x50 for Toshiba display */
mddi_writel ( 0x0050 , DRIVE_LO ) ;
mddi_writel ( 0x003C , DISP_WAKE ) ; /* wakeup counter */
mddi_writel ( MDDI_HOST_REV_RATE_DIV , REV_RATE_DIV ) ;
mddi_writel ( MDDI_REV_BUFFER_SIZE , REV_SIZE ) ;
mddi_writel ( MDDI_MAX_REV_PKT_SIZE , REV_ENCAP_SZ ) ;
/* disable periodic rev encap */
mddi_writel ( MDDI_CMD_PERIODIC_REV_ENCAP , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
if ( mddi_readl ( PAD_CTL ) = = 0 ) {
/* If we are turning on band gap, need to wait 5us before
* turning on the rest of the PAD */
mddi_writel ( 0x08000 , PAD_CTL ) ;
udelay ( 5 ) ;
}
/* Recommendation from PAD hw team */
mddi_writel ( 0xa850f , PAD_CTL ) ;
/* Need an even number for counts */
mddi_writel ( 0x60006 , DRIVER_START_CNT ) ;
mddi_set_auto_hibernate ( & mddi - > client_data , 0 ) ;
mddi_writel ( MDDI_CMD_DISP_IGNORE , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
mddi_init_rev_encap ( mddi ) ;
return mddi_readl ( CORE_VER ) & 0xffff ;
}
static void mddi_suspend ( struct msm_mddi_client_data * cdata )
{
struct mddi_info * mddi = container_of ( cdata , struct mddi_info ,
client_data ) ;
/* turn off the client */
if ( mddi - > power_client )
mddi - > power_client ( & mddi - > client_data , 0 ) ;
/* turn off the link */
mddi_writel ( MDDI_CMD_RESET , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
/* turn off the clock */
clk_disable ( mddi - > clk ) ;
}
static void mddi_resume ( struct msm_mddi_client_data * cdata )
{
struct mddi_info * mddi = container_of ( cdata , struct mddi_info ,
client_data ) ;
mddi_set_auto_hibernate ( & mddi - > client_data , 0 ) ;
/* turn on the client */
if ( mddi - > power_client )
mddi - > power_client ( & mddi - > client_data , 1 ) ;
/* turn on the clock */
clk_enable ( mddi - > clk ) ;
/* set up the local registers */
mddi - > rev_data_curr = 0 ;
mddi_init_registers ( mddi ) ;
mddi_writel ( mddi - > int_enable , INTEN ) ;
mddi_writel ( MDDI_CMD_LINK_ACTIVE , CMD ) ;
mddi_writel ( MDDI_CMD_SEND_RTD , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
mddi_set_auto_hibernate ( & mddi - > client_data , 1 ) ;
}
static int __init mddi_get_client_caps ( struct mddi_info * mddi )
{
int i , j ;
/* clear any stale interrupts */
mddi_writel ( 0xffffffff , INT ) ;
mddi - > int_enable = MDDI_INT_LINK_ACTIVE |
MDDI_INT_IN_HIBERNATION |
MDDI_INT_PRI_LINK_LIST_DONE |
MDDI_INT_REV_DATA_AVAIL |
MDDI_INT_REV_OVERFLOW |
MDDI_INT_REV_OVERWRITE |
MDDI_INT_RTD_FAILURE ;
mddi_writel ( mddi - > int_enable , INTEN ) ;
mddi_writel ( MDDI_CMD_LINK_ACTIVE , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
for ( j = 0 ; j < 3 ; j + + ) {
/* the toshiba vga panel does not respond to get
* caps unless you SEND_RTD , but the first SEND_RTD
* will fail . . .
*/
for ( i = 0 ; i < 4 ; i + + ) {
uint32_t stat ;
mddi_writel ( MDDI_CMD_SEND_RTD , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
stat = mddi_readl ( STAT ) ;
printk ( KERN_INFO " mddi cmd send rtd: int %x, stat %x, "
" rtd val %x \n " , mddi_readl ( INT ) , stat ,
mddi_readl ( RTD_VAL ) ) ;
if ( ( stat & MDDI_STAT_RTD_MEAS_FAIL ) = = 0 )
break ;
msleep ( 1 ) ;
}
mddi_writel ( CMD_GET_CLIENT_CAP , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
wait_event_timeout ( mddi - > int_wait , mddi - > flags & FLAG_HAVE_CAPS ,
HZ / 100 ) ;
if ( mddi - > flags & FLAG_HAVE_CAPS )
break ;
printk ( KERN_INFO KERN_ERR " mddi_init, timeout waiting for "
" caps \n " ) ;
}
return mddi - > flags & FLAG_HAVE_CAPS ;
}
/* link must be active when this is called */
int mddi_check_status ( struct mddi_info * mddi )
{
int ret = - 1 , retry = 3 ;
mutex_lock ( & mddi - > reg_read_lock ) ;
mddi_writel ( MDDI_CMD_PERIODIC_REV_ENCAP | 1 , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
do {
mddi - > flags & = ~ FLAG_HAVE_STATUS ;
mddi_writel ( CMD_GET_CLIENT_STATUS , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
wait_event_timeout ( mddi - > int_wait ,
mddi - > flags & FLAG_HAVE_STATUS ,
HZ / 100 ) ;
if ( mddi - > flags & FLAG_HAVE_STATUS ) {
if ( mddi - > status . crc_error_count )
printk ( KERN_INFO " mddi status: crc_error "
" count: %d \n " ,
mddi - > status . crc_error_count ) ;
else
ret = 0 ;
break ;
} else
printk ( KERN_INFO " mddi status: failed to get client "
" status \n " ) ;
mddi_writel ( MDDI_CMD_SEND_RTD , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
} while ( - - retry ) ;
mddi_writel ( MDDI_CMD_PERIODIC_REV_ENCAP | 0 , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
mutex_unlock ( & mddi - > reg_read_lock ) ;
return ret ;
}
void mddi_remote_write ( struct msm_mddi_client_data * cdata , uint32_t val ,
uint32_t reg )
{
struct mddi_info * mddi = container_of ( cdata , struct mddi_info ,
client_data ) ;
struct mddi_llentry * ll ;
struct mddi_register_access * ra ;
mutex_lock ( & mddi - > reg_write_lock ) ;
ll = mddi - > reg_write_data ;
ra = & ( ll - > u . r ) ;
ra - > length = 14 + 4 ;
ra - > type = TYPE_REGISTER_ACCESS ;
ra - > client_id = 0 ;
ra - > read_write_info = MDDI_WRITE | 1 ;
ra - > crc16 = 0 ;
ra - > register_address = reg ;
ra - > register_data_list = val ;
ll - > flags = 1 ;
ll - > header_count = 14 ;
ll - > data_count = 4 ;
ll - > data = mddi - > reg_write_addr + offsetof ( struct mddi_llentry ,
u . r . register_data_list ) ;
ll - > next = 0 ;
ll - > reserved = 0 ;
mddi_writel ( mddi - > reg_write_addr , PRI_PTR ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_PRI_LINK_LIST_DONE ) ;
mutex_unlock ( & mddi - > reg_write_lock ) ;
}
uint32_t mddi_remote_read ( struct msm_mddi_client_data * cdata , uint32_t reg )
{
struct mddi_info * mddi = container_of ( cdata , struct mddi_info ,
client_data ) ;
struct mddi_llentry * ll ;
struct mddi_register_access * ra ;
struct reg_read_info ri ;
unsigned s ;
int retry_count = 2 ;
unsigned long irq_flags ;
mutex_lock ( & mddi - > reg_read_lock ) ;
ll = mddi - > reg_read_data ;
ra = & ( ll - > u . r ) ;
ra - > length = 14 ;
ra - > type = TYPE_REGISTER_ACCESS ;
ra - > client_id = 0 ;
ra - > read_write_info = MDDI_READ | 1 ;
ra - > crc16 = 0 ;
ra - > register_address = reg ;
ll - > flags = 0x11 ;
ll - > header_count = 14 ;
ll - > data_count = 0 ;
ll - > data = 0 ;
ll - > next = 0 ;
ll - > reserved = 0 ;
s = mddi_readl ( STAT ) ;
ri . reg = reg ;
ri . status = - 1 ;
do {
init_completion ( & ri . done ) ;
mddi - > reg_read = & ri ;
mddi_writel ( mddi - > reg_read_addr , PRI_PTR ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_PRI_LINK_LIST_DONE ) ;
/* Enable Periodic Reverse Encapsulation. */
mddi_writel ( MDDI_CMD_PERIODIC_REV_ENCAP | 1 , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
if ( wait_for_completion_timeout ( & ri . done , HZ / 10 ) = = 0 & &
! ri . done . done ) {
printk ( KERN_INFO " mddi_remote_read(%x) timeout "
" (%d %d %d) \n " ,
reg , ri . status , ri . result , ri . done . done ) ;
spin_lock_irqsave ( & mddi - > int_lock , irq_flags ) ;
mddi - > reg_read = NULL ;
spin_unlock_irqrestore ( & mddi - > int_lock , irq_flags ) ;
ri . status = - 1 ;
ri . result = - 1 ;
}
if ( ri . status = = 0 )
break ;
mddi_writel ( MDDI_CMD_SEND_RTD , CMD ) ;
mddi_writel ( MDDI_CMD_LINK_ACTIVE , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
printk ( KERN_INFO " mddi_remote_read: failed, sent "
" MDDI_CMD_SEND_RTD: int %x, stat %x, rtd val %x "
" curr_rev_ptr %x \n " , mddi_readl ( INT ) , mddi_readl ( STAT ) ,
mddi_readl ( RTD_VAL ) , mddi_readl ( CURR_REV_PTR ) ) ;
} while ( retry_count - - > 0 ) ;
/* Disable Periodic Reverse Encapsulation. */
mddi_writel ( MDDI_CMD_PERIODIC_REV_ENCAP | 0 , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
mddi - > reg_read = NULL ;
mutex_unlock ( & mddi - > reg_read_lock ) ;
return ri . result ;
}
static struct mddi_info mddi_info [ 2 ] ;
static int __init mddi_clk_setup ( struct platform_device * pdev ,
struct mddi_info * mddi ,
unsigned long clk_rate )
{
int ret ;
/* set up the clocks */
mddi - > clk = clk_get ( & pdev - > dev , " mddi_clk " ) ;
if ( IS_ERR ( mddi - > clk ) ) {
printk ( KERN_INFO " mddi: failed to get clock \n " ) ;
return PTR_ERR ( mddi - > clk ) ;
}
ret = clk_enable ( mddi - > clk ) ;
if ( ret )
goto fail ;
ret = clk_set_rate ( mddi - > clk , clk_rate ) ;
if ( ret )
goto fail ;
return 0 ;
fail :
clk_put ( mddi - > clk ) ;
return ret ;
}
static int __init mddi_rev_data_setup ( struct mddi_info * mddi )
{
void * dma ;
dma_addr_t dma_addr ;
/* set up dma buffer */
dma = dma_alloc_coherent ( NULL , 0x1000 , & dma_addr , GFP_KERNEL ) ;
if ( dma = = 0 )
return - ENOMEM ;
mddi - > rev_data = dma ;
mddi - > rev_data_curr = 0 ;
mddi - > rev_addr = dma_addr ;
mddi - > reg_write_data = dma + MDDI_REV_BUFFER_SIZE ;
mddi - > reg_write_addr = dma_addr + MDDI_REV_BUFFER_SIZE ;
mddi - > reg_read_data = mddi - > reg_write_data + 1 ;
mddi - > reg_read_addr = mddi - > reg_write_addr +
sizeof ( * mddi - > reg_write_data ) ;
return 0 ;
}
static int __init mddi_probe ( struct platform_device * pdev )
{
struct msm_mddi_platform_data * pdata = pdev - > dev . platform_data ;
struct mddi_info * mddi = & mddi_info [ pdev - > id ] ;
struct resource * resource ;
int ret , i ;
resource = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! resource ) {
printk ( KERN_ERR " mddi: no associated mem resource! \n " ) ;
return - ENOMEM ;
}
mddi - > base = ioremap ( resource - > start , resource - > end - resource - > start ) ;
if ( ! mddi - > base ) {
printk ( KERN_ERR " mddi: failed to remap base! \n " ) ;
ret = - EINVAL ;
goto error_ioremap ;
}
resource = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
if ( ! resource ) {
printk ( KERN_ERR " mddi: no associated irq resource! \n " ) ;
ret = - EINVAL ;
goto error_get_irq_resource ;
}
mddi - > irq = resource - > start ;
printk ( KERN_INFO " mddi: init() base=0x%p irq=%d \n " , mddi - > base ,
mddi - > irq ) ;
mddi - > power_client = pdata - > power_client ;
mutex_init ( & mddi - > reg_write_lock ) ;
mutex_init ( & mddi - > reg_read_lock ) ;
spin_lock_init ( & mddi - > int_lock ) ;
init_waitqueue_head ( & mddi - > int_wait ) ;
ret = mddi_clk_setup ( pdev , mddi , pdata - > clk_rate ) ;
if ( ret ) {
printk ( KERN_ERR " mddi: failed to setup clock! \n " ) ;
goto error_clk_setup ;
}
ret = mddi_rev_data_setup ( mddi ) ;
if ( ret ) {
printk ( KERN_ERR " mddi: failed to setup rev data! \n " ) ;
goto error_rev_data ;
}
mddi - > int_enable = 0 ;
mddi_writel ( mddi - > int_enable , INTEN ) ;
ret = request_irq ( mddi - > irq , mddi_isr , IRQF_DISABLED , " mddi " ,
& mddi - > client_data ) ;
if ( ret ) {
printk ( KERN_ERR " mddi: failed to request enable irq! \n " ) ;
goto error_request_irq ;
}
/* turn on the mddi client bridge chip */
if ( mddi - > power_client )
mddi - > power_client ( & mddi - > client_data , 1 ) ;
/* initialize the mddi registers */
mddi_set_auto_hibernate ( & mddi - > client_data , 0 ) ;
mddi_writel ( MDDI_CMD_RESET , CMD ) ;
mddi_wait_interrupt ( mddi , MDDI_INT_NO_CMD_PKTS_PEND ) ;
mddi - > version = mddi_init_registers ( mddi ) ;
if ( mddi - > version < 0x20 ) {
printk ( KERN_ERR " mddi: unsupported version 0x%x \n " ,
mddi - > version ) ;
ret = - ENODEV ;
goto error_mddi_version ;
}
/* read the capabilities off the client */
if ( ! mddi_get_client_caps ( mddi ) ) {
printk ( KERN_INFO " mddi: no client found \n " ) ;
/* power down the panel */
mddi_writel ( MDDI_CMD_POWERDOWN , CMD ) ;
printk ( KERN_INFO " mddi powerdown: stat %x \n " , mddi_readl ( STAT ) ) ;
msleep ( 100 ) ;
printk ( KERN_INFO " mddi powerdown: stat %x \n " , mddi_readl ( STAT ) ) ;
return 0 ;
}
mddi_set_auto_hibernate ( & mddi - > client_data , 1 ) ;
if ( mddi - > caps . Mfr_Name = = 0 & & mddi - > caps . Product_Code = = 0 )
pdata - > fixup ( & mddi - > caps . Mfr_Name , & mddi - > caps . Product_Code ) ;
mddi - > client_pdev . id = 0 ;
for ( i = 0 ; i < pdata - > num_clients ; i + + ) {
if ( pdata - > client_platform_data [ i ] . product_id = =
( mddi - > caps . Mfr_Name < < 16 | mddi - > caps . Product_Code ) ) {
mddi - > client_data . private_client_data =
pdata - > client_platform_data [ i ] . client_data ;
mddi - > client_pdev . name =
pdata - > client_platform_data [ i ] . name ;
mddi - > client_pdev . id =
pdata - > client_platform_data [ i ] . id ;
/* XXX: possibly set clock */
break ;
}
}
if ( i > = pdata - > num_clients )
mddi - > client_pdev . name = " mddi_c_dummy " ;
printk ( KERN_INFO " mddi: registering panel %s \n " ,
mddi - > client_pdev . name ) ;
mddi - > client_data . suspend = mddi_suspend ;
mddi - > client_data . resume = mddi_resume ;
mddi - > client_data . activate_link = mddi_activate_link ;
mddi - > client_data . remote_write = mddi_remote_write ;
mddi - > client_data . remote_read = mddi_remote_read ;
mddi - > client_data . auto_hibernate = mddi_set_auto_hibernate ;
mddi - > client_data . fb_resource = pdata - > fb_resource ;
if ( pdev - > id = = 0 )
mddi - > client_data . interface_type = MSM_MDDI_PMDH_INTERFACE ;
else if ( pdev - > id = = 1 )
mddi - > client_data . interface_type = MSM_MDDI_EMDH_INTERFACE ;
else {
printk ( KERN_ERR " mddi: can not determine interface %d! \n " ,
pdev - > id ) ;
ret = - EINVAL ;
goto error_mddi_interface ;
}
mddi - > client_pdev . dev . platform_data = & mddi - > client_data ;
printk ( KERN_INFO " mddi: publish: %s \n " , mddi - > client_name ) ;
platform_device_register ( & mddi - > client_pdev ) ;
return 0 ;
error_mddi_interface :
error_mddi_version :
free_irq ( mddi - > irq , 0 ) ;
error_request_irq :
dma_free_coherent ( NULL , 0x1000 , mddi - > rev_data , mddi - > rev_addr ) ;
error_rev_data :
error_clk_setup :
error_get_irq_resource :
iounmap ( mddi - > base ) ;
error_ioremap :
printk ( KERN_INFO " mddi: mddi_init() failed (%d) \n " , ret ) ;
return ret ;
}
static struct platform_driver mddi_driver = {
. probe = mddi_probe ,
. driver = { . name = " msm_mddi " } ,
} ;
static int __init _mddi_init ( void )
{
return platform_driver_register ( & mddi_driver ) ;
}
module_init ( _mddi_init ) ;