2018-12-21 00:47:40 -08:00
// SPDX-License-Identifier: GPL-2.0
/*
* Raspberry Pi firmware based touchscreen driver
*
* Copyright ( C ) 2015 , 2017 Raspberry Pi
* Copyright ( C ) 2018 Nicolas Saenz Julienne < nsaenzjulienne @ suse . de >
*/
# include <linux/io.h>
# include <linux/of.h>
# include <linux/slab.h>
# include <linux/device.h>
# include <linux/module.h>
# include <linux/bitops.h>
# include <linux/dma-mapping.h>
# include <linux/platform_device.h>
# include <linux/input.h>
# include <linux/input/mt.h>
# include <linux/input/touchscreen.h>
# include <soc/bcm2835/raspberrypi-firmware.h>
# define RPI_TS_DEFAULT_WIDTH 800
# define RPI_TS_DEFAULT_HEIGHT 480
# define RPI_TS_MAX_SUPPORTED_POINTS 10
# define RPI_TS_FTS_TOUCH_DOWN 0
# define RPI_TS_FTS_TOUCH_CONTACT 2
# define RPI_TS_POLL_INTERVAL 17 /* 60fps */
# define RPI_TS_NPOINTS_REG_INVALIDATE 99
struct rpi_ts {
struct platform_device * pdev ;
2019-10-29 16:41:57 -07:00
struct input_dev * input ;
2018-12-21 00:47:40 -08:00
struct touchscreen_properties prop ;
void __iomem * fw_regs_va ;
dma_addr_t fw_regs_phys ;
int known_ids ;
} ;
struct rpi_ts_regs {
u8 device_mode ;
u8 gesture_id ;
u8 num_points ;
struct rpi_ts_touch {
u8 xh ;
u8 xl ;
u8 yh ;
u8 yl ;
u8 pressure ; /* Not supported */
u8 area ; /* Not supported */
} point [ RPI_TS_MAX_SUPPORTED_POINTS ] ;
} ;
2019-10-29 16:41:57 -07:00
static void rpi_ts_poll ( struct input_dev * input )
2018-12-21 00:47:40 -08:00
{
2019-10-29 16:41:57 -07:00
struct rpi_ts * ts = input_get_drvdata ( input ) ;
2018-12-21 00:47:40 -08:00
struct rpi_ts_regs regs ;
int modified_ids = 0 ;
long released_ids ;
int event_type ;
int touchid ;
int x , y ;
int i ;
memcpy_fromio ( & regs , ts - > fw_regs_va , sizeof ( regs ) ) ;
/*
* We poll the memory based register copy of the touchscreen chip using
* the number of points register to know whether the copy has been
* updated ( we write 99 to the memory copy , the GPU will write between
* 0 - 10 points )
*/
iowrite8 ( RPI_TS_NPOINTS_REG_INVALIDATE ,
ts - > fw_regs_va + offsetof ( struct rpi_ts_regs , num_points ) ) ;
if ( regs . num_points = = RPI_TS_NPOINTS_REG_INVALIDATE | |
( regs . num_points = = 0 & & ts - > known_ids = = 0 ) )
return ;
for ( i = 0 ; i < regs . num_points ; i + + ) {
x = ( ( ( int ) regs . point [ i ] . xh & 0xf ) < < 8 ) + regs . point [ i ] . xl ;
y = ( ( ( int ) regs . point [ i ] . yh & 0xf ) < < 8 ) + regs . point [ i ] . yl ;
touchid = ( regs . point [ i ] . yh > > 4 ) & 0xf ;
event_type = ( regs . point [ i ] . xh > > 6 ) & 0x03 ;
modified_ids | = BIT ( touchid ) ;
if ( event_type = = RPI_TS_FTS_TOUCH_DOWN | |
event_type = = RPI_TS_FTS_TOUCH_CONTACT ) {
input_mt_slot ( input , touchid ) ;
input_mt_report_slot_state ( input , MT_TOOL_FINGER , 1 ) ;
touchscreen_report_pos ( input , & ts - > prop , x , y , true ) ;
}
}
released_ids = ts - > known_ids & ~ modified_ids ;
for_each_set_bit ( i , & released_ids , RPI_TS_MAX_SUPPORTED_POINTS ) {
input_mt_slot ( input , i ) ;
input_mt_report_slot_state ( input , MT_TOOL_FINGER , 0 ) ;
modified_ids & = ~ ( BIT ( i ) ) ;
}
ts - > known_ids = modified_ids ;
input_mt_sync_frame ( input ) ;
input_sync ( input ) ;
}
static void rpi_ts_dma_cleanup ( void * data )
{
struct rpi_ts * ts = data ;
struct device * dev = & ts - > pdev - > dev ;
dma_free_coherent ( dev , PAGE_SIZE , ts - > fw_regs_va , ts - > fw_regs_phys ) ;
}
static int rpi_ts_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
2019-10-29 16:41:57 -07:00
struct input_dev * input ;
2018-12-21 00:47:40 -08:00
struct device_node * fw_node ;
struct rpi_firmware * fw ;
struct rpi_ts * ts ;
u32 touchbuf ;
int error ;
fw_node = of_get_parent ( np ) ;
if ( ! fw_node ) {
dev_err ( dev , " Missing firmware node \n " ) ;
return - ENOENT ;
}
fw = rpi_firmware_get ( fw_node ) ;
of_node_put ( fw_node ) ;
if ( ! fw )
return - EPROBE_DEFER ;
ts = devm_kzalloc ( dev , sizeof ( * ts ) , GFP_KERNEL ) ;
if ( ! ts )
return - ENOMEM ;
ts - > pdev = pdev ;
cross-tree: phase out dma_zalloc_coherent()
We already need to zero out memory for dma_alloc_coherent(), as such
using dma_zalloc_coherent() is superflous. Phase it out.
This change was generated with the following Coccinelle SmPL patch:
@ replace_dma_zalloc_coherent @
expression dev, size, data, handle, flags;
@@
-dma_zalloc_coherent(dev, size, handle, flags)
+dma_alloc_coherent(dev, size, handle, flags)
Suggested-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
[hch: re-ran the script on the latest tree]
Signed-off-by: Christoph Hellwig <hch@lst.de>
2019-01-04 09:23:09 +01:00
ts - > fw_regs_va = dma_alloc_coherent ( dev , PAGE_SIZE , & ts - > fw_regs_phys ,
GFP_KERNEL ) ;
2018-12-21 00:47:40 -08:00
if ( ! ts - > fw_regs_va ) {
dev_err ( dev , " failed to dma_alloc_coherent \n " ) ;
return - ENOMEM ;
}
error = devm_add_action_or_reset ( dev , rpi_ts_dma_cleanup , ts ) ;
if ( error ) {
dev_err ( dev , " failed to devm_add_action_or_reset, %d \n " , error ) ;
return error ;
}
touchbuf = ( u32 ) ts - > fw_regs_phys ;
error = rpi_firmware_property ( fw , RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF ,
& touchbuf , sizeof ( touchbuf ) ) ;
if ( error | | touchbuf ! = 0 ) {
dev_warn ( dev , " Failed to set touchbuf, %d \n " , error ) ;
return error ;
}
2019-10-29 16:41:57 -07:00
input = devm_input_allocate_device ( dev ) ;
if ( ! input ) {
2018-12-21 00:47:40 -08:00
dev_err ( dev , " Failed to allocate input device \n " ) ;
return - ENOMEM ;
}
2019-10-29 16:41:57 -07:00
ts - > input = input ;
input_set_drvdata ( input , ts ) ;
2018-12-21 00:47:40 -08:00
input - > name = " raspberrypi-ts " ;
input - > id . bustype = BUS_HOST ;
input_set_abs_params ( input , ABS_MT_POSITION_X , 0 ,
RPI_TS_DEFAULT_WIDTH , 0 , 0 ) ;
input_set_abs_params ( input , ABS_MT_POSITION_Y , 0 ,
RPI_TS_DEFAULT_HEIGHT , 0 , 0 ) ;
touchscreen_parse_properties ( input , true , & ts - > prop ) ;
error = input_mt_init_slots ( input , RPI_TS_MAX_SUPPORTED_POINTS ,
INPUT_MT_DIRECT ) ;
if ( error ) {
dev_err ( dev , " could not init mt slots, %d \n " , error ) ;
return error ;
}
2019-10-29 16:41:57 -07:00
error = input_setup_polling ( input , rpi_ts_poll ) ;
if ( error ) {
dev_err ( dev , " could not set up polling mode, %d \n " , error ) ;
return error ;
}
input_set_poll_interval ( input , RPI_TS_POLL_INTERVAL ) ;
error = input_register_device ( input ) ;
2018-12-21 00:47:40 -08:00
if ( error ) {
dev_err ( dev , " could not register input device, %d \n " , error ) ;
return error ;
}
return 0 ;
}
static const struct of_device_id rpi_ts_match [ ] = {
{ . compatible = " raspberrypi,firmware-ts " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , rpi_ts_match ) ;
static struct platform_driver rpi_ts_driver = {
. driver = {
2019-10-29 16:41:57 -07:00
. name = " raspberrypi-ts " ,
2018-12-21 00:47:40 -08:00
. of_match_table = rpi_ts_match ,
} ,
2019-10-29 16:41:57 -07:00
. probe = rpi_ts_probe ,
2018-12-21 00:47:40 -08:00
} ;
module_platform_driver ( rpi_ts_driver ) ;
MODULE_AUTHOR ( " Gordon Hollingworth " ) ;
MODULE_AUTHOR ( " Nicolas Saenz Julienne <nsaenzjulienne@suse.de> " ) ;
MODULE_DESCRIPTION ( " Raspberry Pi firmware based touchscreen driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;