123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- // ===========================================================================
- // orizon_ts.c
- // Copyright (C) 2003-2010 Bookeen - All rights reserved
- // ===========================================================================
- /* TODO: Verify if all this includes are necessary */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <asm/atomic.h>
- #include <asm/io.h>
- #include <asm/arch/irqs.h>
- #include <asm/arch/gpio.h>
- #include <linux/delay.h>
- #include <linux/interrupt.h>
- #include <linux/irq.h>
- #include <linux/jiffies.h>
- #include <linux/i2c.h>
- #include <linux/proc_fs.h>
- #include <linux/kthread.h>
- #include <linux/mutex.h>
- #include <linux/spinlock.h>
- #include <linux/ctype.h>
- #include <cybook.h>
- #include <linux/cyio.h>
- //#define DEBUG_MESSAGES
- //#define DEBUG_TRACEFUNC
- #define MODULE_NAME "ORIZON-TS"
- #define FINGER_NUM 0x00
- #define INT_DELAY 0x64 /* Default 0x64 */
- #define INT_MODE 0x01
- #define X_SENS 0x22 /* Default 0x14 */
- #define Y_SENS 0x22 /* Default 0x14 */
- #ifdef DEBUG_MESSAGES
- enum InfoLevel
- {
- INFO_ERROR = 0,
- INFO_WARNING,
- INFO_NORMAL,
- INFO_DEBUG,
- INFO_VERBOSE,
- };
- # ifndef VERBOSE_LEVEL
- # define VERBOSE_LEVEL INFO_VERBOSE
- # endif
- # ifdef DEBUG_TRACEFUNC
- static int _dbg_FunctionLevel = 0;
- # define MSG(str) {\
- int __i;\
- printk(KERN_ALERT "+");\
- for (__i = 0; __i < _dbg_FunctionLevel; __i++)\
- printk("-");\
- printk("||" str "\n");\
- }
- # define DBG(str, ...) {\
- int __i;\
- printk(KERN_ALERT "+");\
- for (__i = 0; __i < _dbg_FunctionLevel; __i++)\
- printk("-");\
- printk("||" str "\n", __VA_ARGS__);\
- }
- # define INFOL(level, s) do {\
- if (level <= VERBOSE_LEVEL) {\
- int __i;\
- printk(KERN_ALERT "+");\
- for (__i = 0; __i < _dbg_FunctionLevel; __i++)\
- printk("-");\
- printk("<%d>%s:%s(): ", level, __FILE__, __func__); printk s; printk("\n");\
- }\
- } while(0)
- # define FUNC_IN() {\
- int __i;\
- _dbg_FunctionLevel++;\
- printk(KERN_ALERT "+");\
- for (__i = 0; __i < _dbg_FunctionLevel; __i++)\
- printk("-");\
- printk(">> %s() >>\n", __func__);\
- }
- # define FUNC_OUT() {\
- int __i;\
- printk(KERN_ALERT "+");\
- for (__i = 0; __i < _dbg_FunctionLevel; __i++)\
- printk("-");\
- printk("<< %s() <<\n", __func__);\
- _dbg_FunctionLevel--;\
- }
- # define FUNC_OUTR(val) {\
- int __i;\
- printk(KERN_ALERT "+");\
- for (__i = 0; __i < _dbg_FunctionLevel; __i++)\
- printk("-");\
- printk("<< %s() = %d <<\n", __func__, val);\
- _dbg_FunctionLevel--;\
- }
- # else /* DEBUG_TRACEFUNC */
- # define MSG(str) do {\
- printk(KERN_ALERT MODULE_NAME ": " str "\n");\
- } while(0)
- # define DBG(str, ...) do {\
- printk(KERN_ALERT MODULE_NAME ": " str "\n", __VA_ARGS__);\
- } while(0)
- # define FUNC_IN() do {\
- } while(0)
- # define FUNC_OUT() do {\
- } while(0)
- # define FUNC_OUTR(val) do {\
- printk(KERN_ALERT MODULE_NAME ": %s() return %d\n", __func__, val);\
- } while(0)
- # define INFOL(level, s) do {\
- if (level <= VERBOSE_LEVEL) {\
- printk("<%d>%s:%s(): ", level, __FILE__, __func__); printk s; printk("\n");\
- }\
- } while(0)
- # endif /* DEBUG_TRACEFUNC */
- #else /* DEBUG_MESSAGES */
- # define MSG(str)
- # define DBG(str, ...)
- # define FUNC_IN()
- # define FUNC_OUT()
- # define FUNC_OUTR(val)
- # define INFOL(level, s)
- # define INFO(s)
- #endif /* DEBUG_MESSAGES */
- typedef enum
- {
- POWER_UNDEFINED = -1,
- POWER_OFF = 0,
- POWER_ON,
- POWER_ONAUTOSTANDBY,
- POWER_DEEPSLEEP,
- } Ots_PowerModes;
- /*============================================================================*/
- /*============================= Prototypes ===================================*/
- /*============================================================================*/
- /**************************** i2c functions ***********************************/
- static int ots_attachAdapter (struct i2c_adapter *adapter);
- static int ots_detect (struct i2c_adapter *adapter, int address, int kind);
- static int ots_detachClient (struct i2c_client *client);
- static int ots_suspend (struct device *dev, pm_message_t state);
- static int ots_resume (struct device *dev);
- //static void ots_dumpI2C(void);
- /**************************** irq functions ***********************************/
- static void ots_checkWorkFunction (struct work_struct *work);
- static irqreturn_t ots_interrupt (int irq, void *dev_id);
- /******************************* Chip functions *******************************/
- static void ots_setPowerMode (Ots_PowerModes power);
- static void ots_setDeviceParameters (unsigned char int_mode,
- unsigned char x_sensitivity,
- unsigned char y_sensitivity);
- static void ots_ackInterrupt (void);
- /****************************** Module functions ******************************/
- static int __init ots_init (void);
- static void __exit ots_exit (void);
- /*============================= End of prototypes ============================*/
- /*============================================================================*/
- /*============================= Variables ====================================*/
- /*============================================================================*/
- struct workqueue_struct *ots_check_workqueue;
- struct work_struct ots_check_work;
- static Ots_PowerModes ots_currentPowerMode = POWER_UNDEFINED;
- /****************************** i2c configuration *****************************/
- #define OTS_ADDR_I2C 0x5C
- static unsigned short normal_i2c[] = { OTS_ADDR_I2C,
- I2C_CLIENT_END };
- /* Insmod parameters */
- I2C_CLIENT_INSMOD_1 (ots);
- struct i2c_client *ots_client;
- /* Each client has this additional data */
- struct ots_data
- {
- struct i2c_client client;
- };
- /* This is the I2C driver that will be inserted */
- static struct i2c_driver ots_driver ={
- .driver =
- {
- .name = "orizon_ts",
- .suspend = ots_suspend,
- .resume = ots_resume,
- },
- .id = I2C_DRIVERID_EEPROM,
- .attach_adapter = ots_attachAdapter,
- .detach_client = ots_detachClient,
- };
- /******************************************************************************/
- /**************************** i2c functions ***********************************/
- /******************************************************************************/
- #if 0
- static void ots_dumpI2C(void)
- {
- int Addr = 0;
- unsigned char value;
- int I,J;
- printk("-------------------------------------------------------------------------------\n");
- printk("-------------------------------------I²C TS------------------------------------\n");
- for ( J = 0; J < 16; J++ )
- {
- printk("%04X: ", Addr);
- for ( I = 0; I < 16; I++, Addr++ )
- {
- value = i2c_smbus_read_byte_data(ots_client, Addr);
- mdelay(4);
- printk("%02X ", value);
- }
- printk(" | ");
- Addr -= 16;
- for ( I = 0; I < 16; I++, Addr++ )
- {
- value = i2c_smbus_read_byte_data(ots_client, Addr);
- mdelay(4);
- printk("%c", isprint(value) ? value : '.');
- }
- printk("\n");
- }
- printk("-------------------------------------------------------------------------------\n");
- printk("-------------------------------------------------------------------------------\n");
- }
- #endif
- static int ots_attachAdapter (struct i2c_adapter *adapter)
- {
- return i2c_probe(adapter, &addr_data, ots_detect);
- }
- /* This function is called by i2c_probe */
- static int ots_detect (struct i2c_adapter *adapter, int address, int kind)
- {
- struct ots_data *data = NULL;
- int err = 0;
- FUNC_IN();
- //DBG(">>%s(%p, 0x%X, 0x%X)", __func__, adapter, address, kind);
- if ( !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
- | I2C_FUNC_SMBUS_BYTE) )
- goto exit;
- if ( address == OTS_ADDR_I2C )
- {
- if ( !(data = kzalloc(sizeof (struct ots_data), GFP_KERNEL)) )
- {
- err = -ENOMEM;
- goto exit;
- }
- ots_client = &data->client;
- i2c_set_clientdata(ots_client, data);
- ots_client->addr = address;
- ots_client->adapter = adapter;
- ots_client->driver = &ots_driver;
- ots_client->flags = 0;
- /* Fill in the remaining client fields */
- strlcpy(ots_client->name, "orizon_ts", I2C_NAME_SIZE);
- /* Tell the I2C layer a new client has arrived */
- if ( (err = i2c_attach_client(ots_client)) )
- {
- goto exit_kfree;
- }
- ots_setPowerMode(POWER_ONAUTOSTANDBY);
- ots_setDeviceParameters(INT_MODE, X_SENS, Y_SENS);
- /* Now we are sure that the driver init successfully, then aquire the IRQ */
- set_irq_type(IRQ_EINT2, IRQT_FALLING);
- if ( request_irq(IRQ_EINT2, ots_interrupt, SA_SHIRQ, "orizon_ts", &ots_client) )
- {
- printk(KERN_ERR "failed to get interrupt resouce at IRQ_EINT2.\n");
- goto exit_kfree;
- }
- enable_irq_wake(IRQ_EINT2);
- }
- goto exit;
- exit_kfree:
- kfree(data);
- exit:
- FUNC_OUTR(err);
- return err;
- }
- static int ots_detachClient (struct i2c_client *client)
- {
- int err = 0;
- FUNC_IN();
- err = i2c_detach_client(client);
- if ( err )
- goto exit;
- kfree(i2c_get_clientdata(client));
- exit:
- FUNC_OUTR(err);
- return err;
- }
- static int ots_suspend (struct device *dev, pm_message_t state)
- {
- int ret = 0;
- FUNC_IN();
- FUNC_OUTR(ret);
- return ret;
- }
- static int ots_resume (struct device *dev)
- {
- FUNC_IN();
- FUNC_OUT();
- return 0;
- }
- /******************************************************************************/
- /********************** Interrupt Related functions ***************************/
- /******************************************************************************/
- static int oldX = 0, oldY = 0;
- static void ots_checkWorkFunction (struct work_struct *work)
- {
- unsigned long x1, y1, x2, y2;
- FUNC_IN();
-
- /* Here do what the interrupt should... (ie read touch values) */
- /* TODO: I reversed the X / Y axis to match old driver and revese the X value */
- y1 = i2c_smbus_read_byte_data(ots_client, 0);
- y1 |= i2c_smbus_read_byte_data(ots_client, 1) << 8;
- x1 = i2c_smbus_read_byte_data(ots_client, 2);
- x1 |= i2c_smbus_read_byte_data(ots_client, 3) << 8;
- y2 = i2c_smbus_read_byte_data(ots_client, 4);
- y2 |= i2c_smbus_read_byte_data(ots_client, 5) << 8;
- x2 = i2c_smbus_read_byte_data(ots_client, 6);
- x2 |= i2c_smbus_read_byte_data(ots_client, 7) << 8;
- DBG("x1: %lu\ty1: %lu\tx2: %lu\ty2: %lu", x1, y1, x2, y2);
- if ((x1 == 0) && (y1 == 0))
- { /* We assume that this is a finger up event */
- if ( Cyio_PushEvent('R', 1) >= 0 )
- {
- Cyio_PushEvent((oldX >> 8) & 0xFF, 0);
- Cyio_PushEvent((oldX >> 0) & 0xFF, 0);
- Cyio_PushEvent((oldY >> 8) & 0xFF, 0);
- Cyio_PushEvent((oldY >> 0) & 0xFF, 0);
- }
- }
- else
- {
- oldX = 600 - x1;
- oldY = y1;
- }
- /* Say I get the data */
- ots_ackInterrupt();
-
- FUNC_OUT();
- }
- static irqreturn_t ots_interrupt (int irq, void *dev_id)
- {
- irqreturn_t ret = IRQ_HANDLED;
- static int initialised = 0;
- FUNC_IN();
- if (initialised == 0)
- {
- INIT_WORK(&ots_check_work, ots_checkWorkFunction);
- initialised = 1;
- }
- else
- {
- PREPARE_WORK(&ots_check_work, ots_checkWorkFunction);
- }
- schedule_work(&ots_check_work);
- //FUNC_OUTR((int)ret);
- return ret;
- }
- /******************************************************************************/
- /******************************* Chip functions *******************************/
- /******************************************************************************/
- static void ots_setPowerMode (Ots_PowerModes power)
- {
- unsigned char tmpReg;
-
- FUNC_IN();
- if (power == ots_currentPowerMode)
- return; /* No need to do anything, we are in the same power mode... */
-
- switch(power)
- {
- default:
- case POWER_UNDEFINED:
- /* Error */
- DBG("Error: invalid power mode #%d", power);
- break;
-
- case POWER_ON:
- /* Set the power to on */
- s3c2410_gpio_setpin(S3C2410_GPD10, 1);
- msleep(100);
- /* TODO: Does we need to set chip settings? */
- tmpReg = 0x0; /* Set in "Active Mode" */
- i2c_smbus_write_byte_data(ots_client, 0x73, tmpReg);
- msleep(400);
- break;
- case POWER_OFF:
- /* Set the power to off */
- s3c2410_gpio_setpin(S3C2410_GPD10, 0);
- msleep(10);
- break;
- case POWER_ONAUTOSTANDBY:
- if ( (ots_currentPowerMode != POWER_ON) &&
- (ots_currentPowerMode != POWER_DEEPSLEEP) )
- ots_setPowerMode(POWER_ON); /* Set myself as Power ON before anything */
- //tmpReg = i2c_smbus_read_byte_data(ots_client, 0x24);
- tmpReg = (((50) /* timeout in ms for auto sleep */ ) & 0x0F) << 4;
- tmpReg |= (1<< 2); /* Activate auto sleep mode */
- tmpReg |= 0x1; /* Set in "Sleep Mode" */
- i2c_smbus_write_byte_data(ots_client, 0x73, tmpReg);
- msleep(4);
- break;
- case POWER_DEEPSLEEP:
- if ( (ots_currentPowerMode != POWER_ON) &&
- (ots_currentPowerMode != POWER_ONAUTOSTANDBY) )
- ots_setPowerMode(POWER_ON); /* Set myself as Power ON before anything */
- tmpReg = 0x02;
- i2c_smbus_write_byte_data(ots_client, 0x73, tmpReg);
- msleep(4);
- break;
- }
- ots_currentPowerMode = power;
- FUNC_OUT();
- }
- static void ots_setDeviceParameters (unsigned char int_mode,
- unsigned char x_sensitivity,
- unsigned char y_sensitivity)
- {
- unsigned char tmpReg;
-
- FUNC_IN();
- /* Then set INT_WIDTH */
- /*
- * Interrupt width register @114 [0x72]
- * b7-b0: int width
- * Default: 0x64
- */
- i2c_smbus_write_byte_data(ots_client, 0x72, INT_DELAY);
- mdelay(4);
- /* Now set sensitivity */
- i2c_smbus_write_byte_data(ots_client, 0x6F, x_sensitivity);
- mdelay(4);
- i2c_smbus_write_byte_data(ots_client, 0x70, y_sensitivity);
- mdelay(4);
- i2c_smbus_write_byte_data(ots_client, 0xBE, 0x01); // TEST
- tmpReg = i2c_smbus_read_byte_data(ots_client, 0xBE); // TEST
- DBG("0xBE = %02X", tmpReg);
- DBG("Firmware Version =0x%x\n", i2c_smbus_read_byte_data(ots_client, 0x77));
- /* Activate the device ! */
- /*
- * Interrupt mode Setting register @113 [0x71]
- * b7-b6-b5: TP_NUM : How many finger, 000: none, 001: One, 010: Two
- * b4: INT_RELEASE: used to ack (1) IRQ in periodical mode
- * b3: EN_INT: Enable int (1: enabled, 0: disable)
- * b2: INT_POL: 0: active low, 1: active high
- * b1-b0: INT_MODE: 00: INT periodicaly, 01: INT assert when coord changes
- * 10: TOuch indicate, 11: INT assert when INT_RELEASE modified
- *
- * Default 0x0C [000 0 1 1 00]
- * Qisda: 0x0A [000 0 1 0 10]
- * Int periodicaly, Active High, and INT enabled
- */
- /* 2 Finger Active High Mode */
- tmpReg = (FINGER_NUM << 5) | (1 << 3) | (0 << 2) | ((int_mode & 0x03) << 0);
- DBG("Settings: 0x%02X", tmpReg);
- i2c_smbus_write_byte_data(ots_client, 0x71, tmpReg);
- mdelay(4);
- tmpReg = i2c_smbus_read_byte_data(ots_client, 0x71); // TEST
- DBG("[0x71] Settings = %02X", tmpReg);
- FUNC_OUT();
- }
- static void ots_ackInterrupt (void)
- {
- unsigned char tmpReg;
- FUNC_IN();
- tmpReg = i2c_smbus_read_byte_data(ots_client, 0x71);
- tmpReg |= (1<<4);
- i2c_smbus_write_byte_data(ots_client, 0x71, tmpReg);
- FUNC_OUT();
- }
- /******************************************************************************/
- /****************************** Module functions ******************************/
- /******************************************************************************/
- static int __init ots_init (void)
- {
- FUNC_IN();
- /* if (GET_CAPABILITY(PLAT_CAP_GSENSOR) == PLAT_CAP_GMMA7660)
- {
- }*/
-
- /* Init GPIOs */
- s3c2410_gpio_cfgpin(S3C2410_GPD10, S3C2410_GPD10_OUTP);
- s3c2410_gpio_pullup(S3C2410_GPD10, 2);
- i2c_add_driver(&ots_driver);
- FUNC_OUT();
- return 0;
- }
- // ---------------------------------------------------------------------------
- static void __exit ots_exit (void)
- {
- FUNC_IN();
- /*if (GET_CAPABILITY(PLAT_CAP_GSENSOR) == PLAT_CAP_GMMA7660)
- {
- }*/
- /* Deinit GPIOs */
- /* Nothing to do, leave it as output is good enought, just set the
- * device as power off */
- ots_setPowerMode(POWER_OFF);
- i2c_del_driver(&ots_driver);
- FUNC_OUT();
- }
- // ---------------------------------------------------------------------------
- module_init (ots_init);
- module_exit (ots_exit);
- // ---------------------------------------------------------------------------
- MODULE_LICENSE ("GPL");
- MODULE_AUTHOR ("Bookeen <developers@bookeen.com>");
- MODULE_VERSION ("1.0");
- MODULE_DESCRIPTION ("Cybook Orizon Touchpanel driver");
- // ===========================================================================
|