// =========================================================================== // orizon_ts.c // Copyright (C) 2003-2010 Bookeen - All rights reserved // =========================================================================== /* TODO: Verify if all this includes are necessary */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; } } 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 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) */ x1 = i2c_smbus_read_byte_data(ots_client, 0); x1 |= i2c_smbus_read_byte_data(ots_client, 1) << 8; y1 = i2c_smbus_read_byte_data(ots_client, 2); y1 |= i2c_smbus_read_byte_data(ots_client, 3) << 8; x2 = i2c_smbus_read_byte_data(ots_client, 4); x2 |= i2c_smbus_read_byte_data(ots_client, 5) << 8; y2 = i2c_smbus_read_byte_data(ots_client, 6); y2 |= i2c_smbus_read_byte_data(ots_client, 7) << 8; DBG("x1: %lu\ty1: %lu\ty1: %lu\ty2: %lu", x1, y1, x2, y2); /* 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 "); MODULE_VERSION ("1.0"); MODULE_DESCRIPTION ("Cybook Orizon Touchpanel driver"); // ===========================================================================