This is the reference driver source code for Pixart OTS PAT9125.

Change-Id: Ie366894e9e89707fa2950a7ffbfe4e93059a19a3
Signed-off-by: Lawrence Liao <lawrence_liao@pixart.com>
(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 <shjain@codeaurora.org>
This commit is contained in:
Lawrence Liao 2016-09-16 13:31:15 +05:30 committed by Shantanu Jain
parent b1dbfd04c7
commit 4e09168fe6
4 changed files with 581 additions and 0 deletions

View file

@ -0,0 +1,492 @@
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/miscdevice.h>
#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");

View file

@ -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;
}

View file

@ -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

View file

@ -0,0 +1,19 @@
#ifndef _PIXART_PLATFORM_
#define _PIXART_PLATFORM_
#include <linux/input.h> /* BUS_SPI */
#include <linux/pm.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/types.h>
/* extern functions */
extern unsigned char ReadData(unsigned char addr);
extern void WriteData(unsigned char addr, unsigned char data);
#endif