2012-04-28 14:22:08 +04:00
/*
* auok190xfb . c - - FB driver for AUO - K1901 controllers
*
* Copyright ( C ) 2011 , 2012 Heiko Stuebner < heiko @ sntech . de >
*
* based on broadsheetfb . c
*
* Copyright ( C ) 2008 , Jaya Kumar
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* Layout is based on skeletonfb . c by James Simmons and Geert Uytterhoeven .
*
* This driver is written to be used with the AUO - K1901 display controller .
*
* It is intended to be architecture independent . A board specific driver
* must be used to perform all the physical IO interactions .
*
* The controller supports different update modes :
* mode0 + 1 16 step gray ( 4 bit )
* mode2 + 3 4 step gray ( 2 bit )
* mode4 + 5 2 step gray ( 1 bit )
* - mode4 is described as " without LUT "
* mode7 automatic selection of update mode
*
* The most interesting difference to the K1900 is the ability to do screen
* updates in an asynchronous fashion . Where the K1900 needs to wait for the
* current update to complete , the K1901 can process later updates already .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/slab.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/firmware.h>
# include <linux/gpio.h>
# include <linux/pm_runtime.h>
# include <video/auo_k190xfb.h>
# include "auo_k190x.h"
/*
* AUO - K1901 specific commands
*/
# define AUOK1901_CMD_LUT_INTERFACE 0x0005
# define AUOK1901_CMD_DMA_START 0x1001
# define AUOK1901_CMD_CURSOR_START 0x1007
# define AUOK1901_CMD_CURSOR_STOP AUOK190X_CMD_DATA_STOP
# define AUOK1901_CMD_DDMA_START 0x1009
# define AUOK1901_INIT_GATE_PULSE_LOW (0 << 14)
# define AUOK1901_INIT_GATE_PULSE_HIGH (1 << 14)
# define AUOK1901_INIT_SINGLE_GATE (0 << 13)
# define AUOK1901_INIT_DOUBLE_GATE (1 << 13)
/* Bits to pixels
* Mode 15 - 12 11 - 8 7 - 4 3 - 0
* format2 2 T 1 T
* format3 1 T 2 T
* format4 T 2 T 1
* format5 T 1 T 2
*
* halftone modes :
* format6 2 2 1 1
* format7 1 1 2 2
*/
# define AUOK1901_INIT_FORMAT2 (1 << 7)
# define AUOK1901_INIT_FORMAT3 ((1 << 7) | (1 << 6))
# define AUOK1901_INIT_FORMAT4 (1 << 8)
# define AUOK1901_INIT_FORMAT5 ((1 << 8) | (1 << 6))
# define AUOK1901_INIT_FORMAT6 ((1 << 8) | (1 << 7))
# define AUOK1901_INIT_FORMAT7 ((1 << 8) | (1 << 7) | (1 << 6))
/* res[4] to bit 10
* res [ 3 - 0 ] to bits 5 - 2
*/
# define AUOK1901_INIT_RESOLUTION(_res) (((_res & (1 << 4)) << 6) \
| ( ( _res & 0xf ) < < 2 ) )
/*
* portrait / landscape orientation in AUOK1901_CMD_DMA_START
*/
# define AUOK1901_DMA_ROTATE90(_rot) ((_rot & 1) << 13)
/*
* equivalent to 1 < < 11 , needs the ~ to have same rotation like K1900
*/
# define AUOK1901_DDMA_ROTATE180(_rot) ((~_rot & 2) << 10)
static void auok1901_init ( struct auok190xfb_par * par )
{
2013-03-22 18:13:37 +04:00
struct device * dev = par - > info - > device ;
2012-04-28 14:22:08 +04:00
struct auok190x_board * board = par - > board ;
u16 init_param = 0 ;
2013-03-22 18:13:37 +04:00
pm_runtime_get_sync ( dev ) ;
2012-04-28 14:22:08 +04:00
init_param | = AUOK190X_INIT_INVERSE_WHITE ;
init_param | = AUOK190X_INIT_FORMAT0 ;
init_param | = AUOK1901_INIT_RESOLUTION ( par - > resolution ) ;
init_param | = AUOK190X_INIT_SHIFT_LEFT ;
auok190x_send_cmdargs ( par , AUOK190X_CMD_INIT , 1 , & init_param ) ;
/* let the controller finish */
board - > wait_for_rdy ( par ) ;
2013-03-22 18:13:37 +04:00
pm_runtime_mark_last_busy ( dev ) ;
pm_runtime_put_autosuspend ( dev ) ;
2012-04-28 14:22:08 +04:00
}
static void auok1901_update_region ( struct auok190xfb_par * par , int mode ,
u16 y1 , u16 y2 )
{
struct device * dev = par - > info - > device ;
unsigned char * buf = ( unsigned char * ) par - > info - > screen_base ;
int xres = par - > info - > var . xres ;
2013-03-22 18:13:02 +04:00
int line_length = par - > info - > fix . line_length ;
2012-04-28 14:22:08 +04:00
u16 args [ 5 ] ;
pm_runtime_get_sync ( dev ) ;
mutex_lock ( & ( par - > io_lock ) ) ;
/* y1 and y2 must be a multiple of 2 so drop the lowest bit */
y1 & = 0xfffe ;
y2 & = 0xfffe ;
dev_dbg ( dev , " update (x,y,w,h,mode)=(%d,%d,%d,%d,%d) \n " ,
1 , y1 + 1 , xres , y2 - y1 , mode ) ;
/* K1901: first transfer the region data */
args [ 0 ] = AUOK1901_DMA_ROTATE90 ( par - > rotation ) | 1 ;
args [ 1 ] = y1 + 1 ;
args [ 2 ] = xres ;
args [ 3 ] = y2 - y1 ;
2013-03-22 18:13:02 +04:00
buf + = y1 * line_length ;
2012-04-28 14:22:08 +04:00
auok190x_send_cmdargs_pixels_nowait ( par , AUOK1901_CMD_DMA_START , 4 ,
2013-03-22 18:13:02 +04:00
args , ( ( y2 - y1 ) * line_length ) / 2 ,
2012-04-28 14:22:08 +04:00
( u16 * ) buf ) ;
auok190x_send_command_nowait ( par , AUOK190X_CMD_DATA_STOP ) ;
/* K1901: second tell the controller to update the region with mode */
args [ 0 ] = mode | AUOK1901_DDMA_ROTATE180 ( par - > rotation ) ;
args [ 1 ] = 1 ;
args [ 2 ] = y1 + 1 ;
args [ 3 ] = xres ;
args [ 4 ] = y2 - y1 ;
auok190x_send_cmdargs_nowait ( par , AUOK1901_CMD_DDMA_START , 5 , args ) ;
par - > update_cnt + + ;
mutex_unlock ( & ( par - > io_lock ) ) ;
pm_runtime_mark_last_busy ( dev ) ;
pm_runtime_put_autosuspend ( dev ) ;
}
static void auok1901fb_dpy_update_pages ( struct auok190xfb_par * par ,
u16 y1 , u16 y2 )
{
int mode ;
if ( par - > update_mode < 0 ) {
mode = AUOK190X_UPDATE_MODE ( 1 ) ;
par - > last_mode = - 1 ;
} else {
mode = AUOK190X_UPDATE_MODE ( par - > update_mode ) ;
par - > last_mode = par - > update_mode ;
}
if ( par - > flash )
mode | = AUOK190X_UPDATE_NONFLASH ;
auok1901_update_region ( par , mode , y1 , y2 ) ;
}
static void auok1901fb_dpy_update ( struct auok190xfb_par * par )
{
int mode ;
/* When doing full updates, wait for the controller to be ready
* This will hopefully catch some hangs of the K1901
*/
par - > board - > wait_for_rdy ( par ) ;
if ( par - > update_mode < 0 ) {
mode = AUOK190X_UPDATE_MODE ( 0 ) ;
par - > last_mode = - 1 ;
} else {
mode = AUOK190X_UPDATE_MODE ( par - > update_mode ) ;
par - > last_mode = par - > update_mode ;
}
if ( par - > flash )
mode | = AUOK190X_UPDATE_NONFLASH ;
auok1901_update_region ( par , mode , 0 , par - > info - > var . yres ) ;
par - > update_cnt = 0 ;
}
static bool auok1901fb_need_refresh ( struct auok190xfb_par * par )
{
return ( par - > update_cnt > 10 ) ;
}
2012-12-22 01:07:39 +04:00
static int auok1901fb_probe ( struct platform_device * pdev )
2012-04-28 14:22:08 +04:00
{
struct auok190x_init_data init ;
struct auok190x_board * board ;
/* pick up board specific routines */
board = pdev - > dev . platform_data ;
if ( ! board )
return - EINVAL ;
/* fill temporary init struct for common init */
init . id = " auo_k1901fb " ;
init . board = board ;
init . update_partial = auok1901fb_dpy_update_pages ;
init . update_all = auok1901fb_dpy_update ;
init . need_refresh = auok1901fb_need_refresh ;
init . init = auok1901_init ;
return auok190x_common_probe ( pdev , & init ) ;
}
2012-12-22 01:07:39 +04:00
static int auok1901fb_remove ( struct platform_device * pdev )
2012-04-28 14:22:08 +04:00
{
return auok190x_common_remove ( pdev ) ;
}
static struct platform_driver auok1901fb_driver = {
. probe = auok1901fb_probe ,
2012-12-22 01:07:39 +04:00
. remove = auok1901fb_remove ,
2012-04-28 14:22:08 +04:00
. driver = {
. name = " auo_k1901fb " ,
. pm = & auok190x_pm ,
} ,
} ;
module_platform_driver ( auok1901fb_driver ) ;
MODULE_DESCRIPTION ( " framebuffer driver for the AUO-K1901 EPD controller " ) ;
MODULE_AUTHOR ( " Heiko Stuebner <heiko@sntech.de> " ) ;
MODULE_LICENSE ( " GPL " ) ;