2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2016-01-14 11:55:54 +03:00
/*
* MELFAS MIP4 Touchscreen
*
* Copyright ( C ) 2016 MELFAS Inc .
*
* Author : Sangwon Jee < jeesw @ melfas . 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/slab.h>
# include <asm/unaligned.h>
# define MIP4_DEVICE_NAME "mip4_ts"
/*****************************************************************
* Protocol
2016-10-22 01:28:59 +03:00
* Version : MIP 4.0 Rev 5.4
2016-01-14 11:55:54 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Address */
# define MIP4_R0_BOOT 0x00
# define MIP4_R1_BOOT_MODE 0x01
# define MIP4_R1_BOOT_BUF_ADDR 0x10
# define MIP4_R1_BOOT_STATUS 0x20
# define MIP4_R1_BOOT_CMD 0x30
# define MIP4_R1_BOOT_TARGET_ADDR 0x40
# define MIP4_R1_BOOT_SIZE 0x44
# define MIP4_R0_INFO 0x01
# define MIP4_R1_INFO_PRODUCT_NAME 0x00
# define MIP4_R1_INFO_RESOLUTION_X 0x10
# define MIP4_R1_INFO_RESOLUTION_Y 0x12
# define MIP4_R1_INFO_NODE_NUM_X 0x14
# define MIP4_R1_INFO_NODE_NUM_Y 0x15
# define MIP4_R1_INFO_KEY_NUM 0x16
2016-03-04 22:13:37 +03:00
# define MIP4_R1_INFO_PRESSURE_NUM 0x17
# define MIP4_R1_INFO_LENGTH_X 0x18
# define MIP4_R1_INFO_LENGTH_Y 0x1A
# define MIP4_R1_INFO_PPM_X 0x1C
# define MIP4_R1_INFO_PPM_Y 0x1D
2016-01-14 11:55:54 +03:00
# define MIP4_R1_INFO_VERSION_BOOT 0x20
# define MIP4_R1_INFO_VERSION_CORE 0x22
# define MIP4_R1_INFO_VERSION_APP 0x24
# define MIP4_R1_INFO_VERSION_PARAM 0x26
# define MIP4_R1_INFO_SECT_BOOT_START 0x30
# define MIP4_R1_INFO_SECT_BOOT_END 0x31
# define MIP4_R1_INFO_SECT_CORE_START 0x32
# define MIP4_R1_INFO_SECT_CORE_END 0x33
# define MIP4_R1_INFO_SECT_APP_START 0x34
# define MIP4_R1_INFO_SECT_APP_END 0x35
# define MIP4_R1_INFO_SECT_PARAM_START 0x36
# define MIP4_R1_INFO_SECT_PARAM_END 0x37
# define MIP4_R1_INFO_BUILD_DATE 0x40
# define MIP4_R1_INFO_BUILD_TIME 0x44
# define MIP4_R1_INFO_CHECKSUM_PRECALC 0x48
# define MIP4_R1_INFO_CHECKSUM_REALTIME 0x4A
# define MIP4_R1_INFO_PROTOCOL_NAME 0x50
# define MIP4_R1_INFO_PROTOCOL_VERSION 0x58
# define MIP4_R1_INFO_IC_ID 0x70
# define MIP4_R1_INFO_IC_NAME 0x71
# define MIP4_R1_INFO_IC_VENDOR_ID 0x75
# define MIP4_R1_INFO_IC_HW_CATEGORY 0x77
# define MIP4_R1_INFO_CONTACT_THD_SCR 0x78
# define MIP4_R1_INFO_CONTACT_THD_KEY 0x7A
2016-10-22 01:28:59 +03:00
# define MIP4_R1_INFO_PID 0x7C
# define MIP4_R1_INFO_VID 0x7E
# define MIP4_R1_INFO_SLAVE_ADDR 0x80
2016-01-14 11:55:54 +03:00
# define MIP4_R0_EVENT 0x02
# define MIP4_R1_EVENT_SUPPORTED_FUNC 0x00
# define MIP4_R1_EVENT_FORMAT 0x04
# define MIP4_R1_EVENT_SIZE 0x06
# define MIP4_R1_EVENT_PACKET_INFO 0x10
# define MIP4_R1_EVENT_PACKET_DATA 0x11
# define MIP4_R0_CTRL 0x06
# define MIP4_R1_CTRL_READY_STATUS 0x00
# define MIP4_R1_CTRL_EVENT_READY 0x01
# define MIP4_R1_CTRL_MODE 0x10
# define MIP4_R1_CTRL_EVENT_TRIGGER_TYPE 0x11
# define MIP4_R1_CTRL_RECALIBRATE 0x12
# define MIP4_R1_CTRL_POWER_STATE 0x13
# define MIP4_R1_CTRL_GESTURE_TYPE 0x14
# define MIP4_R1_CTRL_DISABLE_ESD_ALERT 0x18
# define MIP4_R1_CTRL_CHARGER_MODE 0x19
# define MIP4_R1_CTRL_HIGH_SENS_MODE 0x1A
# define MIP4_R1_CTRL_WINDOW_MODE 0x1B
# define MIP4_R1_CTRL_PALM_REJECTION 0x1C
# define MIP4_R1_CTRL_EDGE_CORRECTION 0x1D
# define MIP4_R1_CTRL_ENTER_GLOVE_MODE 0x1E
# define MIP4_R1_CTRL_I2C_ON_LPM 0x1F
# define MIP4_R1_CTRL_GESTURE_DEBUG 0x20
# define MIP4_R1_CTRL_PALM_EVENT 0x22
# define MIP4_R1_CTRL_PROXIMITY_SENSING 0x23
/* Value */
# define MIP4_BOOT_MODE_BOOT 0x01
# define MIP4_BOOT_MODE_APP 0x02
# define MIP4_BOOT_STATUS_BUSY 0x05
# define MIP4_BOOT_STATUS_ERROR 0x0E
# define MIP4_BOOT_STATUS_DONE 0xA0
# define MIP4_BOOT_CMD_MASS_ERASE 0x15
# define MIP4_BOOT_CMD_PROGRAM 0x54
# define MIP4_BOOT_CMD_ERASE 0x8F
# define MIP4_BOOT_CMD_WRITE 0xA5
# define MIP4_BOOT_CMD_READ 0xC2
# define MIP4_EVENT_INPUT_TYPE_KEY 0
# define MIP4_EVENT_INPUT_TYPE_SCREEN 1
# define MIP4_EVENT_INPUT_TYPE_PROXIMITY 2
# define I2C_RETRY_COUNT 3 /* 2~ */
# define MIP4_BUF_SIZE 128
# define MIP4_MAX_FINGERS 10
# define MIP4_MAX_KEYS 4
# define MIP4_TOUCH_MAJOR_MIN 0
# define MIP4_TOUCH_MAJOR_MAX 255
# define MIP4_TOUCH_MINOR_MIN 0
# define MIP4_TOUCH_MINOR_MAX 255
# define MIP4_PRESSURE_MIN 0
# define MIP4_PRESSURE_MAX 255
# define MIP4_FW_NAME "melfas_mip4.fw"
# define MIP4_FW_UPDATE_DEBUG 0 /* 0 (default) or 1 */
struct mip4_fw_version {
u16 boot ;
u16 core ;
u16 app ;
u16 param ;
} ;
struct mip4_ts {
struct i2c_client * client ;
struct input_dev * input ;
struct gpio_desc * gpio_ce ;
char phys [ 32 ] ;
char product_name [ 16 ] ;
2016-10-22 01:28:59 +03:00
u16 product_id ;
2016-10-01 01:38:47 +03:00
char ic_name [ 4 ] ;
2016-10-27 01:53:13 +03:00
char fw_name [ 32 ] ;
2016-01-14 11:55:54 +03:00
unsigned int max_x ;
unsigned int max_y ;
u8 node_x ;
u8 node_y ;
u8 node_key ;
2016-03-04 22:13:37 +03:00
unsigned int ppm_x ;
unsigned int ppm_y ;
2016-01-14 11:55:54 +03:00
struct mip4_fw_version fw_version ;
unsigned int event_size ;
unsigned int event_format ;
unsigned int key_num ;
unsigned short key_code [ MIP4_MAX_KEYS ] ;
bool wake_irq_enabled ;
u8 buf [ MIP4_BUF_SIZE ] ;
} ;
static int mip4_i2c_xfer ( struct mip4_ts * ts ,
char * write_buf , unsigned int write_len ,
char * read_buf , unsigned int read_len )
{
struct i2c_msg msg [ ] = {
{
. addr = ts - > client - > addr ,
. flags = 0 ,
. buf = write_buf ,
. len = write_len ,
} , {
. addr = ts - > client - > addr ,
. flags = I2C_M_RD ,
. buf = read_buf ,
. len = read_len ,
} ,
} ;
int retry = I2C_RETRY_COUNT ;
int res ;
int error ;
do {
res = i2c_transfer ( ts - > client - > adapter , msg , ARRAY_SIZE ( msg ) ) ;
if ( res = = ARRAY_SIZE ( msg ) )
return 0 ;
error = res < 0 ? res : - EIO ;
dev_err ( & ts - > client - > dev ,
" %s - i2c_transfer failed: %d (%d) \n " ,
__func__ , error , res ) ;
} while ( - - retry ) ;
return error ;
}
static void mip4_parse_fw_version ( const u8 * buf , struct mip4_fw_version * v )
{
v - > boot = get_unaligned_le16 ( buf + 0 ) ;
v - > core = get_unaligned_le16 ( buf + 2 ) ;
v - > app = get_unaligned_le16 ( buf + 4 ) ;
v - > param = get_unaligned_le16 ( buf + 6 ) ;
}
/*
* Read chip firmware version
*/
static int mip4_get_fw_version ( struct mip4_ts * ts )
{
u8 cmd [ ] = { MIP4_R0_INFO , MIP4_R1_INFO_VERSION_BOOT } ;
u8 buf [ sizeof ( ts - > fw_version ) ] ;
int error ;
error = mip4_i2c_xfer ( ts , cmd , sizeof ( cmd ) , buf , sizeof ( buf ) ) ;
if ( error ) {
memset ( & ts - > fw_version , 0xff , sizeof ( ts - > fw_version ) ) ;
return error ;
}
mip4_parse_fw_version ( buf , & ts - > fw_version ) ;
return 0 ;
}
/*
* Fetch device characteristics
*/
static int mip4_query_device ( struct mip4_ts * ts )
{
2017-03-25 02:50:28 +03:00
union i2c_smbus_data dummy ;
2016-01-14 11:55:54 +03:00
int error ;
u8 cmd [ 2 ] ;
2016-03-04 22:13:37 +03:00
u8 buf [ 14 ] ;
2016-01-14 11:55:54 +03:00
2017-03-25 02:50:28 +03:00
/*
* Make sure there is something at this address as we do not
* consider subsequent failures as fatal .
*/
if ( i2c_smbus_xfer ( ts - > client - > adapter , ts - > client - > addr ,
0 , I2C_SMBUS_READ , 0 , I2C_SMBUS_BYTE , & dummy ) < 0 ) {
dev_err ( & ts - > client - > dev , " nothing at this address \n " ) ;
return - ENXIO ;
}
2016-01-14 11:55:54 +03:00
/* Product name */
cmd [ 0 ] = MIP4_R0_INFO ;
cmd [ 1 ] = MIP4_R1_INFO_PRODUCT_NAME ;
error = mip4_i2c_xfer ( ts , cmd , sizeof ( cmd ) ,
ts - > product_name , sizeof ( ts - > product_name ) ) ;
if ( error )
dev_warn ( & ts - > client - > dev ,
" Failed to retrieve product name: %d \n " , error ) ;
else
dev_dbg ( & ts - > client - > dev , " product name: %.*s \n " ,
( int ) sizeof ( ts - > product_name ) , ts - > product_name ) ;
2016-10-22 01:28:59 +03:00
/* Product ID */
cmd [ 0 ] = MIP4_R0_INFO ;
cmd [ 1 ] = MIP4_R1_INFO_PID ;
error = mip4_i2c_xfer ( ts , cmd , sizeof ( cmd ) , buf , 2 ) ;
if ( error ) {
dev_warn ( & ts - > client - > dev ,
" Failed to retrieve product id: %d \n " , error ) ;
} else {
ts - > product_id = get_unaligned_le16 ( & buf [ 0 ] ) ;
dev_dbg ( & ts - > client - > dev , " product id: %04X \n " , ts - > product_id ) ;
}
2016-10-27 01:53:13 +03:00
/* Firmware name */
snprintf ( ts - > fw_name , sizeof ( ts - > fw_name ) ,
" melfas_mip4_%04X.fw " , ts - > product_id ) ;
dev_dbg ( & ts - > client - > dev , " firmware name: %s \n " , ts - > fw_name ) ;
2016-10-01 01:38:47 +03:00
/* IC name */
cmd [ 0 ] = MIP4_R0_INFO ;
cmd [ 1 ] = MIP4_R1_INFO_IC_NAME ;
error = mip4_i2c_xfer ( ts , cmd , sizeof ( cmd ) ,
ts - > ic_name , sizeof ( ts - > ic_name ) ) ;
if ( error )
dev_warn ( & ts - > client - > dev ,
" Failed to retrieve IC name: %d \n " , error ) ;
else
dev_dbg ( & ts - > client - > dev , " IC name: %.*s \n " ,
( int ) sizeof ( ts - > ic_name ) , ts - > ic_name ) ;
2016-01-14 11:55:54 +03:00
/* Firmware version */
error = mip4_get_fw_version ( ts ) ;
if ( error )
dev_warn ( & ts - > client - > dev ,
" Failed to retrieve FW version: %d \n " , error ) ;
else
dev_dbg ( & ts - > client - > dev , " F/W Version: %04X %04X %04X %04X \n " ,
ts - > fw_version . boot , ts - > fw_version . core ,
ts - > fw_version . app , ts - > fw_version . param ) ;
/* Resolution */
cmd [ 0 ] = MIP4_R0_INFO ;
cmd [ 1 ] = MIP4_R1_INFO_RESOLUTION_X ;
2016-03-04 22:13:37 +03:00
error = mip4_i2c_xfer ( ts , cmd , sizeof ( cmd ) , buf , 14 ) ;
2016-01-14 11:55:54 +03:00
if ( error ) {
dev_warn ( & ts - > client - > dev ,
" Failed to retrieve touchscreen parameters: %d \n " ,
error ) ;
} else {
ts - > max_x = get_unaligned_le16 ( & buf [ 0 ] ) ;
ts - > max_y = get_unaligned_le16 ( & buf [ 2 ] ) ;
dev_dbg ( & ts - > client - > dev , " max_x: %d, max_y: %d \n " ,
ts - > max_x , ts - > max_y ) ;
ts - > node_x = buf [ 4 ] ;
ts - > node_y = buf [ 5 ] ;
ts - > node_key = buf [ 6 ] ;
dev_dbg ( & ts - > client - > dev ,
" node_x: %d, node_y: %d, node_key: %d \n " ,
ts - > node_x , ts - > node_y , ts - > node_key ) ;
2016-03-04 22:13:37 +03:00
ts - > ppm_x = buf [ 12 ] ;
ts - > ppm_y = buf [ 13 ] ;
dev_dbg ( & ts - > client - > dev , " ppm_x: %d, ppm_y: %d \n " ,
ts - > ppm_x , ts - > ppm_y ) ;
2016-01-14 11:55:54 +03:00
/* Key ts */
if ( ts - > node_key > 0 )
ts - > key_num = ts - > node_key ;
}
/* Protocol */
cmd [ 0 ] = MIP4_R0_EVENT ;
cmd [ 1 ] = MIP4_R1_EVENT_SUPPORTED_FUNC ;
error = mip4_i2c_xfer ( ts , cmd , sizeof ( cmd ) , buf , 7 ) ;
if ( error ) {
dev_warn ( & ts - > client - > dev ,
" Failed to retrieve device type: %d \n " , error ) ;
ts - > event_format = 0xff ;
} else {
ts - > event_format = get_unaligned_le16 ( & buf [ 4 ] ) ;
ts - > event_size = buf [ 6 ] ;
dev_dbg ( & ts - > client - > dev , " event_format: %d, event_size: %d \n " ,
ts - > event_format , ts - > event_size ) ;
if ( ts - > event_format = = 2 | | ts - > event_format > 3 )
dev_warn ( & ts - > client - > dev ,
" Unknown event format %d \n " , ts - > event_format ) ;
}
return 0 ;
}
static int mip4_power_on ( struct mip4_ts * ts )
{
if ( ts - > gpio_ce ) {
gpiod_set_value_cansleep ( ts - > gpio_ce , 1 ) ;
/* Booting delay : 200~300ms */
usleep_range ( 200 * 1000 , 300 * 1000 ) ;
}
return 0 ;
}
static void mip4_power_off ( struct mip4_ts * ts )
{
if ( ts - > gpio_ce )
gpiod_set_value_cansleep ( ts - > gpio_ce , 0 ) ;
}
/*
* Clear touch input event status
*/
static void mip4_clear_input ( struct mip4_ts * ts )
{
int i ;
/* Screen */
for ( i = 0 ; i < MIP4_MAX_FINGERS ; i + + ) {
input_mt_slot ( ts - > input , i ) ;
input_mt_report_slot_state ( ts - > input , MT_TOOL_FINGER , 0 ) ;
}
/* Keys */
for ( i = 0 ; i < ts - > key_num ; i + + )
input_report_key ( ts - > input , ts - > key_code [ i ] , 0 ) ;
input_sync ( ts - > input ) ;
}
static int mip4_enable ( struct mip4_ts * ts )
{
int error ;
error = mip4_power_on ( ts ) ;
if ( error )
return error ;
enable_irq ( ts - > client - > irq ) ;
return 0 ;
}
static void mip4_disable ( struct mip4_ts * ts )
{
disable_irq ( ts - > client - > irq ) ;
mip4_power_off ( ts ) ;
mip4_clear_input ( ts ) ;
}
/*****************************************************************
* Input handling
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void mip4_report_keys ( struct mip4_ts * ts , u8 * packet )
{
u8 key ;
bool down ;
switch ( ts - > event_format ) {
case 0 :
case 1 :
key = packet [ 0 ] & 0x0F ;
down = packet [ 0 ] & 0x80 ;
break ;
case 3 :
default :
key = packet [ 0 ] & 0x0F ;
down = packet [ 1 ] & 0x01 ;
break ;
}
/* Report key event */
if ( key > = 1 & & key < = ts - > key_num ) {
unsigned short keycode = ts - > key_code [ key - 1 ] ;
dev_dbg ( & ts - > client - > dev ,
" Key - ID: %d, keycode: %d, state: %d \n " ,
key , keycode , down ) ;
input_event ( ts - > input , EV_MSC , MSC_SCAN , keycode ) ;
input_report_key ( ts - > input , keycode , down ) ;
} else {
dev_err ( & ts - > client - > dev , " Unknown key: %d \n " , key ) ;
}
}
static void mip4_report_touch ( struct mip4_ts * ts , u8 * packet )
{
int id ;
bool hover ;
bool palm ;
bool state ;
u16 x , y ;
u8 pressure_stage = 0 ;
u8 pressure ;
u8 size ;
u8 touch_major ;
u8 touch_minor ;
switch ( ts - > event_format ) {
case 0 :
case 1 :
/* Touch only */
state = packet [ 0 ] & BIT ( 7 ) ;
hover = packet [ 0 ] & BIT ( 5 ) ;
palm = packet [ 0 ] & BIT ( 4 ) ;
id = ( packet [ 0 ] & 0x0F ) - 1 ;
x = ( ( packet [ 1 ] & 0x0F ) < < 8 ) | packet [ 2 ] ;
y = ( ( ( packet [ 1 ] > > 4 ) & 0x0F ) < < 8 ) |
packet [ 3 ] ;
pressure = packet [ 4 ] ;
size = packet [ 5 ] ;
if ( ts - > event_format = = 0 ) {
touch_major = packet [ 5 ] ;
touch_minor = packet [ 5 ] ;
} else {
touch_major = packet [ 6 ] ;
touch_minor = packet [ 7 ] ;
}
break ;
case 3 :
default :
/* Touch + Force(Pressure) */
id = ( packet [ 0 ] & 0x0F ) - 1 ;
hover = packet [ 1 ] & BIT ( 2 ) ;
palm = packet [ 1 ] & BIT ( 1 ) ;
state = packet [ 1 ] & BIT ( 0 ) ;
x = ( ( packet [ 2 ] & 0x0F ) < < 8 ) | packet [ 3 ] ;
y = ( ( ( packet [ 2 ] > > 4 ) & 0x0F ) < < 8 ) |
packet [ 4 ] ;
size = packet [ 6 ] ;
pressure_stage = ( packet [ 7 ] & 0xF0 ) > > 4 ;
pressure = ( ( packet [ 7 ] & 0x0F ) < < 8 ) |
packet [ 8 ] ;
touch_major = packet [ 9 ] ;
touch_minor = packet [ 10 ] ;
break ;
}
dev_dbg ( & ts - > client - > dev ,
" Screen - Slot: %d State: %d X: %04d Y: %04d Z: %d \n " ,
id , state , x , y , pressure ) ;
if ( unlikely ( id < 0 | | id > = MIP4_MAX_FINGERS ) ) {
dev_err ( & ts - > client - > dev , " Screen - invalid slot ID: %d \n " , id ) ;
} else if ( state ) {
/* Press or Move event */
input_mt_slot ( ts - > input , id ) ;
input_mt_report_slot_state ( ts - > input , MT_TOOL_FINGER , true ) ;
input_report_abs ( ts - > input , ABS_MT_POSITION_X , x ) ;
input_report_abs ( ts - > input , ABS_MT_POSITION_Y , y ) ;
input_report_abs ( ts - > input , ABS_MT_PRESSURE , pressure ) ;
input_report_abs ( ts - > input , ABS_MT_TOUCH_MAJOR , touch_major ) ;
input_report_abs ( ts - > input , ABS_MT_TOUCH_MINOR , touch_minor ) ;
} else {
/* Release event */
input_mt_slot ( ts - > input , id ) ;
input_mt_report_slot_state ( ts - > input , MT_TOOL_FINGER , 0 ) ;
}
input_mt_sync_frame ( ts - > input ) ;
}
static int mip4_handle_packet ( struct mip4_ts * ts , u8 * packet )
{
u8 type ;
switch ( ts - > event_format ) {
case 0 :
case 1 :
type = ( packet [ 0 ] & 0x40 ) > > 6 ;
break ;
case 3 :
type = ( packet [ 0 ] & 0xF0 ) > > 4 ;
break ;
default :
/* Should not happen unless we have corrupted firmware */
return - EINVAL ;
}
dev_dbg ( & ts - > client - > dev , " Type: %d \n " , type ) ;
/* Report input event */
switch ( type ) {
case MIP4_EVENT_INPUT_TYPE_KEY :
mip4_report_keys ( ts , packet ) ;
break ;
case MIP4_EVENT_INPUT_TYPE_SCREEN :
mip4_report_touch ( ts , packet ) ;
break ;
default :
dev_err ( & ts - > client - > dev , " Unknown event type: %d \n " , type ) ;
break ;
}
return 0 ;
}
static irqreturn_t mip4_interrupt ( int irq , void * dev_id )
{
struct mip4_ts * ts = dev_id ;
struct i2c_client * client = ts - > client ;
unsigned int i ;
int error ;
u8 cmd [ 2 ] ;
u8 size ;
bool alert ;
/* Read packet info */
cmd [ 0 ] = MIP4_R0_EVENT ;
cmd [ 1 ] = MIP4_R1_EVENT_PACKET_INFO ;
error = mip4_i2c_xfer ( ts , cmd , sizeof ( cmd ) , ts - > buf , 1 ) ;
if ( error ) {
dev_err ( & client - > dev ,
" Failed to read packet info: %d \n " , error ) ;
goto out ;
}
size = ts - > buf [ 0 ] & 0x7F ;
alert = ts - > buf [ 0 ] & BIT ( 7 ) ;
dev_dbg ( & client - > dev , " packet size: %d, alert: %d \n " , size , alert ) ;
/* Check size */
if ( ! size ) {
dev_err ( & client - > dev , " Empty packet \n " ) ;
goto out ;
}
/* Read packet data */
cmd [ 0 ] = MIP4_R0_EVENT ;
cmd [ 1 ] = MIP4_R1_EVENT_PACKET_DATA ;
error = mip4_i2c_xfer ( ts , cmd , sizeof ( cmd ) , ts - > buf , size ) ;
if ( error ) {
dev_err ( & client - > dev ,
" Failed to read packet data: %d \n " , error ) ;
goto out ;
}
if ( alert ) {
dev_dbg ( & client - > dev , " Alert: %d \n " , ts - > buf [ 0 ] ) ;
} else {
for ( i = 0 ; i < size ; i + = ts - > event_size ) {
error = mip4_handle_packet ( ts , & ts - > buf [ i ] ) ;
if ( error )
break ;
}
input_sync ( ts - > input ) ;
}
out :
return IRQ_HANDLED ;
}
static int mip4_input_open ( struct input_dev * dev )
{
struct mip4_ts * ts = input_get_drvdata ( dev ) ;
return mip4_enable ( ts ) ;
}
static void mip4_input_close ( struct input_dev * dev )
{
struct mip4_ts * ts = input_get_drvdata ( dev ) ;
mip4_disable ( ts ) ;
}
/*****************************************************************
* Firmware update
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Firmware Info */
# define MIP4_BL_PAGE_SIZE 512 /* 512 */
# define MIP4_BL_PACKET_SIZE 512 /* 512, 256, 128, 64, ... */
/*
* Firmware binary tail info
*/
struct mip4_bin_tail {
u8 tail_mark [ 4 ] ;
u8 chip_name [ 4 ] ;
__le32 bin_start_addr ;
__le32 bin_length ;
__le16 ver_boot ;
__le16 ver_core ;
__le16 ver_app ;
__le16 ver_param ;
u8 boot_start ;
u8 boot_end ;
u8 core_start ;
u8 core_end ;
u8 app_start ;
u8 app_end ;
u8 param_start ;
u8 param_end ;
u8 checksum_type ;
u8 hw_category ;
__le16 param_id ;
__le32 param_length ;
__le32 build_date ;
__le32 build_time ;
__le32 reserved1 ;
__le32 reserved2 ;
__le16 reserved3 ;
__le16 tail_size ;
__le32 crc ;
} __packed ;
# define MIP4_BIN_TAIL_MARK "MBT\001"
# define MIP4_BIN_TAIL_SIZE (sizeof(struct mip4_bin_tail))
/*
* Bootloader - Read status
*/
static int mip4_bl_read_status ( struct mip4_ts * ts )
{
u8 cmd [ ] = { MIP4_R0_BOOT , MIP4_R1_BOOT_STATUS } ;
u8 result ;
struct i2c_msg msg [ ] = {
{
. addr = ts - > client - > addr ,
. flags = 0 ,
. buf = cmd ,
. len = sizeof ( cmd ) ,
} , {
. addr = ts - > client - > addr ,
. flags = I2C_M_RD ,
. buf = & result ,
. len = sizeof ( result ) ,
} ,
} ;
int ret ;
int error ;
int retry = 1000 ;
do {
ret = i2c_transfer ( ts - > client - > adapter , msg , ARRAY_SIZE ( msg ) ) ;
if ( ret ! = ARRAY_SIZE ( msg ) ) {
error = ret < 0 ? ret : - EIO ;
dev_err ( & ts - > client - > dev ,
" Failed to read bootloader status: %d \n " ,
error ) ;
return error ;
}
switch ( result ) {
case MIP4_BOOT_STATUS_DONE :
dev_dbg ( & ts - > client - > dev , " %s - done \n " , __func__ ) ;
return 0 ;
case MIP4_BOOT_STATUS_ERROR :
dev_err ( & ts - > client - > dev , " Bootloader failure \n " ) ;
return - EIO ;
case MIP4_BOOT_STATUS_BUSY :
dev_dbg ( & ts - > client - > dev , " %s - Busy \n " , __func__ ) ;
error = - EBUSY ;
break ;
default :
dev_err ( & ts - > client - > dev ,
" Unexpected bootloader status: %#02x \n " ,
result ) ;
error = - EINVAL ;
break ;
}
usleep_range ( 1000 , 2000 ) ;
} while ( - - retry ) ;
return error ;
}
/*
* Bootloader - Change mode
*/
static int mip4_bl_change_mode ( struct mip4_ts * ts , u8 mode )
{
u8 mode_chg_cmd [ ] = { MIP4_R0_BOOT , MIP4_R1_BOOT_MODE , mode } ;
u8 mode_read_cmd [ ] = { MIP4_R0_BOOT , MIP4_R1_BOOT_MODE } ;
u8 result ;
struct i2c_msg msg [ ] = {
{
. addr = ts - > client - > addr ,
. flags = 0 ,
. buf = mode_read_cmd ,
. len = sizeof ( mode_read_cmd ) ,
} , {
. addr = ts - > client - > addr ,
. flags = I2C_M_RD ,
. buf = & result ,
. len = sizeof ( result ) ,
} ,
} ;
int retry = 10 ;
int ret ;
int error ;
do {
/* Send mode change command */
ret = i2c_master_send ( ts - > client ,
mode_chg_cmd , sizeof ( mode_chg_cmd ) ) ;
if ( ret ! = sizeof ( mode_chg_cmd ) ) {
error = ret < 0 ? ret : - EIO ;
dev_err ( & ts - > client - > dev ,
" Failed to send %d mode change: %d (%d) \n " ,
mode , error , ret ) ;
return error ;
}
dev_dbg ( & ts - > client - > dev ,
" Sent mode change request (mode: %d) \n " , mode ) ;
/* Wait */
msleep ( 1000 ) ;
/* Verify target mode */
ret = i2c_transfer ( ts - > client - > adapter , msg , ARRAY_SIZE ( msg ) ) ;
if ( ret ! = ARRAY_SIZE ( msg ) ) {
error = ret < 0 ? ret : - EIO ;
dev_err ( & ts - > client - > dev ,
" Failed to read device mode: %d \n " , error ) ;
return error ;
}
dev_dbg ( & ts - > client - > dev ,
" Current device mode: %d, want: %d \n " , result , mode ) ;
if ( result = = mode )
return 0 ;
} while ( - - retry ) ;
return - EIO ;
}
/*
* Bootloader - Start bootloader mode
*/
static int mip4_bl_enter ( struct mip4_ts * ts )
{
return mip4_bl_change_mode ( ts , MIP4_BOOT_MODE_BOOT ) ;
}
/*
* Bootloader - Exit bootloader mode
*/
static int mip4_bl_exit ( struct mip4_ts * ts )
{
return mip4_bl_change_mode ( ts , MIP4_BOOT_MODE_APP ) ;
}
static int mip4_bl_get_address ( struct mip4_ts * ts , u16 * buf_addr )
{
u8 cmd [ ] = { MIP4_R0_BOOT , MIP4_R1_BOOT_BUF_ADDR } ;
u8 result [ sizeof ( u16 ) ] ;
struct i2c_msg msg [ ] = {
{
. addr = ts - > client - > addr ,
. flags = 0 ,
. buf = cmd ,
. len = sizeof ( cmd ) ,
} , {
. addr = ts - > client - > addr ,
. flags = I2C_M_RD ,
. buf = result ,
. len = sizeof ( result ) ,
} ,
} ;
int ret ;
int error ;
ret = i2c_transfer ( ts - > client - > adapter , msg , ARRAY_SIZE ( msg ) ) ;
if ( ret ! = ARRAY_SIZE ( msg ) ) {
error = ret < 0 ? ret : - EIO ;
dev_err ( & ts - > client - > dev ,
" Failed to retrieve bootloader buffer address: %d \n " ,
error ) ;
return error ;
}
* buf_addr = get_unaligned_le16 ( result ) ;
dev_dbg ( & ts - > client - > dev ,
" Bootloader buffer address %#04x \n " , * buf_addr ) ;
return 0 ;
}
static int mip4_bl_program_page ( struct mip4_ts * ts , int offset ,
const u8 * data , int length , u16 buf_addr )
{
u8 cmd [ 6 ] ;
u8 * data_buf ;
u16 buf_offset ;
int ret ;
int error ;
dev_dbg ( & ts - > client - > dev , " Writing page @%#06x (%d) \n " ,
offset , length ) ;
if ( length > MIP4_BL_PAGE_SIZE | | length % MIP4_BL_PACKET_SIZE ) {
dev_err ( & ts - > client - > dev ,
" Invalid page length: %d \n " , length ) ;
return - EINVAL ;
}
data_buf = kmalloc ( 2 + MIP4_BL_PACKET_SIZE , GFP_KERNEL ) ;
if ( ! data_buf )
return - ENOMEM ;
/* Addr */
cmd [ 0 ] = MIP4_R0_BOOT ;
cmd [ 1 ] = MIP4_R1_BOOT_TARGET_ADDR ;
put_unaligned_le32 ( offset , & cmd [ 2 ] ) ;
ret = i2c_master_send ( ts - > client , cmd , 6 ) ;
if ( ret ! = 6 ) {
error = ret < 0 ? ret : - EIO ;
dev_err ( & ts - > client - > dev ,
" Failed to send write page address: %d \n " , error ) ;
goto out ;
}
/* Size */
cmd [ 0 ] = MIP4_R0_BOOT ;
cmd [ 1 ] = MIP4_R1_BOOT_SIZE ;
put_unaligned_le32 ( length , & cmd [ 2 ] ) ;
ret = i2c_master_send ( ts - > client , cmd , 6 ) ;
if ( ret ! = 6 ) {
error = ret < 0 ? ret : - EIO ;
dev_err ( & ts - > client - > dev ,
" Failed to send write page size: %d \n " , error ) ;
goto out ;
}
/* Data */
for ( buf_offset = 0 ;
buf_offset < length ;
buf_offset + = MIP4_BL_PACKET_SIZE ) {
dev_dbg ( & ts - > client - > dev ,
" writing chunk at %#04x (size %d) \n " ,
buf_offset , MIP4_BL_PACKET_SIZE ) ;
put_unaligned_be16 ( buf_addr + buf_offset , data_buf ) ;
memcpy ( & data_buf [ 2 ] , & data [ buf_offset ] , MIP4_BL_PACKET_SIZE ) ;
ret = i2c_master_send ( ts - > client ,
data_buf , 2 + MIP4_BL_PACKET_SIZE ) ;
if ( ret ! = 2 + MIP4_BL_PACKET_SIZE ) {
error = ret < 0 ? ret : - EIO ;
dev_err ( & ts - > client - > dev ,
" Failed to read chunk at %#04x (size %d): %d \n " ,
buf_offset , MIP4_BL_PACKET_SIZE , error ) ;
goto out ;
}
}
/* Command */
cmd [ 0 ] = MIP4_R0_BOOT ;
cmd [ 1 ] = MIP4_R1_BOOT_CMD ;
cmd [ 2 ] = MIP4_BOOT_CMD_PROGRAM ;
ret = i2c_master_send ( ts - > client , cmd , 3 ) ;
if ( ret ! = 3 ) {
error = ret < 0 ? ret : - EIO ;
dev_err ( & ts - > client - > dev ,
" Failed to send 'write' command: %d \n " , error ) ;
goto out ;
}
/* Status */
error = mip4_bl_read_status ( ts ) ;
out :
kfree ( data_buf ) ;
return error ? error : 0 ;
}
static int mip4_bl_verify_page ( struct mip4_ts * ts , int offset ,
const u8 * data , int length , int buf_addr )
{
u8 cmd [ 8 ] ;
u8 * read_buf ;
int buf_offset ;
struct i2c_msg msg [ ] = {
{
. addr = ts - > client - > addr ,
. flags = 0 ,
. buf = cmd ,
. len = 2 ,
} , {
. addr = ts - > client - > addr ,
. flags = I2C_M_RD ,
. len = MIP4_BL_PACKET_SIZE ,
} ,
} ;
int ret ;
int error ;
dev_dbg ( & ts - > client - > dev , " Validating page @%#06x (%d) \n " ,
offset , length ) ;
/* Addr */
cmd [ 0 ] = MIP4_R0_BOOT ;
cmd [ 1 ] = MIP4_R1_BOOT_TARGET_ADDR ;
put_unaligned_le32 ( offset , & cmd [ 2 ] ) ;
ret = i2c_master_send ( ts - > client , cmd , 6 ) ;
if ( ret ! = 6 ) {
error = ret < 0 ? ret : - EIO ;
dev_err ( & ts - > client - > dev ,
" Failed to send read page address: %d \n " , error ) ;
return error ;
}
/* Size */
cmd [ 0 ] = MIP4_R0_BOOT ;
cmd [ 1 ] = MIP4_R1_BOOT_SIZE ;
put_unaligned_le32 ( length , & cmd [ 2 ] ) ;
ret = i2c_master_send ( ts - > client , cmd , 6 ) ;
if ( ret ! = 6 ) {
error = ret < 0 ? ret : - EIO ;
dev_err ( & ts - > client - > dev ,
" Failed to send read page size: %d \n " , error ) ;
return error ;
}
/* Command */
cmd [ 0 ] = MIP4_R0_BOOT ;
cmd [ 1 ] = MIP4_R1_BOOT_CMD ;
cmd [ 2 ] = MIP4_BOOT_CMD_READ ;
ret = i2c_master_send ( ts - > client , cmd , 3 ) ;
if ( ret ! = 3 ) {
error = ret < 0 ? ret : - EIO ;
dev_err ( & ts - > client - > dev ,
" Failed to send 'read' command: %d \n " , error ) ;
return error ;
}
/* Status */
error = mip4_bl_read_status ( ts ) ;
if ( error )
return error ;
/* Read */
msg [ 1 ] . buf = read_buf = kmalloc ( MIP4_BL_PACKET_SIZE , GFP_KERNEL ) ;
if ( ! read_buf )
return - ENOMEM ;
for ( buf_offset = 0 ;
buf_offset < length ;
buf_offset + = MIP4_BL_PACKET_SIZE ) {
dev_dbg ( & ts - > client - > dev ,
" reading chunk at %#04x (size %d) \n " ,
buf_offset , MIP4_BL_PACKET_SIZE ) ;
put_unaligned_be16 ( buf_addr + buf_offset , cmd ) ;
ret = i2c_transfer ( ts - > client - > adapter , msg , ARRAY_SIZE ( msg ) ) ;
if ( ret ! = ARRAY_SIZE ( msg ) ) {
error = ret < 0 ? ret : - EIO ;
dev_err ( & ts - > client - > dev ,
" Failed to read chunk at %#04x (size %d): %d \n " ,
buf_offset , MIP4_BL_PACKET_SIZE , error ) ;
break ;
}
if ( memcmp ( & data [ buf_offset ] , read_buf , MIP4_BL_PACKET_SIZE ) ) {
dev_err ( & ts - > client - > dev ,
" Failed to validate chunk at %#04x (size %d) \n " ,
buf_offset , MIP4_BL_PACKET_SIZE ) ;
# if MIP4_FW_UPDATE_DEBUG
print_hex_dump ( KERN_DEBUG ,
MIP4_DEVICE_NAME " F/W File: " ,
DUMP_PREFIX_OFFSET , 16 , 1 ,
data + offset , MIP4_BL_PACKET_SIZE ,
false ) ;
print_hex_dump ( KERN_DEBUG ,
MIP4_DEVICE_NAME " F/W Chip: " ,
DUMP_PREFIX_OFFSET , 16 , 1 ,
read_buf , MIP4_BL_PAGE_SIZE , false ) ;
# endif
error = - EINVAL ;
break ;
}
}
kfree ( read_buf ) ;
return error ? error : 0 ;
}
/*
* Flash chip firmware
*/
static int mip4_flash_fw ( struct mip4_ts * ts ,
const u8 * fw_data , u32 fw_size , u32 fw_offset )
{
struct i2c_client * client = ts - > client ;
int offset ;
u16 buf_addr ;
int error , error2 ;
/* Enter bootloader mode */
dev_dbg ( & client - > dev , " Entering bootloader mode \n " ) ;
error = mip4_bl_enter ( ts ) ;
if ( error ) {
dev_err ( & client - > dev ,
" Failed to enter bootloader mode: %d \n " ,
error ) ;
return error ;
}
/* Read info */
error = mip4_bl_get_address ( ts , & buf_addr ) ;
if ( error )
goto exit_bl ;
/* Program & Verify */
dev_dbg ( & client - > dev ,
" Program & Verify, page size: %d, packet size: %d \n " ,
MIP4_BL_PAGE_SIZE , MIP4_BL_PACKET_SIZE ) ;
for ( offset = fw_offset ;
offset < fw_offset + fw_size ;
offset + = MIP4_BL_PAGE_SIZE ) {
/* Program */
error = mip4_bl_program_page ( ts , offset , fw_data + offset ,
MIP4_BL_PAGE_SIZE , buf_addr ) ;
if ( error )
break ;
/* Verify */
error = mip4_bl_verify_page ( ts , offset , fw_data + offset ,
MIP4_BL_PAGE_SIZE , buf_addr ) ;
if ( error )
break ;
}
exit_bl :
/* Exit bootloader mode */
dev_dbg ( & client - > dev , " Exiting bootloader mode \n " ) ;
error2 = mip4_bl_exit ( ts ) ;
if ( error2 ) {
dev_err ( & client - > dev ,
" Failed to exit bootloader mode: %d \n " , error2 ) ;
if ( ! error )
error = error2 ;
}
/* Reset chip */
mip4_power_off ( ts ) ;
mip4_power_on ( ts ) ;
mip4_query_device ( ts ) ;
/* Refresh device parameters */
input_set_abs_params ( ts - > input , ABS_MT_POSITION_X , 0 , ts - > max_x , 0 , 0 ) ;
input_set_abs_params ( ts - > input , ABS_MT_POSITION_Y , 0 , ts - > max_y , 0 , 0 ) ;
input_set_abs_params ( ts - > input , ABS_X , 0 , ts - > max_x , 0 , 0 ) ;
input_set_abs_params ( ts - > input , ABS_Y , 0 , ts - > max_y , 0 , 0 ) ;
2016-03-04 22:13:37 +03:00
input_abs_set_res ( ts - > input , ABS_MT_POSITION_X , ts - > ppm_x ) ;
input_abs_set_res ( ts - > input , ABS_MT_POSITION_Y , ts - > ppm_y ) ;
input_abs_set_res ( ts - > input , ABS_X , ts - > ppm_x ) ;
input_abs_set_res ( ts - > input , ABS_Y , ts - > ppm_y ) ;
2016-01-14 11:55:54 +03:00
return error ? error : 0 ;
}
static int mip4_parse_firmware ( struct mip4_ts * ts , const struct firmware * fw ,
u32 * fw_offset_start , u32 * fw_size ,
const struct mip4_bin_tail * * pfw_info )
{
const struct mip4_bin_tail * fw_info ;
struct mip4_fw_version fw_version ;
u16 tail_size ;
if ( fw - > size < MIP4_BIN_TAIL_SIZE ) {
dev_err ( & ts - > client - > dev ,
" Invalid firmware, size mismatch (tail %zd vs %zd) \n " ,
MIP4_BIN_TAIL_SIZE , fw - > size ) ;
return - EINVAL ;
}
fw_info = ( const void * ) & fw - > data [ fw - > size - MIP4_BIN_TAIL_SIZE ] ;
# if MIP4_FW_UPDATE_DEBUG
print_hex_dump ( KERN_ERR , MIP4_DEVICE_NAME " Bin Info: " ,
DUMP_PREFIX_OFFSET , 16 , 1 , * fw_info , tail_size , false ) ;
# endif
tail_size = get_unaligned_le16 ( & fw_info - > tail_size ) ;
if ( tail_size ! = MIP4_BIN_TAIL_SIZE ) {
dev_err ( & ts - > client - > dev ,
" wrong tail size: %d (expected %zd) \n " ,
tail_size , MIP4_BIN_TAIL_SIZE ) ;
return - EINVAL ;
}
/* Check bin format */
if ( memcmp ( fw_info - > tail_mark , MIP4_BIN_TAIL_MARK ,
sizeof ( fw_info - > tail_mark ) ) ) {
dev_err ( & ts - > client - > dev ,
" unable to locate tail marker (%*ph vs %*ph) \n " ,
( int ) sizeof ( fw_info - > tail_mark ) , fw_info - > tail_mark ,
( int ) sizeof ( fw_info - > tail_mark ) , MIP4_BIN_TAIL_MARK ) ;
return - EINVAL ;
}
* fw_offset_start = get_unaligned_le32 ( & fw_info - > bin_start_addr ) ;
* fw_size = get_unaligned_le32 ( & fw_info - > bin_length ) ;
dev_dbg ( & ts - > client - > dev ,
" F/W Data offset: %#08x, size: %d \n " ,
* fw_offset_start , * fw_size ) ;
if ( * fw_size % MIP4_BL_PAGE_SIZE ) {
dev_err ( & ts - > client - > dev ,
" encoded fw length %d is not multiple of pages (%d) \n " ,
* fw_size , MIP4_BL_PAGE_SIZE ) ;
return - EINVAL ;
}
if ( fw - > size ! = * fw_offset_start + * fw_size ) {
dev_err ( & ts - > client - > dev ,
" Wrong firmware size, expected %d bytes, got %zd \n " ,
* fw_offset_start + * fw_size , fw - > size ) ;
return - EINVAL ;
}
mip4_parse_fw_version ( ( const u8 * ) & fw_info - > ver_boot , & fw_version ) ;
dev_dbg ( & ts - > client - > dev ,
" F/W file version %04X %04X %04X %04X \n " ,
fw_version . boot , fw_version . core ,
fw_version . app , fw_version . param ) ;
dev_dbg ( & ts - > client - > dev , " F/W chip version: %04X %04X %04X %04X \n " ,
ts - > fw_version . boot , ts - > fw_version . core ,
ts - > fw_version . app , ts - > fw_version . param ) ;
/* Check F/W type */
if ( fw_version . boot ! = 0xEEEE & & fw_version . boot ! = 0xFFFF & &
fw_version . core = = 0xEEEE & &
fw_version . app = = 0xEEEE & &
fw_version . param = = 0xEEEE ) {
dev_dbg ( & ts - > client - > dev , " F/W type: Bootloader \n " ) ;
} else if ( fw_version . boot = = 0xEEEE & &
fw_version . core ! = 0xEEEE & & fw_version . core ! = 0xFFFF & &
fw_version . app ! = 0xEEEE & & fw_version . app ! = 0xFFFF & &
fw_version . param ! = 0xEEEE & & fw_version . param ! = 0xFFFF ) {
dev_dbg ( & ts - > client - > dev , " F/W type: Main \n " ) ;
} else {
dev_err ( & ts - > client - > dev , " Wrong firmware type \n " ) ;
return - EINVAL ;
}
return 0 ;
}
static int mip4_execute_fw_update ( struct mip4_ts * ts , const struct firmware * fw )
{
const struct mip4_bin_tail * fw_info ;
u32 fw_start_offset ;
u32 fw_size ;
int retires = 3 ;
int error ;
error = mip4_parse_firmware ( ts , fw ,
& fw_start_offset , & fw_size , & fw_info ) ;
if ( error )
return error ;
if ( ts - > input - > users ) {
disable_irq ( ts - > client - > irq ) ;
} else {
error = mip4_power_on ( ts ) ;
if ( error )
return error ;
}
/* Update firmware */
do {
error = mip4_flash_fw ( ts , fw - > data , fw_size , fw_start_offset ) ;
if ( ! error )
break ;
} while ( - - retires ) ;
if ( error )
dev_err ( & ts - > client - > dev ,
" Failed to flash firmware: %d \n " , error ) ;
/* Enable IRQ */
if ( ts - > input - > users )
enable_irq ( ts - > client - > irq ) ;
else
mip4_power_off ( ts ) ;
return error ? error : 0 ;
}
static ssize_t mip4_sysfs_fw_update ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct mip4_ts * ts = i2c_get_clientdata ( client ) ;
const struct firmware * fw ;
int error ;
2016-10-27 01:53:13 +03:00
error = request_firmware ( & fw , ts - > fw_name , dev ) ;
2016-01-14 11:55:54 +03:00
if ( error ) {
dev_err ( & ts - > client - > dev ,
" Failed to retrieve firmware %s: %d \n " ,
2016-10-27 01:53:13 +03:00
ts - > fw_name , error ) ;
2016-01-14 11:55:54 +03:00
return error ;
}
/*
* Take input mutex to prevent racing with itself and also with
* userspace opening and closing the device and also suspend / resume
* transitions .
*/
mutex_lock ( & ts - > input - > mutex ) ;
error = mip4_execute_fw_update ( ts , fw ) ;
mutex_unlock ( & ts - > input - > mutex ) ;
release_firmware ( fw ) ;
if ( error ) {
dev_err ( & ts - > client - > dev ,
" Firmware update failed: %d \n " , error ) ;
return error ;
}
return count ;
}
static DEVICE_ATTR ( update_fw , S_IWUSR , NULL , mip4_sysfs_fw_update ) ;
static ssize_t mip4_sysfs_read_fw_version ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct mip4_ts * ts = i2c_get_clientdata ( client ) ;
size_t count ;
/* Take lock to prevent racing with firmware update */
mutex_lock ( & ts - > input - > mutex ) ;
count = snprintf ( buf , PAGE_SIZE , " %04X %04X %04X %04X \n " ,
ts - > fw_version . boot , ts - > fw_version . core ,
ts - > fw_version . app , ts - > fw_version . param ) ;
mutex_unlock ( & ts - > input - > mutex ) ;
return count ;
}
static DEVICE_ATTR ( fw_version , S_IRUGO , mip4_sysfs_read_fw_version , NULL ) ;
2016-03-16 22:33:05 +03:00
static ssize_t mip4_sysfs_read_hw_version ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct mip4_ts * ts = i2c_get_clientdata ( client ) ;
size_t count ;
/* Take lock to prevent racing with firmware update */
mutex_lock ( & ts - > input - > mutex ) ;
/*
* product_name shows the name or version of the hardware
* paired with current firmware in the chip .
*/
count = snprintf ( buf , PAGE_SIZE , " %.*s \n " ,
2016-10-01 01:38:47 +03:00
( int ) sizeof ( ts - > product_name ) , ts - > product_name ) ;
2016-03-16 22:33:05 +03:00
mutex_unlock ( & ts - > input - > mutex ) ;
return count ;
}
static DEVICE_ATTR ( hw_version , S_IRUGO , mip4_sysfs_read_hw_version , NULL ) ;
2016-10-22 01:28:59 +03:00
static ssize_t mip4_sysfs_read_product_id ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct mip4_ts * ts = i2c_get_clientdata ( client ) ;
size_t count ;
mutex_lock ( & ts - > input - > mutex ) ;
count = snprintf ( buf , PAGE_SIZE , " %04X \n " , ts - > product_id ) ;
mutex_unlock ( & ts - > input - > mutex ) ;
return count ;
}
static DEVICE_ATTR ( product_id , S_IRUGO , mip4_sysfs_read_product_id , NULL ) ;
2016-10-01 01:38:47 +03:00
static ssize_t mip4_sysfs_read_ic_name ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct mip4_ts * ts = i2c_get_clientdata ( client ) ;
size_t count ;
mutex_lock ( & ts - > input - > mutex ) ;
count = snprintf ( buf , PAGE_SIZE , " %.*s \n " ,
( int ) sizeof ( ts - > ic_name ) , ts - > ic_name ) ;
mutex_unlock ( & ts - > input - > mutex ) ;
return count ;
}
static DEVICE_ATTR ( ic_name , S_IRUGO , mip4_sysfs_read_ic_name , NULL ) ;
2016-01-14 11:55:54 +03:00
static struct attribute * mip4_attrs [ ] = {
& dev_attr_fw_version . attr ,
2016-03-16 22:33:05 +03:00
& dev_attr_hw_version . attr ,
2016-10-22 01:28:59 +03:00
& dev_attr_product_id . attr ,
2016-10-01 01:38:47 +03:00
& dev_attr_ic_name . attr ,
2016-01-14 11:55:54 +03:00
& dev_attr_update_fw . attr ,
NULL ,
} ;
static const struct attribute_group mip4_attr_group = {
. attrs = mip4_attrs ,
} ;
static int mip4_probe ( struct i2c_client * client , const struct i2c_device_id * id )
{
struct mip4_ts * ts ;
struct input_dev * input ;
int error ;
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
dev_err ( & client - > dev , " Not supported I2C adapter \n " ) ;
return - ENXIO ;
}
ts = devm_kzalloc ( & client - > dev , sizeof ( * ts ) , GFP_KERNEL ) ;
if ( ! ts )
return - ENOMEM ;
input = devm_input_allocate_device ( & client - > dev ) ;
if ( ! input )
return - ENOMEM ;
ts - > client = client ;
ts - > input = input ;
snprintf ( ts - > phys , sizeof ( ts - > phys ) ,
" %s/input0 " , dev_name ( & client - > dev ) ) ;
ts - > gpio_ce = devm_gpiod_get_optional ( & client - > dev ,
" ce " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( ts - > gpio_ce ) ) {
error = PTR_ERR ( ts - > gpio_ce ) ;
if ( error ! = EPROBE_DEFER )
dev_err ( & client - > dev ,
" Failed to get gpio: %d \n " , error ) ;
return error ;
}
error = mip4_power_on ( ts ) ;
if ( error )
return error ;
error = mip4_query_device ( ts ) ;
mip4_power_off ( ts ) ;
if ( error )
return error ;
input - > name = " MELFAS MIP4 Touchscreen " ;
input - > phys = ts - > phys ;
input - > id . bustype = BUS_I2C ;
input - > id . vendor = 0x13c5 ;
2016-11-09 03:07:05 +03:00
input - > id . product = ts - > product_id ;
2016-01-14 11:55:54 +03:00
input - > open = mip4_input_open ;
input - > close = mip4_input_close ;
input_set_drvdata ( input , ts ) ;
input - > keycode = ts - > key_code ;
input - > keycodesize = sizeof ( * ts - > key_code ) ;
input - > keycodemax = ts - > key_num ;
input_set_abs_params ( input , ABS_MT_POSITION_X , 0 , ts - > max_x , 0 , 0 ) ;
input_set_abs_params ( input , ABS_MT_POSITION_Y , 0 , ts - > max_y , 0 , 0 ) ;
input_set_abs_params ( input , ABS_MT_PRESSURE ,
MIP4_PRESSURE_MIN , MIP4_PRESSURE_MAX , 0 , 0 ) ;
input_set_abs_params ( input , ABS_MT_TOUCH_MAJOR ,
MIP4_TOUCH_MAJOR_MIN , MIP4_TOUCH_MAJOR_MAX , 0 , 0 ) ;
input_set_abs_params ( input , ABS_MT_TOUCH_MINOR ,
MIP4_TOUCH_MINOR_MIN , MIP4_TOUCH_MINOR_MAX , 0 , 0 ) ;
2016-03-04 22:13:37 +03:00
input_abs_set_res ( ts - > input , ABS_MT_POSITION_X , ts - > ppm_x ) ;
input_abs_set_res ( ts - > input , ABS_MT_POSITION_Y , ts - > ppm_y ) ;
2016-01-14 11:55:54 +03:00
error = input_mt_init_slots ( input , MIP4_MAX_FINGERS , INPUT_MT_DIRECT ) ;
if ( error )
return error ;
i2c_set_clientdata ( client , ts ) ;
error = devm_request_threaded_irq ( & client - > dev , client - > irq ,
NULL , mip4_interrupt ,
IRQF_ONESHOT , MIP4_DEVICE_NAME , ts ) ;
if ( error ) {
dev_err ( & client - > dev ,
" Failed to request interrupt %d: %d \n " ,
client - > irq , error ) ;
return error ;
}
disable_irq ( client - > irq ) ;
error = input_register_device ( input ) ;
if ( error ) {
dev_err ( & client - > dev ,
" Failed to register input device: %d \n " , error ) ;
return error ;
}
2017-09-30 02:38:23 +03:00
error = devm_device_add_group ( & client - > dev , & mip4_attr_group ) ;
2016-01-14 11:55:54 +03:00
if ( error ) {
dev_err ( & client - > dev ,
" Failed to create sysfs attribute group: %d \n " , error ) ;
return error ;
}
return 0 ;
}
static int __maybe_unused mip4_suspend ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct mip4_ts * ts = i2c_get_clientdata ( client ) ;
struct input_dev * input = ts - > input ;
mutex_lock ( & input - > mutex ) ;
if ( device_may_wakeup ( dev ) )
ts - > wake_irq_enabled = enable_irq_wake ( client - > irq ) = = 0 ;
else if ( input - > users )
mip4_disable ( ts ) ;
mutex_unlock ( & input - > mutex ) ;
return 0 ;
}
static int __maybe_unused mip4_resume ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct mip4_ts * ts = i2c_get_clientdata ( client ) ;
struct input_dev * input = ts - > input ;
mutex_lock ( & input - > mutex ) ;
if ( ts - > wake_irq_enabled )
disable_irq_wake ( client - > irq ) ;
else if ( input - > users )
mip4_enable ( ts ) ;
mutex_unlock ( & input - > mutex ) ;
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( mip4_pm_ops , mip4_suspend , mip4_resume ) ;
# ifdef CONFIG_OF
static const struct of_device_id mip4_of_match [ ] = {
{ . compatible = " melfas, " MIP4_DEVICE_NAME , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , mip4_of_match ) ;
# endif
# ifdef CONFIG_ACPI
static const struct acpi_device_id mip4_acpi_match [ ] = {
{ " MLFS0000 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , mip4_acpi_match ) ;
# endif
static const struct i2c_device_id mip4_i2c_ids [ ] = {
{ MIP4_DEVICE_NAME , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , mip4_i2c_ids ) ;
static struct i2c_driver mip4_driver = {
. id_table = mip4_i2c_ids ,
. probe = mip4_probe ,
. driver = {
. name = MIP4_DEVICE_NAME ,
. of_match_table = of_match_ptr ( mip4_of_match ) ,
. acpi_match_table = ACPI_PTR ( mip4_acpi_match ) ,
. pm = & mip4_pm_ops ,
} ,
} ;
module_i2c_driver ( mip4_driver ) ;
MODULE_DESCRIPTION ( " MELFAS MIP4 Touchscreen " ) ;
MODULE_AUTHOR ( " Sangwon Jee <jeesw@melfas.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;