2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2008-06-02 08:38:35 +04:00
/*
* HTC Shift touchscreen driver
*
* Copyright ( C ) 2008 Pau Oliva Fora < pof @ eslack . org >
*/
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/input.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/init.h>
# include <linux/irq.h>
# include <linux/isa.h>
# include <linux/ioport.h>
# include <linux/dmi.h>
MODULE_AUTHOR ( " Pau Oliva Fora <pau@eslack.org> " ) ;
MODULE_DESCRIPTION ( " HTC Shift touchscreen driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define HTCPEN_PORT_IRQ_CLEAR 0x068
# define HTCPEN_PORT_INIT 0x06c
# define HTCPEN_PORT_INDEX 0x0250
# define HTCPEN_PORT_DATA 0x0251
# define HTCPEN_IRQ 3
# define DEVICE_ENABLE 0xa2
# define DEVICE_DISABLE 0xa3
# define X_INDEX 3
# define Y_INDEX 5
# define TOUCH_INDEX 0xb
# define LSB_XY_INDEX 0xc
# define X_AXIS_MAX 2040
# define Y_AXIS_MAX 2040
2012-01-13 03:02:20 +04:00
static bool invert_x ;
2008-06-02 08:38:35 +04:00
module_param ( invert_x , bool , 0644 ) ;
MODULE_PARM_DESC ( invert_x , " If set, X axis is inverted " ) ;
2012-01-13 03:02:20 +04:00
static bool invert_y ;
2008-06-02 08:38:35 +04:00
module_param ( invert_y , bool , 0644 ) ;
MODULE_PARM_DESC ( invert_y , " If set, Y axis is inverted " ) ;
static irqreturn_t htcpen_interrupt ( int irq , void * handle )
{
struct input_dev * htcpen_dev = handle ;
unsigned short x , y , xy ;
/* 0 = press; 1 = release */
outb_p ( TOUCH_INDEX , HTCPEN_PORT_INDEX ) ;
if ( inb_p ( HTCPEN_PORT_DATA ) ) {
input_report_key ( htcpen_dev , BTN_TOUCH , 0 ) ;
} else {
outb_p ( X_INDEX , HTCPEN_PORT_INDEX ) ;
x = inb_p ( HTCPEN_PORT_DATA ) ;
outb_p ( Y_INDEX , HTCPEN_PORT_INDEX ) ;
y = inb_p ( HTCPEN_PORT_DATA ) ;
outb_p ( LSB_XY_INDEX , HTCPEN_PORT_INDEX ) ;
xy = inb_p ( HTCPEN_PORT_DATA ) ;
/* get high resolution value of X and Y using LSB */
x = X_AXIS_MAX - ( ( x * 8 ) + ( ( xy > > 4 ) & 0xf ) ) ;
y = ( y * 8 ) + ( xy & 0xf ) ;
if ( invert_x )
x = X_AXIS_MAX - x ;
if ( invert_y )
y = Y_AXIS_MAX - y ;
if ( x ! = X_AXIS_MAX & & x ! = 0 ) {
input_report_key ( htcpen_dev , BTN_TOUCH , 1 ) ;
input_report_abs ( htcpen_dev , ABS_X , x ) ;
input_report_abs ( htcpen_dev , ABS_Y , y ) ;
}
}
input_sync ( htcpen_dev ) ;
inb_p ( HTCPEN_PORT_IRQ_CLEAR ) ;
return IRQ_HANDLED ;
}
static int htcpen_open ( struct input_dev * dev )
{
outb_p ( DEVICE_ENABLE , HTCPEN_PORT_INIT ) ;
return 0 ;
}
static void htcpen_close ( struct input_dev * dev )
{
outb_p ( DEVICE_DISABLE , HTCPEN_PORT_INIT ) ;
synchronize_irq ( HTCPEN_IRQ ) ;
}
2012-11-24 09:38:25 +04:00
static int htcpen_isa_probe ( struct device * dev , unsigned int id )
2008-06-02 08:38:35 +04:00
{
struct input_dev * htcpen_dev ;
int err = - EBUSY ;
if ( ! request_region ( HTCPEN_PORT_IRQ_CLEAR , 1 , " htcpen " ) ) {
printk ( KERN_ERR " htcpen: unable to get IO region 0x%x \n " ,
HTCPEN_PORT_IRQ_CLEAR ) ;
goto request_region1_failed ;
}
if ( ! request_region ( HTCPEN_PORT_INIT , 1 , " htcpen " ) ) {
printk ( KERN_ERR " htcpen: unable to get IO region 0x%x \n " ,
HTCPEN_PORT_INIT ) ;
goto request_region2_failed ;
}
if ( ! request_region ( HTCPEN_PORT_INDEX , 2 , " htcpen " ) ) {
printk ( KERN_ERR " htcpen: unable to get IO region 0x%x \n " ,
HTCPEN_PORT_INDEX ) ;
goto request_region3_failed ;
}
htcpen_dev = input_allocate_device ( ) ;
if ( ! htcpen_dev ) {
printk ( KERN_ERR " htcpen: can't allocate device \n " ) ;
err = - ENOMEM ;
goto input_alloc_failed ;
}
htcpen_dev - > name = " HTC Shift EC TouchScreen " ;
htcpen_dev - > id . bustype = BUS_ISA ;
htcpen_dev - > evbit [ 0 ] = BIT_MASK ( EV_ABS ) | BIT_MASK ( EV_KEY ) ;
htcpen_dev - > keybit [ BIT_WORD ( BTN_TOUCH ) ] = BIT_MASK ( BTN_TOUCH ) ;
input_set_abs_params ( htcpen_dev , ABS_X , 0 , X_AXIS_MAX , 0 , 0 ) ;
input_set_abs_params ( htcpen_dev , ABS_Y , 0 , Y_AXIS_MAX , 0 , 0 ) ;
htcpen_dev - > open = htcpen_open ;
htcpen_dev - > close = htcpen_close ;
err = request_irq ( HTCPEN_IRQ , htcpen_interrupt , 0 , " htcpen " ,
htcpen_dev ) ;
if ( err ) {
printk ( KERN_ERR " htcpen: irq busy \n " ) ;
goto request_irq_failed ;
}
inb_p ( HTCPEN_PORT_IRQ_CLEAR ) ;
err = input_register_device ( htcpen_dev ) ;
if ( err )
goto input_register_failed ;
dev_set_drvdata ( dev , htcpen_dev ) ;
return 0 ;
input_register_failed :
free_irq ( HTCPEN_IRQ , htcpen_dev ) ;
request_irq_failed :
input_free_device ( htcpen_dev ) ;
input_alloc_failed :
release_region ( HTCPEN_PORT_INDEX , 2 ) ;
request_region3_failed :
release_region ( HTCPEN_PORT_INIT , 1 ) ;
request_region2_failed :
release_region ( HTCPEN_PORT_IRQ_CLEAR , 1 ) ;
request_region1_failed :
return err ;
}
2012-11-24 09:50:47 +04:00
static int htcpen_isa_remove ( struct device * dev , unsigned int id )
2008-06-02 08:38:35 +04:00
{
struct input_dev * htcpen_dev = dev_get_drvdata ( dev ) ;
input_unregister_device ( htcpen_dev ) ;
free_irq ( HTCPEN_IRQ , htcpen_dev ) ;
release_region ( HTCPEN_PORT_INDEX , 2 ) ;
release_region ( HTCPEN_PORT_INIT , 1 ) ;
release_region ( HTCPEN_PORT_IRQ_CLEAR , 1 ) ;
return 0 ;
}
# ifdef CONFIG_PM
static int htcpen_isa_suspend ( struct device * dev , unsigned int n ,
pm_message_t state )
{
outb_p ( DEVICE_DISABLE , HTCPEN_PORT_INIT ) ;
return 0 ;
}
static int htcpen_isa_resume ( struct device * dev , unsigned int n )
{
outb_p ( DEVICE_ENABLE , HTCPEN_PORT_INIT ) ;
return 0 ;
}
# endif
static struct isa_driver htcpen_isa_driver = {
. probe = htcpen_isa_probe ,
2012-11-24 09:27:39 +04:00
. remove = htcpen_isa_remove ,
2008-06-02 08:38:35 +04:00
# ifdef CONFIG_PM
. suspend = htcpen_isa_suspend ,
. resume = htcpen_isa_resume ,
# endif
. driver = {
. owner = THIS_MODULE ,
. name = " htcpen " ,
}
} ;
2017-09-14 12:59:30 +03:00
static const struct dmi_system_id htcshift_dmi_table [ ] __initconst = {
2008-06-02 08:38:35 +04:00
{
. ident = " Shift " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " High Tech Computer Corp " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Shift " ) ,
} ,
} ,
{ }
} ;
2011-11-29 14:20:35 +04:00
MODULE_DEVICE_TABLE ( dmi , htcshift_dmi_table ) ;
2008-06-02 08:38:35 +04:00
static int __init htcpen_isa_init ( void )
{
if ( ! dmi_check_system ( htcshift_dmi_table ) )
return - ENODEV ;
return isa_register_driver ( & htcpen_isa_driver , 1 ) ;
}
static void __exit htcpen_isa_exit ( void )
{
isa_unregister_driver ( & htcpen_isa_driver ) ;
}
module_init ( htcpen_isa_init ) ;
module_exit ( htcpen_isa_exit ) ;