2019-05-29 17:17:58 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-04-01 03:03:33 +03:00
/*
* Raydium touchscreen I2C driver .
*
* Copyright ( C ) 2012 - 2014 , Raydium Semiconductor Corporation .
*
* Raydium reserves the right to make changes without further notice
* to the materials described herein . Raydium does not assume any
* liability arising out of the application described herein .
*
* Contact Raydium Semiconductor Corporation at www . rad - ic . com
*/
# include <linux/acpi.h>
# include <linux/delay.h>
# include <linux/firmware.h>
# include <linux/gpio/consumer.h>
# include <linux/i2c.h>
# include <linux/input.h>
# include <linux/input/mt.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/regulator/consumer.h>
# include <linux/slab.h>
# include <asm/unaligned.h>
/* Slave I2C mode */
# define RM_BOOT_BLDR 0x02
# define RM_BOOT_MAIN 0x03
/* I2C bootoloader commands */
# define RM_CMD_BOOT_PAGE_WRT 0x0B /* send bl page write */
# define RM_CMD_BOOT_WRT 0x11 /* send bl write */
# define RM_CMD_BOOT_ACK 0x22 /* send ack*/
# define RM_CMD_BOOT_CHK 0x33 /* send data check */
# define RM_CMD_BOOT_READ 0x44 /* send wait bl data ready*/
# define RM_BOOT_RDY 0xFF /* bl data ready */
/* I2C main commands */
# define RM_CMD_QUERY_BANK 0x2B
# define RM_CMD_DATA_BANK 0x4D
# define RM_CMD_ENTER_SLEEP 0x4E
# define RM_CMD_BANK_SWITCH 0xAA
# define RM_RESET_MSG_ADDR 0x40000004
# define RM_MAX_READ_SIZE 56
2016-07-12 03:01:17 +03:00
# define RM_PACKET_CRC_SIZE 2
2016-04-01 03:03:33 +03:00
/* Touch relative info */
# define RM_MAX_RETRIES 3
# define RM_MAX_TOUCH_NUM 10
# define RM_BOOT_DELAY_MS 100
/* Offsets in contact data */
# define RM_CONTACT_STATE_POS 0
# define RM_CONTACT_X_POS 1
# define RM_CONTACT_Y_POS 3
# define RM_CONTACT_PRESSURE_POS 5
# define RM_CONTACT_WIDTH_X_POS 6
# define RM_CONTACT_WIDTH_Y_POS 7
/* Bootloader relative info */
# define RM_BL_WRT_CMD_SIZE 3 /* bl flash wrt cmd size */
# define RM_BL_WRT_PKG_SIZE 32 /* bl wrt pkg size */
# define RM_BL_WRT_LEN (RM_BL_WRT_PKG_SIZE + RM_BL_WRT_CMD_SIZE)
# define RM_FW_PAGE_SIZE 128
# define RM_MAX_FW_RETRIES 30
# define RM_MAX_FW_SIZE 0xD000
# define RM_POWERON_DELAY_USEC 500
# define RM_RESET_DELAY_MSEC 50
enum raydium_bl_cmd {
BL_HEADER = 0 ,
BL_PAGE_STR ,
BL_PKG_IDX ,
BL_DATA_STR ,
} ;
enum raydium_bl_ack {
RAYDIUM_ACK_NULL = 0 ,
RAYDIUM_WAIT_READY ,
RAYDIUM_PATH_READY ,
} ;
enum raydium_boot_mode {
RAYDIUM_TS_MAIN = 0 ,
RAYDIUM_TS_BLDR ,
} ;
/* Response to RM_CMD_DATA_BANK request */
struct raydium_data_info {
__le32 data_bank_addr ;
u8 pkg_size ;
u8 tp_info_size ;
} ;
struct raydium_info {
__le32 hw_ver ; /*device version */
u8 main_ver ;
u8 sub_ver ;
__le16 ft_ver ; /* test version */
u8 x_num ;
u8 y_num ;
__le16 x_max ;
__le16 y_max ;
u8 x_res ; /* units/mm */
u8 y_res ; /* units/mm */
} ;
/* struct raydium_data - represents state of Raydium touchscreen device */
struct raydium_data {
struct i2c_client * client ;
struct input_dev * input ;
struct regulator * avdd ;
struct regulator * vccio ;
struct gpio_desc * reset_gpio ;
struct raydium_info info ;
struct mutex sysfs_mutex ;
u8 * report_data ;
u32 data_bank_addr ;
u8 report_size ;
u8 contact_size ;
2016-07-12 03:01:17 +03:00
u8 pkg_size ;
2016-04-01 03:03:33 +03:00
enum raydium_boot_mode boot_mode ;
bool wake_irq_enabled ;
} ;
static int raydium_i2c_send ( struct i2c_client * client ,
u8 addr , const void * data , size_t len )
{
u8 * buf ;
int tries = 0 ;
int ret ;
buf = kmalloc ( len + 1 , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
buf [ 0 ] = addr ;
memcpy ( buf + 1 , data , len ) ;
do {
ret = i2c_master_send ( client , buf , len + 1 ) ;
if ( likely ( ret = = len + 1 ) )
break ;
msleep ( 20 ) ;
} while ( + + tries < RM_MAX_RETRIES ) ;
kfree ( buf ) ;
if ( unlikely ( ret ! = len + 1 ) ) {
if ( ret > = 0 )
ret = - EIO ;
dev_err ( & client - > dev , " %s failed: %d \n " , __func__ , ret ) ;
return ret ;
}
return 0 ;
}
static int raydium_i2c_read ( struct i2c_client * client ,
u8 addr , void * data , size_t len )
{
struct i2c_msg xfer [ ] = {
{
. addr = client - > addr ,
. len = 1 ,
. buf = & addr ,
} ,
{
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = len ,
. buf = data ,
}
} ;
int ret ;
ret = i2c_transfer ( client - > adapter , xfer , ARRAY_SIZE ( xfer ) ) ;
if ( unlikely ( ret ! = ARRAY_SIZE ( xfer ) ) )
return ret < 0 ? ret : - EIO ;
return 0 ;
}
static int raydium_i2c_read_message ( struct i2c_client * client ,
u32 addr , void * data , size_t len )
{
__be32 be_addr ;
size_t xfer_len ;
int error ;
while ( len ) {
xfer_len = min_t ( size_t , len , RM_MAX_READ_SIZE ) ;
be_addr = cpu_to_be32 ( addr ) ;
error = raydium_i2c_send ( client , RM_CMD_BANK_SWITCH ,
& be_addr , sizeof ( be_addr ) ) ;
if ( ! error )
error = raydium_i2c_read ( client , addr & 0xff ,
data , xfer_len ) ;
if ( error )
return error ;
len - = xfer_len ;
data + = xfer_len ;
addr + = xfer_len ;
}
return 0 ;
}
static int raydium_i2c_send_message ( struct i2c_client * client ,
u32 addr , const void * data , size_t len )
{
__be32 be_addr = cpu_to_be32 ( addr ) ;
int error ;
error = raydium_i2c_send ( client , RM_CMD_BANK_SWITCH ,
& be_addr , sizeof ( be_addr ) ) ;
if ( ! error )
error = raydium_i2c_send ( client , addr & 0xff , data , len ) ;
return error ;
}
static int raydium_i2c_sw_reset ( struct i2c_client * client )
{
const u8 soft_rst_cmd = 0x01 ;
int error ;
error = raydium_i2c_send_message ( client , RM_RESET_MSG_ADDR ,
& soft_rst_cmd , sizeof ( soft_rst_cmd ) ) ;
if ( error ) {
dev_err ( & client - > dev , " software reset failed: %d \n " , error ) ;
return error ;
}
msleep ( RM_RESET_DELAY_MSEC ) ;
return 0 ;
}
static int raydium_i2c_query_ts_info ( struct raydium_data * ts )
{
struct i2c_client * client = ts - > client ;
struct raydium_data_info data_info ;
__le32 query_bank_addr ;
int error , retry_cnt ;
for ( retry_cnt = 0 ; retry_cnt < RM_MAX_RETRIES ; retry_cnt + + ) {
error = raydium_i2c_read ( client , RM_CMD_DATA_BANK ,
& data_info , sizeof ( data_info ) ) ;
if ( error )
continue ;
/*
* Warn user if we already allocated memory for reports and
* then the size changed ( due to firmware update ? ) and keep
* old size instead .
*/
2016-07-12 03:01:17 +03:00
if ( ts - > report_data & & ts - > pkg_size ! = data_info . pkg_size ) {
2016-04-01 03:03:33 +03:00
dev_warn ( & client - > dev ,
" report size changes, was: %d, new: %d \n " ,
2016-07-12 03:01:17 +03:00
ts - > pkg_size , data_info . pkg_size ) ;
} else {
ts - > pkg_size = data_info . pkg_size ;
ts - > report_size = ts - > pkg_size - RM_PACKET_CRC_SIZE ;
}
2016-04-01 03:03:33 +03:00
ts - > contact_size = data_info . tp_info_size ;
ts - > data_bank_addr = le32_to_cpu ( data_info . data_bank_addr ) ;
dev_dbg ( & client - > dev ,
" data_bank_addr: %#08x, report_size: %d, contact_size: %d \n " ,
ts - > data_bank_addr , ts - > report_size , ts - > contact_size ) ;
error = raydium_i2c_read ( client , RM_CMD_QUERY_BANK ,
& query_bank_addr ,
sizeof ( query_bank_addr ) ) ;
if ( error )
continue ;
error = raydium_i2c_read_message ( client ,
le32_to_cpu ( query_bank_addr ) ,
& ts - > info , sizeof ( ts - > info ) ) ;
if ( error )
continue ;
return 0 ;
}
dev_err ( & client - > dev , " failed to query device parameters: %d \n " , error ) ;
return error ;
}
static int raydium_i2c_check_fw_status ( struct raydium_data * ts )
{
struct i2c_client * client = ts - > client ;
static const u8 bl_ack = 0x62 ;
static const u8 main_ack = 0x66 ;
u8 buf [ 4 ] ;
int error ;
error = raydium_i2c_read ( client , RM_CMD_BOOT_READ , buf , sizeof ( buf ) ) ;
if ( ! error ) {
if ( buf [ 0 ] = = bl_ack )
ts - > boot_mode = RAYDIUM_TS_BLDR ;
else if ( buf [ 0 ] = = main_ack )
ts - > boot_mode = RAYDIUM_TS_MAIN ;
return 0 ;
}
return error ;
}
static int raydium_i2c_initialize ( struct raydium_data * ts )
{
struct i2c_client * client = ts - > client ;
int error , retry_cnt ;
for ( retry_cnt = 0 ; retry_cnt < RM_MAX_RETRIES ; retry_cnt + + ) {
/* Wait for Hello packet */
msleep ( RM_BOOT_DELAY_MS ) ;
error = raydium_i2c_check_fw_status ( ts ) ;
if ( error ) {
dev_err ( & client - > dev ,
" failed to read 'hello' packet: %d \n " , error ) ;
continue ;
}
if ( ts - > boot_mode = = RAYDIUM_TS_BLDR | |
ts - > boot_mode = = RAYDIUM_TS_MAIN ) {
break ;
}
}
if ( error )
ts - > boot_mode = RAYDIUM_TS_BLDR ;
if ( ts - > boot_mode = = RAYDIUM_TS_BLDR ) {
ts - > info . hw_ver = cpu_to_le32 ( 0xffffffffUL ) ;
ts - > info . main_ver = 0xff ;
ts - > info . sub_ver = 0xff ;
} else {
raydium_i2c_query_ts_info ( ts ) ;
}
return error ;
}
static int raydium_i2c_bl_chk_state ( struct i2c_client * client ,
enum raydium_bl_ack state )
{
static const u8 ack_ok [ ] = { 0xFF , 0x39 , 0x30 , 0x30 , 0x54 } ;
u8 rbuf [ sizeof ( ack_ok ) ] ;
u8 retry ;
int error ;
for ( retry = 0 ; retry < RM_MAX_FW_RETRIES ; retry + + ) {
switch ( state ) {
case RAYDIUM_ACK_NULL :
return 0 ;
case RAYDIUM_WAIT_READY :
error = raydium_i2c_read ( client , RM_CMD_BOOT_CHK ,
& rbuf [ 0 ] , 1 ) ;
if ( ! error & & rbuf [ 0 ] = = RM_BOOT_RDY )
return 0 ;
break ;
case RAYDIUM_PATH_READY :
error = raydium_i2c_read ( client , RM_CMD_BOOT_CHK ,
rbuf , sizeof ( rbuf ) ) ;
if ( ! error & & ! memcmp ( rbuf , ack_ok , sizeof ( ack_ok ) ) )
return 0 ;
break ;
default :
dev_err ( & client - > dev , " %s: invalid target state %d \n " ,
__func__ , state ) ;
return - EINVAL ;
}
msleep ( 20 ) ;
}
return - ETIMEDOUT ;
}
static int raydium_i2c_write_object ( struct i2c_client * client ,
const void * data , size_t len ,
enum raydium_bl_ack state )
{
int error ;
error = raydium_i2c_send ( client , RM_CMD_BOOT_WRT , data , len ) ;
if ( error ) {
dev_err ( & client - > dev , " WRT obj command failed: %d \n " ,
error ) ;
return error ;
}
error = raydium_i2c_send ( client , RM_CMD_BOOT_ACK , NULL , 0 ) ;
if ( error ) {
dev_err ( & client - > dev , " Ack obj command failed: %d \n " , error ) ;
return error ;
}
error = raydium_i2c_bl_chk_state ( client , state ) ;
if ( error ) {
dev_err ( & client - > dev , " BL check state failed: %d \n " , error ) ;
return error ;
}
return 0 ;
}
static bool raydium_i2c_boot_trigger ( struct i2c_client * client )
{
static const u8 cmd [ 7 ] [ 6 ] = {
{ 0x08 , 0x0C , 0x09 , 0x00 , 0x50 , 0xD7 } ,
{ 0x08 , 0x04 , 0x09 , 0x00 , 0x50 , 0xA5 } ,
{ 0x08 , 0x04 , 0x09 , 0x00 , 0x50 , 0x00 } ,
{ 0x08 , 0x04 , 0x09 , 0x00 , 0x50 , 0xA5 } ,
{ 0x08 , 0x0C , 0x09 , 0x00 , 0x50 , 0x00 } ,
{ 0x06 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 } ,
{ 0x02 , 0xA2 , 0x00 , 0x00 , 0x00 , 0x00 } ,
} ;
int i ;
int error ;
for ( i = 0 ; i < 7 ; i + + ) {
error = raydium_i2c_write_object ( client , cmd [ i ] , sizeof ( cmd [ i ] ) ,
RAYDIUM_WAIT_READY ) ;
if ( error ) {
dev_err ( & client - > dev ,
" boot trigger failed at step %d: %d \n " ,
i , error ) ;
return error ;
}
}
2018-08-07 01:20:19 +03:00
return false ;
2016-04-01 03:03:33 +03:00
}
static bool raydium_i2c_fw_trigger ( struct i2c_client * client )
{
static const u8 cmd [ 5 ] [ 11 ] = {
{ 0 , 0x09 , 0x71 , 0x0C , 0x09 , 0x00 , 0x50 , 0xD7 , 0 , 0 , 0 } ,
{ 0 , 0x09 , 0x71 , 0x04 , 0x09 , 0x00 , 0x50 , 0xA5 , 0 , 0 , 0 } ,
{ 0 , 0x09 , 0x71 , 0x04 , 0x09 , 0x00 , 0x50 , 0x00 , 0 , 0 , 0 } ,
{ 0 , 0x09 , 0x71 , 0x04 , 0x09 , 0x00 , 0x50 , 0xA5 , 0 , 0 , 0 } ,
{ 0 , 0x09 , 0x71 , 0x0C , 0x09 , 0x00 , 0x50 , 0x00 , 0 , 0 , 0 } ,
} ;
int i ;
int error ;
for ( i = 0 ; i < 5 ; i + + ) {
error = raydium_i2c_write_object ( client , cmd [ i ] , sizeof ( cmd [ i ] ) ,
RAYDIUM_ACK_NULL ) ;
if ( error ) {
dev_err ( & client - > dev ,
" fw trigger failed at step %d: %d \n " ,
i , error ) ;
return error ;
}
}
2018-08-07 01:20:19 +03:00
return false ;
2016-04-01 03:03:33 +03:00
}
static int raydium_i2c_check_path ( struct i2c_client * client )
{
static const u8 cmd [ ] = { 0x09 , 0x00 , 0x09 , 0x00 , 0x50 , 0x10 , 0x00 } ;
int error ;
error = raydium_i2c_write_object ( client , cmd , sizeof ( cmd ) ,
RAYDIUM_PATH_READY ) ;
if ( error ) {
dev_err ( & client - > dev , " check path command failed: %d \n " , error ) ;
return error ;
}
return 0 ;
}
static int raydium_i2c_enter_bl ( struct i2c_client * client )
{
static const u8 cal_cmd [ ] = { 0x00 , 0x01 , 0x52 } ;
int error ;
error = raydium_i2c_write_object ( client , cal_cmd , sizeof ( cal_cmd ) ,
RAYDIUM_ACK_NULL ) ;
if ( error ) {
dev_err ( & client - > dev , " enter bl command failed: %d \n " , error ) ;
return error ;
}
msleep ( RM_BOOT_DELAY_MS ) ;
return 0 ;
}
static int raydium_i2c_leave_bl ( struct i2c_client * client )
{
static const u8 leave_cmd [ ] = { 0x05 , 0x00 } ;
int error ;
error = raydium_i2c_write_object ( client , leave_cmd , sizeof ( leave_cmd ) ,
RAYDIUM_ACK_NULL ) ;
if ( error ) {
dev_err ( & client - > dev , " leave bl command failed: %d \n " , error ) ;
return error ;
}
msleep ( RM_BOOT_DELAY_MS ) ;
return 0 ;
}
static int raydium_i2c_write_checksum ( struct i2c_client * client ,
size_t length , u16 checksum )
{
u8 checksum_cmd [ ] = { 0x00 , 0x05 , 0x6D , 0x00 , 0x00 , 0x00 , 0x00 } ;
int error ;
put_unaligned_le16 ( length , & checksum_cmd [ 3 ] ) ;
put_unaligned_le16 ( checksum , & checksum_cmd [ 5 ] ) ;
error = raydium_i2c_write_object ( client ,
checksum_cmd , sizeof ( checksum_cmd ) ,
RAYDIUM_ACK_NULL ) ;
if ( error ) {
dev_err ( & client - > dev , " failed to write checksum: %d \n " ,
error ) ;
return error ;
}
return 0 ;
}
static int raydium_i2c_disable_watch_dog ( struct i2c_client * client )
{
static const u8 cmd [ ] = { 0x0A , 0xAA } ;
int error ;
error = raydium_i2c_write_object ( client , cmd , sizeof ( cmd ) ,
RAYDIUM_WAIT_READY ) ;
if ( error ) {
dev_err ( & client - > dev , " disable watchdog command failed: %d \n " ,
error ) ;
return error ;
}
return 0 ;
}
static int raydium_i2c_fw_write_page ( struct i2c_client * client ,
u16 page_idx , const void * data , size_t len )
{
u8 buf [ RM_BL_WRT_LEN ] ;
size_t xfer_len ;
int error ;
int i ;
BUILD_BUG_ON ( ( RM_FW_PAGE_SIZE % RM_BL_WRT_PKG_SIZE ) ! = 0 ) ;
for ( i = 0 ; i < RM_FW_PAGE_SIZE / RM_BL_WRT_PKG_SIZE ; i + + ) {
buf [ BL_HEADER ] = RM_CMD_BOOT_PAGE_WRT ;
buf [ BL_PAGE_STR ] = page_idx ? 0xff : 0 ;
buf [ BL_PKG_IDX ] = i + 1 ;
xfer_len = min_t ( size_t , len , RM_BL_WRT_PKG_SIZE ) ;
memcpy ( & buf [ BL_DATA_STR ] , data , xfer_len ) ;
if ( len < RM_BL_WRT_PKG_SIZE )
memset ( & buf [ BL_DATA_STR + xfer_len ] , 0xff ,
RM_BL_WRT_PKG_SIZE - xfer_len ) ;
error = raydium_i2c_write_object ( client , buf , RM_BL_WRT_LEN ,
RAYDIUM_WAIT_READY ) ;
if ( error ) {
dev_err ( & client - > dev ,
" page write command failed for page %d, chunk %d: %d \n " ,
page_idx , i , error ) ;
return error ;
}
data + = xfer_len ;
len - = xfer_len ;
}
return error ;
}
2016-07-12 03:01:17 +03:00
static u16 raydium_calc_chksum ( const u8 * buf , u16 len )
{
u16 checksum = 0 ;
u16 i ;
for ( i = 0 ; i < len ; i + + )
checksum + = buf [ i ] ;
return checksum ;
}
2016-04-01 03:03:33 +03:00
static int raydium_i2c_do_update_firmware ( struct raydium_data * ts ,
const struct firmware * fw )
{
struct i2c_client * client = ts - > client ;
const void * data ;
size_t data_len ;
size_t len ;
int page_nr ;
int i ;
int error ;
u16 fw_checksum ;
if ( fw - > size = = 0 | | fw - > size > RM_MAX_FW_SIZE ) {
dev_err ( & client - > dev , " Invalid firmware length \n " ) ;
return - EINVAL ;
}
error = raydium_i2c_check_fw_status ( ts ) ;
if ( error ) {
dev_err ( & client - > dev , " Unable to access IC %d \n " , error ) ;
return error ;
}
if ( ts - > boot_mode = = RAYDIUM_TS_MAIN ) {
for ( i = 0 ; i < RM_MAX_RETRIES ; i + + ) {
error = raydium_i2c_enter_bl ( client ) ;
if ( ! error ) {
error = raydium_i2c_check_fw_status ( ts ) ;
if ( error ) {
dev_err ( & client - > dev ,
" unable to access IC: %d \n " ,
error ) ;
return error ;
}
if ( ts - > boot_mode = = RAYDIUM_TS_BLDR )
break ;
}
}
if ( ts - > boot_mode = = RAYDIUM_TS_MAIN ) {
dev_err ( & client - > dev ,
2016-11-17 04:19:57 +03:00
" failed to jump to boot loader: %d \n " ,
2016-04-01 03:03:33 +03:00
error ) ;
return - EIO ;
}
}
error = raydium_i2c_disable_watch_dog ( client ) ;
if ( error )
return error ;
error = raydium_i2c_check_path ( client ) ;
if ( error )
return error ;
error = raydium_i2c_boot_trigger ( client ) ;
if ( error ) {
dev_err ( & client - > dev , " send boot trigger fail: %d \n " , error ) ;
return error ;
}
msleep ( RM_BOOT_DELAY_MS ) ;
data = fw - > data ;
data_len = fw - > size ;
page_nr = 0 ;
while ( data_len ) {
len = min_t ( size_t , data_len , RM_FW_PAGE_SIZE ) ;
error = raydium_i2c_fw_write_page ( client , page_nr + + , data , len ) ;
if ( error )
return error ;
msleep ( 20 ) ;
data + = len ;
data_len - = len ;
}
error = raydium_i2c_leave_bl ( client ) ;
if ( error ) {
dev_err ( & client - > dev ,
" failed to leave boot loader: %d \n " , error ) ;
return error ;
}
dev_dbg ( & client - > dev , " left boot loader mode \n " ) ;
msleep ( RM_BOOT_DELAY_MS ) ;
error = raydium_i2c_check_fw_status ( ts ) ;
if ( error ) {
dev_err ( & client - > dev ,
" failed to check fw status after write: %d \n " ,
error ) ;
return error ;
}
if ( ts - > boot_mode ! = RAYDIUM_TS_MAIN ) {
dev_err ( & client - > dev ,
" failed to switch to main fw after writing firmware: %d \n " ,
error ) ;
return - EINVAL ;
}
error = raydium_i2c_fw_trigger ( client ) ;
if ( error ) {
dev_err ( & client - > dev , " failed to trigger fw: %d \n " , error ) ;
return error ;
}
2016-07-12 03:01:17 +03:00
fw_checksum = raydium_calc_chksum ( fw - > data , fw - > size ) ;
2016-04-01 03:03:33 +03:00
error = raydium_i2c_write_checksum ( client , fw - > size , fw_checksum ) ;
if ( error )
return error ;
return 0 ;
}
static int raydium_i2c_fw_update ( struct raydium_data * ts )
{
struct i2c_client * client = ts - > client ;
const struct firmware * fw = NULL ;
2018-01-05 08:35:23 +03:00
char * fw_file ;
2016-04-01 03:03:33 +03:00
int error ;
2018-01-05 08:35:23 +03:00
fw_file = kasprintf ( GFP_KERNEL , " raydium_%#04x.fw " ,
le32_to_cpu ( ts - > info . hw_ver ) ) ;
if ( ! fw_file )
return - ENOMEM ;
dev_dbg ( & client - > dev , " firmware name: %s \n " , fw_file ) ;
2016-04-01 03:03:33 +03:00
error = request_firmware ( & fw , fw_file , & client - > dev ) ;
if ( error ) {
dev_err ( & client - > dev , " Unable to open firmware %s \n " , fw_file ) ;
2018-01-05 08:35:23 +03:00
goto out_free_fw_file ;
2016-04-01 03:03:33 +03:00
}
disable_irq ( client - > irq ) ;
error = raydium_i2c_do_update_firmware ( ts , fw ) ;
if ( error ) {
dev_err ( & client - > dev , " firmware update failed: %d \n " , error ) ;
ts - > boot_mode = RAYDIUM_TS_BLDR ;
goto out_enable_irq ;
}
error = raydium_i2c_initialize ( ts ) ;
if ( error ) {
dev_err ( & client - > dev ,
" failed to initialize device after firmware update: %d \n " ,
error ) ;
ts - > boot_mode = RAYDIUM_TS_BLDR ;
goto out_enable_irq ;
}
ts - > boot_mode = RAYDIUM_TS_MAIN ;
out_enable_irq :
enable_irq ( client - > irq ) ;
msleep ( 100 ) ;
release_firmware ( fw ) ;
2018-01-05 08:35:23 +03:00
out_free_fw_file :
kfree ( fw_file ) ;
2016-04-01 03:03:33 +03:00
return error ;
}
static void raydium_mt_event ( struct raydium_data * ts )
{
int i ;
for ( i = 0 ; i < ts - > report_size / ts - > contact_size ; i + + ) {
u8 * contact = & ts - > report_data [ ts - > contact_size * i ] ;
bool state = contact [ RM_CONTACT_STATE_POS ] ;
u8 wx , wy ;
input_mt_slot ( ts - > input , i ) ;
input_mt_report_slot_state ( ts - > input , MT_TOOL_FINGER , state ) ;
if ( ! state )
continue ;
input_report_abs ( ts - > input , ABS_MT_POSITION_X ,
get_unaligned_le16 ( & contact [ RM_CONTACT_X_POS ] ) ) ;
input_report_abs ( ts - > input , ABS_MT_POSITION_Y ,
get_unaligned_le16 ( & contact [ RM_CONTACT_Y_POS ] ) ) ;
input_report_abs ( ts - > input , ABS_MT_PRESSURE ,
contact [ RM_CONTACT_PRESSURE_POS ] ) ;
wx = contact [ RM_CONTACT_WIDTH_X_POS ] ;
wy = contact [ RM_CONTACT_WIDTH_Y_POS ] ;
input_report_abs ( ts - > input , ABS_MT_TOUCH_MAJOR , max ( wx , wy ) ) ;
input_report_abs ( ts - > input , ABS_MT_TOUCH_MINOR , min ( wx , wy ) ) ;
}
input_mt_sync_frame ( ts - > input ) ;
input_sync ( ts - > input ) ;
}
static irqreturn_t raydium_i2c_irq ( int irq , void * _dev )
{
struct raydium_data * ts = _dev ;
2016-07-12 03:01:17 +03:00
int error ;
u16 fw_crc ;
u16 calc_crc ;
if ( ts - > boot_mode ! = RAYDIUM_TS_MAIN )
goto out ;
error = raydium_i2c_read_message ( ts - > client , ts - > data_bank_addr ,
ts - > report_data , ts - > pkg_size ) ;
if ( error )
goto out ;
fw_crc = get_unaligned_le16 ( & ts - > report_data [ ts - > report_size ] ) ;
calc_crc = raydium_calc_chksum ( ts - > report_data , ts - > report_size ) ;
if ( unlikely ( fw_crc ! = calc_crc ) ) {
dev_warn ( & ts - > client - > dev ,
" %s: invalid packet crc %#04x vs %#04x \n " ,
__func__ , calc_crc , fw_crc ) ;
goto out ;
}
2016-04-01 03:03:33 +03:00
2016-07-12 03:01:17 +03:00
raydium_mt_event ( ts ) ;
2016-04-01 03:03:33 +03:00
2016-07-12 03:01:17 +03:00
out :
2016-04-01 03:03:33 +03:00
return IRQ_HANDLED ;
}
static ssize_t raydium_i2c_fw_ver_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct raydium_data * ts = i2c_get_clientdata ( client ) ;
return sprintf ( buf , " %d.%d \n " , ts - > info . main_ver , ts - > info . sub_ver ) ;
}
static ssize_t raydium_i2c_hw_ver_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct raydium_data * ts = i2c_get_clientdata ( client ) ;
return sprintf ( buf , " %#04x \n " , le32_to_cpu ( ts - > info . hw_ver ) ) ;
}
static ssize_t raydium_i2c_boot_mode_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct raydium_data * ts = i2c_get_clientdata ( client ) ;
return sprintf ( buf , " %s \n " ,
ts - > boot_mode = = RAYDIUM_TS_MAIN ?
" Normal " : " Recovery " ) ;
}
static ssize_t raydium_i2c_update_fw_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct raydium_data * ts = i2c_get_clientdata ( client ) ;
int error ;
error = mutex_lock_interruptible ( & ts - > sysfs_mutex ) ;
if ( error )
return error ;
error = raydium_i2c_fw_update ( ts ) ;
mutex_unlock ( & ts - > sysfs_mutex ) ;
return error ? : count ;
}
static ssize_t raydium_i2c_calibrate_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct raydium_data * ts = i2c_get_clientdata ( client ) ;
static const u8 cal_cmd [ ] = { 0x00 , 0x01 , 0x9E } ;
int error ;
error = mutex_lock_interruptible ( & ts - > sysfs_mutex ) ;
if ( error )
return error ;
error = raydium_i2c_write_object ( client , cal_cmd , sizeof ( cal_cmd ) ,
RAYDIUM_WAIT_READY ) ;
if ( error )
dev_err ( & client - > dev , " calibrate command failed: %d \n " , error ) ;
mutex_unlock ( & ts - > sysfs_mutex ) ;
return error ? : count ;
}
static DEVICE_ATTR ( fw_version , S_IRUGO , raydium_i2c_fw_ver_show , NULL ) ;
static DEVICE_ATTR ( hw_version , S_IRUGO , raydium_i2c_hw_ver_show , NULL ) ;
static DEVICE_ATTR ( boot_mode , S_IRUGO , raydium_i2c_boot_mode_show , NULL ) ;
static DEVICE_ATTR ( update_fw , S_IWUSR , NULL , raydium_i2c_update_fw_store ) ;
static DEVICE_ATTR ( calibrate , S_IWUSR , NULL , raydium_i2c_calibrate_store ) ;
static struct attribute * raydium_i2c_attributes [ ] = {
& dev_attr_update_fw . attr ,
& dev_attr_boot_mode . attr ,
& dev_attr_fw_version . attr ,
& dev_attr_hw_version . attr ,
& dev_attr_calibrate . attr ,
NULL
} ;
2017-07-11 06:23:06 +03:00
static const struct attribute_group raydium_i2c_attribute_group = {
2016-04-01 03:03:33 +03:00
. attrs = raydium_i2c_attributes ,
} ;
static int raydium_i2c_power_on ( struct raydium_data * ts )
{
int error ;
2016-06-01 04:15:04 +03:00
if ( ! ts - > reset_gpio )
2016-04-01 03:03:33 +03:00
return 0 ;
gpiod_set_value_cansleep ( ts - > reset_gpio , 1 ) ;
error = regulator_enable ( ts - > avdd ) ;
if ( error ) {
dev_err ( & ts - > client - > dev ,
" failed to enable avdd regulator: %d \n " , error ) ;
goto release_reset_gpio ;
}
error = regulator_enable ( ts - > vccio ) ;
if ( error ) {
regulator_disable ( ts - > avdd ) ;
dev_err ( & ts - > client - > dev ,
" failed to enable vccio regulator: %d \n " , error ) ;
goto release_reset_gpio ;
}
udelay ( RM_POWERON_DELAY_USEC ) ;
release_reset_gpio :
gpiod_set_value_cansleep ( ts - > reset_gpio , 0 ) ;
if ( error )
return error ;
msleep ( RM_RESET_DELAY_MSEC ) ;
return 0 ;
}
static void raydium_i2c_power_off ( void * _data )
{
struct raydium_data * ts = _data ;
2016-06-01 04:15:04 +03:00
if ( ts - > reset_gpio ) {
2016-04-01 03:03:33 +03:00
gpiod_set_value_cansleep ( ts - > reset_gpio , 1 ) ;
regulator_disable ( ts - > vccio ) ;
regulator_disable ( ts - > avdd ) ;
}
}
static int raydium_i2c_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
union i2c_smbus_data dummy ;
struct raydium_data * ts ;
int error ;
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
dev_err ( & client - > dev ,
" i2c check functionality error (need I2C_FUNC_I2C) \n " ) ;
return - ENXIO ;
}
ts = devm_kzalloc ( & client - > dev , sizeof ( * ts ) , GFP_KERNEL ) ;
if ( ! ts )
return - ENOMEM ;
mutex_init ( & ts - > sysfs_mutex ) ;
ts - > client = client ;
i2c_set_clientdata ( client , ts ) ;
ts - > avdd = devm_regulator_get ( & client - > dev , " avdd " ) ;
if ( IS_ERR ( ts - > avdd ) ) {
error = PTR_ERR ( ts - > avdd ) ;
if ( error ! = - EPROBE_DEFER )
dev_err ( & client - > dev ,
" Failed to get 'avdd' regulator: %d \n " , error ) ;
return error ;
}
ts - > vccio = devm_regulator_get ( & client - > dev , " vccio " ) ;
if ( IS_ERR ( ts - > vccio ) ) {
error = PTR_ERR ( ts - > vccio ) ;
if ( error ! = - EPROBE_DEFER )
dev_err ( & client - > dev ,
" Failed to get 'vccio' regulator: %d \n " , error ) ;
return error ;
}
ts - > reset_gpio = devm_gpiod_get_optional ( & client - > dev , " reset " ,
GPIOD_OUT_LOW ) ;
if ( IS_ERR ( ts - > reset_gpio ) ) {
error = PTR_ERR ( ts - > reset_gpio ) ;
2016-06-01 04:15:04 +03:00
if ( error ! = - EPROBE_DEFER )
2016-04-01 03:03:33 +03:00
dev_err ( & client - > dev ,
" failed to get reset gpio: %d \n " , error ) ;
2016-06-01 04:15:04 +03:00
return error ;
2016-04-01 03:03:33 +03:00
}
error = raydium_i2c_power_on ( ts ) ;
if ( error )
return error ;
error = devm_add_action ( & client - > dev , raydium_i2c_power_off , ts ) ;
if ( error ) {
dev_err ( & client - > dev ,
" failed to install power off action: %d \n " , error ) ;
raydium_i2c_power_off ( ts ) ;
return error ;
}
/* Make sure there is something at this address */
if ( i2c_smbus_xfer ( client - > adapter , client - > addr , 0 ,
I2C_SMBUS_READ , 0 , I2C_SMBUS_BYTE , & dummy ) < 0 ) {
dev_err ( & client - > dev , " nothing at this address \n " ) ;
return - ENXIO ;
}
error = raydium_i2c_initialize ( ts ) ;
if ( error ) {
dev_err ( & client - > dev , " failed to initialize: %d \n " , error ) ;
return error ;
}
ts - > report_data = devm_kmalloc ( & client - > dev ,
2016-07-12 03:01:17 +03:00
ts - > pkg_size , GFP_KERNEL ) ;
2016-04-01 03:03:33 +03:00
if ( ! ts - > report_data )
return - ENOMEM ;
ts - > input = devm_input_allocate_device ( & client - > dev ) ;
if ( ! ts - > input ) {
dev_err ( & client - > dev , " Failed to allocate input device \n " ) ;
return - ENOMEM ;
}
ts - > input - > name = " Raydium Touchscreen " ;
ts - > input - > id . bustype = BUS_I2C ;
input_set_abs_params ( ts - > input , ABS_MT_POSITION_X ,
0 , le16_to_cpu ( ts - > info . x_max ) , 0 , 0 ) ;
input_set_abs_params ( ts - > input , ABS_MT_POSITION_Y ,
0 , le16_to_cpu ( ts - > info . y_max ) , 0 , 0 ) ;
input_abs_set_res ( ts - > input , ABS_MT_POSITION_X , ts - > info . x_res ) ;
input_abs_set_res ( ts - > input , ABS_MT_POSITION_Y , ts - > info . y_res ) ;
input_set_abs_params ( ts - > input , ABS_MT_TOUCH_MAJOR , 0 , 255 , 0 , 0 ) ;
input_set_abs_params ( ts - > input , ABS_MT_PRESSURE , 0 , 255 , 0 , 0 ) ;
error = input_mt_init_slots ( ts - > input , RM_MAX_TOUCH_NUM ,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED ) ;
if ( error ) {
dev_err ( & client - > dev ,
" failed to initialize MT slots: %d \n " , error ) ;
return error ;
}
error = input_register_device ( ts - > input ) ;
if ( error ) {
dev_err ( & client - > dev ,
" unable to register input device: %d \n " , error ) ;
return error ;
}
error = devm_request_threaded_irq ( & client - > dev , client - > irq ,
NULL , raydium_i2c_irq ,
IRQF_ONESHOT , client - > name , ts ) ;
if ( error ) {
dev_err ( & client - > dev , " Failed to register interrupt \n " ) ;
return error ;
}
2017-09-30 02:38:57 +03:00
error = devm_device_add_group ( & client - > dev ,
2016-04-01 03:03:33 +03:00
& raydium_i2c_attribute_group ) ;
if ( error ) {
dev_err ( & client - > dev , " failed to create sysfs attributes: %d \n " ,
error ) ;
return error ;
}
return 0 ;
}
static void __maybe_unused raydium_enter_sleep ( struct i2c_client * client )
{
static const u8 sleep_cmd [ ] = { 0x5A , 0xff , 0x00 , 0x0f } ;
int error ;
error = raydium_i2c_send ( client , RM_CMD_ENTER_SLEEP ,
sleep_cmd , sizeof ( sleep_cmd ) ) ;
if ( error )
dev_err ( & client - > dev ,
" sleep command failed: %d \n " , error ) ;
}
static int __maybe_unused raydium_i2c_suspend ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct raydium_data * ts = i2c_get_clientdata ( client ) ;
/* Sleep is not available in BLDR recovery mode */
if ( ts - > boot_mode ! = RAYDIUM_TS_MAIN )
2016-06-01 04:15:04 +03:00
return - EBUSY ;
2016-04-01 03:03:33 +03:00
disable_irq ( client - > irq ) ;
if ( device_may_wakeup ( dev ) ) {
raydium_enter_sleep ( client ) ;
ts - > wake_irq_enabled = ( enable_irq_wake ( client - > irq ) = = 0 ) ;
} else {
raydium_i2c_power_off ( ts ) ;
}
return 0 ;
}
static int __maybe_unused raydium_i2c_resume ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct raydium_data * ts = i2c_get_clientdata ( client ) ;
if ( device_may_wakeup ( dev ) ) {
if ( ts - > wake_irq_enabled )
disable_irq_wake ( client - > irq ) ;
raydium_i2c_sw_reset ( client ) ;
} else {
raydium_i2c_power_on ( ts ) ;
raydium_i2c_initialize ( ts ) ;
}
enable_irq ( client - > irq ) ;
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( raydium_i2c_pm_ops ,
raydium_i2c_suspend , raydium_i2c_resume ) ;
static const struct i2c_device_id raydium_i2c_id [ ] = {
{ " raydium_i2c " , 0 } ,
{ " rm32380 " , 0 } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( i2c , raydium_i2c_id ) ;
# ifdef CONFIG_ACPI
static const struct acpi_device_id raydium_acpi_id [ ] = {
{ " RAYD0001 " , 0 } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( acpi , raydium_acpi_id ) ;
# endif
# ifdef CONFIG_OF
static const struct of_device_id raydium_of_match [ ] = {
{ . compatible = " raydium,rm32380 " , } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , raydium_of_match ) ;
# endif
static struct i2c_driver raydium_i2c_driver = {
. probe = raydium_i2c_probe ,
. id_table = raydium_i2c_id ,
. driver = {
. name = " raydium_ts " ,
. pm = & raydium_i2c_pm_ops ,
. acpi_match_table = ACPI_PTR ( raydium_acpi_id ) ,
. of_match_table = of_match_ptr ( raydium_of_match ) ,
} ,
} ;
module_i2c_driver ( raydium_i2c_driver ) ;
MODULE_AUTHOR ( " Raydium " ) ;
MODULE_DESCRIPTION ( " Raydium I2c Touchscreen driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;