3006 lines
64 KiB
C
3006 lines
64 KiB
C
|
/*
|
||
|
* LR388K7 touchscreen driver
|
||
|
*
|
||
|
* Copyright (C) 2014, Sharp Corporation
|
||
|
* Copyright (c) 2015-2018, NVIDIA CORPORATION. All rights reserved.
|
||
|
*
|
||
|
* Author: Makoto Itsuki <itsuki.makoto@sharp.co.jp>
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/input.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/gpio.h>
|
||
|
#include <linux/of.h>
|
||
|
#include <linux/of_device.h>
|
||
|
#include <linux/of_gpio.h>
|
||
|
#include <linux/pm.h>
|
||
|
#include <linux/spi/spi.h>
|
||
|
#include <linux/spi/lr388k7_ts.h>
|
||
|
#include <linux/input/mt.h>
|
||
|
#include <linux/miscdevice.h>
|
||
|
#include <linux/sysfs.h>
|
||
|
#include <linux/clk.h>
|
||
|
#include <linux/regulator/consumer.h>
|
||
|
#include <linux/pinctrl/consumer.h>
|
||
|
#include <linux/uaccess.h>
|
||
|
#include <linux/workqueue.h>
|
||
|
#include <linux/jiffies.h>
|
||
|
#include <linux/spinlock_types.h>
|
||
|
#include <linux/version.h>
|
||
|
|
||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||
|
#include <linux/sched/signal.h>
|
||
|
#endif
|
||
|
|
||
|
/* DEBUG */
|
||
|
/* #define DEBUG_LR388K7 */
|
||
|
|
||
|
/* ACTIVE */
|
||
|
/* #define ACTIVE_ENABLE */
|
||
|
/* UDEV */
|
||
|
#define UDEV_ENABLE
|
||
|
|
||
|
|
||
|
#ifdef __min
|
||
|
#undef __min
|
||
|
#define __min(x, y) ((x) < (y) ? (x) : (y))
|
||
|
#else
|
||
|
#define __min(x, y) ((x) < (y) ? (x) : (y))
|
||
|
#endif
|
||
|
|
||
|
#ifdef __max
|
||
|
#undef __max
|
||
|
#define __max(x, y) ((x) > (y) ? (x) : (y))
|
||
|
#else
|
||
|
#define __max(x, y) ((x) > (y) ? (x) : (y))
|
||
|
#endif
|
||
|
|
||
|
#ifdef __negchk
|
||
|
#undef __negchk
|
||
|
#define __negchk(a) ((a) < 0 ? 0 : (a))
|
||
|
#else
|
||
|
#define __negchk(a) ((a) < 0 ? 0 : (a))
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#define MAX_16BIT 0xffff
|
||
|
#define MAX_12BIT 0xfff
|
||
|
#define MAX_10BIT 0x3ff
|
||
|
#define K7_MAX_TOUCH_NUM (10)
|
||
|
#define K7_MAX_SPEED_HZ (25000000)
|
||
|
#define K7_RD_HEADER_SIZE (5)
|
||
|
#define K7_WR_OPCODE (0x02)
|
||
|
#define K7_RD_OPCODE (0x0B)
|
||
|
#define K7_STATE_CTL_ADDR (0x000024)
|
||
|
#define K7_INT_STATUS_ADDR (0x000000)
|
||
|
#define K7_IND_DONE_ADDR (0x0003E1)
|
||
|
#define K7_DATA_OFFSET (0x1000)
|
||
|
#define K7_DATA_HEADER_WORD_SIZE (0x08)
|
||
|
#define K7_DATA_HEADER_BYTE_SIZE (K7_DATA_HEADER_WORD_SIZE * 2)
|
||
|
#define K7_DATA_READ_SIZE (K7_DATA_HEADER_BYTE_SIZE + K7_RD_HEADER_SIZE)
|
||
|
#define K7_DATA_SIZE (8800 + K7_DATA_READ_SIZE)
|
||
|
#define K7_CMD_DATA_OFFSET (0x030400)
|
||
|
#define K7_CMD_DATA_SIZE (251 + K7_RD_HEADER_SIZE)
|
||
|
#define K7_Q_SIZE (8)
|
||
|
#define K7_DRIVER_VERSION (18)
|
||
|
#define K7_INT_STATUS_MASK_DATA (0x01)
|
||
|
#define K7_INT_STATUS_MASK_BOOT (0x02)
|
||
|
#define K7_INT_STATUS_MASK_CMD (0x04)
|
||
|
#define K7_INT_STATUS_MASK_ERR (0x08)
|
||
|
#define K7_INT_STATUS_MASK_CRC (0x10)
|
||
|
#define K7_POWER_CTL_TAP_WAKEUP (0x00)
|
||
|
#define K7_POWER_CTL_SLEEP (0x01)
|
||
|
#define K7_POWER_CTL_RESUME (0x02)
|
||
|
#define K7_REMOTE_WAKEUP_CODE (0xEA)
|
||
|
#define K7_DEFAULT_MODE (3)
|
||
|
|
||
|
#define U8_SNPRINTF_SIZE 4
|
||
|
|
||
|
enum K7_SCAN_STATE_OPTIONS {
|
||
|
K7_SCAN_STATE_IDLE = 0,
|
||
|
K7_SCAN_STATE_ACTIVE,
|
||
|
};
|
||
|
|
||
|
enum K7_SLOW_SCAN_OPTIONS {
|
||
|
K7_SLOW_SCAN_30 = 30,
|
||
|
K7_SLOW_SCAN_60 = 60,
|
||
|
K7_SLOW_SCAN_100 = 100,
|
||
|
K7_SLOW_SCAN_120 = 120,
|
||
|
};
|
||
|
|
||
|
enum K7_NUM_TAP_OPTIONS {
|
||
|
K7_NUM_TAP_2 = 2,
|
||
|
K7_NUM_TAP_3,
|
||
|
K7_NUM_TAP_4,
|
||
|
};
|
||
|
|
||
|
struct lr388k7_ts_parameter {
|
||
|
u32 u32_pid;
|
||
|
bool b_is_init_finish;
|
||
|
bool b_is_calc_finish;
|
||
|
bool b_is_suspended;
|
||
|
bool b_is_reset;
|
||
|
bool b_is_disabled;
|
||
|
u32 u32SCK;
|
||
|
u16 u16fw_ver;
|
||
|
u16 u16module_ver;
|
||
|
struct workqueue_struct *st_wq_k7;
|
||
|
struct work_struct st_work_k7;
|
||
|
};
|
||
|
|
||
|
struct lr388k7 {
|
||
|
struct spi_device *spi;
|
||
|
struct device *dev;
|
||
|
struct input_dev *idev;
|
||
|
struct pinctrl *pinctrl;
|
||
|
struct pinctrl_state *spi_intf_en;
|
||
|
struct pinctrl_state *spi_intf_dis;
|
||
|
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
struct input_dev *idev_active;
|
||
|
int tool;
|
||
|
#endif
|
||
|
unsigned int gpio_clk_sel;
|
||
|
char phys[32];
|
||
|
struct mutex mutex;
|
||
|
unsigned int irq;
|
||
|
unsigned int gpio_reset;
|
||
|
unsigned int gpio_irq;
|
||
|
spinlock_t lock;
|
||
|
struct timer_list timer;
|
||
|
unsigned int max_num_touch;
|
||
|
int max_x;
|
||
|
int max_y;
|
||
|
int max_z;
|
||
|
int platform_id;
|
||
|
bool flip_x;
|
||
|
bool flip_y;
|
||
|
bool swap_xy;
|
||
|
bool opened;
|
||
|
bool suspended;
|
||
|
bool b_eraser_active;
|
||
|
struct clk *clk;
|
||
|
struct regulator *regulator_3v3;
|
||
|
struct regulator *regulator_1v8;
|
||
|
};
|
||
|
|
||
|
struct lr388k7_queue_info {
|
||
|
u8(*p_queue)[K7_DATA_SIZE];
|
||
|
u16 u16_front;
|
||
|
u16 u16_rear;
|
||
|
};
|
||
|
|
||
|
struct lr388k7_cmd_queue_info {
|
||
|
u8(*p_queue)[K7_CMD_DATA_SIZE];
|
||
|
u16 u16_front;
|
||
|
u16 u16_rear;
|
||
|
};
|
||
|
|
||
|
struct lr388k7_touch_report {
|
||
|
u8 u8_num_of_touch;
|
||
|
struct st_touch_point_t tc[K7_MAX_TOUCH_NUM];
|
||
|
};
|
||
|
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
struct lr388k7_active_report {
|
||
|
s16 x;
|
||
|
s16 y;
|
||
|
u16 z;
|
||
|
u8 status;
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if defined(UDEV_ENABLE)
|
||
|
struct lr388k7_udev_event {
|
||
|
u8 str[512];
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static int dev_open(struct inode *inode, struct file *filp);
|
||
|
static int dev_release(struct inode *inode, struct file *filp);
|
||
|
static ssize_t
|
||
|
dev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos);
|
||
|
static ssize_t
|
||
|
dev_write(struct file *filp, const char __user *buf,
|
||
|
size_t count, loff_t *pos);
|
||
|
static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||
|
|
||
|
static const struct file_operations dev_fops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.open = dev_open,
|
||
|
.release = dev_release,
|
||
|
.read = dev_read,
|
||
|
.write = dev_write,
|
||
|
.unlocked_ioctl = dev_ioctl,
|
||
|
.compat_ioctl = dev_ioctl,
|
||
|
};
|
||
|
|
||
|
static struct miscdevice lr388k7_ts_miscdev = {
|
||
|
.minor = MISC_DYNAMIC_MINOR,
|
||
|
.name = "touch",
|
||
|
.fops = &dev_fops,
|
||
|
};
|
||
|
|
||
|
static struct spi_device *g_spi;
|
||
|
static struct lr388k7_ts_parameter g_st_state;
|
||
|
static struct lr388k7_queue_info g_st_q;
|
||
|
static struct lr388k7_cmd_queue_info g_st_cmd_q;
|
||
|
static struct lr388k7_ts_dbg g_st_dbg;
|
||
|
static u8 g_u8_mode;
|
||
|
static u8 g_u8_scan;
|
||
|
static u16 g_u16_wakeup_enable;
|
||
|
static u32 g_u32_raw_data_size = 50 * 88 * 2;
|
||
|
|
||
|
#define IS_DBG (g_st_dbg.u8ForceCap == 1 || \
|
||
|
g_st_dbg.u8Dump == 1 || \
|
||
|
g_st_dbg.slowscan.enable == 1 ? true : false)
|
||
|
|
||
|
static int lr388k7_spi_write(u8 *txbuf, size_t len);
|
||
|
static int lr388k7_spi_read(u8 *txbuf, u8 *rxbuf, size_t len);
|
||
|
static void lr388k7_init_parameter(void);
|
||
|
static long lr388k7_spi_read_cmd_data(u8 *p, size_t count);
|
||
|
static int lr388k7_input_enable(struct input_dev *idev);
|
||
|
static int lr388k7_input_disable(struct input_dev *idev);
|
||
|
|
||
|
static void lr388k7_queue_reset(void)
|
||
|
{
|
||
|
g_st_q.u16_rear = 0;
|
||
|
g_st_q.u16_front = 0;
|
||
|
g_st_cmd_q.u16_rear = 0;
|
||
|
g_st_cmd_q.u16_front = 0;
|
||
|
}
|
||
|
|
||
|
static int lr388k7_hardreset(unsigned long wait)
|
||
|
{
|
||
|
struct lr388k7 *ts = spi_get_drvdata(g_spi);
|
||
|
if (!ts)
|
||
|
return false;
|
||
|
|
||
|
gpio_set_value_cansleep(ts->gpio_reset, 0);
|
||
|
g_st_state.b_is_reset = true;
|
||
|
usleep_range(wait * 1000, wait * 1000 + 1000);
|
||
|
gpio_set_value_cansleep(ts->gpio_reset, 1);
|
||
|
g_st_state.b_is_reset = false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
static int lr388k7_set_clk_sel(unsigned int sel)
|
||
|
{
|
||
|
struct lr388k7 *ts = spi_get_drvdata(g_spi);
|
||
|
|
||
|
if (!ts)
|
||
|
return false;
|
||
|
|
||
|
if (!gpio_is_valid(ts->gpio_clk_sel))
|
||
|
return false;
|
||
|
|
||
|
if (!sel)
|
||
|
gpio_set_value_cansleep(ts->gpio_clk_sel, 0);
|
||
|
else
|
||
|
gpio_set_value_cansleep(ts->gpio_clk_sel, 1);
|
||
|
return true;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static int lr388k7_queue_init(void)
|
||
|
{
|
||
|
lr388k7_queue_reset();
|
||
|
/*
|
||
|
* Memory Allocation
|
||
|
*/
|
||
|
g_st_q.p_queue = kmalloc(K7_DATA_SIZE * K7_Q_SIZE, GFP_KERNEL);
|
||
|
if (g_st_q.p_queue == NULL)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
g_st_cmd_q.p_queue = kmalloc(K7_CMD_DATA_SIZE * K7_Q_SIZE, GFP_KERNEL);
|
||
|
if (g_st_cmd_q.p_queue == NULL) {
|
||
|
kfree(g_st_q.p_queue);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void lr388k7_queue_free(void)
|
||
|
{
|
||
|
if (!g_st_q.p_queue)
|
||
|
return;
|
||
|
kfree(g_st_q.p_queue);
|
||
|
g_st_q.p_queue = NULL;
|
||
|
|
||
|
if (!g_st_cmd_q.p_queue)
|
||
|
return;
|
||
|
kfree(g_st_cmd_q.p_queue);
|
||
|
g_st_cmd_q.p_queue = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int lr388k7_queue_is_full(void)
|
||
|
{
|
||
|
u16 u16_front = g_st_q.u16_front;
|
||
|
if (g_st_q.u16_rear + 1 == u16_front)
|
||
|
return 1;
|
||
|
|
||
|
if ((g_st_q.u16_rear == (K7_Q_SIZE - 1)) && (u16_front == 0))
|
||
|
return 1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int lr388k7_queue_is_empty(void)
|
||
|
{
|
||
|
if (g_st_q.u16_rear == g_st_q.u16_front)
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int lr388k7_cmd_queue_is_full(void)
|
||
|
{
|
||
|
u16 u16_front = g_st_cmd_q.u16_front;
|
||
|
if (g_st_cmd_q.u16_rear + 1 == u16_front)
|
||
|
return 1;
|
||
|
|
||
|
if ((g_st_cmd_q.u16_rear == (K7_Q_SIZE - 1)) && (u16_front == 0))
|
||
|
return 1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int lr388k7_cmd_queue_is_empty(void)
|
||
|
{
|
||
|
if (g_st_cmd_q.u16_rear == g_st_cmd_q.u16_front)
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void *lr388k7_enqueue_start(void)
|
||
|
{
|
||
|
if (!g_st_q.p_queue)
|
||
|
return NULL;
|
||
|
|
||
|
if (!lr388k7_queue_is_full())
|
||
|
return &g_st_q.p_queue[g_st_q.u16_rear];
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void lr388k7_enqueue_finish(void)
|
||
|
{
|
||
|
if (g_st_q.u16_rear == (K7_Q_SIZE - 1))
|
||
|
g_st_q.u16_rear = 0;
|
||
|
else
|
||
|
g_st_q.u16_rear++;
|
||
|
}
|
||
|
|
||
|
static void *lr388k7_dequeue_start(void)
|
||
|
{
|
||
|
if (!lr388k7_queue_is_empty())
|
||
|
return &g_st_q.p_queue[g_st_q.u16_front];
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void lr388k7_dequeue_finish(void)
|
||
|
{
|
||
|
if (g_st_q.u16_front == (K7_Q_SIZE - 1))
|
||
|
g_st_q.u16_front = 0;
|
||
|
else
|
||
|
g_st_q.u16_front++;
|
||
|
}
|
||
|
|
||
|
static int lr388k7_queue_read_raw_data(u8 *p, u32 u32_len)
|
||
|
{
|
||
|
u8 *p_queue;
|
||
|
u32 u32_ret;
|
||
|
p_queue = lr388k7_dequeue_start();
|
||
|
if (!p_queue)
|
||
|
return 0;
|
||
|
|
||
|
u32_ret = copy_to_user(p, p_queue, u32_len);
|
||
|
if (u32_ret != 0)
|
||
|
return 0;
|
||
|
|
||
|
lr388k7_dequeue_finish();
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static void *lr388k7_cmd_enqueue_start(void)
|
||
|
{
|
||
|
if (!g_st_cmd_q.p_queue)
|
||
|
return NULL;
|
||
|
|
||
|
if (!lr388k7_cmd_queue_is_full())
|
||
|
return &g_st_cmd_q.p_queue[g_st_cmd_q.u16_rear];
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void lr388k7_cmd_enqueue_finish(void)
|
||
|
{
|
||
|
if (g_st_cmd_q.u16_rear == (K7_Q_SIZE - 1))
|
||
|
g_st_cmd_q.u16_rear = 0;
|
||
|
else
|
||
|
g_st_cmd_q.u16_rear++;
|
||
|
}
|
||
|
|
||
|
static void *lr388k7_cmd_dequeue_start(void)
|
||
|
{
|
||
|
if (!lr388k7_cmd_queue_is_empty())
|
||
|
return &g_st_cmd_q.p_queue[g_st_cmd_q.u16_front];
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void lr388k7_cmd_dequeue_finish(void)
|
||
|
{
|
||
|
if (g_st_cmd_q.u16_front == (K7_Q_SIZE - 1))
|
||
|
g_st_cmd_q.u16_front = 0;
|
||
|
else
|
||
|
g_st_cmd_q.u16_front++;
|
||
|
}
|
||
|
|
||
|
static int lr388k7_cmd_queue_read(u8 *p, u32 u32_len)
|
||
|
{
|
||
|
u8 *p_queue;
|
||
|
u32 u32_ret;
|
||
|
p_queue = lr388k7_cmd_dequeue_start();
|
||
|
if (!p_queue)
|
||
|
return 0;
|
||
|
|
||
|
if (u32_len > K7_CMD_DATA_SIZE)
|
||
|
u32_len = K7_CMD_DATA_SIZE;
|
||
|
|
||
|
u32_ret = copy_to_user(p, p_queue, u32_len);
|
||
|
if (u32_ret != 0)
|
||
|
return 0;
|
||
|
|
||
|
lr388k7_cmd_dequeue_finish();
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static u8 lr388k7_clear_irq(void)
|
||
|
{
|
||
|
u8 u8_tx_buf[K7_RD_HEADER_SIZE + 1], u8_rx_buf[K7_RD_HEADER_SIZE + 1];
|
||
|
u8 u8Ret = 0;
|
||
|
size_t count = 1;
|
||
|
|
||
|
if (g_st_state.b_is_reset)
|
||
|
return u8Ret;
|
||
|
|
||
|
u8_tx_buf[0] = K7_RD_OPCODE;
|
||
|
u8_tx_buf[1] = (K7_INT_STATUS_ADDR >> 16) & 0xFF;
|
||
|
u8_tx_buf[2] = (K7_INT_STATUS_ADDR >> 8) & 0xFF;
|
||
|
u8_tx_buf[3] = (K7_INT_STATUS_ADDR >> 0) & 0xFF;
|
||
|
u8_tx_buf[4] = 0x00;
|
||
|
|
||
|
g_st_state.u32SCK = 0;
|
||
|
|
||
|
if (lr388k7_spi_read(u8_tx_buf, u8_rx_buf, count))
|
||
|
u8Ret = u8_rx_buf[K7_RD_HEADER_SIZE];
|
||
|
|
||
|
g_st_state.u32SCK = g_spi->max_speed_hz;
|
||
|
|
||
|
return u8Ret;
|
||
|
}
|
||
|
|
||
|
static long lr388k7_ts_get_mode(u8 *p)
|
||
|
{
|
||
|
ssize_t status = 0;
|
||
|
|
||
|
if (p == 0) {
|
||
|
status = -EFAULT;
|
||
|
goto exit_get_mode;
|
||
|
}
|
||
|
|
||
|
if (copy_to_user(p, &g_u8_mode, sizeof(u8)))
|
||
|
status = -EFAULT;
|
||
|
|
||
|
exit_get_mode:
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static long lr388k7_ts_get_debug_status(u8 *p)
|
||
|
{
|
||
|
ssize_t status = 0;
|
||
|
|
||
|
if (p == 0) {
|
||
|
status = -EFAULT;
|
||
|
goto exit_get_debug_status;
|
||
|
}
|
||
|
|
||
|
if (copy_to_user(p, &g_st_dbg, sizeof(struct lr388k7_ts_dbg)))
|
||
|
status = -EFAULT;
|
||
|
|
||
|
exit_get_debug_status:
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static void lr388k7_indicate_done(void)
|
||
|
{
|
||
|
#if 0
|
||
|
u8 u8_buf[5];
|
||
|
size_t count = 0;
|
||
|
|
||
|
u8_buf[count++] = K7_WR_OPCODE;
|
||
|
u8_buf[count++] = (K7_IND_DONE_ADDR >> 16) & 0xFF;
|
||
|
u8_buf[count++] = (K7_IND_DONE_ADDR >> 8) & 0xFF;
|
||
|
u8_buf[count++] = (K7_IND_DONE_ADDR >> 0) & 0xFF;
|
||
|
u8_buf[count++] = 0x01;
|
||
|
lr388k7_spi_write(u8_buf, count);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static int lr388k7_read_data(u8 *p)
|
||
|
{
|
||
|
u8 *p_tx_buf;
|
||
|
size_t count = 0;
|
||
|
ssize_t status = 0;
|
||
|
|
||
|
/*
|
||
|
* Memory Allocation
|
||
|
*/
|
||
|
count = (size_t)g_u32_raw_data_size;
|
||
|
|
||
|
p_tx_buf = kmalloc(K7_DATA_READ_SIZE + count, GFP_KERNEL);
|
||
|
if (p_tx_buf == NULL) {
|
||
|
status = -ENOMEM;
|
||
|
goto exit_read_data;
|
||
|
}
|
||
|
|
||
|
p_tx_buf[0] = K7_RD_OPCODE;
|
||
|
p_tx_buf[1] = (K7_DATA_OFFSET >> 16) & 0xFF;
|
||
|
p_tx_buf[2] = (K7_DATA_OFFSET >> 8) & 0xFF;
|
||
|
p_tx_buf[3] = (K7_DATA_OFFSET >> 0) & 0xFF;
|
||
|
p_tx_buf[4] = 0x00;
|
||
|
|
||
|
if (lr388k7_spi_read(p_tx_buf, p, K7_DATA_HEADER_BYTE_SIZE + count))
|
||
|
status = count;
|
||
|
else{
|
||
|
status = -EFAULT;
|
||
|
goto exit_read_data_free_tx;
|
||
|
}
|
||
|
|
||
|
exit_read_data_free_tx:
|
||
|
kfree(p_tx_buf);
|
||
|
exit_read_data:
|
||
|
if (status < 0)
|
||
|
return 0;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int lr388k7_send_signal(int pid, int n_info)
|
||
|
{
|
||
|
struct siginfo info;
|
||
|
struct task_struct *t;
|
||
|
int ret = 0;
|
||
|
|
||
|
static DEFINE_MUTEX(lock);
|
||
|
|
||
|
if (!pid)
|
||
|
return ret;
|
||
|
|
||
|
mutex_lock(&lock);
|
||
|
|
||
|
/*
|
||
|
* Set signal parameters
|
||
|
*/
|
||
|
memset(&info, 0, sizeof(struct siginfo));
|
||
|
info.si_signo = LR388K7_SIGNAL;
|
||
|
info.si_code = SI_QUEUE;
|
||
|
info.si_int = n_info;/*real time signals may have 32 bits of data. */
|
||
|
|
||
|
rcu_read_lock();
|
||
|
/* To avoid link error for module build, it was replaced by pid_task.
|
||
|
t = find_task_by_vpid(pid);
|
||
|
*/
|
||
|
t = pid_task(find_vpid(pid), PIDTYPE_PID);
|
||
|
rcu_read_unlock();
|
||
|
if (t == NULL) {
|
||
|
dev_err(&g_spi->dev, " : no such pid\n");
|
||
|
ret = -ENODEV;
|
||
|
} else{
|
||
|
ret = send_sig_info(LR388K7_SIGNAL, &info, t);
|
||
|
}
|
||
|
|
||
|
if (ret < 0)
|
||
|
dev_err(&g_spi->dev, " : sending signal\n");
|
||
|
|
||
|
mutex_unlock(&lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void lr388k7_read_spec_size(void)
|
||
|
{
|
||
|
int n_ret;
|
||
|
u8 *p_buf;
|
||
|
|
||
|
p_buf = kmalloc(K7_DATA_SIZE, GFP_KERNEL);
|
||
|
if (p_buf) {
|
||
|
n_ret = lr388k7_read_data((u8 *)p_buf);
|
||
|
kfree(p_buf);
|
||
|
} else {
|
||
|
dev_err(&g_spi->dev, " : %d\n", -ENOMEM);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void lr388k7_event_handler(u8 u8Status)
|
||
|
{
|
||
|
void *p_q_buf;
|
||
|
void *p_cmd_q_buf;
|
||
|
int n_ret;
|
||
|
|
||
|
if (u8Status & K7_INT_STATUS_MASK_BOOT) {
|
||
|
/*
|
||
|
* Let module know device has booted up.
|
||
|
*/
|
||
|
#if defined(DEBUG_LR388K7)
|
||
|
dev_info(&g_spi->dev, "boot\n");
|
||
|
#endif
|
||
|
lr388k7_send_signal(g_st_state.u32_pid, LR388K7_SIGNAL_BOOT);
|
||
|
}
|
||
|
|
||
|
if (u8Status & K7_INT_STATUS_MASK_CRC)
|
||
|
lr388k7_send_signal(g_st_state.u32_pid, LR388K7_SIGNAL_CRCE);
|
||
|
|
||
|
if (u8Status & K7_INT_STATUS_MASK_DATA) {
|
||
|
|
||
|
/*
|
||
|
* Handle data
|
||
|
*/
|
||
|
if (g_st_state.b_is_init_finish) {
|
||
|
|
||
|
/*
|
||
|
* Get pointer of queue buffer
|
||
|
*/
|
||
|
p_q_buf = lr388k7_enqueue_start();
|
||
|
if (p_q_buf) {
|
||
|
n_ret = lr388k7_read_data((u8 *)p_q_buf);
|
||
|
if (n_ret)
|
||
|
lr388k7_enqueue_finish();
|
||
|
} else {
|
||
|
lr388k7_read_spec_size();
|
||
|
}
|
||
|
} else {
|
||
|
lr388k7_read_spec_size();
|
||
|
}
|
||
|
|
||
|
if (g_st_state.b_is_calc_finish) {
|
||
|
g_st_state.b_is_calc_finish = 0;
|
||
|
lr388k7_send_signal(
|
||
|
g_st_state.u32_pid,
|
||
|
LR388K7_SIGNAL_INTR
|
||
|
);
|
||
|
}
|
||
|
lr388k7_indicate_done();
|
||
|
}
|
||
|
|
||
|
if (u8Status & K7_INT_STATUS_MASK_CMD) {
|
||
|
|
||
|
/*
|
||
|
* Process command result
|
||
|
*/
|
||
|
p_cmd_q_buf = lr388k7_cmd_enqueue_start();
|
||
|
if (p_cmd_q_buf) {
|
||
|
n_ret = lr388k7_spi_read_cmd_data(
|
||
|
(u8 *)p_cmd_q_buf,
|
||
|
K7_CMD_DATA_SIZE
|
||
|
- K7_RD_HEADER_SIZE
|
||
|
);
|
||
|
if (n_ret) {
|
||
|
lr388k7_cmd_enqueue_finish();
|
||
|
lr388k7_send_signal(
|
||
|
g_st_state.u32_pid,
|
||
|
LR388K7_SIGNAL_CMDR
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void lr388k7_work_handler(struct work_struct *work)
|
||
|
{
|
||
|
struct lr388k7 *ts = spi_get_drvdata(g_spi);
|
||
|
u8 u8_status;
|
||
|
int irq_value;
|
||
|
|
||
|
if (g_st_state.b_is_suspended) {
|
||
|
dev_dbg(
|
||
|
&g_spi->dev,
|
||
|
"[WARN] work handler is called regardless of b_is_suspended\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&ts->mutex);
|
||
|
|
||
|
/*
|
||
|
* Clear Interrupt
|
||
|
*/
|
||
|
u8_status = lr388k7_clear_irq();
|
||
|
|
||
|
while (u8_status) {
|
||
|
|
||
|
lr388k7_event_handler(u8_status);
|
||
|
|
||
|
/*
|
||
|
* Check if irq is already cleared.
|
||
|
*/
|
||
|
irq_value = gpio_get_value(ts->gpio_irq);
|
||
|
|
||
|
if (!irq_value)
|
||
|
u8_status = lr388k7_clear_irq();
|
||
|
else
|
||
|
u8_status = 0;
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&ts->mutex);
|
||
|
}
|
||
|
|
||
|
static irqreturn_t lr388k7_wakeup_thread(int irq, void *_ts)
|
||
|
{
|
||
|
struct lr388k7 *ts = (struct lr388k7 *)_ts;
|
||
|
struct input_dev *input_dev = ts->idev;
|
||
|
u8 u8_status;
|
||
|
|
||
|
dev_info(&g_spi->dev, "[ENTER] Wakeup thread\n");
|
||
|
|
||
|
/* just try to clear */
|
||
|
u8_status = lr388k7_clear_irq();
|
||
|
|
||
|
dev_info(&g_spi->dev, "Tap wakeup\n");
|
||
|
|
||
|
input_report_key(input_dev, KEY_POWER, 1);
|
||
|
input_sync(input_dev);
|
||
|
input_report_key(input_dev, KEY_POWER, 0);
|
||
|
input_sync(input_dev);
|
||
|
|
||
|
if (g_st_state.b_is_suspended)
|
||
|
g_st_state.b_is_suspended = false;
|
||
|
|
||
|
dev_info(&g_spi->dev, "[EXIT] Wakeup thread\n");
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
static irqreturn_t lr388k7_irq_thread(int irq, void *_ts)
|
||
|
{
|
||
|
struct lr388k7 *ts = (struct lr388k7 *)_ts;
|
||
|
struct input_dev *input_dev = ts->idev;
|
||
|
|
||
|
if (g_u8_scan == K7_SCAN_STATE_IDLE) {
|
||
|
input_event(input_dev, EV_MSC, MSC_ACTIVITY, 1);
|
||
|
input_sync(input_dev);
|
||
|
}
|
||
|
|
||
|
if (g_st_state.b_is_suspended) {
|
||
|
if (g_st_dbg.wakeup.enable == 1) {
|
||
|
dev_info(&g_spi->dev, "Throw IRQ_WAKE_THREAD\n");
|
||
|
return IRQ_WAKE_THREAD;
|
||
|
}
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
queue_work(g_st_state.st_wq_k7, &g_st_state.st_work_k7);
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
/* must be called with ts->mutex held */
|
||
|
static void __lr388k7_disable(struct lr388k7 *ts)
|
||
|
{
|
||
|
disable_irq(ts->irq);
|
||
|
}
|
||
|
|
||
|
/* must be called with ts->mutex held */
|
||
|
static void __lr388k7_enable(struct lr388k7 *ts)
|
||
|
{
|
||
|
enable_irq(ts->irq);
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_force_cap_show(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
return snprintf(buf, U8_SNPRINTF_SIZE, "%d\n", g_st_dbg.u8ForceCap);
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_force_cap_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf,
|
||
|
size_t count)
|
||
|
{
|
||
|
ssize_t ret;
|
||
|
|
||
|
ret = (ssize_t)count;
|
||
|
|
||
|
if (g_st_state.b_is_suspended)
|
||
|
return ret;
|
||
|
|
||
|
if (count != 1)
|
||
|
return ret;
|
||
|
|
||
|
if (buf[0] == '0')
|
||
|
g_st_dbg.u8ForceCap = 0;
|
||
|
else if (buf[0] == '1')
|
||
|
g_st_dbg.u8ForceCap = 1;
|
||
|
|
||
|
lr388k7_send_signal(g_st_state.u32_pid, LR388K7_SIGNAL_CTRL);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_dump_show(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
return snprintf(buf, U8_SNPRINTF_SIZE, "%d\n", g_st_dbg.u8Dump);
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_dump_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf,
|
||
|
size_t count)
|
||
|
{
|
||
|
ssize_t ret;
|
||
|
|
||
|
ret = (ssize_t)count;
|
||
|
|
||
|
if (g_st_state.b_is_suspended)
|
||
|
return ret;
|
||
|
|
||
|
if (count != 1)
|
||
|
return ret;
|
||
|
|
||
|
if (buf[0] == '0')
|
||
|
g_st_dbg.u8Dump = 0;
|
||
|
else if (buf[0] == '1')
|
||
|
g_st_dbg.u8Dump = 1;
|
||
|
|
||
|
lr388k7_send_signal(g_st_state.u32_pid, LR388K7_SIGNAL_CTRL);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_report_mode_show(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
return snprintf(buf, U8_SNPRINTF_SIZE, "%d\n", g_u8_mode);
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_report_mode_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf,
|
||
|
size_t count)
|
||
|
{
|
||
|
ssize_t ret;
|
||
|
u8 prev_mode = g_u8_mode;
|
||
|
|
||
|
ret = (ssize_t)count;
|
||
|
|
||
|
if (count != 2)
|
||
|
return ret;
|
||
|
|
||
|
if (buf[0] >= 0x30 && buf[0] <= 0x36)
|
||
|
g_u8_mode = (u8)buf[0] - 0x30;
|
||
|
|
||
|
if (prev_mode != g_u8_mode)
|
||
|
lr388k7_send_signal(g_st_state.u32_pid,
|
||
|
LR388K7_SIGNAL_MODE);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_version_show(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
return snprintf(buf, PAGE_SIZE, "FW %d, Driver %d, Module %d\n",
|
||
|
g_st_state.u16fw_ver,
|
||
|
K7_DRIVER_VERSION,
|
||
|
g_st_state.u16module_ver
|
||
|
);
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_version_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t lr388k7_ts_slowscan_enable_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
#ifdef ENABLE_SLOW_SCAN
|
||
|
unsigned long val;
|
||
|
ssize_t err;
|
||
|
ssize_t ret;
|
||
|
if (count < 2)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (g_st_state.b_is_suspended)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = (ssize_t) count;
|
||
|
|
||
|
if (count == 2) {
|
||
|
if (buf[0] == '0') {
|
||
|
g_st_dbg.slowscan.enable = 0;
|
||
|
lr388k7_send_signal(g_st_state.u32_pid,
|
||
|
LR388K7_SIGNAL_CTRL);
|
||
|
} else if (buf[0] == '1') {
|
||
|
g_st_dbg.slowscan.enable = 1;
|
||
|
lr388k7_send_signal(g_st_state.u32_pid,
|
||
|
LR388K7_SIGNAL_CTRL);
|
||
|
}
|
||
|
} else if ((buf[0] == '2') && (buf[1] == ' ')) {
|
||
|
err = kstrtoul(&buf[2], 10, &val);
|
||
|
|
||
|
if (err) {
|
||
|
ret = err;
|
||
|
} else {
|
||
|
/* check if prefined value*/
|
||
|
if ((val == K7_SLOW_SCAN_30) ||
|
||
|
(val == K7_SLOW_SCAN_60) ||
|
||
|
(val == K7_SLOW_SCAN_100) ||
|
||
|
(val == K7_SLOW_SCAN_120)) {
|
||
|
g_st_dbg.slowscan.scan_rate = (u16)val;
|
||
|
g_st_dbg.slowscan.enable = 1;
|
||
|
lr388k7_send_signal(g_st_state.u32_pid,
|
||
|
LR388K7_SIGNAL_CTRL);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
#else
|
||
|
return count;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_slowscan_enable_show(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
#ifdef ENABLE_SLOW_SCAN
|
||
|
return snprintf(buf, PAGE_SIZE, "Slow Scan:%s Scan Rate:%dHz\n",
|
||
|
g_st_dbg.slowscan.enable ?
|
||
|
"Enabled" : "Disabled",
|
||
|
g_st_dbg.slowscan.scan_rate);
|
||
|
#else
|
||
|
return snprintf(buf, PAGE_SIZE, "Not implemented yet\n");
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_wakeup_enable_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
ssize_t ret;
|
||
|
|
||
|
if (count < 2)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (g_st_state.b_is_suspended)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = (ssize_t) count;
|
||
|
|
||
|
if (count == 2) {
|
||
|
if (buf[0] == '0') {
|
||
|
g_st_dbg.wakeup.enable = 0;
|
||
|
lr388k7_send_signal(g_st_state.u32_pid,
|
||
|
LR388K7_SIGNAL_CTRL);
|
||
|
} else if (buf[0] == '2') {
|
||
|
if (g_st_dbg.wakeup.enable <= 1)
|
||
|
g_u16_wakeup_enable = g_st_dbg.wakeup.enable;
|
||
|
g_st_dbg.wakeup.enable = 2;
|
||
|
lr388k7_send_signal(g_st_state.u32_pid,
|
||
|
LR388K7_SIGNAL_CTRL);
|
||
|
}
|
||
|
} else if ((buf[0] == '1') && (buf[1] == ' ')) {
|
||
|
/* check if prefined value*/
|
||
|
if ((buf[2] == '2') ||
|
||
|
(buf[2] == '3') ||
|
||
|
(buf[2] == '4')) {
|
||
|
g_st_dbg.wakeup.num_tap = buf[2] - 0x30;
|
||
|
g_st_dbg.wakeup.enable = 1;
|
||
|
lr388k7_send_signal(g_st_state.u32_pid,
|
||
|
LR388K7_SIGNAL_CTRL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_wakeup_enable_show(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
return snprintf(buf, PAGE_SIZE,
|
||
|
"wakeup_enable:%s(%d) Number of taps:%d\n",
|
||
|
g_st_dbg.wakeup.enable == 1 ?
|
||
|
"Enabled" : "Disabled",
|
||
|
g_st_dbg.wakeup.enable,
|
||
|
g_st_dbg.wakeup.num_tap);
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_test_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
ssize_t ret;
|
||
|
|
||
|
if (count != 2)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = (ssize_t) count;
|
||
|
|
||
|
if (buf[0] == '1') {
|
||
|
g_st_dbg.u8Test = 1;
|
||
|
lr388k7_send_signal(g_st_state.u32_pid,
|
||
|
LR388K7_SIGNAL_CTRL);
|
||
|
} else if (buf[0] == '0') {
|
||
|
g_st_dbg.u8Test = 0;
|
||
|
lr388k7_send_signal(g_st_state.u32_pid,
|
||
|
LR388K7_SIGNAL_CTRL);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_test_show(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
return snprintf(buf, PAGE_SIZE, "%s\n", g_st_dbg.u8Test == 1 ?
|
||
|
"Enabled" : "Disabled");
|
||
|
}
|
||
|
|
||
|
#if defined(DEBUG_LR388K7)
|
||
|
static ssize_t lr388k7_ts_check_state_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
ssize_t ret;
|
||
|
ret = (ssize_t) count;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_check_state_show(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
struct lr388k7 *ts = spi_get_drvdata(g_spi);
|
||
|
u8 u8_tx_buf[K7_RD_HEADER_SIZE + 5], u8_rx_buf[K7_RD_HEADER_SIZE + 5];
|
||
|
u8 u8Ret;
|
||
|
u8 u8HWR;
|
||
|
u32 u32RES, u32FWR;
|
||
|
size_t count = 0;
|
||
|
|
||
|
u8_tx_buf[count++] = K7_RD_OPCODE;
|
||
|
u8_tx_buf[count++] = (K7_STATE_CTL_ADDR >> 16) & 0xFF;
|
||
|
u8_tx_buf[count++] = (K7_STATE_CTL_ADDR >> 8) & 0xFF;
|
||
|
u8_tx_buf[count++] = (K7_STATE_CTL_ADDR >> 0) & 0xFF;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
|
||
|
g_st_state.u32SCK = 0;
|
||
|
|
||
|
if (lr388k7_spi_read(u8_tx_buf, u8_rx_buf, count)) {
|
||
|
u8Ret = u8_rx_buf[K7_RD_HEADER_SIZE];
|
||
|
} else {
|
||
|
return snprintf(buf, PAGE_SIZE,
|
||
|
"status :IRQ(%s) Failed to read\n",
|
||
|
gpio_get_value(ts->gpio_irq) ?
|
||
|
"High" : "Low"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
count = 0;
|
||
|
u8_tx_buf[count++] = K7_RD_OPCODE;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
u8_tx_buf[count++] = 0x02;
|
||
|
u8_tx_buf[count++] = 0xAC;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
|
||
|
if (lr388k7_spi_read(u8_tx_buf, u8_rx_buf, count)) {
|
||
|
u8HWR = u8_rx_buf[K7_RD_HEADER_SIZE];
|
||
|
} else {
|
||
|
g_st_state.u32SCK = g_spi->max_speed_hz;
|
||
|
return snprintf(buf, PAGE_SIZE,
|
||
|
"status :IRQ(%s) Failed to read\n",
|
||
|
gpio_get_value(ts->gpio_irq) ?
|
||
|
"High" : "Low"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
count = 0;
|
||
|
u8_tx_buf[count++] = K7_RD_OPCODE;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
u8_tx_buf[count++] = 0x02;
|
||
|
u8_tx_buf[count++] = 0xB0;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
|
||
|
if (lr388k7_spi_read(u8_tx_buf, u8_rx_buf, count)) {
|
||
|
u32FWR = u8_rx_buf[K7_RD_HEADER_SIZE] |
|
||
|
u8_rx_buf[K7_RD_HEADER_SIZE + 1] << 8 |
|
||
|
u8_rx_buf[K7_RD_HEADER_SIZE + 2] << 16 |
|
||
|
u8_rx_buf[K7_RD_HEADER_SIZE + 3] << 24;
|
||
|
} else {
|
||
|
g_st_state.u32SCK = g_spi->max_speed_hz;
|
||
|
return snprintf(buf, PAGE_SIZE,
|
||
|
"status :IRQ(%s) Failed to read\n",
|
||
|
gpio_get_value(ts->gpio_irq) ?
|
||
|
"High" : "Low"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
count = 0;
|
||
|
u8_tx_buf[count++] = K7_RD_OPCODE;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
u8_tx_buf[count++] = 0x40;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
u8_tx_buf[count++] = 0x00;
|
||
|
|
||
|
if (lr388k7_spi_read(u8_tx_buf, u8_rx_buf, count)) {
|
||
|
u32RES = u8_rx_buf[K7_RD_HEADER_SIZE] |
|
||
|
u8_rx_buf[K7_RD_HEADER_SIZE + 1] << 8 |
|
||
|
u8_rx_buf[K7_RD_HEADER_SIZE + 2] << 16 |
|
||
|
u8_rx_buf[K7_RD_HEADER_SIZE + 3] << 24;
|
||
|
} else {
|
||
|
g_st_state.u32SCK = g_spi->max_speed_hz;
|
||
|
return snprintf(buf, PAGE_SIZE,
|
||
|
"status :IRQ(%s) Failed to read\n",
|
||
|
gpio_get_value(ts->gpio_irq) ?
|
||
|
"High" : "Low"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
g_st_state.u32SCK = g_spi->max_speed_hz;
|
||
|
|
||
|
return snprintf(
|
||
|
buf, PAGE_SIZE,
|
||
|
"IRQ(%s) status=0x%02X, HWRev=%d, FWRev=0x%0X, Res=0x%04X\n",
|
||
|
gpio_get_value(ts->gpio_irq) ?
|
||
|
"High" : "Low",
|
||
|
u8Ret,
|
||
|
u8HWR,
|
||
|
u32FWR,
|
||
|
u32RES
|
||
|
);
|
||
|
}
|
||
|
|
||
|
#define LR388K7_LOG_MAX_SIZE (2 * 1024 * 1024)
|
||
|
#define LR388K7_LOG_DATA_SIZE (512)
|
||
|
|
||
|
struct lr388k7_log {
|
||
|
unsigned long seq_num;
|
||
|
unsigned long time;
|
||
|
unsigned char data[LR388K7_LOG_DATA_SIZE];
|
||
|
};
|
||
|
|
||
|
DEFINE_SPINLOCK(lr388k7log_lock);
|
||
|
#define LR388K7_LOG_ENTRY (LR388K7_LOG_MAX_SIZE / sizeof(struct lr388k7_log))
|
||
|
static struct lr388k7_log glog[LR388K7_LOG_ENTRY];
|
||
|
static int log_size = sizeof(glog) / sizeof(glog[0]);
|
||
|
static unsigned long g_seq_num;
|
||
|
static int g_log_front;
|
||
|
static int g_log_rear;
|
||
|
|
||
|
|
||
|
static ssize_t lr388k7_ts_log_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
ssize_t ret;
|
||
|
struct lr388k7_log log;
|
||
|
unsigned long flags;
|
||
|
|
||
|
ret = (ssize_t) count;
|
||
|
|
||
|
g_seq_num++;
|
||
|
log.seq_num = g_seq_num;
|
||
|
log.time = jiffies;
|
||
|
memcpy(log.data, buf, sizeof(log.data));
|
||
|
spin_lock_irqsave(&lr388k7log_lock, flags);
|
||
|
|
||
|
if (((g_log_rear + 1) % log_size) == g_log_front) {
|
||
|
g_log_front++;
|
||
|
if (g_log_front >= log_size)
|
||
|
g_log_front = 0;
|
||
|
g_log_rear++;
|
||
|
if (g_log_rear >= log_size)
|
||
|
g_log_rear = 0;
|
||
|
} else {
|
||
|
g_log_rear++;
|
||
|
if (g_log_rear >= log_size)
|
||
|
g_log_rear = 0;
|
||
|
}
|
||
|
|
||
|
glog[g_log_rear] = log;
|
||
|
|
||
|
spin_unlock_irqrestore(&lr388k7log_lock, flags);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static ssize_t lr388k7_ts_log_show(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
char *s;
|
||
|
unsigned long flags;
|
||
|
struct lr388k7_log log;
|
||
|
|
||
|
s = buf;
|
||
|
|
||
|
snprintf(s, PAGE_SIZE,
|
||
|
"FW %d, Driver %d, Module %d\n",
|
||
|
g_st_state.u16fw_ver,
|
||
|
K7_DRIVER_VERSION,
|
||
|
g_st_state.u16module_ver
|
||
|
);
|
||
|
|
||
|
s += strlen(s);
|
||
|
|
||
|
for (; (s - buf) + LR388K7_LOG_DATA_SIZE < PAGE_SIZE; ) {
|
||
|
spin_lock_irqsave(&lr388k7log_lock, flags);
|
||
|
|
||
|
if (g_log_rear == g_log_front) {
|
||
|
spin_unlock_irqrestore(&lr388k7log_lock, flags);
|
||
|
return (ssize_t)(s - buf);
|
||
|
}
|
||
|
|
||
|
g_log_front++;
|
||
|
if (g_log_front >= log_size)
|
||
|
g_log_front = 0;
|
||
|
log = glog[g_log_front];
|
||
|
spin_unlock_irqrestore(&lr388k7log_lock, flags);
|
||
|
|
||
|
snprintf(s, PAGE_SIZE, "[%08lx|%08lx] %s",
|
||
|
log.seq_num,
|
||
|
log.time,
|
||
|
log.data);
|
||
|
s += strlen(s);
|
||
|
}
|
||
|
|
||
|
return (ssize_t)(s - buf);
|
||
|
}
|
||
|
#endif /* DEBUG_LR388K7 */
|
||
|
|
||
|
static DEVICE_ATTR(force_cap, 0640, lr388k7_ts_force_cap_show,
|
||
|
lr388k7_ts_force_cap_store);
|
||
|
static DEVICE_ATTR(dump, 0640, lr388k7_ts_dump_show,
|
||
|
lr388k7_ts_dump_store);
|
||
|
static DEVICE_ATTR(report_mode, 0640, lr388k7_ts_report_mode_show,
|
||
|
lr388k7_ts_report_mode_store);
|
||
|
static DEVICE_ATTR(version, 0640,
|
||
|
lr388k7_ts_version_show,
|
||
|
lr388k7_ts_version_store);
|
||
|
static DEVICE_ATTR(slowscan_enable, 0640,
|
||
|
lr388k7_ts_slowscan_enable_show,
|
||
|
lr388k7_ts_slowscan_enable_store);
|
||
|
static DEVICE_ATTR(wakeup_enable, 0640,
|
||
|
lr388k7_ts_wakeup_enable_show,
|
||
|
lr388k7_ts_wakeup_enable_store);
|
||
|
static DEVICE_ATTR(test, 0640,
|
||
|
lr388k7_ts_test_show,
|
||
|
lr388k7_ts_test_store);
|
||
|
#if defined(DEBUG_LR388K7)
|
||
|
static DEVICE_ATTR(check_state, 0640,
|
||
|
lr388k7_ts_check_state_show,
|
||
|
lr388k7_ts_check_state_store);
|
||
|
|
||
|
static DEVICE_ATTR(log, 0640,
|
||
|
lr388k7_ts_log_show,
|
||
|
lr388k7_ts_log_store);
|
||
|
#endif
|
||
|
|
||
|
static struct attribute *lr388k7_ts_attributes[] = {
|
||
|
&dev_attr_force_cap.attr,
|
||
|
&dev_attr_dump.attr,
|
||
|
&dev_attr_report_mode.attr,
|
||
|
&dev_attr_version.attr,
|
||
|
&dev_attr_slowscan_enable.attr,
|
||
|
&dev_attr_wakeup_enable.attr,
|
||
|
&dev_attr_test.attr,
|
||
|
#if defined(DEBUG_LR388K7)
|
||
|
&dev_attr_check_state.attr,
|
||
|
&dev_attr_log.attr,
|
||
|
#endif
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
static const struct attribute_group lr388k7_ts_attr_group = {
|
||
|
.attrs = lr388k7_ts_attributes,
|
||
|
};
|
||
|
|
||
|
static int lr388k7_open(struct input_dev *input)
|
||
|
{
|
||
|
struct lr388k7 *ts = input_get_drvdata(input);
|
||
|
|
||
|
dev_info(&ts->spi->dev, "[ENTER] open\n");
|
||
|
|
||
|
mutex_lock(&ts->mutex);
|
||
|
|
||
|
__lr388k7_enable(ts);
|
||
|
|
||
|
ts->opened = true;
|
||
|
|
||
|
mutex_unlock(&ts->mutex);
|
||
|
|
||
|
dev_info(&ts->spi->dev, "[EXIT] open\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void lr388k7_close(struct input_dev *input)
|
||
|
{
|
||
|
struct lr388k7 *ts = input_get_drvdata(input);
|
||
|
|
||
|
dev_info(&ts->spi->dev, "[ENTER] close\n");
|
||
|
mutex_lock(&ts->mutex);
|
||
|
|
||
|
__lr388k7_disable(ts);
|
||
|
|
||
|
ts->opened = false;
|
||
|
|
||
|
mutex_unlock(&ts->mutex);
|
||
|
dev_info(&ts->spi->dev, "[EXIT] close\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
#if defined(UDEV_ENABLE)
|
||
|
static void lr388k7_send_udev_event(void *p)
|
||
|
{
|
||
|
struct lr388k7_udev_event *p_udev_event;
|
||
|
char *envp[2];
|
||
|
char *event;
|
||
|
|
||
|
if (!p)
|
||
|
return;
|
||
|
|
||
|
p_udev_event = kmalloc(
|
||
|
sizeof(struct lr388k7_udev_event),
|
||
|
GFP_KERNEL);
|
||
|
|
||
|
if (!p_udev_event)
|
||
|
return;
|
||
|
|
||
|
if (copy_from_user(p_udev_event,
|
||
|
p,
|
||
|
sizeof(struct lr388k7_udev_event))) {
|
||
|
kfree(p_udev_event);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
event = kasprintf(GFP_KERNEL, "TRIGGER=%s", p_udev_event->str);
|
||
|
if (event) {
|
||
|
envp[0] = event;
|
||
|
envp[1] = NULL;
|
||
|
kobject_uevent_env(&lr388k7_ts_miscdev.this_device->kobj,
|
||
|
KOBJ_CHANGE,
|
||
|
envp);
|
||
|
kfree(event);
|
||
|
}
|
||
|
kfree(p_udev_event);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
static void lr388k7_active_report(void *p)
|
||
|
{
|
||
|
struct lr388k7_active_report *p_active_report;
|
||
|
struct lr388k7 *ts = spi_get_drvdata(g_spi);
|
||
|
struct input_dev *input_dev = ts->idev_active;
|
||
|
int x, y, z;
|
||
|
int tool = 0;
|
||
|
u8 status;
|
||
|
|
||
|
if (!p)
|
||
|
return;
|
||
|
|
||
|
/*
|
||
|
* Memory Allocation
|
||
|
*/
|
||
|
p_active_report = kmalloc(
|
||
|
sizeof(struct lr388k7_active_report),
|
||
|
GFP_KERNEL);
|
||
|
if (!p_active_report)
|
||
|
return;
|
||
|
|
||
|
if (copy_from_user(p_active_report,
|
||
|
p,
|
||
|
sizeof(struct lr388k7_active_report))) {
|
||
|
kfree(p_active_report);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
status = p_active_report->status;
|
||
|
|
||
|
/* Get current tool */
|
||
|
if (status & 0x0C)
|
||
|
tool = BTN_TOOL_RUBBER;
|
||
|
else
|
||
|
tool = BTN_TOOL_PEN;
|
||
|
|
||
|
if (!status)
|
||
|
tool = 0; /* Reset */
|
||
|
|
||
|
/* Check if tool changed then notify input subsystem */
|
||
|
if (ts->tool != tool) {
|
||
|
if (ts->tool) {
|
||
|
/* Reset old tool state */
|
||
|
input_report_key(input_dev, BTN_TOUCH, 0);
|
||
|
input_report_key(input_dev, BTN_STYLUS, 0);
|
||
|
input_report_abs(input_dev, ABS_PRESSURE, 0);
|
||
|
}
|
||
|
input_report_key(input_dev, ts->tool, 0);
|
||
|
input_sync(input_dev);
|
||
|
|
||
|
/* Update current*/
|
||
|
ts->tool = tool;
|
||
|
if (tool)
|
||
|
input_report_key(input_dev, tool, 1);
|
||
|
}
|
||
|
|
||
|
if (tool) {
|
||
|
x = p_active_report->x;
|
||
|
y = p_active_report->y;
|
||
|
z = p_active_report->z;
|
||
|
|
||
|
input_report_abs(input_dev, ABS_X, x);
|
||
|
input_report_abs(input_dev, ABS_Y, y);
|
||
|
input_report_abs(input_dev, ABS_PRESSURE, z);
|
||
|
input_report_key(input_dev, BTN_TOUCH, status & 0x09);
|
||
|
input_report_key(input_dev, BTN_STYLUS, status & 0x10);
|
||
|
input_sync(input_dev);
|
||
|
}
|
||
|
|
||
|
kfree(p_active_report);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if defined(PROTOCOL_A)
|
||
|
static void lr388k7_touch_report(void *p)
|
||
|
{
|
||
|
u8 u8_num, i, u8_num_of_touch;
|
||
|
struct lr388k7_touch_report *p_touch_report;
|
||
|
struct lr388k7 *ts = spi_get_drvdata(g_spi);
|
||
|
struct input_dev *input_dev = ts->idev;
|
||
|
bool b_is_eraser = false;
|
||
|
|
||
|
if (!p)
|
||
|
return;
|
||
|
|
||
|
/*
|
||
|
* Memory Allocation
|
||
|
*/
|
||
|
p_touch_report = kmalloc(
|
||
|
sizeof(struct lr388k7_touch_report),
|
||
|
GFP_KERNEL);
|
||
|
if (!p_touch_report)
|
||
|
return;
|
||
|
|
||
|
if (copy_from_user(p_touch_report,
|
||
|
p,
|
||
|
sizeof(struct lr388k7_touch_report))) {
|
||
|
kfree(p_touch_report);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
u8_num = p_touch_report->u8_num_of_touch;
|
||
|
|
||
|
if (u8_num == 0) {
|
||
|
if (ts->b_eraser_active) {
|
||
|
ts->b_eraser_active = false;
|
||
|
input_report_key(input_dev, BTN_TOOL_RUBBER, 0);
|
||
|
}
|
||
|
input_mt_sync(input_dev);
|
||
|
input_sync(input_dev);
|
||
|
kfree(p_touch_report);
|
||
|
return;
|
||
|
}
|
||
|
u8_num_of_touch = 0;
|
||
|
|
||
|
for (i = 0; i < u8_num; i++) {
|
||
|
|
||
|
#if defined(DEBUG_LR388K7_REPORT)
|
||
|
dev_info(&g_spi->dev, "ID=%2d, status=%02d, x=%5d, y=%5d, w=%d, h=%d, z=%5d, num=%2d\n",
|
||
|
p_touch_report->tc[i] . id,
|
||
|
p_touch_report->tc[i] . status,
|
||
|
p_touch_report->tc[i] . x,
|
||
|
p_touch_report->tc[i] . y,
|
||
|
p_touch_report->tc[i] . width,
|
||
|
p_touch_report->tc[i] . height,
|
||
|
p_touch_report->tc[i] . z,
|
||
|
u8_num
|
||
|
);
|
||
|
#endif
|
||
|
|
||
|
if ((p_touch_report->tc[i] . status & 0x80) != 0)
|
||
|
b_is_eraser = true;
|
||
|
else
|
||
|
b_is_eraser = false;
|
||
|
|
||
|
if (b_is_eraser) {
|
||
|
input_report_key(input_dev, BTN_TOOL_RUBBER, 1);
|
||
|
ts->b_eraser_active = true;
|
||
|
} else if ((p_touch_report->tc[i] . status & 0x7F) <= 3 ||
|
||
|
(p_touch_report->tc[i] . status & 0x7F) == 8)
|
||
|
input_report_abs(input_dev,
|
||
|
ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
|
||
|
else if ((p_touch_report->tc[i] . status & 0x7F) <= 7 ||
|
||
|
(p_touch_report->tc[i] . status & 0x7F) == 12)
|
||
|
input_report_abs(input_dev,
|
||
|
ABS_MT_TOOL_TYPE, MT_TOOL_PEN);
|
||
|
input_report_abs(input_dev,
|
||
|
ABS_MT_TRACKING_ID,
|
||
|
p_touch_report->tc[i] .id);
|
||
|
input_report_abs(input_dev,
|
||
|
ABS_MT_POSITION_X,
|
||
|
p_touch_report->tc[i] . x);
|
||
|
input_report_abs(input_dev,
|
||
|
ABS_MT_POSITION_Y,
|
||
|
p_touch_report->tc[i] . y);
|
||
|
input_report_abs(input_dev,
|
||
|
ABS_MT_PRESSURE,
|
||
|
p_touch_report->tc[i] . z);
|
||
|
input_mt_sync(input_dev);
|
||
|
u8_num_of_touch++;
|
||
|
}
|
||
|
input_sync(input_dev);
|
||
|
|
||
|
kfree(p_touch_report);
|
||
|
}
|
||
|
#else /* PROTOCOL_B */
|
||
|
static void lr388k7_touch_report(void *p)
|
||
|
{
|
||
|
int num, i;
|
||
|
struct lr388k7_touch_report touch_report;
|
||
|
struct lr388k7 *ts = spi_get_drvdata(g_spi);
|
||
|
struct input_dev *input_dev = ts->idev;
|
||
|
|
||
|
if (!p)
|
||
|
return;
|
||
|
|
||
|
if (copy_from_user((void *)&touch_report,
|
||
|
p,
|
||
|
sizeof(struct lr388k7_touch_report))) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
num = touch_report.u8_num_of_touch > K7_MAX_TOUCH_NUM ?
|
||
|
K7_MAX_TOUCH_NUM : touch_report.u8_num_of_touch;
|
||
|
|
||
|
for (i = 0; i < num; i++) {
|
||
|
|
||
|
#if defined(DEBUG_LR388K7_REPORT)
|
||
|
dev_info(&g_spi->dev, "ID=%2d, status=%02d, x=%5d, y=%5d, w=%d, h=%d, z=%5d, num=%2d\n",
|
||
|
touch_report.tc[i] . id,
|
||
|
touch_report.tc[i] . status,
|
||
|
touch_report.tc[i] . x,
|
||
|
touch_report.tc[i] . y,
|
||
|
touch_report.tc[i] . width,
|
||
|
touch_report.tc[i] . height,
|
||
|
touch_report.tc[i] . z,
|
||
|
num
|
||
|
);
|
||
|
#endif
|
||
|
input_mt_slot(input_dev,
|
||
|
touch_report.tc[i] . id);
|
||
|
if (((touch_report.tc[i] . status & 0x7F) == 8) ||
|
||
|
((touch_report.tc[i] . status & 0x7F) == 12) ||
|
||
|
((touch_report.tc[i] . status & 0x7F) == 14)) {
|
||
|
input_mt_report_slot_state(input_dev,
|
||
|
MT_TOOL_FINGER,
|
||
|
false);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ((touch_report.tc[i] . status & 0x7F) <= 3)
|
||
|
input_report_abs(input_dev,
|
||
|
ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
|
||
|
else if (((touch_report.tc[i] . status & 0x7F) <= 7) ||
|
||
|
((touch_report.tc[i] . status & 0x7F) > 12))
|
||
|
input_report_abs(input_dev,
|
||
|
ABS_MT_TOOL_TYPE, MT_TOOL_PEN);
|
||
|
|
||
|
if (ts->flip_x)
|
||
|
touch_report.tc[i].x =
|
||
|
__negchk((ts->max_x - touch_report.tc[i].x));
|
||
|
if (ts->flip_y)
|
||
|
touch_report.tc[i].y =
|
||
|
__negchk((ts->max_y - touch_report.tc[i].y));
|
||
|
if (ts->swap_xy) {
|
||
|
u16 tmp;
|
||
|
tmp = touch_report.tc[i].x;
|
||
|
touch_report.tc[i].x = touch_report.tc[i].y;
|
||
|
touch_report.tc[i].y = tmp;
|
||
|
}
|
||
|
|
||
|
input_report_abs(input_dev,
|
||
|
ABS_MT_TRACKING_ID,
|
||
|
touch_report.tc[i] .id);
|
||
|
input_report_abs(input_dev,
|
||
|
ABS_MT_POSITION_X,
|
||
|
touch_report.tc[i].x);
|
||
|
input_report_abs(input_dev,
|
||
|
ABS_MT_POSITION_Y,
|
||
|
touch_report.tc[i].y);
|
||
|
input_report_abs(input_dev,
|
||
|
ABS_MT_PRESSURE,
|
||
|
touch_report.tc[i] . z);
|
||
|
}
|
||
|
input_sync(input_dev);
|
||
|
}
|
||
|
#endif /* #if defined(PROTOCOL_A) */
|
||
|
|
||
|
|
||
|
static atomic_t open_count = ATOMIC_INIT(0);
|
||
|
|
||
|
static int dev_open(struct inode *inode, struct file *filp)
|
||
|
{
|
||
|
if (atomic_read(&open_count)) {
|
||
|
dev_err(&g_spi->dev, "already open : %d\n", -EBUSY);
|
||
|
return -EBUSY;
|
||
|
}
|
||
|
atomic_set(&open_count, 1);
|
||
|
dev_info(&g_spi->dev, "dev_open\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int dev_release(struct inode *inode, struct file *filp)
|
||
|
{
|
||
|
struct lr388k7 *ts = spi_get_drvdata(g_spi);
|
||
|
|
||
|
dev_info(&g_spi->dev, "dev_release\n");
|
||
|
if (!ts)
|
||
|
return -EINVAL;
|
||
|
|
||
|
g_st_state.b_is_init_finish = 0;
|
||
|
|
||
|
atomic_set(&open_count, 0);
|
||
|
/* Reset assert */
|
||
|
gpio_set_value_cansleep(ts->gpio_reset, 0);
|
||
|
g_st_state.b_is_reset = true;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief lr388k7 spi interface
|
||
|
* @param[in] txbuf command to read data
|
||
|
* @param[in] rxbuf data to be read
|
||
|
* @param[in] len length of data
|
||
|
* @retval TRUE if success, otherwise FALSE
|
||
|
*/
|
||
|
static int lr388k7_spi_read(u8 *txbuf, u8 *rxbuf, size_t len)
|
||
|
{
|
||
|
static DEFINE_MUTEX(lock);
|
||
|
|
||
|
int status;
|
||
|
struct spi_message msg;
|
||
|
struct spi_transfer t = {
|
||
|
.bits_per_word = 8,
|
||
|
.tx_buf = txbuf,
|
||
|
.rx_buf = rxbuf,
|
||
|
.speed_hz =
|
||
|
g_st_state.u32SCK == 0 ? 12000000 : g_spi->max_speed_hz,
|
||
|
};
|
||
|
|
||
|
mutex_lock(&lock);
|
||
|
|
||
|
t.len = len + K7_RD_HEADER_SIZE;
|
||
|
|
||
|
spi_message_init(&msg);
|
||
|
spi_message_add_tail(&t, &msg);
|
||
|
status = spi_sync(g_spi, &msg);
|
||
|
|
||
|
mutex_unlock(&lock);
|
||
|
|
||
|
if (status)
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static long lr388k7_spi_read_cmd_data(u8 *p, size_t count)
|
||
|
{
|
||
|
u8 *p_tx_buf;
|
||
|
ssize_t status = 0;
|
||
|
|
||
|
/*
|
||
|
* Memory Allocation
|
||
|
*/
|
||
|
p_tx_buf = kmalloc(count + K7_RD_HEADER_SIZE, GFP_KERNEL);
|
||
|
if (p_tx_buf == NULL) {
|
||
|
status = -ENOMEM;
|
||
|
goto exit_dev_read_cmd_data;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Clear and Set command to read data
|
||
|
*/
|
||
|
memset(p_tx_buf, 0, count + K7_RD_HEADER_SIZE);
|
||
|
|
||
|
p_tx_buf[0] = K7_RD_OPCODE;
|
||
|
p_tx_buf[1] = (K7_CMD_DATA_OFFSET >> 16) & 0xFF;
|
||
|
p_tx_buf[2] = (K7_CMD_DATA_OFFSET >> 8) & 0xFF;
|
||
|
p_tx_buf[3] = (K7_CMD_DATA_OFFSET >> 0) & 0xFF;
|
||
|
p_tx_buf[4] = 0x00;
|
||
|
|
||
|
/*
|
||
|
* Read data
|
||
|
*/
|
||
|
if (lr388k7_spi_read(p_tx_buf, p, count))
|
||
|
status = count;
|
||
|
else
|
||
|
status = -EFAULT;
|
||
|
|
||
|
kfree(p_tx_buf);
|
||
|
|
||
|
exit_dev_read_cmd_data:
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static long lr388k7_spi_read_wo_limit(u8 *p, size_t count)
|
||
|
{
|
||
|
u8 *p_buf;
|
||
|
u8 *p_tx_buf;
|
||
|
ssize_t status = 0;
|
||
|
|
||
|
/*
|
||
|
* Memory Allocation
|
||
|
*/
|
||
|
p_buf = kmalloc(count + K7_RD_HEADER_SIZE, GFP_KERNEL);
|
||
|
if (p_buf == NULL) {
|
||
|
status = -ENOMEM;
|
||
|
goto ext_dev_read_wo_limit;
|
||
|
}
|
||
|
|
||
|
p_tx_buf = kmalloc(count + K7_RD_HEADER_SIZE, GFP_KERNEL);
|
||
|
if (p_tx_buf == NULL) {
|
||
|
status = -ENOMEM;
|
||
|
goto ext_dev_read_wo_limit_free;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Clear and Set command to read data
|
||
|
*/
|
||
|
memset(p_tx_buf, 0, count + K7_RD_HEADER_SIZE);
|
||
|
|
||
|
if (copy_from_user(p_tx_buf, p, K7_RD_HEADER_SIZE)) {
|
||
|
status = -EFAULT;
|
||
|
goto ext_dev_read_wo_limit_free_tx;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read data
|
||
|
*/
|
||
|
if (lr388k7_spi_read(p_tx_buf, p_buf, count)) {
|
||
|
if (copy_to_user(p, &p_buf[K7_RD_HEADER_SIZE], count)) {
|
||
|
status = -EFAULT;
|
||
|
goto ext_dev_read_wo_limit_free_tx;
|
||
|
}
|
||
|
|
||
|
status = count;
|
||
|
|
||
|
} else
|
||
|
status = -EFAULT;
|
||
|
|
||
|
ext_dev_read_wo_limit_free_tx:
|
||
|
kfree(p_tx_buf);
|
||
|
ext_dev_read_wo_limit_free:
|
||
|
kfree(p_buf);
|
||
|
ext_dev_read_wo_limit:
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static long lr388k7_spi_read_from_ioctl(u8 *p, size_t count)
|
||
|
{
|
||
|
u8 *p_buf;
|
||
|
u8 *p_tx_buf;
|
||
|
ssize_t status = 0;
|
||
|
|
||
|
/*
|
||
|
* Memory Allocation
|
||
|
*/
|
||
|
p_buf = kmalloc(count + K7_RD_HEADER_SIZE, GFP_KERNEL);
|
||
|
if (p_buf == NULL) {
|
||
|
status = -ENOMEM;
|
||
|
goto ext_dev_read_from_ioctl;
|
||
|
}
|
||
|
|
||
|
p_tx_buf = kmalloc(count + K7_RD_HEADER_SIZE, GFP_KERNEL);
|
||
|
if (p_tx_buf == NULL) {
|
||
|
status = -ENOMEM;
|
||
|
goto ext_dev_read_from_ioctl_free;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Clear and Set command to read data
|
||
|
*/
|
||
|
memset(p_tx_buf, 0, count + K7_RD_HEADER_SIZE);
|
||
|
|
||
|
if (copy_from_user(p_tx_buf, p, K7_RD_HEADER_SIZE)) {
|
||
|
status = -EFAULT;
|
||
|
goto ext_dev_read_from_ioctl_free_tx;
|
||
|
}
|
||
|
|
||
|
g_st_state.u32SCK = 0;
|
||
|
/*
|
||
|
* Read data
|
||
|
*/
|
||
|
if (lr388k7_spi_read(p_tx_buf, p_buf, count)) {
|
||
|
if (copy_to_user(p, &p_buf[K7_RD_HEADER_SIZE], count)) {
|
||
|
status = -EFAULT;
|
||
|
goto ext_dev_read_from_ioctl_free_tx;
|
||
|
}
|
||
|
|
||
|
status = count;
|
||
|
|
||
|
} else
|
||
|
status = -EFAULT;
|
||
|
|
||
|
ext_dev_read_from_ioctl_free_tx:
|
||
|
kfree(p_tx_buf);
|
||
|
ext_dev_read_from_ioctl_free:
|
||
|
kfree(p_buf);
|
||
|
ext_dev_read_from_ioctl:
|
||
|
g_st_state.u32SCK = g_spi->max_speed_hz;
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static ssize_t
|
||
|
dev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
|
||
|
{
|
||
|
#if 0
|
||
|
u8 *p_buf;
|
||
|
ssize_t status = 0;
|
||
|
|
||
|
p_buf = kmalloc(count + K7_RD_HEADER_SIZE, GFP_KERNEL);
|
||
|
if (!p_buf) {
|
||
|
status = -ENOMEM;
|
||
|
goto exit_dev_read;
|
||
|
}
|
||
|
|
||
|
memset(p_buf, 0, count + K7_RD_HEADER_SIZE);
|
||
|
|
||
|
if (copy_from_user(p_buf, buf, K7_RD_HEADER_SIZE)) {
|
||
|
status = -EFAULT;
|
||
|
goto exit_dev_read_free;
|
||
|
}
|
||
|
|
||
|
if (lr388k7_spi_read(p_buf, count)) {
|
||
|
if (copy_to_user(buf, &p_buf[K7_RD_HEADER_SIZE], count)) {
|
||
|
status = -EFAULT;
|
||
|
goto exit_dev_read_free;
|
||
|
}
|
||
|
|
||
|
status = count;
|
||
|
|
||
|
} else
|
||
|
status = -EFAULT;
|
||
|
|
||
|
exit_dev_read_free:
|
||
|
kfree(p_buf);
|
||
|
exit_dev_read:
|
||
|
return status;
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief lr388k7 spi interface
|
||
|
* @param[in] txbuf data to be written
|
||
|
* @param[in] len length of data
|
||
|
* @retval TRUE if success, otherwise FALSE
|
||
|
*/
|
||
|
static int lr388k7_spi_write(u8 *txbuf, size_t len)
|
||
|
{
|
||
|
static DEFINE_MUTEX(lock);
|
||
|
int status;
|
||
|
struct spi_message msg;
|
||
|
struct spi_transfer t = {
|
||
|
.bits_per_word = 8,
|
||
|
.tx_buf = txbuf,
|
||
|
.len = len,
|
||
|
.speed_hz =
|
||
|
len < 16 ? 12000000 : g_spi->max_speed_hz,
|
||
|
};
|
||
|
|
||
|
mutex_lock(&lock);
|
||
|
|
||
|
spi_message_init(&msg);
|
||
|
spi_message_add_tail(&t, &msg);
|
||
|
/*It returns zero on succcess,else a negative error code. */
|
||
|
status = spi_sync(g_spi, &msg);
|
||
|
|
||
|
mutex_unlock(&lock);
|
||
|
|
||
|
if (status)
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static u32 lr388k7_get_platform_id(u8 *p)
|
||
|
{
|
||
|
u32 ret;
|
||
|
struct lr388k7 *ts = spi_get_drvdata(g_spi);
|
||
|
|
||
|
ret = copy_to_user(p,
|
||
|
&ts->platform_id,
|
||
|
sizeof(ts->platform_id));
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static u32 lr388k7_get_value(u8 *arg, unsigned int index)
|
||
|
{
|
||
|
u32 ret = 0;
|
||
|
switch (index) {
|
||
|
case LR388K7_GET_PLATFORM_ID:
|
||
|
ret = lr388k7_get_platform_id(arg);
|
||
|
break;
|
||
|
default:
|
||
|
ret = -EINVAL;
|
||
|
break;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
dev_write(struct file *filp, const char __user *buf,
|
||
|
size_t count, loff_t *pos)
|
||
|
{
|
||
|
u8 *p_buf;
|
||
|
ssize_t status = 0;
|
||
|
|
||
|
p_buf = kmalloc(count, GFP_KERNEL);
|
||
|
if (!p_buf) {
|
||
|
status = -ENOMEM;
|
||
|
goto exit_dev_write;
|
||
|
}
|
||
|
|
||
|
if (copy_from_user(p_buf, buf, count)) {
|
||
|
status = -EFAULT;
|
||
|
goto exit_dev_write_free;
|
||
|
}
|
||
|
|
||
|
if (lr388k7_spi_write(p_buf, count))
|
||
|
status = count;
|
||
|
else
|
||
|
status = -EFAULT;
|
||
|
|
||
|
exit_dev_write_free:
|
||
|
kfree(p_buf);
|
||
|
exit_dev_write:
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||
|
{
|
||
|
long ret = true;
|
||
|
unsigned int index;
|
||
|
|
||
|
index = (cmd >> 16) & 0xFFFF;
|
||
|
|
||
|
switch (cmd & 0xFFFF) {
|
||
|
case LR388K7_IOCTL_TOUCH_REPORT:
|
||
|
lr388k7_touch_report((void *) arg);
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_READ_REGISTER:
|
||
|
ret = lr388k7_spi_read_from_ioctl((u8 *) arg, index);
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_READ_WO_LIMIT:
|
||
|
ret = lr388k7_spi_read_wo_limit((u8 *) arg, index);
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_SET_HAL_PID:
|
||
|
g_st_state.u32_pid = arg;
|
||
|
g_st_state.b_is_calc_finish = 1;
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_READ_RAW_DATA:
|
||
|
ret = lr388k7_queue_read_raw_data((u8 *) arg, index);
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_READ_CMD_DATA:
|
||
|
ret = lr388k7_cmd_queue_read((u8 *) arg, index);
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_FINISH_CALC:
|
||
|
g_st_state.b_is_calc_finish = 1;
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_SET_RAW_DATA_SIZE:
|
||
|
g_u32_raw_data_size = arg;
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_SET_REVISION:
|
||
|
g_st_state.u16fw_ver = (arg & 0xFFFF0000) >> 16;
|
||
|
g_st_state.u16module_ver = arg & 0x0000FFFF;
|
||
|
|
||
|
dev_info(&g_spi->dev, "Version %d.%d.%d\n",
|
||
|
g_st_state.u16fw_ver,
|
||
|
K7_DRIVER_VERSION,
|
||
|
g_st_state.u16module_ver
|
||
|
);
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_GET_DEBUG_STATUS:
|
||
|
lr388k7_ts_get_debug_status((u8 *)arg);
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_SET_MODE:
|
||
|
g_u8_mode = (u8)arg;
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_GET_MODE:
|
||
|
lr388k7_ts_get_mode((u8 *)arg);
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_RESET:
|
||
|
ret = lr388k7_hardreset(arg);
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_CLEAR_BUFFER:
|
||
|
lr388k7_queue_reset();
|
||
|
g_st_state.b_is_init_finish = arg;
|
||
|
if (g_st_state.b_is_init_finish)
|
||
|
lr388k7_send_signal(g_st_state.u32_pid,
|
||
|
LR388K7_SIGNAL_MODE);
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_ACTIVE_REPORT:
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
lr388k7_active_report((void *) arg);
|
||
|
#endif
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_GET_VALUE:
|
||
|
ret = lr388k7_get_value((u8 *)arg, index);
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_SET_CLK_SEL:
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
lr388k7_set_clk_sel((unsigned int)arg);
|
||
|
#endif
|
||
|
break;
|
||
|
|
||
|
case LR388K7_IOCTL_SET_STATE:
|
||
|
g_u8_scan = (u8)arg;
|
||
|
break;
|
||
|
|
||
|
#if defined(UDEV_ENABLE)
|
||
|
case LR388K7_IOCTL_SEND_UDEV:
|
||
|
lr388k7_send_udev_event((void *)arg);
|
||
|
break;
|
||
|
#endif
|
||
|
|
||
|
default:
|
||
|
ret = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void lr388k7_init_parameter(void)
|
||
|
{
|
||
|
#if defined(DEBUG_LR388K7)
|
||
|
unsigned long flags;
|
||
|
#endif
|
||
|
g_u8_mode = K7_DEFAULT_MODE;
|
||
|
g_u8_scan = K7_SCAN_STATE_IDLE;
|
||
|
g_st_state.u32_pid = 0;
|
||
|
g_st_state.b_is_init_finish = false;
|
||
|
g_st_state.b_is_suspended = false;
|
||
|
g_st_state.b_is_reset = false;
|
||
|
g_st_state.b_is_disabled = false;
|
||
|
g_st_state.u32SCK = 0;
|
||
|
lr388k7_queue_reset();
|
||
|
|
||
|
g_st_dbg.slowscan.enable = 0;
|
||
|
g_st_dbg.slowscan.scan_rate = K7_SLOW_SCAN_60;
|
||
|
g_st_dbg.wakeup.enable = 0;
|
||
|
g_st_dbg.wakeup.num_tap = K7_NUM_TAP_2;
|
||
|
g_st_dbg.u8ForceCap = 0;
|
||
|
g_st_dbg.u8Dump = 0;
|
||
|
g_st_dbg.u8Test = 0;
|
||
|
g_u16_wakeup_enable = 0;
|
||
|
|
||
|
#if defined(DEBUG_LR388K7)
|
||
|
spin_lock_irqsave(&lr388k7log_lock, flags);
|
||
|
g_log_front = 0;
|
||
|
g_log_rear = 0;
|
||
|
spin_unlock_irqrestore(&lr388k7log_lock, flags);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void lr388k7_init_ts(void)
|
||
|
{
|
||
|
lr388k7_init_parameter();
|
||
|
memset(&g_st_state, 0, sizeof(struct lr388k7_ts_parameter));
|
||
|
|
||
|
g_st_state.st_wq_k7 = create_singlethread_workqueue("lr388k7_work");
|
||
|
INIT_WORK(&g_st_state.st_work_k7, lr388k7_work_handler);
|
||
|
}
|
||
|
|
||
|
static struct lr388k7_platform_data *lr388k7_parse_dt(struct device *dev,
|
||
|
int irq)
|
||
|
{
|
||
|
struct lr388k7_platform_data *pdata;
|
||
|
struct device_node *np = dev->of_node;
|
||
|
int ret, val, irq_gpio;
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
const char *str;
|
||
|
#endif
|
||
|
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||
|
if (!pdata)
|
||
|
return ERR_PTR(-ENOMEM);
|
||
|
|
||
|
pdata->gpio_reset = of_get_named_gpio_flags(np, "reset-gpio", 0, NULL);
|
||
|
if (!gpio_is_valid(pdata->gpio_reset)) {
|
||
|
dev_err(dev, "Invalid reset-gpio\n");
|
||
|
return ERR_PTR(-EINVAL);
|
||
|
}
|
||
|
ret = gpio_request(pdata->gpio_reset, "reset-gpio");
|
||
|
if (ret < 0) {
|
||
|
dev_err(dev, "gpio_request failed\n");
|
||
|
return ERR_PTR(-EINVAL);
|
||
|
}
|
||
|
gpio_direction_output(pdata->gpio_reset, 0);
|
||
|
|
||
|
irq_gpio = of_get_named_gpio(np, "irq-gpio", 0);
|
||
|
if (!gpio_is_valid(irq_gpio)) {
|
||
|
dev_err(dev, "Invalid irq-gpio\n");
|
||
|
ret = -EINVAL;
|
||
|
goto exit_release_reset_gpio;
|
||
|
}
|
||
|
ret = gpio_request(irq_gpio, "irq-gpio");
|
||
|
if (ret < 0) {
|
||
|
dev_err(dev, "irq_request fail\n");
|
||
|
ret = -EINVAL;
|
||
|
goto exit_release_reset_gpio;
|
||
|
}
|
||
|
gpio_direction_input(irq_gpio);
|
||
|
pdata->gpio_irq = irq_gpio;
|
||
|
|
||
|
ret = of_property_read_u32(np, "swap-xy", &val);
|
||
|
if (ret < 0)
|
||
|
val = 0;
|
||
|
pdata->ts_swap_xy = val != 0 ? 1 : 0;
|
||
|
|
||
|
ret = of_property_read_u32(np, "flip-x", &val);
|
||
|
if (ret < 0)
|
||
|
val = 0;
|
||
|
pdata->ts_flip_x = val != 0 ? 1 : 0;
|
||
|
|
||
|
ret = of_property_read_u32(np, "flip-y", &val);
|
||
|
if (ret < 0)
|
||
|
val = 0;
|
||
|
pdata->ts_flip_y = val != 0 ? 1 : 0;
|
||
|
|
||
|
ret = of_property_read_u32(np, "x-max", &val);
|
||
|
if (ret < 0)
|
||
|
goto exit_release_all_gpio;
|
||
|
pdata->ts_x_max = val;
|
||
|
|
||
|
ret = of_property_read_u32(np, "y-max", &val);
|
||
|
if (ret < 0)
|
||
|
goto exit_release_all_gpio;
|
||
|
pdata->ts_y_max = val;
|
||
|
|
||
|
ret = of_property_read_u32(np, "z-max", &val);
|
||
|
if (ret < 0)
|
||
|
goto exit_release_all_gpio;
|
||
|
pdata->ts_pressure_max = val;
|
||
|
|
||
|
ret = of_property_read_u32(np, "touch-num-max", &val);
|
||
|
if (ret < 0)
|
||
|
goto exit_release_all_gpio;
|
||
|
pdata->ts_touch_num_max = val;
|
||
|
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
ret = of_property_read_string(np, "name-of-clock", &str);
|
||
|
if (ret < 0)
|
||
|
goto exit_release_all_gpio;
|
||
|
pdata->name_of_clock = (char *)str;
|
||
|
|
||
|
ret = of_property_read_string(np, "name-of-clock-con", &str);
|
||
|
if (ret < 0)
|
||
|
goto exit_release_all_gpio;
|
||
|
pdata->name_of_clock_con = (char *)str;
|
||
|
|
||
|
#endif
|
||
|
pdata->gpio_clk_sel = of_get_named_gpio_flags(np,
|
||
|
"clock-sel-gpio",
|
||
|
0,
|
||
|
NULL);
|
||
|
if (gpio_is_valid(pdata->gpio_clk_sel)) {
|
||
|
ret = gpio_request(pdata->gpio_clk_sel, "clock-sel-gpio");
|
||
|
if (ret)
|
||
|
dev_err(dev, "gpio_request(clk_sel) failed\n");
|
||
|
else
|
||
|
gpio_direction_output(pdata->gpio_clk_sel, 0);
|
||
|
}
|
||
|
|
||
|
ret = of_property_read_u32(np, "platform-id", &val);
|
||
|
if (ret < 0)
|
||
|
goto exit_release_all_gpio;
|
||
|
pdata->platform_id = val;
|
||
|
|
||
|
return pdata;
|
||
|
|
||
|
exit_release_all_gpio:
|
||
|
gpio_free(irq_gpio);
|
||
|
exit_release_reset_gpio:
|
||
|
gpio_free(pdata->gpio_reset);
|
||
|
return ERR_PTR(ret);
|
||
|
}
|
||
|
|
||
|
static void init_spi_pinctrl(struct lr388k7 *ts, struct device *dev)
|
||
|
{
|
||
|
struct pinctrl *pin = NULL;
|
||
|
struct pinctrl_state *active, *inactive;
|
||
|
|
||
|
pin = devm_pinctrl_get(dev);
|
||
|
if (IS_ERR(pin)) {
|
||
|
dev_info(dev, "missing pinctrl device.\n");
|
||
|
return;
|
||
|
}
|
||
|
ts->pinctrl = pin;
|
||
|
|
||
|
active = pinctrl_lookup_state(pin, "spi_intf_normal");
|
||
|
if (IS_ERR_OR_NULL(active)) {
|
||
|
dev_info(dev, "missing spi_intf_normal state.\n");
|
||
|
goto out;
|
||
|
}
|
||
|
ts->spi_intf_en = active;
|
||
|
inactive = pinctrl_lookup_state(pin, "spi_intf_lowpower");
|
||
|
if (IS_ERR_OR_NULL(active)) {
|
||
|
dev_info(dev, "missing spi_intf_lowpower state.\n");
|
||
|
goto out;
|
||
|
}
|
||
|
ts->spi_intf_dis = inactive;
|
||
|
|
||
|
return;
|
||
|
out:
|
||
|
if (ts->pinctrl)
|
||
|
ts->pinctrl = NULL;
|
||
|
|
||
|
}
|
||
|
|
||
|
static int lr388k7_probe(struct spi_device *spi)
|
||
|
{
|
||
|
struct lr388k7_platform_data *pdata;/* = spi->dev.platform_data;*/
|
||
|
struct lr388k7 *ts;
|
||
|
struct input_dev *input_dev;
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
struct input_dev *input_dev_active;
|
||
|
#endif
|
||
|
struct device *dev;
|
||
|
int error;
|
||
|
|
||
|
dev = &spi->dev;
|
||
|
dev_info(dev, "[ENTER] probe\n");
|
||
|
|
||
|
lr388k7_init_ts();
|
||
|
|
||
|
g_spi = spi;
|
||
|
|
||
|
if (spi->irq <= 0) {
|
||
|
dev_err(dev, "no irq\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
spi->mode = SPI_MODE_3;
|
||
|
spi->bits_per_word = 8;
|
||
|
if (!spi->max_speed_hz)
|
||
|
spi->max_speed_hz = K7_MAX_SPEED_HZ;
|
||
|
|
||
|
error = spi_setup(spi);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
|
||
|
input_dev = input_allocate_device();
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
input_dev_active = input_allocate_device();
|
||
|
if (!ts || !input_dev || !input_dev_active) {
|
||
|
#else
|
||
|
if (!ts || !input_dev) {
|
||
|
#endif
|
||
|
error = -ENOMEM;
|
||
|
goto err_free_mem;
|
||
|
}
|
||
|
|
||
|
|
||
|
ts->spi = spi;
|
||
|
ts->dev = dev;
|
||
|
ts->idev = input_dev;
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
ts->idev_active = input_dev_active;
|
||
|
#endif
|
||
|
ts->irq = spi->irq;
|
||
|
|
||
|
if (dev->of_node) {
|
||
|
|
||
|
dev_info(dev, "DT support\n");
|
||
|
/* Loading data from DT */
|
||
|
pdata = lr388k7_parse_dt(dev, spi->irq);
|
||
|
if (IS_ERR(pdata)) {
|
||
|
dev_err(dev, "failed to parse device tree\n");
|
||
|
error = -EINVAL;
|
||
|
goto err_free_mem;
|
||
|
}
|
||
|
dev->platform_data = pdata;
|
||
|
} else {
|
||
|
/* Non-DT */
|
||
|
dev_info(dev, "Non-DT\n");
|
||
|
}
|
||
|
|
||
|
pdata = dev->platform_data;
|
||
|
|
||
|
ts->max_num_touch = __min(pdata->ts_touch_num_max, K7_MAX_TOUCH_NUM);
|
||
|
ts->max_x = pdata->ts_x_max ? : MAX_16BIT;
|
||
|
ts->max_y = pdata->ts_y_max ? : MAX_16BIT;
|
||
|
ts->max_z = pdata->ts_pressure_max ? : MAX_16BIT;
|
||
|
ts->swap_xy = pdata->ts_swap_xy ? true : false;
|
||
|
ts->flip_x = pdata->ts_flip_x ? true : false;
|
||
|
ts->flip_y = pdata->ts_flip_y ? true : false;
|
||
|
ts->gpio_reset = pdata->gpio_reset;
|
||
|
ts->gpio_irq = pdata->gpio_irq;
|
||
|
ts->b_eraser_active = false;
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
ts->tool = 0;
|
||
|
#endif
|
||
|
ts->gpio_clk_sel = pdata->gpio_clk_sel;
|
||
|
ts->platform_id = pdata->platform_id;
|
||
|
|
||
|
mutex_init(&ts->mutex);
|
||
|
|
||
|
spin_lock_init(&ts->lock);
|
||
|
|
||
|
snprintf(ts->phys,
|
||
|
sizeof(ts->phys),
|
||
|
"%s/input-ts",
|
||
|
dev_name(dev));
|
||
|
|
||
|
#if defined(DEBUG_LR388K7)
|
||
|
dev_info(&spi->dev, "Success register miscdev\n");
|
||
|
#endif
|
||
|
|
||
|
if (lr388k7_queue_init()) {
|
||
|
dev_err(dev, "Cannot allocate queue memory\n");
|
||
|
error = -ENOMEM;
|
||
|
goto err_free_mem;
|
||
|
}
|
||
|
|
||
|
if (gpio_is_valid(ts->gpio_clk_sel)) {
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
/* Select external clock */
|
||
|
gpio_set_value_cansleep(ts->gpio_clk_sel, 1);
|
||
|
#else
|
||
|
/* Select internal clock */
|
||
|
gpio_set_value_cansleep(ts->gpio_clk_sel, 0);
|
||
|
#endif
|
||
|
}
|
||
|
/* Reset assert */
|
||
|
gpio_set_value_cansleep(ts->gpio_reset, 0);
|
||
|
g_st_state.b_is_reset = true;
|
||
|
|
||
|
init_spi_pinctrl(ts, dev);
|
||
|
|
||
|
/* regulator */
|
||
|
ts->regulator_3v3 = devm_regulator_get(&g_spi->dev, "avdd");
|
||
|
if (IS_ERR(ts->regulator_3v3)) {
|
||
|
dev_err(dev,
|
||
|
"LR388K7 TS: regulator_get failed: %ld\n",
|
||
|
PTR_ERR(ts->regulator_3v3));
|
||
|
error = -ENODEV;
|
||
|
goto err_free_mem;
|
||
|
}
|
||
|
|
||
|
ts->regulator_1v8 = devm_regulator_get(&g_spi->dev, "dvdd");
|
||
|
if (IS_ERR(ts->regulator_1v8)) {
|
||
|
dev_err(dev,
|
||
|
"LR388K7 TS: regulator_get failed: %ld\n",
|
||
|
PTR_ERR(ts->regulator_1v8));
|
||
|
error = -ENODEV;
|
||
|
goto err_free_mem;
|
||
|
}
|
||
|
|
||
|
/* Enable 1v8 first*/
|
||
|
error = regulator_enable(ts->regulator_1v8);
|
||
|
if (error < 0)
|
||
|
dev_err(dev,
|
||
|
"LR388K7 TS: regulator enable failed: %d\n",
|
||
|
error);
|
||
|
usleep_range(5000, 6000);
|
||
|
error = regulator_enable(ts->regulator_3v3);
|
||
|
if (error < 0)
|
||
|
dev_err(dev,
|
||
|
"LR388K7 TS: regulator enable failed: %d\n",
|
||
|
error);
|
||
|
|
||
|
input_dev->name = "touch";
|
||
|
input_dev->phys = ts->phys;
|
||
|
input_dev->id.bustype = BUS_SPI;
|
||
|
input_dev->dev.parent = &spi->dev;
|
||
|
__set_bit(EV_SYN, input_dev->evbit);
|
||
|
__set_bit(EV_ABS, input_dev->evbit);
|
||
|
__set_bit(EV_KEY, input_dev->evbit);
|
||
|
__set_bit(KEY_POWER, input_dev->keybit);
|
||
|
|
||
|
if (ts->swap_xy) {
|
||
|
input_set_abs_params(input_dev,
|
||
|
ABS_MT_POSITION_X,
|
||
|
0,
|
||
|
ts->max_y,
|
||
|
0,
|
||
|
0);
|
||
|
input_set_abs_params(input_dev,
|
||
|
ABS_MT_POSITION_Y,
|
||
|
0,
|
||
|
ts->max_x,
|
||
|
0,
|
||
|
0);
|
||
|
} else {
|
||
|
input_set_abs_params(input_dev,
|
||
|
ABS_MT_POSITION_X,
|
||
|
0,
|
||
|
ts->max_x,
|
||
|
0,
|
||
|
0);
|
||
|
input_set_abs_params(input_dev,
|
||
|
ABS_MT_POSITION_Y,
|
||
|
0, ts->max_y,
|
||
|
0,
|
||
|
0);
|
||
|
}
|
||
|
input_set_abs_params(input_dev,
|
||
|
ABS_MT_PRESSURE,
|
||
|
0,
|
||
|
ts->max_z,
|
||
|
0,
|
||
|
0);
|
||
|
input_set_abs_params(input_dev,
|
||
|
ABS_MT_TOOL_TYPE,
|
||
|
0,
|
||
|
MT_TOOL_MAX,
|
||
|
0,
|
||
|
0);
|
||
|
#if defined(PROTOCOL_A)
|
||
|
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
||
|
|
||
|
input_set_abs_params(input_dev,
|
||
|
ABS_MT_TRACKING_ID,
|
||
|
0,
|
||
|
ts->max_num_touch,
|
||
|
0,
|
||
|
0);
|
||
|
#else /* PROTOCOL B */
|
||
|
error = input_mt_init_slots(input_dev,
|
||
|
ts->max_num_touch,
|
||
|
0);
|
||
|
if (error) {
|
||
|
dev_err(dev, "Failed to mt_init_slots err: %d\n", error);
|
||
|
goto err_free_mem;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
input_set_capability(input_dev, EV_MSC, MSC_ACTIVITY);
|
||
|
|
||
|
input_dev->open = lr388k7_open;
|
||
|
input_dev->close = lr388k7_close;
|
||
|
input_dev->enable = lr388k7_input_enable;
|
||
|
input_dev->disable = lr388k7_input_disable;
|
||
|
input_dev->enabled = true;
|
||
|
input_set_drvdata(input_dev, ts);
|
||
|
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
input_dev_active->name = "touch_active";
|
||
|
input_dev_active->phys = ts->phys;
|
||
|
input_dev_active->id.bustype = BUS_SPI;
|
||
|
input_dev_active->dev.parent = &spi->dev;
|
||
|
|
||
|
__set_bit(EV_ABS, input_dev_active->evbit);
|
||
|
__set_bit(EV_KEY, input_dev_active->evbit);
|
||
|
__set_bit(INPUT_PROP_POINTER, input_dev_active->propbit);
|
||
|
__set_bit(BTN_TOUCH, input_dev_active->keybit);
|
||
|
__set_bit(BTN_TOOL_PEN, input_dev_active->keybit);
|
||
|
__set_bit(BTN_TOOL_RUBBER, input_dev_active->keybit);
|
||
|
__set_bit(BTN_STYLUS, input_dev_active->keybit);
|
||
|
|
||
|
if (ts->swap_xy) {
|
||
|
input_set_abs_params(input_dev_active,
|
||
|
ABS_X,
|
||
|
0,
|
||
|
ts->max_y,
|
||
|
0,
|
||
|
0);
|
||
|
input_set_abs_params(input_dev_active,
|
||
|
ABS_Y,
|
||
|
0,
|
||
|
ts->max_x,
|
||
|
0,
|
||
|
0);
|
||
|
} else {
|
||
|
input_set_abs_params(input_dev_active,
|
||
|
ABS_X,
|
||
|
0,
|
||
|
ts->max_x,
|
||
|
0,
|
||
|
0);
|
||
|
input_set_abs_params(input_dev_active,
|
||
|
ABS_Y,
|
||
|
0, ts->max_y,
|
||
|
0,
|
||
|
0);
|
||
|
}
|
||
|
input_set_abs_params(input_dev_active,
|
||
|
ABS_PRESSURE,
|
||
|
0,
|
||
|
MAX_10BIT,
|
||
|
0,
|
||
|
0);
|
||
|
input_set_drvdata(input_dev_active, ts);
|
||
|
#endif
|
||
|
|
||
|
error = request_threaded_irq(spi->irq,
|
||
|
lr388k7_irq_thread,
|
||
|
lr388k7_wakeup_thread,
|
||
|
IRQF_TRIGGER_FALLING,
|
||
|
"lr388k7_ts", ts);
|
||
|
|
||
|
if (error) {
|
||
|
dev_err(dev, "Failed to request irq, err: %d\n", error);
|
||
|
goto err_free_mem;
|
||
|
}
|
||
|
|
||
|
disable_irq(ts->irq);
|
||
|
|
||
|
spi_set_drvdata(spi, ts);
|
||
|
|
||
|
error = input_register_device(ts->idev);
|
||
|
if (error) {
|
||
|
dev_err(dev,
|
||
|
"Failed to register input device, err: %d\n", error);
|
||
|
goto err_clear_drvdata;
|
||
|
}
|
||
|
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
error = input_register_device(ts->idev_active);
|
||
|
if (error) {
|
||
|
dev_err(dev,
|
||
|
"Failed to register input active device , err: %d\n",
|
||
|
error);
|
||
|
goto err_clear_drvdata;
|
||
|
}
|
||
|
#endif
|
||
|
/* misc */
|
||
|
if (misc_register(&lr388k7_ts_miscdev) != 0) {
|
||
|
dev_err(dev, "cannot register miscdev\n");
|
||
|
error = -ENOMEM;
|
||
|
goto err_clear_drvdata;
|
||
|
}
|
||
|
|
||
|
error = sysfs_create_group(&lr388k7_ts_miscdev.this_device->kobj,
|
||
|
&lr388k7_ts_attr_group);
|
||
|
if (error) {
|
||
|
dev_err(dev, "failed to create sysfs group\n");
|
||
|
goto err_clear_drvdata;
|
||
|
}
|
||
|
|
||
|
error = sysfs_create_link(&dev->kobj,
|
||
|
&lr388k7_ts_miscdev.this_device->kobj,
|
||
|
"touch");
|
||
|
if (error) {
|
||
|
dev_err(dev, "failed to create sysfs group\n");
|
||
|
goto err_clear_drvdata;
|
||
|
}
|
||
|
|
||
|
/* Enable async suspend/resume to reduce LP0 latency */
|
||
|
device_enable_async_suspend(dev);
|
||
|
|
||
|
dev_info(dev, "[EXIT] probe\n");
|
||
|
return 0;
|
||
|
|
||
|
err_clear_drvdata:
|
||
|
spi_set_drvdata(spi, NULL);
|
||
|
free_irq(spi->irq, ts);
|
||
|
err_free_mem:
|
||
|
#if defined(ACTIVE_ENABLE)
|
||
|
input_free_device(input_dev_active);
|
||
|
#endif
|
||
|
input_free_device(input_dev);
|
||
|
kfree(ts);
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
static int lr388k7_remove(struct spi_device *spi)
|
||
|
{
|
||
|
struct lr388k7 *ts = spi_get_drvdata(spi);
|
||
|
|
||
|
lr388k7_queue_free();
|
||
|
|
||
|
if (g_st_state.st_wq_k7)
|
||
|
destroy_workqueue(g_st_state.st_wq_k7);
|
||
|
|
||
|
sysfs_remove_group(&lr388k7_ts_miscdev.this_device->kobj,
|
||
|
&lr388k7_ts_attr_group);
|
||
|
|
||
|
sysfs_remove_link(&ts->dev->kobj, "touch");
|
||
|
|
||
|
misc_deregister(&lr388k7_ts_miscdev);
|
||
|
free_irq(ts->spi->irq, ts);
|
||
|
|
||
|
gpio_free(ts->gpio_reset);
|
||
|
gpio_free(ts->gpio_irq);
|
||
|
if (gpio_is_valid(ts->gpio_clk_sel))
|
||
|
gpio_free(ts->gpio_clk_sel);
|
||
|
|
||
|
input_unregister_device(ts->idev);
|
||
|
|
||
|
kfree(ts);
|
||
|
|
||
|
spi_set_drvdata(spi, NULL);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void lr388k7_shutdown(struct spi_device *spi)
|
||
|
{
|
||
|
struct lr388k7 *ts = spi_get_drvdata(spi);
|
||
|
|
||
|
free_irq(ts->irq, ts);
|
||
|
|
||
|
if (ts->regulator_3v3)
|
||
|
regulator_disable(ts->regulator_3v3);
|
||
|
|
||
|
if (ts->regulator_1v8)
|
||
|
regulator_disable(ts->regulator_1v8);
|
||
|
}
|
||
|
|
||
|
static void lr388k7_ctrl_resume(struct lr388k7 *ts)
|
||
|
{
|
||
|
u8 u8_buf[5];
|
||
|
size_t count = 0;
|
||
|
|
||
|
u8_buf[count++] = K7_WR_OPCODE;
|
||
|
u8_buf[count++] = (K7_STATE_CTL_ADDR >> 16) & 0xFF;
|
||
|
u8_buf[count++] = (K7_STATE_CTL_ADDR >> 8) & 0xFF;
|
||
|
u8_buf[count++] = (K7_STATE_CTL_ADDR >> 0) & 0xFF;
|
||
|
u8_buf[count++] = K7_POWER_CTL_RESUME;
|
||
|
lr388k7_spi_write(u8_buf, count);
|
||
|
}
|
||
|
|
||
|
static void lr388k7_start(struct lr388k7 *ts)
|
||
|
{
|
||
|
int error;
|
||
|
|
||
|
g_st_state.b_is_suspended = false;
|
||
|
|
||
|
mutex_lock(&ts->mutex);
|
||
|
|
||
|
if (g_st_dbg.wakeup.enable == 1) {
|
||
|
lr388k7_ctrl_resume(ts);
|
||
|
mutex_unlock(&ts->mutex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Reset assert */
|
||
|
gpio_set_value_cansleep(ts->gpio_reset, 0);
|
||
|
g_st_state.b_is_reset = true;
|
||
|
|
||
|
/*
|
||
|
* Enable regulator, if necessary
|
||
|
*/
|
||
|
/* Enable 1v8 first*/
|
||
|
error = regulator_enable(ts->regulator_1v8);
|
||
|
if (error < 0)
|
||
|
dev_err(&g_spi->dev,
|
||
|
"LR388K7 TS: regulator enable failed: %d\n", error);
|
||
|
|
||
|
error = regulator_enable(ts->regulator_3v3);
|
||
|
if (error < 0)
|
||
|
dev_err(&g_spi->dev,
|
||
|
"LR388K7 TS: regulator enable failed: %d\n", error);
|
||
|
|
||
|
usleep_range(5000, 6000);
|
||
|
|
||
|
if (ts->spi_intf_en) {
|
||
|
error = pinctrl_select_state(ts->pinctrl,
|
||
|
ts->spi_intf_en);
|
||
|
if (error < 0)
|
||
|
dev_warn(&g_spi->dev,
|
||
|
"setting spi interface pin state failed.\n");
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Enable clock, if necessary
|
||
|
*/
|
||
|
if (ts->clk)
|
||
|
clk_enable(ts->clk);
|
||
|
|
||
|
/* Reset deassert */
|
||
|
gpio_set_value_cansleep(ts->gpio_reset, 1);
|
||
|
g_st_state.b_is_reset = false;
|
||
|
|
||
|
usleep_range(12000, 13000);
|
||
|
|
||
|
mutex_unlock(&ts->mutex);
|
||
|
}
|
||
|
|
||
|
static void lr388k7_ctrl_suspend(struct lr388k7 *ts)
|
||
|
{
|
||
|
u8 u8_buf[5];
|
||
|
size_t count = 0;
|
||
|
int error;
|
||
|
u8 u8_status = 0;
|
||
|
int irq_value;
|
||
|
|
||
|
mutex_lock(&ts->mutex);
|
||
|
|
||
|
if (g_st_dbg.wakeup.enable == 1) {
|
||
|
u8_buf[count++] = K7_WR_OPCODE;
|
||
|
u8_buf[count++] = (K7_STATE_CTL_ADDR >> 16) & 0xFF;
|
||
|
u8_buf[count++] = (K7_STATE_CTL_ADDR >> 8) & 0xFF;
|
||
|
u8_buf[count++] = (K7_STATE_CTL_ADDR >> 0) & 0xFF;
|
||
|
u8_buf[count++] = K7_POWER_CTL_TAP_WAKEUP;
|
||
|
lr388k7_spi_write(u8_buf, count);
|
||
|
|
||
|
irq_value = gpio_get_value(ts->gpio_irq);
|
||
|
|
||
|
if (!irq_value) {
|
||
|
u8_status = lr388k7_clear_irq();
|
||
|
|
||
|
lr388k7_event_handler(u8_status);
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&ts->mutex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (ts->spi_intf_dis) {
|
||
|
error = pinctrl_select_state(ts->pinctrl,
|
||
|
ts->spi_intf_dis);
|
||
|
if (error < 0)
|
||
|
dev_warn(&g_spi->dev,
|
||
|
"setting spi interface pin state failed.\n");
|
||
|
}
|
||
|
|
||
|
/* Disable (3.3V) */
|
||
|
if (ts->regulator_3v3) {
|
||
|
error = regulator_disable(ts->regulator_3v3);
|
||
|
if (error < 0)
|
||
|
dev_err(&g_spi->dev,
|
||
|
"lr388k7 regulator 3.3V disable failed: %d\n",
|
||
|
error);
|
||
|
}
|
||
|
/* handle platforms w/ and w/out regulator switches */
|
||
|
/* 2) delay for platforms w/ regulator switches */
|
||
|
/* usleep_range(15000, 20000); */ /*msleep(15); */
|
||
|
/* 3) disable clock */
|
||
|
if (ts->clk)
|
||
|
clk_disable(ts->clk);
|
||
|
/* 4) disable 1.8 */
|
||
|
if (ts->regulator_1v8 && ts->regulator_3v3) {
|
||
|
error = regulator_disable(ts->regulator_1v8);
|
||
|
if (error < 0)
|
||
|
dev_err(&g_spi->dev,
|
||
|
"lr388k7 1.8V disable failed: %d\n",
|
||
|
error);
|
||
|
}
|
||
|
|
||
|
/* Reset assert */
|
||
|
gpio_set_value_cansleep(ts->gpio_reset, 0);
|
||
|
g_st_state.b_is_reset = true;
|
||
|
|
||
|
mutex_unlock(&ts->mutex);
|
||
|
}
|
||
|
|
||
|
static void lr388k7_stop(struct lr388k7 *ts)
|
||
|
{
|
||
|
flush_workqueue(g_st_state.st_wq_k7);
|
||
|
|
||
|
lr388k7_ctrl_suspend(ts);
|
||
|
}
|
||
|
|
||
|
static int lr388k7_suspend(struct lr388k7 *ts)
|
||
|
{
|
||
|
|
||
|
lr388k7_stop(ts);
|
||
|
|
||
|
lr388k7_send_signal(g_st_state.u32_pid, LR388K7_SIGNAL_SLEP);
|
||
|
|
||
|
g_st_state.b_is_suspended = true;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int lr388k7_resume(struct lr388k7 *ts)
|
||
|
{
|
||
|
u8 u8_status = 0;
|
||
|
int irq_value;
|
||
|
|
||
|
lr388k7_start(ts);
|
||
|
|
||
|
lr388k7_send_signal(g_st_state.u32_pid, LR388K7_SIGNAL_WAKE);
|
||
|
|
||
|
|
||
|
if (g_st_dbg.wakeup.enable == 2)
|
||
|
g_st_dbg.wakeup.enable = g_u16_wakeup_enable;
|
||
|
|
||
|
if (IS_DBG) {
|
||
|
g_st_dbg.u8ForceCap = 0;
|
||
|
g_st_dbg.u8Dump = 0;
|
||
|
g_st_dbg.slowscan.enable = 0;
|
||
|
|
||
|
lr388k7_send_signal(g_st_state.u32_pid, LR388K7_SIGNAL_CTRL);
|
||
|
}
|
||
|
|
||
|
if (g_st_dbg.wakeup.enable == 0)
|
||
|
return 0;
|
||
|
|
||
|
/* check irq */
|
||
|
irq_value = gpio_get_value(ts->gpio_irq);
|
||
|
if (!irq_value) {
|
||
|
|
||
|
u8_status = lr388k7_clear_irq();
|
||
|
|
||
|
while (u8_status) {
|
||
|
|
||
|
lr388k7_event_handler(u8_status);
|
||
|
irq_value = gpio_get_value(ts->gpio_irq);
|
||
|
|
||
|
if (!irq_value)
|
||
|
u8_status = lr388k7_clear_irq();
|
||
|
else
|
||
|
u8_status = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_PM
|
||
|
static int lr388k7_dev_pm_suspend(struct device *dev)
|
||
|
{
|
||
|
struct lr388k7 *ts = dev_get_drvdata(dev);
|
||
|
|
||
|
if (!g_st_state.b_is_suspended && !g_st_state.b_is_disabled) {
|
||
|
/* only called when input device is not disabled/enabled via
|
||
|
* /sys/class/input/input0/enabled interface.
|
||
|
* Android uses sysfs by default and will not run into here
|
||
|
*/
|
||
|
if (!g_st_dbg.wakeup.enable)
|
||
|
disable_irq(ts->irq);
|
||
|
lr388k7_suspend(ts);
|
||
|
#if defined(CONFIG_ANDROID)
|
||
|
dev_info(ts->dev, "disabled without input powerhal support.\n");
|
||
|
#endif
|
||
|
}
|
||
|
if (g_st_dbg.wakeup.enable == 1)
|
||
|
enable_irq_wake(ts->irq);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int lr388k7_dev_pm_resume(struct device *dev)
|
||
|
{
|
||
|
struct lr388k7 *ts = dev_get_drvdata(dev);
|
||
|
|
||
|
if (!g_st_state.b_is_disabled) {
|
||
|
/* only called when input device is not disabled/enabled via
|
||
|
* /sys/class/input/input0/enabled interface.
|
||
|
* Android uses sysfs by default and will not run into here
|
||
|
*/
|
||
|
lr388k7_resume(ts);
|
||
|
if (!g_st_dbg.wakeup.enable)
|
||
|
enable_irq(ts->irq);
|
||
|
#if defined(CONFIG_ANDROID)
|
||
|
dev_info(ts->dev, "enabled without input powerhal support.\n");
|
||
|
#endif
|
||
|
}
|
||
|
if (g_st_dbg.wakeup.enable == 1)
|
||
|
disable_irq_wake(ts->irq);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
#endif /*CONFIG_PM */
|
||
|
|
||
|
static int lr388k7_input_enable(struct input_dev *idev)
|
||
|
{
|
||
|
struct lr388k7 *ts = input_get_drvdata(idev);
|
||
|
int err = 0;
|
||
|
#if defined(DEBUG_LR388K7)
|
||
|
dev_info(ts->dev, "[ENTER] input enable\n");
|
||
|
#endif
|
||
|
|
||
|
g_st_state.b_is_disabled = false;
|
||
|
err = lr388k7_resume(ts);
|
||
|
|
||
|
#if defined(DEBUG_LR388K7)
|
||
|
dev_info(ts->dev, "[EXIT] input enable\n");
|
||
|
#endif
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int lr388k7_input_disable(struct input_dev *idev)
|
||
|
{
|
||
|
struct lr388k7 *ts = input_get_drvdata(idev);
|
||
|
int err = 0;
|
||
|
|
||
|
#if defined(DEBUG_LR388K7)
|
||
|
dev_info(ts->dev, "[ENTER] input disable\n");
|
||
|
#endif
|
||
|
|
||
|
err = lr388k7_suspend(ts);
|
||
|
g_st_state.b_is_disabled = true;
|
||
|
|
||
|
#if defined(DEBUG_LR388K7)
|
||
|
dev_info(ts->dev, "[EXIT] input disable\n");
|
||
|
#endif
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#if defined(CONFIG_PM)
|
||
|
static const struct dev_pm_ops lr388k7_pm_ops = {
|
||
|
.suspend = lr388k7_dev_pm_suspend,
|
||
|
.resume = lr388k7_dev_pm_resume,
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static const struct of_device_id lr388k7_ts_match[] = {
|
||
|
{ .compatible = "sharp,lr388k7_ts" },
|
||
|
{ },
|
||
|
};
|
||
|
MODULE_DEVICE_TABLE(of, lr388k7_ts_match);
|
||
|
|
||
|
static struct spi_driver lr388k7_driver = {
|
||
|
.driver = {
|
||
|
.name = "lr388k7_ts",
|
||
|
.owner = THIS_MODULE,
|
||
|
#if defined(CONFIG_PM)
|
||
|
.pm = &lr388k7_pm_ops,
|
||
|
#endif
|
||
|
.of_match_table = lr388k7_ts_match,
|
||
|
},
|
||
|
.probe = lr388k7_probe,
|
||
|
.remove = lr388k7_remove,
|
||
|
.shutdown = lr388k7_shutdown,
|
||
|
};
|
||
|
|
||
|
module_spi_driver(lr388k7_driver);
|
||
|
|
||
|
MODULE_AUTHOR("Makoto Itsuki <itsuki.makoto@sharp.co.jp>");
|
||
|
MODULE_DESCRIPTION("LR388K7 Touchscreen Driver");
|
||
|
MODULE_LICENSE("GPL");
|