From 4e09168fe611b498b251b8e12560f453ff1e8920 Mon Sep 17 00:00:00 2001 From: Lawrence Liao Date: Fri, 16 Sep 2016 13:31:15 +0530 Subject: This is the reference driver source code for Pixart OTS PAT9125. Change-Id: Ie366894e9e89707fa2950a7ffbfe4e93059a19a3 Signed-off-by: Lawrence Liao (cherry picked from commit dfc4349a8aa02bb0b7b0639ad5368a5c85f6520f) Git-commit: dfc4349a8aa02bb0b7b0639ad5368a5c85f6520f Git-repo: https://github.com/PixartOpen/pixart-sensor-db810-linux-driver [shjain@codeaurora.org: removed below listed files from original commit as they are not needed and to resolve trivial merge conflicts: 1. arch/arm/boot/dts/qcom/apq8094-dragonboard.dtsi 2. arch/arm/boot/dts/qcom/msm8994-pinctrl.dtsi 3. arch/arm/boot/dts/qcom/msm8994.dtsi 4. drivers/input/misc/Makefile 5. drivers/input/misc/ots_pat9125/Makefile Remove the unused pieces of algorithm from the reference driver of Pixart PAT9125 Rotating side button.] Signed-off-by: Shantanu Jain --- .../input/misc/ots_pat9125/pat9125_linux_driver.c | 492 +++++++++++++++++++++ drivers/input/misc/ots_pat9125/pixart_ots.c | 60 +++ drivers/input/misc/ots_pat9125/pixart_ots.h | 10 + drivers/input/misc/ots_pat9125/pixart_platform.h | 19 + 4 files changed, 581 insertions(+) create mode 100644 drivers/input/misc/ots_pat9125/pat9125_linux_driver.c create mode 100644 drivers/input/misc/ots_pat9125/pixart_ots.c create mode 100644 drivers/input/misc/ots_pat9125/pixart_ots.h create mode 100644 drivers/input/misc/ots_pat9125/pixart_platform.h (limited to 'drivers/input') diff --git a/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c new file mode 100644 index 000000000000..c909b213c077 --- /dev/null +++ b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c @@ -0,0 +1,492 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pixart_ots.h" +#include "pixart_platform.h" + +static int pat9125_init_input_data(void); + +#define pat9125_name "pixart_pat9125" + +#define pat9125_DEV_NAME pat9125_name + +static struct pat9125_linux_data_t pat9125data; + +static int pat9125_i2c_write(u8 reg, u8 *data, int len); +static int pat9125_i2c_read(u8 reg, u8 *data); + +/**************************************/ + +extern unsigned char ReadData(unsigned char addr) +{ + u8 data = 0xff; + pat9125_i2c_read(addr, &data); + return data; +} +extern void WriteData(unsigned char addr, unsigned char data) +{ + pat9125_i2c_write(addr, &data, 1); +} +extern void delay_ms(int ms) +{ + msleep(ms); +} +/**************************************/ +static int pat9125_i2c_write(u8 reg, u8 *data, int len) +{ + u8 buf[20]; + int rc; + int ret = 0; + int i; + + buf[0] = reg; + if (len >= 20) { + pr_debug( + "%s (%d) : FAILED: buffer size is limitted(20) %d\n", + __func__, __LINE__, len); + dev_err(&pat9125data.client->dev, "pat9125_i2c_write FAILED: buffer size is limitted(20)\n"); + return -ENODEV; + } + + for (i = 0 ; i < len; i++) + buf[i+1] = data[i]; + + /* Returns negative errno, or else the number of bytes written. */ + rc = i2c_master_send(pat9125data.client, buf, len+1); + + if (rc != len+1) { + pr_debug( + "%s (%d) : FAILED: writing to reg 0x%x\n", + __func__, __LINE__, reg); + + ret = -ENODEV; + } + + return ret; +} + +static int pat9125_i2c_read(u8 reg, u8 *data) +{ + + u8 buf[20]; + int rc; + + buf[0] = reg; + + /* If everything went ok (i.e. 1 msg transmitted), + return #bytes transmitted, else error code. + thus if transmit is ok return value 1 */ + rc = i2c_master_send(pat9125data.client, buf, 1); + if (rc != 1) { + pr_debug( + "%s (%d) : FAILED: writing to address 0x%x\n", + __func__, __LINE__, reg); + return -ENODEV; + } + + /* returns negative errno, or else the number of bytes read */ + rc = i2c_master_recv(pat9125data.client, buf, 1); + if (rc != 1) { + pr_debug( + "%s (%d) : FAILED: reading data\n", + __func__, __LINE__); + return -ENODEV; + } + + *data = buf[0]; + return 0; +} + +void pixart_pat9125_ist(void) +{ + +} + +static irqreturn_t pixart_pat9125_irq(int irq, void *handle) +{ +/* "cat /proc/kmsg" to see kernel message */ + pixart_pat9125_ist(); + return IRQ_HANDLED; +} + +static int pat9125_start(void) +{ + int err = (-1); + pr_debug(">>> %s (%d)\n", __func__, __LINE__); + + err = request_threaded_irq(pat9125data.irq, NULL, pixart_pat9125_irq, + pat9125data.irq_flags, + "pixart_pat9125_irq", + &pat9125data); + if (err) + pr_debug("irq %d busy?\n", pat9125data.irq); + + pat9125data.last_jiffies = jiffies_64; + + return err; +} + +static void pat9125_stop(void) +{ + pr_debug(">>> %s (%d)\n", __func__, __LINE__); + free_irq(pat9125data.irq, &pat9125data); +} + +static ssize_t pat9125_fops_read(struct file *filp, + char *buf, size_t count, loff_t *l) +{ + pr_debug(">>> %s (%d)\n", __func__, __LINE__); + return 0; +} + +static ssize_t pat9125_fops_write(struct file *filp, + const char *buf, size_t count, loff_t *f_ops) +{ + pr_debug(">>> %s (%d)\n", __func__, __LINE__); + return 0; +} + +static long pat9125_fops_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +/* static int pat9125_fops_ioctl(struct inode *inode, + struct file *file, unsigned int cmd, unsigned long arg) */ +{ + pr_debug(">>> %s (%d)\n", __func__, __LINE__); + return 0; +} + +static int pat9125_fops_open(struct inode *inode, struct file *filp) +{ + pr_debug(">>> %s (%d)\n", __func__, __LINE__); + return 0; +} + +static int pat9125_fops_release(struct inode *inode, struct file *filp) +{ + pr_debug(">>> %s (%d)\n", __func__, __LINE__); + return 0; +} +static const struct file_operations pat9125_fops = { +owner: THIS_MODULE, + read : pat9125_fops_read, + write : pat9125_fops_write, + /* ioctl : pat9125_fops_ioctl, */ + unlocked_ioctl : pat9125_fops_ioctl, + open : pat9125_fops_open, + release : pat9125_fops_release, +}; + +/*----------------------------------------------------------------------------*/ +struct miscdevice pat9125_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = pat9125_name, + .fops = &pat9125_fops, +}; +static ssize_t pat9125_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char s[256]; + char *p = s; + + pr_debug("%s (%d) : write_reg_store\n", __func__, __LINE__); + + memcpy(s, buf, sizeof(s)); + + *(s+1) = '\0'; + *(s+4) = '\0'; + *(s+7) = '\0'; + /* example(in console): echo w 12 34 > rw_reg */ + if (*p == 'w') { + long write_addr, write_data; + p += 2; + if (!kstrtol(p, 16, &write_addr)) { + p += 3; + if (!kstrtol(p, 16, &write_data)) { + pr_debug( + "w 0x%x 0x%x\n", + (u8)write_addr, (u8)write_data); + WriteData((u8)write_addr, (u8)write_data); + } + } + /* example(in console): echo r 12 > rw_reg */ + } else if (*p == 'r') { + long read_addr; + p += 2; + + if (!kstrtol(p, 16, &read_addr)) { + int data = 0; + data = ReadData((u8)read_addr); + pr_debug( + "r 0x%x 0x%x\n", + (unsigned int)read_addr, data); + } + } + return count; +} + +static ssize_t pat9125_test_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + + /* cat */ + pr_debug("%s (%d) :\n", __func__, __LINE__); + + return 0; +} +static DEVICE_ATTR( + test, + S_IRUGO | S_IWUGO , pat9125_test_show, pat9125_test_store); +static struct device_attribute *pat9125_attr_list[] = { + &dev_attr_test, +}; + + +/*----------------------------------------------------------------------------*/ +static int pat9125_create_attr(struct device *dev) +{ + int idx, err = 0; + int num = (int)(sizeof(pat9125_attr_list)/sizeof(pat9125_attr_list[0])); + if (!dev) + return -EINVAL; + + for (idx = 0; idx < num; idx++) { + err = device_create_file(dev, pat9125_attr_list[idx]); + if (err) { + pr_debug( + "device_create_file (%s) = %d\n", + pat9125_attr_list[idx]->attr.name, err); + break; + } + } + + return err; +} +/*----------------------------------------------------------------------------*/ +static int pat9125_delete_attr(struct device *dev) +{ + + int idx , err = 0; + int num = (int)(sizeof(pat9125_attr_list)/sizeof(pat9125_attr_list[0])); + if (!dev) + return -EINVAL; + + for (idx = 0; idx < num; idx++) + device_remove_file(dev, pat9125_attr_list[idx]); + + return err; +} + +static int pat9125_i2c_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err = 0; + struct device_node *np; + + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + + pr_debug("%s (%d) : probe module....\n", __func__, __LINE__); + + memset(&pat9125data, 0, sizeof(pat9125data)); + err = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE); + if (err < 0) + goto error_return; + + pat9125data.client = client; + err = misc_register(&pat9125_device); + if (err) { + pr_debug("pat9125_device register failed\n"); + goto error_return; + } + + pat9125data.pat9125_device = pat9125_device.this_device; + err = pat9125_create_attr(pat9125data.pat9125_device); + if (err) { + pr_debug("create attribute err = %d\n", err); + goto error_return; + } + + if (pat9125_init_input_data() < 0) + goto error_return; + + /* interrupt initialization */ + pat9125data.i2c_dev = &client->dev; + + np = pat9125data.i2c_dev->of_node; + pat9125data.irq_gpio = of_get_named_gpio_flags(np, + "pixart_pat9125,irq-gpio", 0, &pat9125data.irq_flags); + + pr_debug( + "irq_gpio: %d, irq_flags: 0x%x\n", + pat9125data.irq_gpio, pat9125data.irq_flags); + + if (!gpio_is_valid(pat9125data.irq_gpio)) { + err = (-1); + pr_debug( + "invalid irq_gpio: %d\n", + pat9125data.irq_gpio); + goto error_return; + } + + err = gpio_request(pat9125data.irq_gpio, "pixart_pat9125_irq_gpio"); + if (err) { + pr_debug( + "unable to request gpio [%d], [%d]\n", + pat9125data.irq_gpio, err); + goto error_return; + } + + err = gpio_direction_input(pat9125data.irq_gpio); + if (err) { + pr_debug("unable to set dir for gpio[%d], [%d]\n", + pat9125data.irq_gpio, err); + goto error_return; + } + + pat9125data.irq = gpio_to_irq(pat9125data.irq_gpio); + + if (!OTS_Sensor_Init()) + goto error_return; + + if (!pat9125_start()) + goto error_return; + + return 0; + +error_return: + + return err; + +} + + +static int pat9125_i2c_remove(struct i2c_client *client) +{ + + return 0; +} + +static int pat9125_suspend(struct device *dev) +{ pr_debug("%s (%d) : pat9125 suspend\n", __func__, __LINE__); + return 0; +} + +static int pat9125_resume(struct device *dev) +{ + pr_debug("%s (%d) : pat9125 resume\n", __func__, __LINE__); + return 0; +} + +static const struct i2c_device_id pat9125_device_id[] = { + {pat9125_DEV_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, pat9125_device_id); + +static const struct dev_pm_ops pat9125_pm_ops = { + .suspend = pat9125_suspend, + .resume = pat9125_resume +}; + +static struct of_device_id pixart_pat9125_match_table[] = { + { .compatible = "pixart,pat9125",}, + { }, +}; + +static struct i2c_driver pat9125_i2c_driver = { + .driver = { + .name = pat9125_DEV_NAME, + .owner = THIS_MODULE, + .pm = &pat9125_pm_ops, + .of_match_table = pixart_pat9125_match_table, + }, + .probe = pat9125_i2c_probe, + .remove = pat9125_i2c_remove, + .id_table = pat9125_device_id, +}; +static int pat9125_open(struct input_dev *dev) +{ + pr_debug(">>> %s (%d)\n", __func__, __LINE__); + return 0; +} + +static void pat9125_close(struct input_dev *dev) +{ + pr_debug(">>> %s (%d)\n", __func__, __LINE__); +} + +static int pat9125_init_input_data(void) +{ + int ret = 0; + + pr_debug("%s (%d) : initialize data\n", __func__, __LINE__); + + pat9125data.pat9125_input_dev = input_allocate_device(); + + if (!pat9125data.pat9125_input_dev) { + pr_debug( + "%s (%d) : could not allocate mouse input device\n", + __func__, __LINE__); + return -ENOMEM; + } + + input_set_drvdata(pat9125data.pat9125_input_dev, &pat9125data); + pat9125data.pat9125_input_dev->name = "Pixart pat9125"; + + pat9125data.pat9125_input_dev->open = pat9125_open; + pat9125data.pat9125_input_dev->close = pat9125_close; + + ret = input_register_device(pat9125data.pat9125_input_dev); + if (ret < 0) { + input_free_device(pat9125data.pat9125_input_dev); + pr_debug( + "%s (%d) : could not register input device\n", + __func__, __LINE__); + return ret; + } + + return 0; +} + +static int __init pat9125_linux_init(void) +{ + pr_debug("%s (%d) :init module\n", __func__, __LINE__); + pr_debug("Date : %s\n", __DATE__); + pr_debug("Time : %s\n", __TIME__); + + return i2c_add_driver(&pat9125_i2c_driver); +} + + + + +static void __exit pat9125_linux_exit(void) +{ + pr_debug("%s (%d) : exit module\n", __func__, __LINE__); + pat9125_stop(); + misc_register(&pat9125_device); + pat9125_delete_attr(pat9125data.pat9125_device); + i2c_del_driver(&pat9125_i2c_driver); +} + + +module_init(pat9125_linux_init); +module_exit(pat9125_linux_exit); +MODULE_AUTHOR("pixart"); +MODULE_DESCRIPTION("pixart pat9125 driver"); +MODULE_LICENSE("GPL"); + + + diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.c b/drivers/input/misc/ots_pat9125/pixart_ots.c new file mode 100644 index 000000000000..3ec8de9d2149 --- /dev/null +++ b/drivers/input/misc/ots_pat9125/pixart_ots.c @@ -0,0 +1,60 @@ + +#include "pixart_ots.h" + +static void OTS_WriteRead(uint8_t address, uint8_t wdata); + +bool OTS_Sensor_Init(void) +{ + unsigned char sensor_pid = 0, read_id_ok = 0; + + /* Read sensor_pid in address 0x00 to check if the + serial link is valid, read value should be 0x31. */ + sensor_pid = ReadData(0x00); + + if (sensor_pid == 0x31) { + read_id_ok = 1; + + /* PAT9125 sensor recommended settings: */ + /* switch to bank0, not allowed to perform OTS_RegWriteRead */ + WriteData(0x7F, 0x00); + /* software reset (i.e. set bit7 to 1). + It will reset to 0 automatically */ + /* so perform OTS_RegWriteRead is not allowed. */ + WriteData(0x06, 0x97); + + delay_ms(1); /* delay 1ms */ + + /* disable write protect */ + OTS_WriteRead(0x09, 0x5A); + /* set X-axis resolution (depends on application) */ + OTS_WriteRead(0x0D, 0x65); + /* set Y-axis resolution (depends on application) */ + OTS_WriteRead(0x0E, 0xFF); + /* set 12-bit X/Y data format (depends on application) */ + OTS_WriteRead(0x19, 0x04); + /* ONLY for VDD=VDDA=1.7~1.9V: for power saving */ + OTS_WriteRead(0x4B, 0x04); + + if (ReadData(0x5E) == 0x04) { + OTS_WriteRead(0x5E, 0x08); + if (ReadData(0x5D) == 0x10) + OTS_WriteRead(0x5D, 0x19); + } + OTS_WriteRead(0x09, 0x00);/* enable write protect */ + } + return read_id_ok; +} + +static void OTS_WriteRead(uint8_t address, uint8_t wdata) +{ + uint8_t read_value; + do { + /* Write data to specified address */ + WriteData(address, wdata); + /* Read back previous written data */ + read_value = ReadData(address); + /* Check if the data is correctly written */ + } while (read_value != wdata); + return; +} + diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.h b/drivers/input/misc/ots_pat9125/pixart_ots.h new file mode 100644 index 000000000000..a1429a7954ee --- /dev/null +++ b/drivers/input/misc/ots_pat9125/pixart_ots.h @@ -0,0 +1,10 @@ +#ifndef _PIXART_OTS_H_ +#define _PIXART_OTS_H_ + +#include "pixart_platform.h" + +/* export funtions */ +bool OTS_Sensor_Init(void); +void OTS_Sensor_ReadMotion(int16_t *dx, int16_t *dy); + +#endif diff --git a/drivers/input/misc/ots_pat9125/pixart_platform.h b/drivers/input/misc/ots_pat9125/pixart_platform.h new file mode 100644 index 000000000000..12e6337aac5a --- /dev/null +++ b/drivers/input/misc/ots_pat9125/pixart_platform.h @@ -0,0 +1,19 @@ +#ifndef _PIXART_PLATFORM_ +#define _PIXART_PLATFORM_ + +#include /* BUS_SPI */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* extern functions */ +extern unsigned char ReadData(unsigned char addr); +extern void WriteData(unsigned char addr, unsigned char data); + +#endif -- cgit v1.2.3