2021-04-09 13:29:03 -07:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for MStar msg2638 touchscreens
*
* Copyright ( c ) 2021 Vincent Knecht < vincent . knecht @ mailoo . org >
*
* Checksum and IRQ handler based on mstar_drv_common . c and
* mstar_drv_mutual_fw_control . c
* Copyright ( c ) 2006 - 2012 MStar Semiconductor , Inc .
*
* Driver structure based on zinitix . c by Michael Srba < Michael . Srba @ seznam . cz >
*/
# include <linux/delay.h>
# include <linux/gpio/consumer.h>
# include <linux/i2c.h>
# include <linux/input.h>
# include <linux/input/mt.h>
# include <linux/input/touchscreen.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/mod_devicetable.h>
# include <linux/module.h>
2022-11-16 15:49:15 -08:00
# include <linux/property.h>
2021-04-09 13:29:03 -07:00
# include <linux/regulator/consumer.h>
# include <linux/slab.h>
# define MODE_DATA_RAW 0x5A
2022-11-14 16:59:23 -08:00
# define MSG2138_MAX_FINGERS 2
2022-11-14 16:58:23 -08:00
# define MSG2638_MAX_FINGERS 5
2021-04-09 13:29:03 -07:00
2022-11-16 15:49:15 -08:00
# define MAX_BUTTONS 4
2021-04-09 13:29:03 -07:00
# define CHIP_ON_DELAY_MS 15
# define FIRMWARE_ON_DELAY_MS 50
# define RESET_DELAY_MIN_US 10000
# define RESET_DELAY_MAX_US 11000
2022-11-14 16:58:23 -08:00
struct msg_chip_data {
irq_handler_t irq_handler ;
unsigned int max_fingers ;
} ;
2022-11-14 16:59:23 -08:00
struct msg2138_packet {
u8 xy_hi ; /* higher bits of x and y coordinates */
u8 x_low ;
u8 y_low ;
} ;
struct msg2138_touch_event {
u8 magic ;
struct msg2138_packet pkt [ MSG2138_MAX_FINGERS ] ;
u8 checksum ;
} ;
2022-11-14 16:58:23 -08:00
struct msg2638_packet {
2021-04-09 13:29:03 -07:00
u8 xy_hi ; /* higher bits of x and y coordinates */
u8 x_low ;
u8 y_low ;
u8 pressure ;
} ;
2022-11-14 16:58:23 -08:00
struct msg2638_touch_event {
2021-04-09 13:29:03 -07:00
u8 mode ;
2022-11-14 16:58:23 -08:00
struct msg2638_packet pkt [ MSG2638_MAX_FINGERS ] ;
2021-04-09 13:29:03 -07:00
u8 proximity ;
u8 checksum ;
} ;
struct msg2638_ts_data {
struct i2c_client * client ;
struct input_dev * input_dev ;
struct touchscreen_properties prop ;
struct regulator_bulk_data supplies [ 2 ] ;
struct gpio_desc * reset_gpiod ;
2022-11-14 16:58:23 -08:00
int max_fingers ;
2022-11-16 15:49:15 -08:00
u32 keycodes [ MAX_BUTTONS ] ;
int num_keycodes ;
2021-04-09 13:29:03 -07:00
} ;
static u8 msg2638_checksum ( u8 * data , u32 length )
{
s32 sum = 0 ;
u32 i ;
for ( i = 0 ; i < length ; i + + )
sum + = data [ i ] ;
return ( u8 ) ( ( - sum ) & 0xFF ) ;
}
2022-11-16 15:49:15 -08:00
static void msg2138_report_keys ( struct msg2638_ts_data * msg2638 , u8 keys )
{
int i ;
/* keys can be 0x00 or 0xff when all keys have been released */
if ( keys = = 0xff )
keys = 0 ;
for ( i = 0 ; i < msg2638 - > num_keycodes ; + + i )
input_report_key ( msg2638 - > input_dev , msg2638 - > keycodes [ i ] ,
keys & BIT ( i ) ) ;
}
2022-11-14 16:59:23 -08:00
static irqreturn_t msg2138_ts_irq_handler ( int irq , void * msg2638_handler )
{
struct msg2638_ts_data * msg2638 = msg2638_handler ;
struct i2c_client * client = msg2638 - > client ;
struct input_dev * input = msg2638 - > input_dev ;
struct msg2138_touch_event touch_event ;
u32 len = sizeof ( touch_event ) ;
struct i2c_msg msg [ ] = {
{
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = sizeof ( touch_event ) ,
. buf = ( u8 * ) & touch_event ,
} ,
} ;
struct msg2138_packet * p0 , * p1 ;
u16 x , y , delta_x , delta_y ;
int ret ;
ret = i2c_transfer ( client - > adapter , msg , ARRAY_SIZE ( msg ) ) ;
if ( ret ! = ARRAY_SIZE ( msg ) ) {
dev_err ( & client - > dev ,
" Failed I2C transfer in irq handler: %d \n " ,
ret < 0 ? ret : - EIO ) ;
goto out ;
}
if ( msg2638_checksum ( ( u8 * ) & touch_event , len - 1 ) ! =
touch_event . checksum ) {
dev_err ( & client - > dev , " Failed checksum! \n " ) ;
goto out ;
}
p0 = & touch_event . pkt [ 0 ] ;
p1 = & touch_event . pkt [ 1 ] ;
2022-11-16 15:49:15 -08:00
/* Ignore non-pressed finger data, but check for key code */
if ( p0 - > xy_hi = = 0xFF & & p0 - > x_low = = 0xFF & & p0 - > y_low = = 0xFF ) {
if ( p1 - > xy_hi = = 0xFF & & p1 - > y_low = = 0xFF )
msg2138_report_keys ( msg2638 , p1 - > x_low ) ;
2022-11-14 16:59:23 -08:00
goto report ;
2022-11-16 15:49:15 -08:00
}
2022-11-14 16:59:23 -08:00
x = ( ( p0 - > xy_hi & 0xF0 ) < < 4 ) | p0 - > x_low ;
y = ( ( p0 - > xy_hi & 0x0F ) < < 8 ) | p0 - > y_low ;
input_mt_slot ( input , 0 ) ;
input_mt_report_slot_state ( input , MT_TOOL_FINGER , true ) ;
touchscreen_report_pos ( input , & msg2638 - > prop , x , y , true ) ;
/* Ignore non-pressed finger data */
if ( p1 - > xy_hi = = 0xFF & & p1 - > x_low = = 0xFF & & p1 - > y_low = = 0xFF )
goto report ;
/* Second finger is reported as a delta position */
delta_x = ( ( p1 - > xy_hi & 0xF0 ) < < 4 ) | p1 - > x_low ;
delta_y = ( ( p1 - > xy_hi & 0x0F ) < < 8 ) | p1 - > y_low ;
/* Ignore second finger if both deltas equal 0 */
if ( delta_x = = 0 & & delta_y = = 0 )
goto report ;
x + = delta_x ;
y + = delta_y ;
input_mt_slot ( input , 1 ) ;
input_mt_report_slot_state ( input , MT_TOOL_FINGER , true ) ;
touchscreen_report_pos ( input , & msg2638 - > prop , x , y , true ) ;
report :
input_mt_sync_frame ( msg2638 - > input_dev ) ;
input_sync ( msg2638 - > input_dev ) ;
out :
return IRQ_HANDLED ;
}
2021-04-09 13:29:03 -07:00
static irqreturn_t msg2638_ts_irq_handler ( int irq , void * msg2638_handler )
{
struct msg2638_ts_data * msg2638 = msg2638_handler ;
struct i2c_client * client = msg2638 - > client ;
struct input_dev * input = msg2638 - > input_dev ;
2022-11-14 16:58:23 -08:00
struct msg2638_touch_event touch_event ;
2021-04-09 13:29:03 -07:00
u32 len = sizeof ( touch_event ) ;
struct i2c_msg msg [ ] = {
{
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = sizeof ( touch_event ) ,
. buf = ( u8 * ) & touch_event ,
} ,
} ;
2022-11-14 16:58:23 -08:00
struct msg2638_packet * p ;
2021-04-09 13:29:03 -07:00
u16 x , y ;
int ret ;
int i ;
ret = i2c_transfer ( client - > adapter , msg , ARRAY_SIZE ( msg ) ) ;
if ( ret ! = ARRAY_SIZE ( msg ) ) {
dev_err ( & client - > dev ,
" Failed I2C transfer in irq handler: %d \n " ,
ret < 0 ? ret : - EIO ) ;
goto out ;
}
if ( touch_event . mode ! = MODE_DATA_RAW )
goto out ;
if ( msg2638_checksum ( ( u8 * ) & touch_event , len - 1 ) ! =
touch_event . checksum ) {
dev_err ( & client - > dev , " Failed checksum! \n " ) ;
goto out ;
}
2022-11-14 16:58:23 -08:00
for ( i = 0 ; i < msg2638 - > max_fingers ; i + + ) {
2021-04-09 13:29:03 -07:00
p = & touch_event . pkt [ i ] ;
/* Ignore non-pressed finger data */
if ( p - > xy_hi = = 0xFF & & p - > x_low = = 0xFF & & p - > y_low = = 0xFF )
continue ;
x = ( ( ( p - > xy_hi & 0xF0 ) < < 4 ) | p - > x_low ) ;
y = ( ( ( p - > xy_hi & 0x0F ) < < 8 ) | p - > y_low ) ;
input_mt_slot ( input , i ) ;
input_mt_report_slot_state ( input , MT_TOOL_FINGER , true ) ;
touchscreen_report_pos ( input , & msg2638 - > prop , x , y , true ) ;
}
input_mt_sync_frame ( msg2638 - > input_dev ) ;
input_sync ( msg2638 - > input_dev ) ;
out :
return IRQ_HANDLED ;
}
static void msg2638_reset ( struct msg2638_ts_data * msg2638 )
{
gpiod_set_value_cansleep ( msg2638 - > reset_gpiod , 1 ) ;
usleep_range ( RESET_DELAY_MIN_US , RESET_DELAY_MAX_US ) ;
gpiod_set_value_cansleep ( msg2638 - > reset_gpiod , 0 ) ;
msleep ( FIRMWARE_ON_DELAY_MS ) ;
}
static int msg2638_start ( struct msg2638_ts_data * msg2638 )
{
int error ;
error = regulator_bulk_enable ( ARRAY_SIZE ( msg2638 - > supplies ) ,
msg2638 - > supplies ) ;
if ( error ) {
dev_err ( & msg2638 - > client - > dev ,
" Failed to enable regulators: %d \n " , error ) ;
return error ;
}
msleep ( CHIP_ON_DELAY_MS ) ;
msg2638_reset ( msg2638 ) ;
enable_irq ( msg2638 - > client - > irq ) ;
return 0 ;
}
static int msg2638_stop ( struct msg2638_ts_data * msg2638 )
{
int error ;
disable_irq ( msg2638 - > client - > irq ) ;
error = regulator_bulk_disable ( ARRAY_SIZE ( msg2638 - > supplies ) ,
msg2638 - > supplies ) ;
if ( error ) {
dev_err ( & msg2638 - > client - > dev ,
" Failed to disable regulators: %d \n " , error ) ;
return error ;
}
return 0 ;
}
static int msg2638_input_open ( struct input_dev * dev )
{
struct msg2638_ts_data * msg2638 = input_get_drvdata ( dev ) ;
return msg2638_start ( msg2638 ) ;
}
static void msg2638_input_close ( struct input_dev * dev )
{
struct msg2638_ts_data * msg2638 = input_get_drvdata ( dev ) ;
msg2638_stop ( msg2638 ) ;
}
static int msg2638_init_input_dev ( struct msg2638_ts_data * msg2638 )
{
struct device * dev = & msg2638 - > client - > dev ;
struct input_dev * input_dev ;
int error ;
2022-11-16 15:49:15 -08:00
int i ;
2021-04-09 13:29:03 -07:00
input_dev = devm_input_allocate_device ( dev ) ;
if ( ! input_dev ) {
dev_err ( dev , " Failed to allocate input device. \n " ) ;
return - ENOMEM ;
}
input_set_drvdata ( input_dev , msg2638 ) ;
msg2638 - > input_dev = input_dev ;
input_dev - > name = " MStar TouchScreen " ;
input_dev - > phys = " input/ts " ;
input_dev - > id . bustype = BUS_I2C ;
input_dev - > open = msg2638_input_open ;
input_dev - > close = msg2638_input_close ;
2022-11-16 15:49:15 -08:00
if ( msg2638 - > num_keycodes ) {
input_dev - > keycode = msg2638 - > keycodes ;
input_dev - > keycodemax = msg2638 - > num_keycodes ;
input_dev - > keycodesize = sizeof ( msg2638 - > keycodes [ 0 ] ) ;
for ( i = 0 ; i < msg2638 - > num_keycodes ; i + + )
input_set_capability ( input_dev ,
EV_KEY , msg2638 - > keycodes [ i ] ) ;
}
2021-04-09 13:29:03 -07:00
input_set_capability ( input_dev , EV_ABS , ABS_MT_POSITION_X ) ;
input_set_capability ( input_dev , EV_ABS , ABS_MT_POSITION_Y ) ;
touchscreen_parse_properties ( input_dev , true , & msg2638 - > prop ) ;
if ( ! msg2638 - > prop . max_x | | ! msg2638 - > prop . max_y ) {
dev_err ( dev , " touchscreen-size-x and/or touchscreen-size-y not set in properties \n " ) ;
return - EINVAL ;
}
2022-11-14 16:58:23 -08:00
error = input_mt_init_slots ( input_dev , msg2638 - > max_fingers ,
2021-04-09 13:29:03 -07:00
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED ) ;
if ( error ) {
dev_err ( dev , " Failed to initialize MT slots: %d \n " , error ) ;
return error ;
}
error = input_register_device ( input_dev ) ;
if ( error ) {
dev_err ( dev , " Failed to register input device: %d \n " , error ) ;
return error ;
}
return 0 ;
}
static int msg2638_ts_probe ( struct i2c_client * client )
{
2022-11-14 16:58:23 -08:00
const struct msg_chip_data * chip_data ;
2021-04-09 13:29:03 -07:00
struct device * dev = & client - > dev ;
struct msg2638_ts_data * msg2638 ;
int error ;
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
dev_err ( dev , " Failed to assert adapter's support for plain I2C. \n " ) ;
return - ENXIO ;
}
msg2638 = devm_kzalloc ( dev , sizeof ( * msg2638 ) , GFP_KERNEL ) ;
if ( ! msg2638 )
return - ENOMEM ;
msg2638 - > client = client ;
i2c_set_clientdata ( client , msg2638 ) ;
2022-11-14 16:58:23 -08:00
chip_data = device_get_match_data ( & client - > dev ) ;
if ( ! chip_data | | ! chip_data - > max_fingers ) {
dev_err ( dev , " Invalid or missing chip data \n " ) ;
return - EINVAL ;
}
msg2638 - > max_fingers = chip_data - > max_fingers ;
2021-04-09 13:29:03 -07:00
msg2638 - > supplies [ 0 ] . supply = " vdd " ;
msg2638 - > supplies [ 1 ] . supply = " vddio " ;
error = devm_regulator_bulk_get ( dev , ARRAY_SIZE ( msg2638 - > supplies ) ,
msg2638 - > supplies ) ;
if ( error ) {
dev_err ( dev , " Failed to get regulators: %d \n " , error ) ;
return error ;
}
msg2638 - > reset_gpiod = devm_gpiod_get ( dev , " reset " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( msg2638 - > reset_gpiod ) ) {
error = PTR_ERR ( msg2638 - > reset_gpiod ) ;
dev_err ( dev , " Failed to request reset GPIO: %d \n " , error ) ;
return error ;
}
2022-11-16 15:49:15 -08:00
msg2638 - > num_keycodes = device_property_count_u32 ( dev ,
" linux,keycodes " ) ;
if ( msg2638 - > num_keycodes = = - EINVAL ) {
msg2638 - > num_keycodes = 0 ;
} else if ( msg2638 - > num_keycodes < 0 ) {
dev_err ( dev , " Unable to parse linux,keycodes property: %d \n " ,
msg2638 - > num_keycodes ) ;
return msg2638 - > num_keycodes ;
} else if ( msg2638 - > num_keycodes > ARRAY_SIZE ( msg2638 - > keycodes ) ) {
dev_warn ( dev , " Found %d linux,keycodes but max is %zd, ignoring the rest \n " ,
msg2638 - > num_keycodes , ARRAY_SIZE ( msg2638 - > keycodes ) ) ;
msg2638 - > num_keycodes = ARRAY_SIZE ( msg2638 - > keycodes ) ;
}
2022-11-30 15:15:25 -08:00
if ( msg2638 - > num_keycodes > 0 ) {
error = device_property_read_u32_array ( dev , " linux,keycodes " ,
msg2638 - > keycodes ,
msg2638 - > num_keycodes ) ;
if ( error ) {
dev_err ( dev , " Unable to read linux,keycodes values: %d \n " ,
error ) ;
return error ;
}
2021-04-09 13:29:03 -07:00
}
error = devm_request_threaded_irq ( dev , client - > irq ,
2022-11-14 16:58:23 -08:00
NULL , chip_data - > irq_handler ,
2021-04-09 13:29:03 -07:00
IRQF_ONESHOT | IRQF_NO_AUTOEN ,
client - > name , msg2638 ) ;
if ( error ) {
dev_err ( dev , " Failed to request IRQ: %d \n " , error ) ;
return error ;
}
2022-11-16 15:49:15 -08:00
error = msg2638_init_input_dev ( msg2638 ) ;
if ( error ) {
dev_err ( dev , " Failed to initialize input device: %d \n " , error ) ;
return error ;
}
2021-04-09 13:29:03 -07:00
return 0 ;
}
2023-01-02 18:18:29 +00:00
static int msg2638_suspend ( struct device * dev )
2021-04-09 13:29:03 -07:00
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct msg2638_ts_data * msg2638 = i2c_get_clientdata ( client ) ;
mutex_lock ( & msg2638 - > input_dev - > mutex ) ;
if ( input_device_enabled ( msg2638 - > input_dev ) )
msg2638_stop ( msg2638 ) ;
mutex_unlock ( & msg2638 - > input_dev - > mutex ) ;
return 0 ;
}
2023-01-02 18:18:29 +00:00
static int msg2638_resume ( struct device * dev )
2021-04-09 13:29:03 -07:00
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct msg2638_ts_data * msg2638 = i2c_get_clientdata ( client ) ;
int ret = 0 ;
mutex_lock ( & msg2638 - > input_dev - > mutex ) ;
if ( input_device_enabled ( msg2638 - > input_dev ) )
ret = msg2638_start ( msg2638 ) ;
mutex_unlock ( & msg2638 - > input_dev - > mutex ) ;
return ret ;
}
2023-01-02 18:18:29 +00:00
static DEFINE_SIMPLE_DEV_PM_OPS ( msg2638_pm_ops , msg2638_suspend , msg2638_resume ) ;
2021-04-09 13:29:03 -07:00
2022-11-14 16:59:23 -08:00
static const struct msg_chip_data msg2138_data = {
. irq_handler = msg2138_ts_irq_handler ,
. max_fingers = MSG2138_MAX_FINGERS ,
} ;
2022-11-14 16:58:23 -08:00
static const struct msg_chip_data msg2638_data = {
. irq_handler = msg2638_ts_irq_handler ,
. max_fingers = MSG2638_MAX_FINGERS ,
} ;
2021-04-09 13:29:03 -07:00
static const struct of_device_id msg2638_of_match [ ] = {
2022-11-14 16:59:23 -08:00
{ . compatible = " mstar,msg2138 " , . data = & msg2138_data } ,
2022-11-14 16:58:23 -08:00
{ . compatible = " mstar,msg2638 " , . data = & msg2638_data } ,
2021-04-09 13:29:03 -07:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , msg2638_of_match ) ;
static struct i2c_driver msg2638_ts_driver = {
. probe_new = msg2638_ts_probe ,
. driver = {
. name = " MStar-TS " ,
2023-01-02 18:18:29 +00:00
. pm = pm_sleep_ptr ( & msg2638_pm_ops ) ,
2021-04-09 13:29:03 -07:00
. of_match_table = msg2638_of_match ,
} ,
} ;
module_i2c_driver ( msg2638_ts_driver ) ;
MODULE_AUTHOR ( " Vincent Knecht <vincent.knecht@mailoo.org> " ) ;
MODULE_DESCRIPTION ( " MStar MSG2638 touchscreen driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;