drivers/input/touchscreen: Import siw driver from 52.1.A.3.49
Change-Id: Ida8e1eb66ce130322d2e20095a959516503d4f7c Signed-off-by: Sorayukii <sorayukii69@gmail.com>
This commit is contained in:
parent
88034ba0e9
commit
566c39486d
37 changed files with 35711 additions and 0 deletions
|
|
@ -1474,5 +1474,6 @@ config TOUCHSCREEN_RAYDIUM_CHIPSET
|
|||
If unsure, say N.
|
||||
|
||||
source "drivers/input/touchscreen/atmel_mxt640u/Kconfig"
|
||||
source "drivers/input/touchscreen/siw/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ obj-$(CONFIG_TOUCHSCREEN_CLEARPAD) += clearpad_core.o
|
|||
obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_I2C) += clearpad_i2c.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_TCM) += synaptics_tcm/
|
||||
obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT640U) += atmel_mxt640u/
|
||||
obj-$(CONFIG_TOUCHSCREEN_SIW) += siw/
|
||||
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o
|
||||
|
|
|
|||
21
drivers/input/touchscreen/siw/Kconfig
Normal file
21
drivers/input/touchscreen/siw/Kconfig
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
menu "SiW Touch Support"
|
||||
|
||||
config TOUCHSCREEN_SIW
|
||||
tristate "Silicon Works Touch Driver Core"
|
||||
depends on SPI_MASTER && I2C
|
||||
default n
|
||||
help
|
||||
Say Y here if you have a touchscreen interface using
|
||||
the Silicon Works touch device.
|
||||
|
||||
config TOUCHSCREEN_SIW_SW49501
|
||||
bool "Silicon Works Touch Driver for SW49501"
|
||||
depends on TOUCHSCREEN_SIW
|
||||
default n
|
||||
help
|
||||
If your device is SW49501, say Y
|
||||
|
||||
source "drivers/input/touchscreen/siw/mon/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
24
drivers/input/touchscreen/siw/Makefile
Normal file
24
drivers/input/touchscreen/siw/Makefile
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# Makefile for SiW touch driver
|
||||
#
|
||||
EXTRA_CFLAGS += -DSOMC_TOUCH_BRINGUP
|
||||
|
||||
siwtch-y := siw_touch.o
|
||||
siwtch-y += siw_touch_hal.o siw_touch_hal_sysfs.o
|
||||
siwtch-y += siw_touch_hal_abt.o
|
||||
siwtch-y += siw_touch_hal_prd.o
|
||||
siwtch-y += siw_touch_hal_watch.o
|
||||
siwtch-y += siw_touch_bus.o
|
||||
siwtch-y += siw_touch_bus_i2c.o
|
||||
siwtch-y += siw_touch_of.o
|
||||
siwtch-y += siw_touch_irq.o siw_touch_gpio.o
|
||||
siwtch-y += siw_touch_event.o siw_touch_notify.o
|
||||
siwtch-y += siw_touch_sys.o siw_touch_sysfs.o
|
||||
siwtch-y += siw_touch_misc.o
|
||||
|
||||
siwtch-$(CONFIG_TOUCHSCREEN_SIW_SW49501) += touch_sw49501.o
|
||||
|
||||
obj-$(CONFIG_TOUCHSCREEN_SIW) += siwtch.o
|
||||
|
||||
obj-$(CONFIG_TOUCHSCREEN_SIWMON) += mon/
|
||||
|
||||
14
drivers/input/touchscreen/siw/mon/Kconfig
Normal file
14
drivers/input/touchscreen/siw/mon/Kconfig
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
menu "SiW Touch Monitor"
|
||||
depends on TOUCHSCREEN_SIW
|
||||
|
||||
config TOUCHSCREEN_SIWMON
|
||||
tristate "Silicon Works Touch Driver Monitor"
|
||||
depends on TOUCHSCREEN_SIW
|
||||
default y
|
||||
help
|
||||
If you select this option,
|
||||
a component which captures the SiW operation traffic such as bus, input
|
||||
will be build.
|
||||
|
||||
endmenu
|
||||
|
||||
8
drivers/input/touchscreen/siw/mon/Makefile
Normal file
8
drivers/input/touchscreen/siw/mon/Makefile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#
|
||||
# Makefile for SiW touch monitor
|
||||
#
|
||||
|
||||
siwmon-y := siw_touch_mon.o siw_touch_mon_prt.o
|
||||
|
||||
obj-$(CONFIG_TOUCHSCREEN_SIWMON) += siwmon.o
|
||||
|
||||
857
drivers/input/touchscreen/siw/mon/siw_touch_mon.c
Normal file
857
drivers/input/touchscreen/siw/mon/siw_touch_mon.c
Normal file
|
|
@ -0,0 +1,857 @@
|
|||
/*
|
||||
* siw_touch_mon_main.c - SiW touch monitor
|
||||
*
|
||||
* Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr
|
||||
* Copyright (C) 2018 Sony Mobile Communications Inc.
|
||||
* Author: Hyunho Kim <kimhh@siliconworks.co.kr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "../siw_touch.h"
|
||||
#include "../siw_touch_bus.h"
|
||||
|
||||
#include "siw_touch_mon.h"
|
||||
|
||||
|
||||
#define SIW_MON_VERSION "v1.00"
|
||||
|
||||
extern int siw_mon_buf_max(void);
|
||||
extern int siw_mon_prt_max(void);
|
||||
|
||||
int siw_mon_prt_add(struct siw_mon_term *mterm, char *name);
|
||||
void siw_mon_prt_del(struct siw_mon_term *mterm);
|
||||
|
||||
extern int siw_mon_prt_init(void);
|
||||
extern void siw_mon_prt_exit(void);
|
||||
|
||||
DEFINE_MUTEX(__siw_mon_lock);
|
||||
|
||||
static LIST_HEAD(__siw_mon_term_list);
|
||||
|
||||
static struct siw_mon_term __siw_mon_term = { 0, };
|
||||
|
||||
struct siw_mon_buf_cache_operations {
|
||||
int (*create)(void);
|
||||
void (*destroy)(void);
|
||||
// void *(*zalloc)(void);
|
||||
void *(*get)(unsigned char *src, int len);
|
||||
void (*put)(void *buf);
|
||||
};
|
||||
|
||||
#if defined(__SIW_MON_USE_BUF_SLAB)
|
||||
|
||||
#define SIW_MON_CACHE_TAG "cache: "
|
||||
|
||||
#define mon_pr_cache_info(fmt, args...) \
|
||||
mon_pr_info(SIW_MON_CACHE_TAG fmt, ##args)
|
||||
|
||||
#define mon_pr_cache_err(fmt, args...) \
|
||||
mon_pr_err(SIW_MON_CACHE_TAG fmt, ##args)
|
||||
|
||||
#define SIW_MON_CREATE_TAG "create: "
|
||||
|
||||
#define mon_pr_create_info(fmt, args...) \
|
||||
mon_pr_cache_info(SIW_MON_CREATE_TAG fmt, ##args)
|
||||
|
||||
#define mon_pr_create_err(fmt, args...) \
|
||||
mon_pr_cache_err(SIW_MON_CREATE_TAG fmt, ##args)
|
||||
|
||||
#define SIW_MON_ZALLOC_TAG "zalloc: "
|
||||
|
||||
#define mon_pr_zalloc_info(fmt, args...) \
|
||||
mon_pr_cache_info(SIW_MON_ZALLOC_TAG fmt, ##args)
|
||||
|
||||
#define mon_pr_zalloc_err(fmt, args...) \
|
||||
mon_pr_cache_err(SIW_MON_ZALLOC_TAG fmt, ##args)
|
||||
|
||||
struct siw_mon_buf_cache_ctrl {
|
||||
struct list_head list;
|
||||
struct kmem_cache *cache_buf;
|
||||
struct kmem_cache *cache_unit;
|
||||
atomic_t init_done;
|
||||
};
|
||||
|
||||
struct siw_mon_buf_cache_unit {
|
||||
struct list_head link;
|
||||
unsigned char *buf;
|
||||
};
|
||||
|
||||
static struct siw_mon_buf_cache_ctrl __siw_mon_buf_cache_ctrl = {
|
||||
.list = { NULL, },
|
||||
.cache_buf = NULL,
|
||||
.cache_unit = NULL,
|
||||
.init_done = { .counter = 0, },
|
||||
};
|
||||
|
||||
static void __siw_mon_buf_ctor(void *mem)
|
||||
{
|
||||
memset(mem, 0, siw_mon_prt_max());
|
||||
}
|
||||
|
||||
static void __siw_mon_unit_ctor(void *mem)
|
||||
{
|
||||
memset(mem, 0, sizeof(struct siw_mon_buf_cache_unit));
|
||||
}
|
||||
|
||||
static int __siw_mon_create_buf_cache(void)
|
||||
{
|
||||
struct siw_mon_buf_cache_ctrl *ctrl = &__siw_mon_buf_cache_ctrl;
|
||||
struct kmem_cache *cache;
|
||||
int ret = 0;
|
||||
|
||||
INIT_LIST_HEAD(&ctrl->list);
|
||||
|
||||
cache = kmem_cache_create("siwmon_cache_buf",
|
||||
siw_mon_prt_max(), sizeof(u32), 0,
|
||||
__siw_mon_buf_ctor);
|
||||
if (cache == NULL) {
|
||||
mon_pr_create_err("failed to create memory cache for cache_buf\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
ctrl->cache_buf = cache;
|
||||
|
||||
// mon_pr_create_info("cache_buf created\n");
|
||||
|
||||
cache = kmem_cache_create("siwmon_cache_unit",
|
||||
sizeof(struct siw_mon_buf_cache_unit), 0, 0,
|
||||
__siw_mon_unit_ctor);
|
||||
if (cache == NULL) {
|
||||
mon_pr_create_err("failed to create memory cache for cache_unit\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_ctrl;
|
||||
}
|
||||
ctrl->cache_unit = cache;
|
||||
|
||||
// mon_pr_create_info("cache_unit created\n");
|
||||
|
||||
atomic_set(&ctrl->init_done, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
out_ctrl:
|
||||
kmem_cache_destroy(ctrl->cache_buf);
|
||||
ctrl->cache_buf = NULL;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __siw_mon_del_unit_cache(struct siw_mon_buf_cache_unit *unit)
|
||||
{
|
||||
struct siw_mon_buf_cache_ctrl *ctrl = &__siw_mon_buf_cache_ctrl;
|
||||
|
||||
kmem_cache_free(ctrl->cache_buf, unit->buf);
|
||||
kmem_cache_free(ctrl->cache_unit, unit);
|
||||
}
|
||||
|
||||
static void __siw_mon_del_buf_list(void *buf)
|
||||
{
|
||||
struct siw_mon_buf_cache_ctrl *ctrl = &__siw_mon_buf_cache_ctrl;
|
||||
struct siw_mon_buf_cache_unit *unit;
|
||||
struct list_head *p;
|
||||
|
||||
while (!list_empty(&ctrl->list)) {
|
||||
p = ctrl->list.next;
|
||||
unit = list_entry(p, struct siw_mon_buf_cache_unit, link);
|
||||
if (buf == NULL) {
|
||||
/* Clear all */
|
||||
list_del(p);
|
||||
__siw_mon_del_unit_cache(unit);
|
||||
} else {
|
||||
if (unit->buf == buf) {
|
||||
list_del(p);
|
||||
__siw_mon_del_unit_cache(unit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __siw_mon_destroy_buf_cache(void)
|
||||
{
|
||||
struct siw_mon_buf_cache_ctrl *ctrl = &__siw_mon_buf_cache_ctrl;
|
||||
|
||||
if (!atomic_read(&ctrl->init_done)) {
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_set(&ctrl->init_done, 0);
|
||||
|
||||
/* Clear all */
|
||||
__siw_mon_del_buf_list(NULL);
|
||||
|
||||
kmem_cache_destroy(ctrl->cache_unit);
|
||||
kmem_cache_destroy(ctrl->cache_buf);
|
||||
|
||||
ctrl->cache_buf = NULL;
|
||||
ctrl->cache_unit = NULL;
|
||||
|
||||
INIT_LIST_HEAD(&ctrl->list);
|
||||
}
|
||||
|
||||
static void *__siw_mon_zalloc_buf_cache(void)
|
||||
{
|
||||
struct siw_mon_buf_cache_ctrl *ctrl = &__siw_mon_buf_cache_ctrl;
|
||||
struct siw_mon_buf_cache_unit *unit;
|
||||
void *buf = NULL;
|
||||
|
||||
if (!atomic_read(&ctrl->init_done)) {
|
||||
mon_pr_zalloc_err("not initialized\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
unit = kmem_cache_zalloc(ctrl->cache_unit, GFP_ATOMIC);
|
||||
if (unit == NULL) {
|
||||
mon_pr_zalloc_err("failed to get memory from cache_unit\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf = kmem_cache_zalloc(ctrl->cache_buf, GFP_ATOMIC);
|
||||
if (buf == NULL) {
|
||||
mon_pr_zalloc_err("failed to get memory from cache_buf\n");
|
||||
kmem_cache_free(ctrl->cache_unit, unit);
|
||||
goto out;
|
||||
}
|
||||
|
||||
unit->buf = buf;
|
||||
list_add_tail(&unit->link, &ctrl->list);
|
||||
|
||||
// mon_pr_zalloc_err("new cache_unit(%p, %p) created\n", (void *)unit, (void *)buf);
|
||||
|
||||
out:
|
||||
return buf;
|
||||
}
|
||||
|
||||
void *__siw_mon_get_buf_cache(unsigned char *src, int len)
|
||||
{
|
||||
struct siw_mon_buf_cache_ctrl *ctrl = &__siw_mon_buf_cache_ctrl;
|
||||
unsigned char *buf;
|
||||
|
||||
if (!atomic_read(&ctrl->init_done)) {
|
||||
return src;
|
||||
}
|
||||
|
||||
buf = __siw_mon_zalloc_buf_cache();
|
||||
if (buf && src) {
|
||||
memcpy(buf, src, len);
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void __siw_mon_put_buf_cache(void *buf)
|
||||
{
|
||||
struct siw_mon_buf_cache_ctrl *ctrl = &__siw_mon_buf_cache_ctrl;
|
||||
|
||||
if (!atomic_read(&ctrl->init_done)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clear target buf : buf should be non-zero */
|
||||
__siw_mon_del_buf_list(buf);
|
||||
}
|
||||
|
||||
static const struct siw_mon_buf_cache_operations __siw_mon_buf_cache_ops = {
|
||||
.create = __siw_mon_create_buf_cache,
|
||||
.destroy = __siw_mon_destroy_buf_cache,
|
||||
.get = __siw_mon_get_buf_cache,
|
||||
.put = __siw_mon_put_buf_cache,
|
||||
};
|
||||
#else /* __SIW_MON_USE_BUF_SLAB */
|
||||
static const struct siw_mon_buf_cache_operations __siw_mon_buf_cache_ops = { NULL, };
|
||||
#endif /* __SIW_MON_USE_BUF_SLAB */
|
||||
|
||||
static int __used siw_mon_create_buf_cache(void)
|
||||
{
|
||||
return (__siw_mon_buf_cache_ops.create) ? \
|
||||
(__siw_mon_buf_cache_ops.create)() : 0;
|
||||
}
|
||||
|
||||
static void __used siw_mon_destroy_buf_cache(void)
|
||||
{
|
||||
return (__siw_mon_buf_cache_ops.destroy) ? \
|
||||
(__siw_mon_buf_cache_ops.destroy)() : 0;
|
||||
}
|
||||
|
||||
void __used *siw_mon_get_buf_cache(unsigned char *src, int len)
|
||||
{
|
||||
return (__siw_mon_buf_cache_ops.get) ? \
|
||||
(__siw_mon_buf_cache_ops.get)(src, len) : src;
|
||||
}
|
||||
|
||||
void __used siw_mon_put_buf_cache(void *buf)
|
||||
{
|
||||
if (__siw_mon_buf_cache_ops.put)
|
||||
(__siw_mon_buf_cache_ops.put)(buf);
|
||||
}
|
||||
|
||||
static void siw_mon_stop(struct siw_mon_term *mterm)
|
||||
{
|
||||
struct list_head *p;
|
||||
|
||||
if (mterm == &__siw_mon_term) {
|
||||
list_for_each (p, &__siw_mon_term_list) {
|
||||
mterm = list_entry(p, struct siw_mon_term, term_link);
|
||||
if (!mterm->nreaders)
|
||||
mterm->monitored = 0;
|
||||
}
|
||||
} else {
|
||||
if (!__siw_mon_term.nreaders) {
|
||||
__siw_mon_term.monitored = 0;
|
||||
mb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void siw_mon_drop(struct kref *r)
|
||||
{
|
||||
struct siw_mon_term *mterm = container_of(r, struct siw_mon_term, ref);
|
||||
|
||||
mon_pr_dbg("siw mon term[0x%02X, %s] released\n",
|
||||
mterm->type, mterm->name);
|
||||
|
||||
if (mterm == &__siw_mon_term)
|
||||
return;
|
||||
|
||||
kfree(mterm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear usb_bus and mon_bus apart.
|
||||
*/
|
||||
static void siw_mon_dissolve(struct siw_mon_term *mterm)
|
||||
{
|
||||
|
||||
if (mterm->monitored) {
|
||||
mterm->monitored = 0;
|
||||
mb();
|
||||
}
|
||||
/* We want synchronize_irq() here, but that needs an argument. */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Link a reader into the bus.
|
||||
*
|
||||
* This must be called with mon_lock taken because of mbus->ref.
|
||||
*/
|
||||
void siw_mon_reader_add(struct siw_mon_term *mterm,
|
||||
struct siw_mon_reader *r)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head *p;
|
||||
|
||||
spin_lock_irqsave(&mterm->lock, flags);
|
||||
if (mterm->nreaders == 0) {
|
||||
if (mterm == &__siw_mon_term) {
|
||||
list_for_each (p, &__siw_mon_term_list) {
|
||||
struct siw_mon_term *mt;
|
||||
mt = list_entry(p, struct siw_mon_term, term_link);
|
||||
mt->monitored = 1;
|
||||
}
|
||||
} else {
|
||||
mterm->monitored = 1;
|
||||
}
|
||||
}
|
||||
mterm->nreaders++;
|
||||
list_add_tail(&r->r_link, &mterm->r_list);
|
||||
spin_unlock_irqrestore(&mterm->lock, flags);
|
||||
|
||||
kref_get(&mterm->ref);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlink reader from the bus.
|
||||
*
|
||||
* This is called with mon_lock taken, so we can decrement mbus->ref.
|
||||
*/
|
||||
void siw_mon_reader_del(struct siw_mon_term *mterm,
|
||||
struct siw_mon_reader *r)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mterm->lock, flags);
|
||||
list_del(&r->r_link);
|
||||
--mterm->nreaders;
|
||||
if (mterm->nreaders == 0)
|
||||
siw_mon_stop(mterm);
|
||||
spin_unlock_irqrestore(&mterm->lock, flags);
|
||||
|
||||
kref_put(&mterm->ref, siw_mon_drop);
|
||||
}
|
||||
|
||||
|
||||
static void siw_mon_do_data_submit(struct siw_mon_term *mterm,
|
||||
struct siw_mon_data *data)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head *pos;
|
||||
struct siw_mon_reader *r;
|
||||
|
||||
if (mterm == NULL) {
|
||||
mon_pr_warn("NULL mterm\n");
|
||||
return;
|
||||
}
|
||||
if (mterm->type == SIW_MON_NONE) {
|
||||
mon_pr_warn("NULL type\n");
|
||||
return;
|
||||
}
|
||||
if ((mterm->type > SIW_MON_OPS) && (mterm->type != SIW_MON_ALL)) {
|
||||
mon_pr_warn("Invalid type, %d\n", mterm->type);
|
||||
return;
|
||||
}
|
||||
if (data == NULL) {
|
||||
mon_pr_warn("NULL data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&mterm->lock, flags);
|
||||
|
||||
if (mterm != &__siw_mon_term) {
|
||||
if (!mterm->monitored)
|
||||
goto out;
|
||||
}
|
||||
|
||||
mterm->cnt_events += data->inc_cnt;
|
||||
data->cnt_events = mterm->cnt_events;
|
||||
list_for_each (pos, &mterm->r_list) {
|
||||
r = list_entry(pos, struct siw_mon_reader, r_link);
|
||||
r->submit(r->r_data, data, &flags);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&mterm->lock, flags);
|
||||
}
|
||||
|
||||
static void siw_mon_data_submit(struct siw_mon_data *data, int inc_cnt)
|
||||
{
|
||||
struct list_head *pos;
|
||||
struct siw_mon_term *mterm = NULL;
|
||||
|
||||
data->inc_cnt = inc_cnt;
|
||||
|
||||
list_for_each (pos, &__siw_mon_term_list) {
|
||||
mterm = list_entry(pos, struct siw_mon_term, term_link);
|
||||
if (mterm->type == data->type) {
|
||||
siw_mon_do_data_submit(mterm, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
siw_mon_do_data_submit(&__siw_mon_term, data);
|
||||
}
|
||||
|
||||
static void siw_mon_submit_bus_slice(struct siw_mon_data *smdata, void *data)
|
||||
{
|
||||
struct siw_mon_data_bus *bus = &smdata->d.bus;
|
||||
struct touch_bus_msg *msg = data;
|
||||
unsigned char *buf_org = NULL;
|
||||
int tx_size, rx_size;
|
||||
int tx_len, rx_len, cur;
|
||||
int bus_cnt, tx_cnt, rx_cnt;
|
||||
int buf_max, prt_max;
|
||||
int bus_priv = bus->priv & MASK_BIT16;
|
||||
// int tx_s_only, rx_s_only;
|
||||
int i, inc_cnt = 1;
|
||||
|
||||
if (bus_priv) { //delay for xfer case
|
||||
usleep_range(1000, 1000);
|
||||
}
|
||||
|
||||
buf_max = siw_mon_buf_max();
|
||||
prt_max = siw_mon_prt_max();
|
||||
|
||||
// 1. backup original data size
|
||||
tx_size = msg->tx_size;
|
||||
rx_size = msg->rx_size;
|
||||
|
||||
// tx_s_only = (!rx_size && (tx_size < prt_max);
|
||||
// rx_s_only = (!tx_size && (rx_size < prt_max);
|
||||
|
||||
// 2. total limit : up to buffer limit
|
||||
tx_len = (buf_max) ? min(tx_size, buf_max) : tx_size;
|
||||
rx_len = (buf_max) ? min(rx_size, buf_max) : rx_size;
|
||||
|
||||
// 3. line count : 1 line has 16 hex numbers
|
||||
tx_cnt = (tx_len + prt_max - 1)/prt_max;
|
||||
rx_cnt = (rx_len + prt_max - 1)/prt_max;
|
||||
bus_cnt = tx_cnt + rx_cnt;
|
||||
|
||||
//Tx part
|
||||
bus->rx_buf = NULL;
|
||||
bus->rx_size = 0;
|
||||
buf_org = msg->tx_buf;
|
||||
for (i = 0; i < tx_cnt; i++) {
|
||||
cur = min(tx_len, prt_max);
|
||||
|
||||
bus->tx_buf = buf_org;
|
||||
bus->tx_size = siw_mon_set_buf_size(cur, tx_size);
|
||||
tx_len -= cur;
|
||||
buf_org += cur;
|
||||
|
||||
#if 1
|
||||
bus->priv = siw_mon_set_priv_sub(bus->priv, i, bus_cnt);
|
||||
#else
|
||||
bus->priv = (tx_s_only) ? \
|
||||
0 : siw_mon_set_priv_sub(bus->priv, i, bus_cnt);
|
||||
#endif
|
||||
|
||||
siw_mon_data_submit(smdata, inc_cnt);
|
||||
inc_cnt = 0;
|
||||
}
|
||||
|
||||
//Rx part
|
||||
bus->tx_buf = NULL;
|
||||
bus->tx_size = 0;
|
||||
buf_org = msg->rx_buf;
|
||||
for (i = 0; i < rx_cnt; i++) {
|
||||
cur = min(rx_len, prt_max);
|
||||
|
||||
bus->rx_buf = buf_org;
|
||||
bus->rx_size = siw_mon_set_buf_size(cur, rx_size);
|
||||
rx_len -= cur;
|
||||
buf_org += cur;
|
||||
|
||||
#if 1
|
||||
bus->priv = siw_mon_set_priv_sub(bus->priv, i+tx_cnt, bus_cnt);
|
||||
#else
|
||||
bus->priv = (rx_s_only) ? \
|
||||
0 : siw_mon_set_priv_sub(bus->priv, i+tx_cnt, bus_cnt);
|
||||
#endif
|
||||
|
||||
siw_mon_data_submit(smdata, inc_cnt);
|
||||
inc_cnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void siw_mon_submit_bus(struct device *dev, char *dir, void *data, int ret)
|
||||
{
|
||||
struct touch_bus_msg *msg = data;
|
||||
struct siw_mon_data smdata = {
|
||||
.type = SIW_MON_BUS,
|
||||
.dev = dev,
|
||||
.ret = ret,
|
||||
.d = {
|
||||
.bus = {
|
||||
.tx_buf = msg->tx_buf,
|
||||
.tx_size = msg->tx_size,
|
||||
.rx_buf = msg->rx_buf,
|
||||
.rx_size = msg->rx_size,
|
||||
.tx_dma = msg->tx_dma,
|
||||
.rx_dma = msg->rx_dma,
|
||||
.priv = msg->priv,
|
||||
},
|
||||
},
|
||||
};
|
||||
struct siw_mon_data_bus *bus = &smdata.d.bus;
|
||||
|
||||
snprintf(bus->dir, BUS_NAME_SZ, "%s", dir);
|
||||
|
||||
siw_mon_submit_bus_slice(&smdata, data);
|
||||
}
|
||||
|
||||
static void siw_mon_submit_evt(struct device *dev, char *type, int type_v,
|
||||
char *code, int code_v, int value, int ret)
|
||||
{
|
||||
struct siw_mon_data smdata = {
|
||||
.type = SIW_MON_EVT,
|
||||
.dev = dev,
|
||||
.ret = ret,
|
||||
.d = {
|
||||
.evt = {
|
||||
.type_v = type_v,
|
||||
.code_v = code_v,
|
||||
.value = value,
|
||||
},
|
||||
},
|
||||
};
|
||||
struct siw_mon_data_evt *evt = &smdata.d.evt;
|
||||
|
||||
snprintf(evt->type, EVT_NAME_SZ, "%s", type);
|
||||
snprintf(evt->code, EVT_NAME_SZ, "%s", code);
|
||||
|
||||
siw_mon_data_submit(&smdata, 1);
|
||||
}
|
||||
|
||||
static int siw_mon_submit_ops_sock(struct siw_mon_data *smdata, int ret)
|
||||
{
|
||||
struct siw_mon_data_ops *ops = &smdata->d.ops;
|
||||
unsigned char *buf = NULL;
|
||||
unsigned char *buf_org = NULL;
|
||||
int buf_max, prt_max;
|
||||
int buf_len = 0;
|
||||
int idx;
|
||||
int tot, cur;
|
||||
int ops_cnt, i;
|
||||
int inc_cnt = 1;
|
||||
|
||||
idx = siw_mon_txt_sock_str_cmp(ops->ops);
|
||||
if (idx < 0) {
|
||||
return idx;
|
||||
}
|
||||
|
||||
if (ret <= 0) {
|
||||
switch (idx) {
|
||||
case SIW_MON_SOCK_R:
|
||||
memset(ops->data, 0, sizeof(ops->data));
|
||||
break;
|
||||
case SIW_MON_SOCK_S:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
buf_max = siw_mon_buf_max();
|
||||
prt_max = siw_mon_prt_max();
|
||||
|
||||
buf_org = (unsigned char *)ops->data[0];
|
||||
buf_len = ops->data[1];
|
||||
buf_len = (ret >= 0) ? min(buf_len, ret) : buf_len;
|
||||
|
||||
if (!buf_org ||
|
||||
(!prt_max || (buf_len <= prt_max))) {
|
||||
if (buf_org && buf_len) {
|
||||
buf = buf_org;
|
||||
}
|
||||
ops->data[0] = (buf_len) ? ((size_t)buf) : 0;
|
||||
ops->data[1] = buf_len;
|
||||
ops->priv = 0;
|
||||
|
||||
siw_mon_data_submit(smdata, inc_cnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tot = buf_len;
|
||||
buf_len = (buf_max) ? min(buf_len, buf_max) : buf_len;
|
||||
ops_cnt = (buf_len + prt_max - 1)/prt_max;
|
||||
|
||||
for (i = 0; i < ops_cnt; i++) {
|
||||
cur = min(buf_len, prt_max);
|
||||
|
||||
buf = buf_org;
|
||||
|
||||
ops->data[0] = (cur) ? ((size_t)buf) : 0;
|
||||
ops->data[1] = siw_mon_set_buf_size(cur, tot);
|
||||
buf_len -= cur;
|
||||
buf_org += cur;
|
||||
|
||||
ops->priv = siw_mon_set_priv_sub(ops->priv, i, ops_cnt);
|
||||
|
||||
siw_mon_data_submit(smdata, inc_cnt);
|
||||
inc_cnt = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void siw_mon_submit_ops(struct device *dev,
|
||||
char *ops_str, void *data,
|
||||
int size, int ret)
|
||||
{
|
||||
struct siw_mon_data smdata = {
|
||||
.type = SIW_MON_OPS,
|
||||
.dev = dev,
|
||||
.ret = ret,
|
||||
.d = {
|
||||
.ops = {
|
||||
.priv = 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
struct siw_mon_data_ops *ops = &smdata.d.ops;
|
||||
int len = (data != NULL) ? min(OPS_DATA_SZ, size) : 0;
|
||||
|
||||
snprintf(ops->ops, OPS_NAME_SZ, "%s", ops_str);
|
||||
if (len && data) {
|
||||
memcpy(ops->data, data, sizeof(ops->data[0]) * len);
|
||||
} else {
|
||||
memset(ops->data, 0, sizeof(ops->data));
|
||||
}
|
||||
ops->len = len;
|
||||
|
||||
if (!siw_mon_submit_ops_sock(&smdata, ret)) {
|
||||
return;
|
||||
}
|
||||
|
||||
siw_mon_data_submit(&smdata, 1);
|
||||
}
|
||||
|
||||
static void __siw_mon_init_term(struct siw_mon_term *mterm,
|
||||
int type, char *name)
|
||||
{
|
||||
mterm->type = type;
|
||||
snprintf(mterm->name, sizeof(mterm->name), "%s", name);
|
||||
mterm->prt_inited = siw_mon_prt_add(mterm, name);
|
||||
|
||||
kref_init(&mterm->ref);
|
||||
spin_lock_init(&mterm->lock);
|
||||
INIT_LIST_HEAD(&mterm->r_list);
|
||||
|
||||
mon_pr_dbg("siw mon term[0x%02X, %s] created\n",
|
||||
type, name);
|
||||
}
|
||||
|
||||
static void __siw_mon_exit_term(struct siw_mon_term *mterm)
|
||||
{
|
||||
if (mterm->prt_inited)
|
||||
siw_mon_prt_del(mterm);
|
||||
|
||||
if (mterm->nreaders) {
|
||||
mon_pr_err("Outstanding opens (%d), leaking...\n",
|
||||
mterm->nreaders);
|
||||
atomic_set(&mterm->ref.refcount, 2); /* Force leak */
|
||||
}
|
||||
|
||||
siw_mon_dissolve(mterm);
|
||||
kref_put(&mterm->ref, siw_mon_drop);
|
||||
}
|
||||
|
||||
static int siw_mon_subterm_add(int type, char *name)
|
||||
{
|
||||
struct siw_mon_term *mterm = NULL;
|
||||
|
||||
mterm = kzalloc(sizeof(struct siw_mon_term), GFP_KERNEL);
|
||||
if (mterm == NULL) {
|
||||
mon_pr_warn("unable to allocate mon terminal[%s]\n", name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
__siw_mon_init_term(mterm, type, name);
|
||||
|
||||
mutex_lock(&__siw_mon_lock);
|
||||
list_add_tail(&mterm->term_link, &__siw_mon_term_list);
|
||||
mutex_unlock(&__siw_mon_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int siw_mon_subterm_init(void)
|
||||
{
|
||||
(void)siw_mon_subterm_add(SIW_MON_BUS, SIW_MON_BUS_NAME);
|
||||
|
||||
(void)siw_mon_subterm_add(SIW_MON_EVT, SIW_MON_EVT_NAME);
|
||||
|
||||
(void)siw_mon_subterm_add(SIW_MON_OPS, SIW_MON_OPS_NAME);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void siw_mon_subterm_exit(void)
|
||||
{
|
||||
struct siw_mon_term *mterm = NULL;
|
||||
struct list_head *p;
|
||||
|
||||
mutex_lock(&__siw_mon_lock);
|
||||
|
||||
while (!list_empty(&__siw_mon_term_list)) {
|
||||
p = __siw_mon_term_list.next;
|
||||
mterm = list_entry(p, struct siw_mon_term, term_link);
|
||||
list_del(p);
|
||||
|
||||
__siw_mon_exit_term(mterm);
|
||||
}
|
||||
|
||||
mutex_unlock(&__siw_mon_lock);
|
||||
}
|
||||
|
||||
static void siw_mon_mainterm_init(void)
|
||||
{
|
||||
__siw_mon_init_term(&__siw_mon_term, SIW_MON_ALL, SIW_MON_ALL_NAME);
|
||||
}
|
||||
|
||||
static void siw_mon_mainterm_exit(void)
|
||||
{
|
||||
__siw_mon_exit_term(&__siw_mon_term);
|
||||
memset(&__siw_mon_term, 0, sizeof(__siw_mon_term));
|
||||
}
|
||||
|
||||
static struct siw_mon_operations siw_mon_0 = {
|
||||
.submit_bus = siw_mon_submit_bus,
|
||||
.submit_evt = siw_mon_submit_evt,
|
||||
.submit_ops = siw_mon_submit_ops,
|
||||
};
|
||||
|
||||
static int __init siw_mon_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = siw_mon_create_buf_cache();
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = siw_mon_prt_init();
|
||||
if (ret)
|
||||
goto out_prt;
|
||||
|
||||
siw_mon_mainterm_init();
|
||||
|
||||
ret = siw_mon_subterm_init();
|
||||
if (ret)
|
||||
goto out_subterm;
|
||||
|
||||
if (siw_mon_register(&siw_mon_0) != 0) {
|
||||
mon_pr_err("unable to register with the core\n");
|
||||
ret = -ENODEV;
|
||||
goto out_reg;
|
||||
}
|
||||
|
||||
mon_pr_info("siw mon created - %s\n", SIW_MON_VERSION);
|
||||
|
||||
return 0;
|
||||
|
||||
out_reg:
|
||||
siw_mon_subterm_exit();
|
||||
|
||||
out_subterm:
|
||||
siw_mon_mainterm_exit();
|
||||
siw_mon_prt_exit();
|
||||
|
||||
out_prt:
|
||||
siw_mon_destroy_buf_cache();
|
||||
|
||||
out:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit siw_mon_exit(void)
|
||||
{
|
||||
siw_mon_deregister();
|
||||
|
||||
siw_mon_subterm_exit();
|
||||
|
||||
siw_mon_mainterm_exit();
|
||||
|
||||
siw_mon_prt_exit();
|
||||
|
||||
siw_mon_destroy_buf_cache();
|
||||
|
||||
mon_pr_info("siw mon removed - %s\n", SIW_MON_VERSION);
|
||||
}
|
||||
|
||||
module_init(siw_mon_init);
|
||||
module_exit(siw_mon_exit);
|
||||
|
||||
MODULE_AUTHOR("kimhh@siliconworks.co.kr");
|
||||
MODULE_DESCRIPTION("SiW Touch Monitor Driver");
|
||||
MODULE_VERSION(SIW_MON_VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
|
||||
245
drivers/input/touchscreen/siw/mon/siw_touch_mon.h
Normal file
245
drivers/input/touchscreen/siw/mon/siw_touch_mon.h
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* SiW touch monitor
|
||||
*
|
||||
* Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr
|
||||
* Copyright (C) 2018 Sony Mobile Communications Inc.
|
||||
* Author: Hyunho Kim <kimhh@siliconworks.co.kr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __SIW_TOUCH_MON_H
|
||||
#define __SIW_TOUCH_MON_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kref.h>
|
||||
|
||||
/*
|
||||
* Do not use __SIW_MON_USE_BUF_SLAB
|
||||
* memory leakage problem found
|
||||
*/
|
||||
//#define __SIW_MON_USE_BUF_SLAB
|
||||
#if defined(__SIW_MON_USE_BUF_SLAB)
|
||||
#define __SIW_MON_USE_BUF_SLAB_BUS
|
||||
#define __SIW_MON_USE_BUF_SLAB_OPS
|
||||
#endif
|
||||
|
||||
#ifndef KERN_WARN
|
||||
#define KERN_WARN KERN_WARNING
|
||||
#endif
|
||||
|
||||
#define SIW_MON_NAME "siwmon"
|
||||
#define SIW_MON_TAG "siwmon"
|
||||
|
||||
#define mon_pr_info(fmt, args...) pr_info(SIW_MON_TAG ": " fmt, ##args)
|
||||
#define mon_pr_noti(fmt, args...) pr_notice(SIW_MON_TAG ": " fmt, ##args)
|
||||
#define mon_pr_warn(fmt, args...) pr_warning(SIW_MON_TAG ": " fmt, ##args)
|
||||
#define mon_pr_err(fmt, args...) pr_err(SIW_MON_TAG ": " fmt, ##args)
|
||||
#define mon_pr_dbg(fmt, args...) pr_debug(SIW_MON_TAG ": " fmt, ##args)
|
||||
|
||||
#if 0
|
||||
#define mon_pr_test mon_pr_info
|
||||
#else
|
||||
#define mon_pr_test mon_pr_dbg
|
||||
#endif
|
||||
|
||||
enum {
|
||||
BIT_OFF16 = 16,
|
||||
BIT_OFF8 = 8,
|
||||
//
|
||||
MASK_BIT16 = ((1L<<BIT_OFF16)-1),
|
||||
MASK_BIT8 = ((1L<<BIT_OFF8)-1),
|
||||
};
|
||||
|
||||
/*
|
||||
* [31:16] : original total size
|
||||
* [15:00] : adjusted total size by print limit
|
||||
* See '__siw_mon_txt_read_get_len'
|
||||
*/
|
||||
static inline int siw_mon_buf_size_tot(int size)
|
||||
{
|
||||
return (size>>BIT_OFF16);
|
||||
}
|
||||
|
||||
static inline int siw_mon_buf_size_cur(int size)
|
||||
{
|
||||
return (size & MASK_BIT16);
|
||||
}
|
||||
|
||||
static inline int siw_mon_set_buf_size(int cur, int tot)
|
||||
{
|
||||
return ((tot<<BIT_OFF16) | cur);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static inline int siw_mon_priv_sub(int priv)
|
||||
{
|
||||
return (priv>>BIT_OFF16);
|
||||
}
|
||||
|
||||
static inline int siw_mon_priv_main(int priv)
|
||||
{
|
||||
return (priv & MASK_BIT16);
|
||||
}
|
||||
|
||||
/*
|
||||
* [15:08] : curr idx
|
||||
* [07:00] : total count
|
||||
*/
|
||||
static inline int siw_mon_priv_cur(int priv)
|
||||
{
|
||||
return (priv>>BIT_OFF8);
|
||||
}
|
||||
|
||||
static inline int siw_mon_priv_tot(int priv)
|
||||
{
|
||||
return (priv & MASK_BIT8);
|
||||
}
|
||||
|
||||
static inline int siw_mon_set_priv_sub(int priv, int cur, int tot)
|
||||
{
|
||||
int priv_sub = (cur<<BIT_OFF8) | tot;
|
||||
priv &= MASK_BIT16;
|
||||
priv |= priv_sub<<BIT_OFF16;
|
||||
return priv;
|
||||
}
|
||||
|
||||
enum {
|
||||
BUS_NAME_SZ = 32,
|
||||
};
|
||||
|
||||
struct siw_mon_data_bus {
|
||||
char dir[BUS_NAME_SZ];
|
||||
u8 *tx_buf;
|
||||
int tx_size;
|
||||
u8 *rx_buf;
|
||||
int rx_size;
|
||||
dma_addr_t tx_dma;
|
||||
dma_addr_t rx_dma;
|
||||
int priv;
|
||||
};
|
||||
|
||||
enum {
|
||||
EVT_NAME_SZ = 32,
|
||||
};
|
||||
|
||||
struct siw_mon_data_evt {
|
||||
char type[EVT_NAME_SZ];
|
||||
int type_v;
|
||||
char code[EVT_NAME_SZ];
|
||||
int code_v;
|
||||
int value;
|
||||
int priv;
|
||||
};
|
||||
|
||||
enum {
|
||||
OPS_NAME_SZ = 128,
|
||||
OPS_DATA_SZ = 8,
|
||||
};
|
||||
|
||||
struct siw_mon_data_ops {
|
||||
char ops[OPS_NAME_SZ];
|
||||
size_t data[OPS_DATA_SZ];
|
||||
int len;
|
||||
int priv;
|
||||
};
|
||||
|
||||
struct siw_mon_data {
|
||||
int type;
|
||||
int cnt_events;
|
||||
void *dev;
|
||||
int ret;
|
||||
union {
|
||||
struct siw_mon_data_bus bus;
|
||||
struct siw_mon_data_evt evt;
|
||||
struct siw_mon_data_ops ops;
|
||||
} d;
|
||||
int inc_cnt;
|
||||
};
|
||||
|
||||
enum _SIW_MON_TYPE {
|
||||
SIW_MON_NONE = 0,
|
||||
SIW_MON_BUS,
|
||||
SIW_MON_EVT,
|
||||
SIW_MON_OPS,
|
||||
/* */
|
||||
SIW_MON_ALL = 0x80,
|
||||
};
|
||||
|
||||
#define SIW_MON_ALL_NAME "all"
|
||||
#define SIW_MON_BUS_NAME "bus"
|
||||
#define SIW_MON_EVT_NAME "evt"
|
||||
#define SIW_MON_OPS_NAME "ops"
|
||||
|
||||
|
||||
#define SIW_MON_SOCK_RSTR "@ABTR"
|
||||
#define SIW_MON_SOCK_SSTR "@ABTS"
|
||||
|
||||
enum {
|
||||
SIW_MON_SOCK_NONE = 0,
|
||||
SIW_MON_SOCK_R,
|
||||
SIW_MON_SOCK_S,
|
||||
};
|
||||
|
||||
static inline int siw_mon_txt_sock_str_cmp(char *ops)
|
||||
{
|
||||
if (!strncmp(SIW_MON_SOCK_RSTR, ops, sizeof(SIW_MON_SOCK_RSTR))) {
|
||||
return SIW_MON_SOCK_R;
|
||||
}
|
||||
if (!strncmp(SIW_MON_SOCK_SSTR, ops, sizeof(SIW_MON_SOCK_SSTR))) {
|
||||
return SIW_MON_SOCK_S;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Mon terminal
|
||||
*/
|
||||
struct siw_mon_term {
|
||||
int type;
|
||||
char name[16];
|
||||
|
||||
struct list_head term_link;
|
||||
spinlock_t lock;
|
||||
|
||||
int prt_inited;
|
||||
|
||||
struct dentry *mfile; /* Text interface file */
|
||||
|
||||
/* Ref */
|
||||
int nreaders;
|
||||
struct list_head r_list;
|
||||
struct kref ref;
|
||||
|
||||
unsigned int cnt_events;
|
||||
unsigned int cnt_text_lost;
|
||||
|
||||
int monitored;
|
||||
};
|
||||
|
||||
/*
|
||||
* An instance of a process which opened a file (but can fork later)
|
||||
*/
|
||||
struct siw_mon_reader {
|
||||
struct list_head r_link;
|
||||
struct siw_mon_term *mterm;
|
||||
void *r_data; /* Use container_of instead? */
|
||||
|
||||
void (*submit)(void *r_data, struct siw_mon_data *data,
|
||||
unsigned long *flags);
|
||||
};
|
||||
|
||||
extern struct mutex __siw_mon_lock;
|
||||
|
||||
extern void siw_mon_reader_add(struct siw_mon_term *mterm, struct siw_mon_reader *r);
|
||||
extern void siw_mon_reader_del(struct siw_mon_term *mterm, struct siw_mon_reader *r);
|
||||
|
||||
#endif /* __SIW_TOUCH_MON_H */
|
||||
|
||||
|
||||
1017
drivers/input/touchscreen/siw/mon/siw_touch_mon_prt.c
Normal file
1017
drivers/input/touchscreen/siw/mon/siw_touch_mon_prt.c
Normal file
File diff suppressed because it is too large
Load diff
1962
drivers/input/touchscreen/siw/siw_touch.c
Normal file
1962
drivers/input/touchscreen/siw/siw_touch.c
Normal file
File diff suppressed because it is too large
Load diff
1835
drivers/input/touchscreen/siw/siw_touch.h
Normal file
1835
drivers/input/touchscreen/siw/siw_touch.h
Normal file
File diff suppressed because it is too large
Load diff
648
drivers/input/touchscreen/siw/siw_touch_bus.c
Normal file
648
drivers/input/touchscreen/siw/siw_touch_bus.c
Normal file
|
|
@ -0,0 +1,648 @@
|
|||
/*
|
||||
* siw_touch_bus.c - SiW touch bus core driver
|
||||
*
|
||||
* Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr
|
||||
* Copyright (C) 2018 Sony Mobile Communications Inc.
|
||||
* Author: Hyunho Kim <kimhh@siliconworks.co.kr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/async.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/memory.h>
|
||||
|
||||
#ifndef SOMC_TOUCH_BRINGUP
|
||||
#include <linux/spi/spi.h>
|
||||
#endif
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "siw_touch.h"
|
||||
#include "siw_touch_hal.h"
|
||||
#include "siw_touch_irq.h"
|
||||
#include "siw_touch_bus.h"
|
||||
#include "siw_touch_bus_i2c.h"
|
||||
#ifndef SOMC_TOUCH_BRINGUP
|
||||
#include "siw_touch_bus_spi.h"
|
||||
#endif
|
||||
#include "siw_touch_sys.h"
|
||||
|
||||
|
||||
int siw_touch_bus_tr_data_init(struct siw_ts *ts)
|
||||
{
|
||||
struct device *dev = ts->dev;
|
||||
struct siw_touch_pdata *pdata = ts->pdata;
|
||||
|
||||
ts->bus_tx_hdr_size = pdata_tx_hdr_size(pdata);
|
||||
ts->bus_rx_hdr_size = pdata_rx_hdr_size(pdata);
|
||||
ts->bus_tx_dummy_size = pdata_tx_dummy_size(pdata);
|
||||
ts->bus_rx_dummy_size = pdata_rx_dummy_size(pdata);
|
||||
|
||||
#ifndef SOMC_TOUCH_BRINGUP
|
||||
#if defined(__SIW_SPI_TYPE_1)
|
||||
if (touch_bus_type(ts) == BUS_IF_SPI) {
|
||||
if (ts->bus_rx_dummy_size == SPI_BUS_RX_DUMMY_SZ_128BIT) {
|
||||
ts->bus_rx_dummy_size |= (SPI_BUS_RX_DUMMY_FLAG_128BIT<<16);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
t_dev_dbg_base(dev, "bus_tx_hdr_size : %Xh\n", ts->bus_tx_hdr_size);
|
||||
t_dev_dbg_base(dev, "bus_rx_hdr_size : %Xh\n", ts->bus_rx_hdr_size);
|
||||
t_dev_dbg_base(dev, "bus_tx_dummy_size: %Xh\n", ts->bus_tx_dummy_size);
|
||||
t_dev_dbg_base(dev, "bus_rx_dummy_size: %Xh\n", ts->bus_rx_dummy_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void siw_touch_bus_tr_data_free(struct siw_ts *ts)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
#define TOUCH_PINCTRL_ACTIVE "touch_pin_active"
|
||||
#define TOUCH_PINCTRL_SLEEP "touch_pin_sleep"
|
||||
|
||||
#if defined(__SIW_SUPPORT_PINCTRL) //See siw_touch_cfg.h
|
||||
int siw_touch_bus_pin_get(struct siw_ts *ts)
|
||||
{
|
||||
struct device *dev = ts->dev;
|
||||
struct touch_pinctrl *pinctrl = &ts->pinctrl;
|
||||
struct pinctrl *pin_ctrl = NULL;
|
||||
struct pinctrl_state *pin_active = NULL;
|
||||
struct pinctrl_state *pin_suspend = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (!(touch_flags(ts) & TOUCH_USE_PINCTRL)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
t_dev_info(dev, "get pinctrl\n");
|
||||
|
||||
pin_ctrl = devm_pinctrl_get(dev);
|
||||
if (IS_ERR_OR_NULL(pin_ctrl)) {
|
||||
if (PTR_ERR(pin_ctrl) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
return ret;
|
||||
}
|
||||
|
||||
t_dev_info(dev, "pinctrl not used\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
pin_active = pinctrl_lookup_state(pin_ctrl,
|
||||
TOUCH_PINCTRL_ACTIVE);
|
||||
if (IS_ERR_OR_NULL(pin_active)) {
|
||||
t_dev_dbg_gpio(dev, "cannot get pinctrl active\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
pin_suspend = pinctrl_lookup_state(pin_ctrl,
|
||||
TOUCH_PINCTRL_SLEEP);
|
||||
if (IS_ERR_OR_NULL(pin_suspend)) {
|
||||
t_dev_dbg_gpio(dev, "cannot get pinctrl suspend\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = pinctrl_select_state(pin_ctrl, pin_active);
|
||||
if (ret) {
|
||||
t_dev_dbg_gpio(dev, "cannot set pinctrl active\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
t_dev_info(dev, "pinctrl set active\n");
|
||||
|
||||
pinctrl->ctrl = pin_ctrl;
|
||||
pinctrl->active = pin_active;
|
||||
pinctrl->suspend = pin_suspend;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int siw_touch_bus_pin_put(struct siw_ts *ts)
|
||||
{
|
||||
struct device *dev = ts->dev;
|
||||
struct touch_pinctrl *pinctrl = &ts->pinctrl;
|
||||
|
||||
if (!(touch_flags(ts) & TOUCH_USE_PINCTRL)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
t_dev_info(dev, "put pinctrl\n");
|
||||
|
||||
if (pinctrl->ctrl && !IS_ERR_OR_NULL(pinctrl->ctrl)) {
|
||||
devm_pinctrl_put(ts->pinctrl.ctrl);
|
||||
memset((void *)pinctrl, 0, sizeof(struct touch_pinctrl));
|
||||
}
|
||||
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
#else /* __SIW_SUPPORT_PINCTRL */
|
||||
int siw_touch_bus_pin_get(struct siw_ts *ts)
|
||||
{
|
||||
struct device *dev = ts->dev;
|
||||
|
||||
t_dev_info(dev, "get pinctrl, nop ...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int siw_touch_bus_pin_put(struct siw_ts *ts)
|
||||
{
|
||||
struct device *dev = ts->dev;
|
||||
|
||||
t_dev_info(dev, "put pinctrl, nop ...\n");
|
||||
return 0;
|
||||
}
|
||||
#endif /* __SIW_SUPPORT_PINCTRL */
|
||||
|
||||
void *siw_touch_bus_create_bus_drv(int bus_type)
|
||||
{
|
||||
struct siw_touch_bus_drv *bus_drv;
|
||||
|
||||
bus_drv = kzalloc(sizeof(*bus_drv), GFP_KERNEL);
|
||||
if (!bus_drv) {
|
||||
t_pr_err("faied to allocate bus_drv(%d)\n", bus_type);
|
||||
}
|
||||
return bus_drv;
|
||||
}
|
||||
|
||||
void siw_touch_bus_free_bus_drv(void *bus_drv)
|
||||
{
|
||||
kfree(bus_drv);
|
||||
}
|
||||
|
||||
void *siw_touch_bus_create_bus_pdata(int bus_type)
|
||||
{
|
||||
struct siw_touch_pdata *pdata;
|
||||
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
t_pr_err("faied to allocate pdata(%d)\n", bus_type);
|
||||
}
|
||||
return pdata;
|
||||
}
|
||||
|
||||
void siw_touch_bus_free_bus_pdata(void *pdata)
|
||||
{
|
||||
kfree(pdata);
|
||||
}
|
||||
|
||||
static void *__buffer_alloc(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t gfp,
|
||||
char *name)
|
||||
{
|
||||
void *buf = NULL;
|
||||
dma_addr_t _dma = 0;
|
||||
|
||||
if (siw_touch_sys_bus_use_dma(dev) && dma_handle) {
|
||||
gfp &= ~GFP_DMA;
|
||||
buf = dma_alloc_coherent(NULL, size, &_dma, gfp);
|
||||
if (!buf || !_dma) {
|
||||
t_dev_err(dev, "failed to allocate dma buffer %s, %p %zxh\n",
|
||||
name, buf, (size_t)_dma);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
t_dev_dbg_base(dev, "alloc %s: buf %p, phy %zxh, size %zxh\n",
|
||||
name, buf, (size_t)_dma, size);
|
||||
} else {
|
||||
buf = kmalloc(size, gfp);
|
||||
if (!buf) {
|
||||
t_dev_err(dev, "failed to allocate %s\n", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
t_dev_dbg_base(dev, "alloc %s: buf %p, size %zxh\n",
|
||||
name, buf, size);
|
||||
}
|
||||
|
||||
if (dma_handle)
|
||||
*dma_handle = _dma;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void __buffer_free(struct device *dev, size_t size,
|
||||
void *buf, dma_addr_t dma_handle,
|
||||
char *name)
|
||||
{
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
if (siw_touch_sys_bus_use_dma(dev) && dma_handle) {
|
||||
t_dev_dbg_base(dev, "free %s: buf %p, phy %zxh, size %zxh\n",
|
||||
name, buf, (size_t)dma_handle, size);
|
||||
dma_free_coherent(NULL, size, buf, dma_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
t_dev_dbg_base(dev, "free %s: buf %p, size %zxh\n",
|
||||
name, buf, size);
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
static void siw_touch_buf_free(struct siw_ts *ts, int _tx)
|
||||
{
|
||||
struct device *dev = ts->dev;
|
||||
struct siw_touch_buf *t_buf = NULL;
|
||||
char *title;
|
||||
char name[16];
|
||||
int i;
|
||||
|
||||
t_buf = (_tx) ? ts->tx_buf : ts->rx_buf;
|
||||
title = (_tx) ? "tx_buf" : "rx_buf";
|
||||
|
||||
for (i = 0; i < SIW_TOUCH_MAX_BUF_IDX; i++) {
|
||||
sprintf(name, "%s%d", title, i);
|
||||
__buffer_free(dev, t_buf->size,
|
||||
t_buf->buf, t_buf->dma, name);
|
||||
t_buf++;
|
||||
}
|
||||
|
||||
memset(t_buf, 0, sizeof(*t_buf) * SIW_TOUCH_MAX_BUF_IDX);
|
||||
}
|
||||
|
||||
static int siw_touch_buf_alloc(struct siw_ts *ts, int _tx)
|
||||
{
|
||||
struct device *dev = ts->dev;
|
||||
struct siw_touch_buf *t_buf = NULL;
|
||||
int buf_size = touch_get_act_buf_size(ts);
|
||||
char *title;
|
||||
char name[16];
|
||||
u8 *buf;
|
||||
dma_addr_t dma;
|
||||
int i;
|
||||
|
||||
t_buf = (_tx) ? ts->tx_buf : ts->rx_buf;
|
||||
title = (_tx) ? "tx_buf" : "rx_buf";
|
||||
|
||||
memset(t_buf, 0, sizeof(*t_buf) * SIW_TOUCH_MAX_BUF_IDX);
|
||||
|
||||
for (i = 0; i < SIW_TOUCH_MAX_BUF_IDX; i++) {
|
||||
sprintf(name, "%s%d", title, i);
|
||||
buf = __buffer_alloc(dev, buf_size, &dma,
|
||||
GFP_KERNEL | GFP_DMA, name);
|
||||
if (!buf) {
|
||||
goto out;
|
||||
}
|
||||
t_buf->buf = buf;
|
||||
t_buf->dma = dma;
|
||||
t_buf->size = buf_size;
|
||||
t_buf++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
siw_touch_buf_free(ts, _tx);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int siw_touch_bus_alloc_buffer(struct siw_ts *ts)
|
||||
{
|
||||
struct device *dev = ts->dev;
|
||||
struct touch_xfer_msg *xfer = NULL;
|
||||
int buf_size = touch_buf_size(ts);
|
||||
int ret = 0;
|
||||
|
||||
if (!buf_size) {
|
||||
buf_size = SIW_TOUCH_MAX_BUF_SIZE;
|
||||
}
|
||||
touch_set_act_buf_size(ts, buf_size);
|
||||
|
||||
t_dev_dbg_base(dev, "allocate touch bus buffer\n");
|
||||
|
||||
ts->tx_buf_idx = 0;
|
||||
ts->rx_buf_idx = 0;
|
||||
|
||||
ret = siw_touch_buf_alloc(ts, 1);
|
||||
if (ret < 0) {
|
||||
goto out_tx_buf;
|
||||
}
|
||||
|
||||
ret = siw_touch_buf_alloc(ts, 0);
|
||||
if (ret < 0) {
|
||||
goto out_rx_buf;
|
||||
}
|
||||
|
||||
xfer = __buffer_alloc(dev, sizeof(struct touch_xfer_msg),
|
||||
NULL, GFP_KERNEL, "xfer");
|
||||
if (!xfer) {
|
||||
ret = -ENOMEM;
|
||||
goto out_xfer;
|
||||
}
|
||||
ts->xfer = xfer;
|
||||
|
||||
return 0;
|
||||
|
||||
out_xfer:
|
||||
siw_touch_buf_alloc(ts, 0);
|
||||
|
||||
out_rx_buf:
|
||||
siw_touch_buf_alloc(ts, 1);
|
||||
|
||||
out_tx_buf:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int siw_touch_bus_free_buffer(struct siw_ts *ts)
|
||||
{
|
||||
struct device *dev = ts->dev;
|
||||
|
||||
t_dev_dbg_base(dev, "release touch bus buffer\n");
|
||||
|
||||
if (ts->xfer) {
|
||||
__buffer_free(dev, sizeof(struct touch_xfer_msg),
|
||||
ts->xfer, 0, "xfer");
|
||||
ts->xfer = NULL;
|
||||
}
|
||||
|
||||
siw_touch_buf_free(ts, 0);
|
||||
siw_touch_buf_free(ts, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int siw_touch_bus_init(struct device *dev)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
|
||||
if (!ts->bus_init) {
|
||||
t_dev_err(dev, "no bus_init %s(%d)\n",
|
||||
dev_name(dev), touch_bus_type(ts));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ts->bus_init(dev);
|
||||
}
|
||||
|
||||
int siw_touch_bus_read(struct device *dev,
|
||||
struct touch_bus_msg *msg)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
|
||||
if (!ts->bus_read) {
|
||||
t_dev_err(dev, "no bus_read %s(%d)\n",
|
||||
dev_name(dev), touch_bus_type(ts));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ts->bus_read(dev, msg);
|
||||
}
|
||||
|
||||
int siw_touch_bus_write(struct device *dev, struct touch_bus_msg *msg)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
|
||||
if (!ts->bus_write) {
|
||||
t_dev_err(dev, "no bus_write for %s(%d)\n",
|
||||
dev_name(dev), touch_bus_type(ts));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ts->bus_write(dev, msg);
|
||||
}
|
||||
|
||||
int siw_touch_bus_xfer(struct device *dev, struct touch_xfer_msg *xfer)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
|
||||
if (!ts->bus_xfer) {
|
||||
t_dev_err(dev, "No bus_xfer for %s(%d)\n",
|
||||
dev_name(dev), touch_bus_type(ts));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ts->bus_xfer(dev, xfer);
|
||||
}
|
||||
|
||||
enum {
|
||||
SIW_BUS_ERR_PRT_BOUNDARY = 16,
|
||||
SIW_BUS_ERR_PRT_BUF_SZ = 128,
|
||||
};
|
||||
void siw_touch_bus_err_dump_data(struct device *dev,
|
||||
u8 *buf, int len,
|
||||
int idx, char *name)
|
||||
{
|
||||
char __prt_buf[SIW_BUS_ERR_PRT_BUF_SZ + 1] = {0, };
|
||||
char *prt_buf;
|
||||
int prt_len;
|
||||
int prt_pos = 0;
|
||||
int prt_idx;
|
||||
int cnt, total;
|
||||
int i, k;
|
||||
|
||||
if (!buf || !len)
|
||||
return;
|
||||
|
||||
cnt = (len + SIW_BUS_ERR_PRT_BOUNDARY - 1)/SIW_BUS_ERR_PRT_BOUNDARY;
|
||||
total = cnt;
|
||||
|
||||
prt_idx = 0;
|
||||
for (i = 0; i < cnt; i++) {
|
||||
prt_len = min(len, SIW_BUS_ERR_PRT_BOUNDARY);
|
||||
prt_buf = __prt_buf;
|
||||
prt_pos = 0;
|
||||
for (k = 0; k < prt_len; k++) {
|
||||
prt_pos += snprintf(prt_buf + prt_pos,
|
||||
SIW_BUS_ERR_PRT_BUF_SZ - prt_pos,
|
||||
"%02X ",
|
||||
buf[prt_idx+k]);
|
||||
if (prt_pos >= SIW_BUS_ERR_PRT_BUF_SZ) {
|
||||
cnt = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
t_dev_err(dev,
|
||||
" - %s[%d] buf[%3d~%3d] %s\n",
|
||||
name, idx,
|
||||
prt_idx,
|
||||
prt_idx + prt_len - 1,
|
||||
__prt_buf);
|
||||
|
||||
len -= prt_len;
|
||||
prt_idx += prt_len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const struct siw_op_dbg siw_bus_init_ops[2][2] = {
|
||||
[BUS_IF_I2C] = {
|
||||
_SIW_OP_DBG(DRIVER_FREE, siw_touch_i2c_del_driver, NULL, 0),
|
||||
_SIW_OP_DBG(DRIVER_INIT, siw_touch_i2c_add_driver, NULL, 0),
|
||||
},
|
||||
};
|
||||
|
||||
#define SIW_BUS_MAX (sizeof(siw_bus_init_ops) / sizeof(siw_bus_init_ops[0]))
|
||||
|
||||
static int __siw_touch_bus_add_chk(struct siw_touch_chip_data *chip_data)
|
||||
{
|
||||
int bus_type;
|
||||
|
||||
if (chip_data == NULL) {
|
||||
t_pr_err("NULL touch chip data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (chip_data->pdata == NULL) {
|
||||
t_pr_err("NULL touch pdata\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bus_type = pdata_bus_type((struct siw_touch_pdata *)chip_data->pdata);
|
||||
if (bus_type >= SIW_BUS_MAX) {
|
||||
t_pr_err("Unknown touch interface : %d\n", bus_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __siw_touch_bus_add_op(
|
||||
struct siw_touch_chip_data *chip_data,
|
||||
void *op_func)
|
||||
{
|
||||
struct siw_op_dbg *op = op_func;
|
||||
int ret = 0;
|
||||
|
||||
ret = __siw_touch_bus_add_chk(chip_data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
// t_pr_info("%s\n", op->name);
|
||||
ret = __siw_touch_op_dbg(op, chip_data);
|
||||
if (ret) {
|
||||
t_pr_err("%s failed, %d\n", op->name, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __siw_touch_bus_add_driver(struct siw_touch_chip_data *chip_data, int on_off)
|
||||
{
|
||||
return __siw_touch_bus_add_op(chip_data,
|
||||
(void *)&siw_bus_init_ops[chip_data->pdata->bus_info.bus_type][on_off]);
|
||||
}
|
||||
|
||||
int siw_touch_bus_add_driver(struct siw_touch_chip_data *chip_data)
|
||||
{
|
||||
return __siw_touch_bus_add_driver(chip_data, DRIVER_INIT);
|
||||
}
|
||||
|
||||
int siw_touch_bus_del_driver(struct siw_touch_chip_data *chip_data)
|
||||
{
|
||||
return __siw_touch_bus_add_driver(chip_data, DRIVER_FREE);
|
||||
}
|
||||
|
||||
#if defined(__SIW_CONFIG_USER_FB)
|
||||
#define siw_touch_bus_suspend_call(_dev) t_dev_info(_dev, "bus_suspend_call\n")
|
||||
#define siw_touch_bus_resume_call(_dev) t_dev_info(_dev, "bus_resume_call\n")
|
||||
#else /* __SIW_CONFIG_USER_FB */
|
||||
#define siw_touch_bus_suspend_call(_dev) siw_touch_suspend_call(_dev)
|
||||
#define siw_touch_bus_resume_call(_dev) siw_touch_resume_call(_dev)
|
||||
#endif /* __SIW_CONFIG_USER_FB */
|
||||
|
||||
static int siw_touch_bus_do_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
|
||||
siw_touch_bus_suspend_call(dev);
|
||||
|
||||
atomic_set(&ts->state.pm, DEV_PM_SUSPEND);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int siw_touch_bus_do_pm_resume(struct device *dev)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
int resume_irq = 0;
|
||||
|
||||
if (atomic_read(&ts->state.pm) == DEV_PM_SUSPEND_IRQ) {
|
||||
resume_irq = 1;
|
||||
}
|
||||
|
||||
siw_touch_bus_resume_call(dev);
|
||||
|
||||
atomic_set(&ts->state.pm, DEV_PM_RESUME);
|
||||
|
||||
if (resume_irq) {
|
||||
siw_touch_resume_irq(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int siw_touch_bus_pm_suspend(struct device *dev, int freeze)
|
||||
{
|
||||
// struct siw_ts *ts = to_touch_core(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (freeze) {
|
||||
siw_touch_mon_pause(dev);
|
||||
}
|
||||
|
||||
ret = siw_touch_bus_do_pm_suspend(dev);
|
||||
|
||||
if (freeze) {
|
||||
// siw_touch_notify(ts, NOTIFY_TOUCH_RESET, NULL);
|
||||
}
|
||||
|
||||
|
||||
t_dev_info(dev, "touch bus pm %s done\n",
|
||||
(freeze) ? "freeze" : "suspend");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int siw_touch_bus_pm_resume(struct device *dev, int thaw)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = siw_touch_bus_do_pm_resume(dev);
|
||||
|
||||
if (thaw) {
|
||||
siw_touch_mon_resume(dev);
|
||||
}
|
||||
|
||||
t_dev_info(dev, "touch bus pm %s done\n",
|
||||
(thaw) ? "thaw" : "resume");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
160
drivers/input/touchscreen/siw/siw_touch_bus.h
Normal file
160
drivers/input/touchscreen/siw/siw_touch_bus.h
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* SiW touch bus core driver
|
||||
*
|
||||
* Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr
|
||||
* Copyright (C) 2018 Sony Mobile Communications Inc.
|
||||
* Author: Hyunho Kim <kimhh@siliconworks.co.kr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __SIW_TOUCH_BUS_H
|
||||
#define __SIW_TOUCH_BUS_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
|
||||
enum {
|
||||
I2C_BUS_TX_HDR_SZ = 2,
|
||||
I2C_BUS_RX_HDR_SZ = 0,
|
||||
I2C_BUS_TX_DUMMY_SZ = 0,
|
||||
I2C_BUS_RX_DUMMY_SZ = 0,
|
||||
};
|
||||
|
||||
enum {
|
||||
SPI_BUS_TX_HDR_SZ = 2,
|
||||
SPI_BUS_RX_HDR_SZ = 4,
|
||||
SPI_BUS_TX_DUMMY_SZ = 0,
|
||||
SPI_BUS_RX_DUMMY_SZ = 2,
|
||||
};
|
||||
|
||||
/* __SIW_SPI_TYPE_1 */
|
||||
enum {
|
||||
SPI_BUS_RX_HDR_SZ_32BIT = (4+2),
|
||||
SPI_BUS_RX_DUMMY_SZ_32BIT = 4,
|
||||
/* */
|
||||
SPI_BUS_RX_HDR_SZ_128BIT = (16+2),
|
||||
SPI_BUS_RX_DUMMY_SZ_128BIT = 16,
|
||||
};
|
||||
|
||||
enum {
|
||||
SPI_BUS_RX_DUMMY_FLAG_128BIT = 0x10,
|
||||
};
|
||||
|
||||
#define __CLOCK_1KHZ 1000
|
||||
#define __CLOCK_100KHZ (100 * __CLOCK_1KHZ)
|
||||
#define __CLOCK_1MHZ (__CLOCK_1KHZ * __CLOCK_1KHZ)
|
||||
|
||||
#define SPI_MAX_FREQ (20 * __CLOCK_1MHZ)
|
||||
#define SPI_MIN_FREQ (1 * __CLOCK_1MHZ)
|
||||
|
||||
static inline int spi_freq_out_of_range(u32 freq)
|
||||
{
|
||||
return ((freq > SPI_MAX_FREQ) || (freq < SPI_MIN_FREQ));
|
||||
}
|
||||
|
||||
static inline u32 freq_to_mhz_unit(u32 freq)
|
||||
{
|
||||
return (freq / __CLOCK_1MHZ);
|
||||
}
|
||||
|
||||
static inline u32 freq_to_khz_unit(u32 freq)
|
||||
{
|
||||
return (freq / __CLOCK_1KHZ);
|
||||
}
|
||||
|
||||
static inline u32 freq_to_khz_top(u32 freq)
|
||||
{
|
||||
return ((freq % __CLOCK_1MHZ) / __CLOCK_100KHZ);
|
||||
}
|
||||
|
||||
/* __SIW_SPI_TYPE_1 */
|
||||
|
||||
struct touch_bus_msg {
|
||||
u8 *tx_buf;
|
||||
int tx_size;
|
||||
u8 *rx_buf;
|
||||
int rx_size;
|
||||
dma_addr_t tx_dma;
|
||||
dma_addr_t rx_dma;
|
||||
u8 bits_per_word;
|
||||
int priv;
|
||||
};
|
||||
|
||||
struct touch_xfer_data_t {
|
||||
u16 addr;
|
||||
u16 size;
|
||||
u8 *buf;
|
||||
u8 data[SIW_TOUCH_MAX_BUF_SIZE];
|
||||
};
|
||||
|
||||
struct touch_xfer_data {
|
||||
struct touch_xfer_data_t tx;
|
||||
struct touch_xfer_data_t rx;
|
||||
};
|
||||
|
||||
struct touch_xfer_msg {
|
||||
struct touch_xfer_data data[SIW_TOUCH_MAX_XFER_COUNT];
|
||||
u8 bits_per_word;
|
||||
u8 msg_count;
|
||||
};
|
||||
|
||||
struct siw_touch_bus_drv {
|
||||
union {
|
||||
struct i2c_driver i2c_drv;
|
||||
struct spi_driver spi_drv;
|
||||
} bus;
|
||||
struct siw_touch_pdata *pdata;
|
||||
};
|
||||
|
||||
|
||||
#define t_dev_info_bus_parent(_dev) \
|
||||
do { \
|
||||
t_dev_info(_dev, "dev bus probe : %s/%s/%s\n", \
|
||||
dev_name(_dev->parent->parent), \
|
||||
dev_name(_dev->parent), \
|
||||
dev_name(_dev)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
extern int siw_touch_bus_tr_data_init(struct siw_ts *ts);
|
||||
extern void siw_touch_bus_tr_data_free(struct siw_ts *ts);
|
||||
|
||||
extern int siw_touch_bus_pin_get(struct siw_ts *ts);
|
||||
extern int siw_touch_bus_pin_put(struct siw_ts *ts);
|
||||
|
||||
extern void *siw_touch_bus_create_bus_drv(int bus_type);
|
||||
extern void siw_touch_bus_free_bus_drv(void *bus_drv);
|
||||
|
||||
extern void *siw_touch_bus_create_bus_pdata(int bus_type);
|
||||
extern void siw_touch_bus_free_bus_pdata(void *pdata);
|
||||
|
||||
extern int siw_touch_bus_alloc_buffer(struct siw_ts *ts);
|
||||
extern int siw_touch_bus_free_buffer(struct siw_ts *ts);
|
||||
|
||||
extern int siw_touch_bus_init(struct device *dev);
|
||||
extern int siw_touch_bus_read(struct device *dev, struct touch_bus_msg *msg);
|
||||
extern int siw_touch_bus_write(struct device *dev, struct touch_bus_msg *msg);
|
||||
extern int siw_touch_bus_xfer(struct device *dev, struct touch_xfer_msg *xfer);
|
||||
|
||||
extern void siw_touch_bus_err_dump_data(struct device *dev,
|
||||
u8 *buf, int len,
|
||||
int idx, char *name);
|
||||
|
||||
extern int siw_touch_bus_add_driver(struct siw_touch_chip_data *chip_data);
|
||||
extern int siw_touch_bus_del_driver(struct siw_touch_chip_data *chip_data);
|
||||
|
||||
extern int siw_touch_bus_pm_suspend(struct device *dev, int freeze);
|
||||
extern int siw_touch_bus_pm_resume(struct device *dev, int thaw);
|
||||
|
||||
|
||||
#endif /* __SIW_TOUCH_BUS_H */
|
||||
|
||||
482
drivers/input/touchscreen/siw/siw_touch_bus_i2c.c
Normal file
482
drivers/input/touchscreen/siw/siw_touch_bus_i2c.c
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
/*
|
||||
* siw_touch_bus_i2c.c - SiW touch bus i2c driver
|
||||
*
|
||||
* Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr
|
||||
* Copyright (C) 2018 Sony Mobile Communications Inc.
|
||||
* Author: Hyunho Kim <kimhh@siliconworks.co.kr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/async.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/memory.h>
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#include "siw_touch.h"
|
||||
#include "siw_touch_bus.h"
|
||||
#include "siw_touch_irq.h"
|
||||
|
||||
#if defined(CONFIG_I2C)
|
||||
|
||||
#define siwmon_submit_bus_i2c_read(_client, _data, _ret) \
|
||||
siwmon_submit_bus(&_client->dev, "I2C_R", _data, _ret)
|
||||
|
||||
#define siwmon_submit_bus_i2c_write(_client, _data, _ret) \
|
||||
siwmon_submit_bus(&_client->dev, "I2C_W", _data, _ret)
|
||||
|
||||
|
||||
static void siw_touch_i2c_err_dump(struct i2c_client *client,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct i2c_msg *msg = msgs;
|
||||
int i;
|
||||
|
||||
t_dev_err(&client->dev, "i2c transfer err : ");
|
||||
for (i = 0; i < num; i++) {
|
||||
t_dev_err(&client->dev,
|
||||
" - msgs[%d] : client 0x%04X, flags 0x%04X, len %d\n",
|
||||
i, msg->addr, msg->flags, msg->len);
|
||||
siw_touch_bus_err_dump_data(&client->dev,
|
||||
msg->buf, msg->len, i, "msgs");
|
||||
msg++;
|
||||
}
|
||||
}
|
||||
|
||||
static int siw_touch_i2c_init(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int siw_touch_do_i2c_read(struct i2c_client *client,
|
||||
struct touch_bus_msg *msg)
|
||||
{
|
||||
// struct siw_ts *ts = (struct siw_ts *)i2c_get_clientdata(client);
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = msg->tx_size,
|
||||
.buf = msg->tx_buf,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = msg->rx_size,
|
||||
.buf = msg->rx_buf,
|
||||
},
|
||||
};
|
||||
int ret = 0;
|
||||
|
||||
if ((msg->rx_size > SIW_TOUCH_MAX_BUF_SIZE) ||
|
||||
(msg->tx_size > SIW_TOUCH_MAX_BUF_SIZE)) {
|
||||
t_dev_err(&client->dev, "i2c rd: buffer overflow - rx %Xh, tx %Xh\n",
|
||||
msg->rx_size, msg->tx_size);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bus control can need to be modifyed up to main chipset sepc.
|
||||
*/
|
||||
|
||||
ret = i2c_transfer(client->adapter, msgs, 2);
|
||||
siwmon_submit_bus_i2c_read(client, msg, ret);
|
||||
if (ret < 0)
|
||||
siw_touch_i2c_err_dump(client, msgs, 2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int siw_touch_i2c_read(struct device *dev, void *msg)
|
||||
{
|
||||
return siw_touch_do_i2c_read(to_i2c_client(dev), (struct touch_bus_msg *)msg);
|
||||
}
|
||||
|
||||
int siw_touch_do_i2c_write(struct i2c_client *client,
|
||||
struct touch_bus_msg *msg)
|
||||
{
|
||||
// struct siw_ts *ts = (struct siw_ts *)i2c_get_clientdata(client);
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = msg->tx_size,
|
||||
.buf = msg->tx_buf,
|
||||
},
|
||||
};
|
||||
int ret = 0;
|
||||
|
||||
if (msg->tx_size > SIW_TOUCH_MAX_BUF_SIZE) {
|
||||
t_dev_err(&client->dev, "i2c wr: buffer overflow - tx %Xh\n",
|
||||
msg->tx_size);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bus control can need to be modifyed up to main chipset sepc.
|
||||
*/
|
||||
|
||||
ret = i2c_transfer(client->adapter, msgs, 1);
|
||||
siwmon_submit_bus_i2c_write(client, msg, ret);
|
||||
if (ret < 0)
|
||||
siw_touch_i2c_err_dump(client, msgs, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int siw_touch_i2c_write(struct device *dev, void *msg)
|
||||
{
|
||||
return siw_touch_do_i2c_write(to_i2c_client(dev), (struct touch_bus_msg *)msg);
|
||||
}
|
||||
|
||||
static int siw_touch_i2c_do_xfer(struct i2c_client *client, struct touch_xfer_msg *xfer)
|
||||
{
|
||||
// struct siw_ts *ts = (struct siw_ts *)i2c_get_clientdata(client);
|
||||
|
||||
t_dev_info(&client->dev, "I2C xfer : not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int siw_touch_i2c_xfer(struct device *dev, void *xfer)
|
||||
{
|
||||
return siw_touch_i2c_do_xfer(to_i2c_client(dev), (struct touch_xfer_msg *)xfer);
|
||||
}
|
||||
|
||||
static struct siw_ts *siw_touch_i2c_alloc(
|
||||
struct i2c_client *i2c,
|
||||
struct siw_touch_bus_drv *bus_drv)
|
||||
{
|
||||
struct device *dev = &i2c->dev;
|
||||
struct siw_ts *ts = NULL;
|
||||
struct siw_touch_pdata *pdata = NULL;
|
||||
int ret;
|
||||
|
||||
ts = touch_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
|
||||
if (ts == NULL) {
|
||||
t_dev_err(dev,
|
||||
"i2c alloc: failed to allocate memory for touch data\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ts->bus_dev = i2c;
|
||||
ts->addr = (size_t)i2c->addr;
|
||||
ts->dev = dev;
|
||||
ts->irq = i2c->irq;
|
||||
pdata = bus_drv->pdata;
|
||||
if (!pdata) {
|
||||
t_dev_err(dev, "i2c alloc: NULL pdata\n");
|
||||
goto out_pdata;
|
||||
}
|
||||
|
||||
ret = siw_setup_params(ts, pdata);
|
||||
if (ret < 0) {
|
||||
goto out_name;
|
||||
}
|
||||
|
||||
ts->pdata = pdata;
|
||||
|
||||
siw_setup_operations(ts, bus_drv->pdata->ops);
|
||||
|
||||
ts->bus_init = siw_touch_i2c_init;
|
||||
ts->bus_read = siw_touch_i2c_read;
|
||||
ts->bus_write = siw_touch_i2c_write;
|
||||
ts->bus_xfer = siw_touch_i2c_xfer;
|
||||
|
||||
ret = siw_touch_bus_tr_data_init(ts);
|
||||
if (ret < 0) {
|
||||
goto out_tr;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(i2c, ts);
|
||||
|
||||
return ts;
|
||||
|
||||
out_tr:
|
||||
|
||||
out_name:
|
||||
|
||||
out_pdata:
|
||||
touch_kfree(dev, ts);
|
||||
|
||||
out:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void siw_touch_i2c_free(struct i2c_client *i2c)
|
||||
{
|
||||
struct device *dev = &i2c->dev;
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
|
||||
i2c_set_clientdata(i2c, NULL);
|
||||
|
||||
siw_touch_bus_tr_data_free(ts);
|
||||
|
||||
touch_kfree(dev, ts);
|
||||
}
|
||||
|
||||
static int siw_touch_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct siw_touch_bus_drv *bus_drv = NULL;
|
||||
struct siw_ts *ts = NULL;
|
||||
struct device *dev = &i2c->dev;
|
||||
int ret = 0;
|
||||
|
||||
t_dev_info_bus_parent(dev);
|
||||
|
||||
bus_drv = container_of(to_i2c_driver(dev->driver),
|
||||
struct siw_touch_bus_drv, bus.i2c_drv);
|
||||
if (bus_drv == NULL) {
|
||||
t_dev_err(dev, "NULL bus_drv info\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
|
||||
t_dev_err(dev, "i2c func not Supported\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
// t_dev_dbg(DBG_BUS, dev, "i2c->dev.platform_data = %p\n", dev->platform_data);
|
||||
|
||||
ts = siw_touch_i2c_alloc(i2c, bus_drv);
|
||||
if (ts == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = siw_touch_probe(ts);
|
||||
if (ret)
|
||||
goto out_plat;
|
||||
|
||||
return 0;
|
||||
|
||||
out_plat:
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int siw_touch_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(&i2c->dev);
|
||||
|
||||
siw_touch_remove(ts);
|
||||
|
||||
siw_touch_i2c_free(i2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PM_SLEEP)
|
||||
static int siw_touch_i2c_pm_suspend(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = siw_touch_bus_pm_suspend(dev, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int siw_touch_i2c_pm_resume(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = siw_touch_bus_pm_resume(dev, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(__SIW_CONFIG_FASTBOOT)
|
||||
static int siw_touch_i2c_pm_freeze(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = siw_touch_bus_pm_suspend(dev, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int siw_touch_i2c_pm_thaw(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = siw_touch_bus_pm_resume(dev, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops siw_touch_i2c_pm_ops = {
|
||||
.suspend = siw_touch_i2c_pm_suspend,
|
||||
.resume = siw_touch_i2c_pm_resume,
|
||||
#if defined(__SIW_CONFIG_FASTBOOT)
|
||||
.freeze = siw_touch_i2c_pm_freeze,
|
||||
.thaw = siw_touch_i2c_pm_thaw,
|
||||
.poweroff = siw_touch_i2c_pm_freeze,
|
||||
.restore = siw_touch_i2c_pm_thaw,
|
||||
#endif
|
||||
};
|
||||
#define DEV_PM_OPS (&siw_touch_i2c_pm_ops)
|
||||
#else /* CONFIG_PM_SLEEP */
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static struct i2c_device_id siw_touch_i2c_id[] = {
|
||||
{ SIW_TOUCH_NAME, 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
int siw_touch_i2c_add_driver(void *data)
|
||||
{
|
||||
struct siw_touch_chip_data *chip_data = data;
|
||||
struct siw_touch_bus_drv *bus_drv = NULL;
|
||||
struct siw_touch_pdata *pdata = NULL;
|
||||
struct i2c_driver *i2c_drv = NULL;
|
||||
char *drv_name = NULL;
|
||||
int bus_type;
|
||||
int ret = 0;
|
||||
|
||||
if (chip_data == NULL) {
|
||||
t_pr_err("NULL chip data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (chip_data->pdata == NULL) {
|
||||
t_pr_err("NULL chip pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bus_type = pdata_bus_type((struct siw_touch_pdata *)chip_data->pdata);
|
||||
|
||||
bus_drv = siw_touch_bus_create_bus_drv(bus_type);
|
||||
if (!bus_drv) {
|
||||
ret = -ENOMEM;
|
||||
goto out_drv;
|
||||
}
|
||||
|
||||
pdata = siw_touch_bus_create_bus_pdata(bus_type);
|
||||
if (!pdata) {
|
||||
ret = -ENOMEM;
|
||||
goto out_pdata;
|
||||
}
|
||||
|
||||
memcpy(pdata, chip_data->pdata, sizeof(*pdata));
|
||||
|
||||
drv_name = pdata_drv_name(pdata);
|
||||
|
||||
bus_drv->pdata = pdata;
|
||||
|
||||
i2c_drv = &bus_drv->bus.i2c_drv;
|
||||
i2c_drv->driver.name = drv_name;
|
||||
i2c_drv->driver.owner = pdata->owner;
|
||||
#if defined(__SIW_CONFIG_OF)
|
||||
i2c_drv->driver.of_match_table = pdata->of_match_table;
|
||||
#endif
|
||||
i2c_drv->driver.pm = DEV_PM_OPS;
|
||||
|
||||
i2c_drv->probe = siw_touch_i2c_probe;
|
||||
i2c_drv->remove = siw_touch_i2c_remove;
|
||||
i2c_drv->id_table = siw_touch_i2c_id;
|
||||
if (drv_name) {
|
||||
memset((void *)siw_touch_i2c_id[0].name, 0, I2C_NAME_SIZE);
|
||||
snprintf((char *)siw_touch_i2c_id[0].name,
|
||||
I2C_NAME_SIZE,
|
||||
"%s",
|
||||
drv_name);
|
||||
}
|
||||
|
||||
ret = i2c_add_driver(i2c_drv);
|
||||
if (ret) {
|
||||
t_pr_err("i2c_register_driver[%s] failed, %d\n",
|
||||
drv_name, ret);
|
||||
goto out_client;
|
||||
}
|
||||
|
||||
chip_data->bus_drv = bus_drv;
|
||||
|
||||
return 0;
|
||||
|
||||
out_client:
|
||||
siw_touch_bus_free_bus_pdata(pdata);
|
||||
|
||||
out_pdata:
|
||||
siw_touch_bus_free_bus_drv(bus_drv);
|
||||
|
||||
out_drv:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int siw_touch_i2c_del_driver(void *data)
|
||||
{
|
||||
struct siw_touch_chip_data *chip_data = data;
|
||||
struct siw_touch_bus_drv *bus_drv = NULL;
|
||||
// struct siw_touch_pdata *pdata = NULL;
|
||||
|
||||
if (chip_data == NULL) {
|
||||
t_pr_err("NULL touch chip data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bus_drv = (void *)chip_data->bus_drv;
|
||||
if (bus_drv) {
|
||||
i2c_del_driver(&((struct siw_touch_bus_drv *)bus_drv)->bus.i2c_drv);
|
||||
|
||||
if (bus_drv->pdata) {
|
||||
siw_touch_bus_free_bus_pdata(bus_drv->pdata);
|
||||
}
|
||||
|
||||
siw_touch_bus_free_bus_drv(bus_drv);
|
||||
chip_data->bus_drv = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else /* CONFIG_I2C */
|
||||
int siw_touch_i2c_add_driver(void *data)
|
||||
{
|
||||
// struct siw_touch_pdata *pdata = data;
|
||||
|
||||
t_pr_err("I2C : not supported in this system\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int siw_touch_i2c_del_driver(void *data)
|
||||
{
|
||||
// struct siw_touch_pdata *pdata = data;
|
||||
|
||||
t_pr_err("I2C : not supported in this system\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_I2C */
|
||||
|
||||
|
||||
|
||||
|
||||
20
drivers/input/touchscreen/siw/siw_touch_bus_i2c.h
Normal file
20
drivers/input/touchscreen/siw/siw_touch_bus_i2c.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* SiW touch bus i2c driver
|
||||
*
|
||||
* Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr
|
||||
* Copyright (C) 2018 Sony Mobile Communications Inc.
|
||||
* Author: Hyunho Kim <kimhh@siliconworks.co.kr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __SIW_TOUCH_BUS_I2C_H
|
||||
#define __SIW_TOUCH_BUS_I2C_H
|
||||
|
||||
extern int siw_touch_i2c_add_driver(void *data);
|
||||
extern int siw_touch_i2c_del_driver(void *data);
|
||||
|
||||
#endif /* __SIW_TOUCH_BUS_I2C_H */
|
||||
|
||||
188
drivers/input/touchscreen/siw/siw_touch_cfg.h
Normal file
188
drivers/input/touchscreen/siw/siw_touch_cfg.h
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* SiW touch core driver
|
||||
*
|
||||
* Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr
|
||||
* Copyright (C) 2018 Sony Mobile Communications Inc.
|
||||
* Author: Hyunho Kim <kimhh@siliconworks.co.kr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
#ifndef __SIW_TOUCH_CFG_H
|
||||
#define __SIW_TOUCH_CFG_H
|
||||
|
||||
#if defined(CONFIG_PINCTRL)
|
||||
#define __SIW_SUPPORT_PINCTRL
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_REGULATOR)
|
||||
#define __SIW_SUPPORT_PWRCTRL
|
||||
#endif
|
||||
|
||||
//#define __SIW_SUPPORT_MISC
|
||||
|
||||
//#define __SIW_SUPPORT_ASC
|
||||
|
||||
#if defined(CONFIG_NET)
|
||||
//#define __SIW_SUPPORT_ABT
|
||||
#endif
|
||||
|
||||
#define __SIW_SUPPORT_PRD
|
||||
|
||||
#if defined(CONFIG_TOUCHSCREEN_SIW_LG4895) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_LG4946) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_SW49406) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_SW49407)
|
||||
#define __SIW_SUPPORT_WATCH
|
||||
|
||||
#if defined(CONFIG_TOUCHSCREEN_SIW_OPT_SINGLE_SCR)
|
||||
#undef __SIW_SUPPORT_WATCH
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define __SIW_SUPPORT_XFER
|
||||
|
||||
//#define __SIW_SUPPORT_DEBUG_OPTION
|
||||
|
||||
#if defined(CONFIG_ANDROID)
|
||||
//#define __SIW_SUPPORT_WAKE_LOCK
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TOUCHSCREEN_SIW_LG4894) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_LG4895) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_LG4946) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_LG4951) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_SW49105) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_SW49106) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_SW49406) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_SW49407) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_SW49408) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_SW49501)
|
||||
#define __SIW_SUPPORT_UEVENT
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TOUCHSCREEN_SIW_SW49105) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_SW49407) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_SW49408)
|
||||
#define __SIW_I2C_TYPE_1
|
||||
#define __SIW_SPI_TYPE_1
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TOUCHSCREEN_SIW_SW49407) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_SW49408) || \
|
||||
defined(CONFIG_TOUCHSCREEN_SIW_SW49501)
|
||||
#define __SIW_FW_TYPE_1
|
||||
#endif
|
||||
|
||||
//#define __SIW_SUPPORT_PM_QOS
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
#define __SIW_CONFIG_OF
|
||||
#endif
|
||||
|
||||
#ifndef SOMC_TOUCH_BRINGUP
|
||||
#if defined(CONFIG_HAS_EARLYSUSPEND)
|
||||
//#define __SIW_CONFIG_EARLYSUSPEND
|
||||
#elif defined(CONFIG_FB)
|
||||
#define __SIW_CONFIG_FB
|
||||
#endif
|
||||
#else
|
||||
#define __SIW_CONFIG_USER_FB
|
||||
#define __SIW_CONFIG_SYSTEM_PM
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TOUCHSCREEN_SIW_SW1828)
|
||||
#ifdef __SIW_SUPPORT_ABT
|
||||
#undef __SIW_SUPPORT_ABT
|
||||
#endif
|
||||
|
||||
#ifdef __SIW_CONFIG_FB
|
||||
#undef __SIW_CONFIG_FB
|
||||
#endif
|
||||
|
||||
#define __SIW_CONFIG_SYSTEM_PM
|
||||
#elif defined(CONFIG_TOUCHSCREEN_SIW_SW42101)
|
||||
#define __SIW_BUS_ADDR_16BIT
|
||||
|
||||
#ifdef __SIW_SUPPORT_ABT
|
||||
#undef __SIW_SUPPORT_ABT
|
||||
#endif
|
||||
|
||||
#ifdef __SIW_SUPPORT_PRD
|
||||
#undef __SIW_SUPPORT_PRD
|
||||
#endif
|
||||
|
||||
#define __SIW_CONFIG_SYSTEM_PM
|
||||
#else /* General case for Mobile */
|
||||
#define __SIW_CONFIG_PROX_ON_SUSPEND
|
||||
#define __SIW_CONFIG_PROX_ON_RESUME
|
||||
|
||||
#define __SIW_CONFIG_KNOCK
|
||||
|
||||
//#define __SIW_CONFIG_SYSTEM_PM //Custom option for special scenario
|
||||
#endif
|
||||
|
||||
#if defined(__SIW_CONFIG_SYSTEM_PM)
|
||||
#if defined(CONFIG_HIBERNATE_CALLBACKS)
|
||||
//#define __SIW_CONFIG_FASTBOOT //Custom option for special scenario
|
||||
#endif /* CONFIG_HIBERNATE_CALLBACKS */
|
||||
#endif
|
||||
|
||||
#if defined(__SIW_SUPPORT_WATCH)
|
||||
#define __SIW_CONFIG_SWIPE
|
||||
#endif
|
||||
|
||||
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0))
|
||||
#define __SIW_ATTR_PERMISSION_ALL
|
||||
#endif
|
||||
|
||||
#define __SIW_ATTR_RST_BY_READ
|
||||
|
||||
#define SIW_TOUCH_NAME "siw_touch"
|
||||
#define SIW_TOUCH_CORE "siw_touch_core"
|
||||
#define SIW_TOUCH_INPUT "siw_touch_input"
|
||||
#define SIW_TOUCH_EXT_WATCH "siw_ext_watch"
|
||||
|
||||
#define MAX_FINGER 10
|
||||
#define MAX_LPWG_CODE 128
|
||||
|
||||
enum _SIW_CHIP_TYPE {
|
||||
CHIP_NONE = 0x0000,
|
||||
//
|
||||
CHIP_LG4894 = 0x0001,
|
||||
CHIP_LG4895 = 0x0002,
|
||||
CHIP_LG4946 = 0x0003,
|
||||
CHIP_LG4951 = 0x0004,
|
||||
//
|
||||
CHIP_SW1828 = 0x0080,
|
||||
//
|
||||
CHIP_SW49105 = 0x0105,
|
||||
CHIP_SW49106 = 0x0106,
|
||||
//
|
||||
CHIP_SW49406 = 0x0406,
|
||||
CHIP_SW49407 = 0x0407,
|
||||
CHIP_SW49408 = 0x0408,
|
||||
CHIP_SW49409 = 0x0409,
|
||||
//
|
||||
CHIP_SW49501 = 0x0501,
|
||||
//
|
||||
CHIP_SW42101 = 0x0211,
|
||||
};
|
||||
|
||||
//#define __SIW_TEST_IRQ_OFF
|
||||
|
||||
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))
|
||||
#define mod_delayed_work queue_delayed_work
|
||||
#endif
|
||||
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0))
|
||||
#define subsys_system_register(_subsys, _group) bus_register(_subsys)
|
||||
#endif
|
||||
|
||||
#endif /* __SIW_TOUCH_CFG_H */
|
||||
|
||||
206
drivers/input/touchscreen/siw/siw_touch_dbg.h
Normal file
206
drivers/input/touchscreen/siw/siw_touch_dbg.h
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* SiW touch debug
|
||||
*
|
||||
* Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr
|
||||
* Copyright (C) 2018 Sony Mobile Communications Inc.
|
||||
* Author: Hyunho Kim <kimhh@siliconworks.co.kr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __SIW_TOUCH_DBG_H
|
||||
#define __SIW_TOUCH_DBG_H
|
||||
|
||||
#include <linux/errno.h>
|
||||
|
||||
#define __SIZE_POW(x) (1<<(x))
|
||||
|
||||
enum _SIW_TOUCH_DBG_FLAG {
|
||||
DBG_NONE = 0,
|
||||
DBG_BASE = (1U << 0),
|
||||
DBG_TRACE = (1U << 1),
|
||||
DBG_GET_DATA = (1U << 2),
|
||||
DBG_ABS = (1U << 3),
|
||||
//
|
||||
DBG_BUTTON = (1U << 4),
|
||||
DBG_FW_UPGRADE = (1U << 5),
|
||||
DBG_GHOST = (1U << 6),
|
||||
DBG_IRQ = (1U << 7),
|
||||
//
|
||||
DBG_PM = (1U << 8),
|
||||
DBG_JITTER = (1U << 9),
|
||||
DBG_ACCURACY = (1U << 10),
|
||||
DBG_BOUNCING = (1U << 11),
|
||||
//
|
||||
DBG_GRIP = (1U << 12),
|
||||
DBG_FILTER_RESULT = (1U << 13),
|
||||
DBG_QUICKCOVER = (1U << 14),
|
||||
DBG_LPWG = (1U << 15),
|
||||
//
|
||||
DBG_LPWG_COOR = (1U << 16),
|
||||
DBG_NOISE = (1U << 17),
|
||||
DBG_WATCH = (1U << 18),
|
||||
DBG_RSVD18 = (1U << 19),
|
||||
//
|
||||
DBG_RSVD20 = (1U << 20),
|
||||
DBG_RSVD21 = (1U << 21),
|
||||
DBG_RSVD22 = (1U << 22),
|
||||
DBG_RSVD23 = (1U << 23),
|
||||
//
|
||||
DBG_GPIO = (1U << 24),
|
||||
DBG_RSVD25 = (1U << 25),
|
||||
DBG_NOTI = (1U << 26),
|
||||
DBG_EVENT = (1U << 27),
|
||||
//
|
||||
DBG_RSVD28 = (1U << 28),
|
||||
DBG_RSVD29 = (1U << 29),
|
||||
DBG_OF = (1U << 30),
|
||||
DBG_ETC = (1U << 31),
|
||||
};
|
||||
|
||||
extern u32 t_pr_dbg_mask;
|
||||
extern u32 t_dev_dbg_mask;
|
||||
|
||||
#define t_pr_info(fmt, args...) pr_info(fmt, ##args)
|
||||
#define t_pr_noti(fmt, args...) pr_notice(fmt, ##args)
|
||||
#define t_pr_warn(fmt, args...) pr_warning(fmt, ##args)
|
||||
#define t_pr_err(fmt, args...) pr_err(fmt, ##args)
|
||||
|
||||
#define t_pr_dbg(condition, fmt, args...) \
|
||||
do { \
|
||||
if (unlikely(t_pr_dbg_mask & (condition))) \
|
||||
pr_info(fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define __t_dev_none(_dev, fmt, args...) \
|
||||
do{ \
|
||||
if (0) dev_printk(KERN_DEBUG, _dev, fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define __t_dev_info(_dev, fmt, args...) dev_info(_dev, fmt, ##args)
|
||||
#define __t_dev_warn(_dev, fmt, args...) dev_warn(_dev, fmt, ##args)
|
||||
#define __t_dev_err(_dev, fmt, args...) dev_err(_dev, fmt, ##args)
|
||||
|
||||
#if 1
|
||||
#define t_dev_info(_dev, fmt, args...) __t_dev_info(_dev, fmt, ##args)
|
||||
#define t_dev_warn(_dev, fmt, args...) __t_dev_warn(_dev, fmt, ##args)
|
||||
#else
|
||||
#define t_dev_info(_dev, fmt, args...) __t_dev_none(_dev, fmt, ##args)
|
||||
#define t_dev_warn(_dev, fmt, args...) __t_dev_none(_dev, fmt, ##args)
|
||||
#endif
|
||||
|
||||
#define t_dev_trace(_dev, fmt, args...) __t_dev_info(_dev, fmt, ##args)
|
||||
#define t_dev_err(_dev, fmt, args...) __t_dev_err(_dev, fmt, ##args)
|
||||
|
||||
#define t_dev_info_sel(_dev, _prt, fmt, args...) \
|
||||
do { \
|
||||
if (_prt) \
|
||||
t_dev_info(_dev, fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define t_dev_trcf(_dev) t_dev_trace(_dev, "[%s]\n", __func__);
|
||||
|
||||
#define t_dev_dbg(condition, _dev, fmt, args...) \
|
||||
do { \
|
||||
if (unlikely(t_dev_dbg_mask & (condition))) \
|
||||
__t_dev_info(_dev, fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define t_dev_dbg_base(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_BASE, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_trace(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_TRACE, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_getd(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_GET_DATA, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_abs(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_ABS, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_button(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_BUTTON, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_fwup(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_FW_UPGRADE, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_ghost(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_GHOST, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_irq(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_IRQ, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_pm(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_PM, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_jitter(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_JITTER, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_acc(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_ACCURACY, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_bounce(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_BOUNCING, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_grip(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_GRIP, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_filter(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_FILTER_RESULT, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_qcover(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_QUICKCOVER, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_lpwg(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_LPWG, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_lpwg_coor(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_LPWG_COOR, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_noise(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_NOISE, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_watch(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_WATCH, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_noti(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_NOTI, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_gpio(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_GPIO, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_event(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_EVENT, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_of(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_OF, _dev, fmt, ##args)
|
||||
|
||||
#define t_dev_dbg_etc(_dev, fmt, args...) \
|
||||
t_dev_dbg(DBG_ETC, _dev, fmt, ##args)
|
||||
|
||||
|
||||
extern u32 t_dbg_flag;
|
||||
|
||||
enum {
|
||||
DBG_FLAG_SKIP_IRQ = (1<<0),
|
||||
DBG_FLAG_SKIP_IRQ_RESET = (1<<1),
|
||||
/* */
|
||||
DBG_FLAG_SKIP_IEVENT = (1<<8),
|
||||
DBG_FLAG_SKIP_UEVENT = (1<<9),
|
||||
/* */
|
||||
DBG_FLAG_SKIP_INIT_LATE = (1<<16),
|
||||
DBG_FLAG_TEST_INIT_LATE = (1<<17),
|
||||
/* */
|
||||
DBG_FLAG_SKIP_MON_THREAD = (1<<24),
|
||||
DBG_FLAG_TEST_MON_THREAD = (1<<25),
|
||||
};
|
||||
|
||||
#define ETDBOOTFAIL ((ENOMEDIUM<<3) + 0x00)
|
||||
#define ETDSENTESD ((ENOMEDIUM<<3) + 0x01)
|
||||
#define ETDSENTESDIRQ ((ENOMEDIUM<<3) + 0x0F)
|
||||
|
||||
#endif /* __SIW_TOUCH_DBG_H */
|
||||
|
||||
517
drivers/input/touchscreen/siw/siw_touch_event.c
Normal file
517
drivers/input/touchscreen/siw/siw_touch_event.c
Normal file
|
|
@ -0,0 +1,517 @@
|
|||
/*
|
||||
* siw_touch_event.c - SiW touch event driver
|
||||
*
|
||||
* Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr
|
||||
* Copyright (C) 2018 Sony Mobile Communications Inc.
|
||||
* Author: Hyunho Kim <kimhh@siliconworks.co.kr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/async.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/memory.h>
|
||||
|
||||
#include "siw_touch.h"
|
||||
#include "siw_touch_event.h"
|
||||
|
||||
|
||||
#define __siw_input_report_key(_idev, _code, _value) \
|
||||
do { \
|
||||
if (t_dbg_flag & DBG_FLAG_SKIP_IEVENT) { \
|
||||
t_dev_dbg_event(&_idev->dev, "skip input report: c %d, v %d\n", _code, _value); \
|
||||
} else { \
|
||||
input_report_key(_idev, _code, _value); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define siw_input_report_key(_idev, _code, _value) \
|
||||
do { \
|
||||
__siw_input_report_key(_idev, _code, _value); \
|
||||
siwmon_submit_evt(&_idev->dev, "EV_KEY", EV_KEY, #_code, _code, _value, 0); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define __siw_input_report_abs(_idev, _code, _value) \
|
||||
do { \
|
||||
if (t_dbg_flag & DBG_FLAG_SKIP_IEVENT) { \
|
||||
t_dev_dbg_event(&_idev->dev, "skip input report: c %d, v %d\n", _code, _value); \
|
||||
} else { \
|
||||
input_report_abs(_idev, _code, _value); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define siw_input_report_abs(_idev, _code, _value) \
|
||||
do { \
|
||||
__siw_input_report_abs(_idev, _code, _value); \
|
||||
siwmon_submit_evt(&_idev->dev, "EV_ABS", EV_ABS, #_code, _code, _value, 0); \
|
||||
} while (0)
|
||||
|
||||
static void siw_touch_report_cancel_event(struct siw_ts *ts)
|
||||
{
|
||||
struct input_dev *input = ts->input;
|
||||
u16 old_mask = ts->old_mask;
|
||||
int i = 0;
|
||||
|
||||
if (!input) {
|
||||
t_dev_err(ts->dev, "no input device (cancel)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < touch_max_finger(ts); i++) {
|
||||
if (old_mask & (1 << i)) {
|
||||
input_mt_slot(input, i);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
|
||||
siw_input_report_key(input, BTN_TOUCH, 1);
|
||||
siw_input_report_key(input, BTN_TOOL_FINGER, 1);
|
||||
siw_input_report_abs(input, ABS_MT_PRESSURE, 255);
|
||||
t_dev_info(&input->dev, "finger canceled <%d> (%4d, %4d, %4d)\n",
|
||||
i,
|
||||
ts->tdata[i].x,
|
||||
ts->tdata[i].y,
|
||||
ts->tdata[i].pressure);
|
||||
}
|
||||
}
|
||||
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
void siw_touch_report_event(void *ts_data)
|
||||
{
|
||||
struct siw_ts *ts = ts_data;
|
||||
struct input_dev *input = ts->input;
|
||||
struct device *idev = NULL;
|
||||
struct touch_data *tdata = NULL;
|
||||
u16 old_mask = ts->old_mask;
|
||||
u16 new_mask = ts->new_mask;
|
||||
u16 press_mask = 0;
|
||||
u16 release_mask = 0;
|
||||
u16 change_mask = 0;
|
||||
int i;
|
||||
|
||||
// t_dev_trcf(idev);
|
||||
|
||||
if (!input) {
|
||||
t_dev_err(ts->dev, "no input device (report)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
idev = &input->dev;
|
||||
|
||||
change_mask = old_mask ^ new_mask;
|
||||
press_mask = new_mask & change_mask;
|
||||
release_mask = old_mask & change_mask;
|
||||
|
||||
t_dev_dbg_abs(idev,
|
||||
"mask [new: %04x, old: %04x]\n",
|
||||
new_mask, old_mask);
|
||||
t_dev_dbg_abs(idev,
|
||||
"mask [change: %04x, press: %04x, release: %04x]\n",
|
||||
change_mask, press_mask, release_mask);
|
||||
|
||||
/* Palm state - Report Pressure value 255 */
|
||||
if (ts->is_palm) {
|
||||
siw_touch_report_cancel_event(ts);
|
||||
ts->is_palm = 0;
|
||||
}
|
||||
|
||||
tdata = ts->tdata;
|
||||
for (i = 0; i < touch_max_finger(ts); i++, tdata++) {
|
||||
if (new_mask & (1 << i)) {
|
||||
input_mt_slot(input, i);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
|
||||
siw_input_report_key(input, BTN_TOUCH, 1);
|
||||
siw_input_report_key(input, BTN_TOOL_FINGER, 1);
|
||||
siw_input_report_abs(input, ABS_MT_TRACKING_ID, tdata->id);
|
||||
siw_input_report_abs(input, ABS_MT_POSITION_X, tdata->x);
|
||||
siw_input_report_abs(input, ABS_MT_POSITION_Y, tdata->y);
|
||||
siw_input_report_abs(input, ABS_MT_PRESSURE, tdata->pressure);
|
||||
siw_input_report_abs(input, ABS_MT_WIDTH_MAJOR, tdata->width_major);
|
||||
siw_input_report_abs(input, ABS_MT_WIDTH_MINOR, tdata->width_minor);
|
||||
siw_input_report_abs(input, ABS_MT_ORIENTATION, tdata->orientation);
|
||||
|
||||
if (press_mask & (1 << i)) {
|
||||
t_dev_dbg_button(idev, "%d finger press <%d> (%4d, %4d, %4d)\n",
|
||||
ts->tcount,
|
||||
i,
|
||||
ts->tdata[i].x,
|
||||
ts->tdata[i].y,
|
||||
ts->tdata[i].pressure);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (release_mask & (1 << i)) {
|
||||
input_mt_slot(input, i);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
|
||||
// siw_input_report_abs(ts->input, ABS_MT_TRACKING_ID, -1);
|
||||
t_dev_dbg_button(idev, "finger release <%d> (%4d, %4d, %4d)\n",
|
||||
i,
|
||||
ts->tdata[i].x,
|
||||
ts->tdata[i].y,
|
||||
ts->tdata[i].pressure);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ts->tcount) {
|
||||
siw_input_report_key(input, BTN_TOUCH, 0);
|
||||
siw_input_report_key(input, BTN_TOOL_FINGER, 0);
|
||||
}
|
||||
|
||||
ts->old_mask = new_mask;
|
||||
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
void siw_touch_report_all_event(void *ts_data)
|
||||
{
|
||||
struct siw_ts *ts = ts_data;
|
||||
ts->is_palm = 1;
|
||||
if (ts->old_mask) {
|
||||
ts->new_mask = 0;
|
||||
siw_touch_report_event(ts);
|
||||
ts->tcount = 0;
|
||||
memset(ts->tdata, 0, sizeof(struct touch_data) * touch_max_finger(ts));
|
||||
}
|
||||
ts->is_palm = 0;
|
||||
}
|
||||
|
||||
#if defined(__SIW_SUPPORT_UEVENT)
|
||||
static void siw_touch_uevent_release(struct device *dev)
|
||||
{
|
||||
if (dev->platform_data)
|
||||
dev->platform_data = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
static const struct bus_type siw_touch_uevent_subsys = {
|
||||
.name = SIW_TOUCH_NAME,
|
||||
.dev_name = SIW_TOUCH_NAME,
|
||||
};
|
||||
|
||||
static const struct device siw_device_uevent_touch = {
|
||||
.id = 0,
|
||||
.bus = &siw_touch_uevent_subsys,
|
||||
.release = siw_touch_uevent_release,
|
||||
};
|
||||
*/
|
||||
|
||||
static const struct siw_touch_uevent_ctrl siw_uevent_ctrl_default = {
|
||||
.str = {
|
||||
[TOUCH_UEVENT_KNOCK] = {"TOUCH_GESTURE_WAKEUP=WAKEUP", NULL},
|
||||
[TOUCH_UEVENT_PASSWD] = {"TOUCH_GESTURE_WAKEUP=PASSWORD", NULL},
|
||||
[TOUCH_UEVENT_SWIPE_RIGHT] = {"TOUCH_GESTURE_WAKEUP=SWIPE_RIGHT", NULL},
|
||||
[TOUCH_UEVENT_SWIPE_LEFT] = {"TOUCH_GESTURE_WAKEUP=SWIPE_LEFT", NULL},
|
||||
[TOUCH_UEVENT_GESTURE_C] = {"TOUCH_GESTURE_WAKEUP=GESTURE_C", NULL},
|
||||
[TOUCH_UEVENT_GESTURE_W] = {"TOUCH_GESTURE_WAKEUP=GESTURE_W", NULL},
|
||||
[TOUCH_UEVENT_GESTURE_V] = {"TOUCH_GESTURE_WAKEUP=GESTURE_V", NULL},
|
||||
[TOUCH_UEVENT_GESTURE_O] = {"TOUCH_GESTURE_WAKEUP=GESTURE_O", NULL},
|
||||
[TOUCH_UEVENT_GESTURE_S] = {"TOUCH_GESTURE_WAKEUP=GESTURE_S", NULL},
|
||||
[TOUCH_UEVENT_GESTURE_E] = {"TOUCH_GESTURE_WAKEUP=GESTURE_E", NULL},
|
||||
[TOUCH_UEVENT_GESTURE_M] = {"TOUCH_GESTURE_WAKEUP=GESTURE_M", NULL},
|
||||
[TOUCH_UEVENT_GESTURE_Z] = {"TOUCH_GESTURE_WAKEUP=GESTURE_Z", NULL},
|
||||
[TOUCH_UEVENT_GESTURE_DIR_RIGHT] = {"TOUCH_GESTURE_WAKEUP=GESTURE_DIR_RIGHT", NULL},
|
||||
[TOUCH_UEVENT_GESTURE_DIR_DOWN] = {"TOUCH_GESTURE_WAKEUP=GESTURE_DIR_DOWN", NULL},
|
||||
[TOUCH_UEVENT_GESTURE_DIR_LEFT] = {"TOUCH_GESTURE_WAKEUP=GESTURE_DIR_LEFT", NULL},
|
||||
[TOUCH_UEVENT_GESTURE_DIR_UP] = {"TOUCH_GESTURE_WAKEUP=GESTURE_DIR_UP", NULL},
|
||||
},
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
#define siw_kobject_uevent_env(_idev, _type, _kobj, _action, _envp_ext) \
|
||||
do { \
|
||||
kobject_uevent_env(_kobj, _action, _envp_ext); \
|
||||
siwmon_submit_evt(&_idev->dev, "@UEVENT", _type, #_action, _action, 0, 0); \
|
||||
} while (0)
|
||||
|
||||
void siw_touch_send_uevent(void *ts_data, int type)
|
||||
{
|
||||
struct siw_ts *ts = ts_data;
|
||||
struct input_dev *input = ts->input;
|
||||
struct device *idev = NULL;
|
||||
struct siw_touch_uevent_ctrl *uevent_ctrl = touch_uevent_ctrl(ts);
|
||||
char *str = NULL;
|
||||
|
||||
if (!input) {
|
||||
t_dev_err(ts->dev, "no input device (uevent)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
idev = &ts->input->dev;
|
||||
|
||||
if (uevent_ctrl == NULL) {
|
||||
uevent_ctrl = (struct siw_touch_uevent_ctrl *)&siw_uevent_ctrl_default;
|
||||
}
|
||||
|
||||
if (type >= TOUCH_UEVENT_MAX) {
|
||||
t_dev_err(idev, "Invalid event type, %d\n", type);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((uevent_ctrl->str[type] == NULL) ||
|
||||
(uevent_ctrl->str[type][0] == NULL)) {
|
||||
t_dev_err(idev, "empty event str, %d\n", type);
|
||||
return;
|
||||
}
|
||||
|
||||
str = uevent_ctrl->str[type][0];
|
||||
|
||||
if (t_dbg_flag & DBG_FLAG_SKIP_UEVENT) {
|
||||
t_dev_dbg_event(idev, "skip uevent, %s\n", str);
|
||||
return;
|
||||
}
|
||||
|
||||
if (atomic_read(&ts->state.uevent) == UEVENT_IDLE) {
|
||||
#if defined(__SIW_SUPPORT_WAKE_LOCK)
|
||||
wake_lock_timeout(&ts->lpwg_wake_lock,
|
||||
msecs_to_jiffies(3000));
|
||||
#endif
|
||||
|
||||
atomic_set(&ts->state.uevent, UEVENT_BUSY);
|
||||
|
||||
siw_kobject_uevent_env(input, type, &ts->udev.kobj,
|
||||
KOBJ_CHANGE, uevent_ctrl->str[type]);
|
||||
|
||||
t_dev_info(ts->dev, "%s\n", str);
|
||||
siw_touch_report_all_event(ts);
|
||||
|
||||
/*
|
||||
* Echo reply to lpwg_data(_store_lpwg_data) is required to
|
||||
* clear(UEVENT_IDLE) state.event
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
int siw_touch_init_uevent(void *ts_data)
|
||||
{
|
||||
struct siw_ts *ts = ts_data;
|
||||
char *drv_name = touch_drv_name(ts);
|
||||
int ret = 0;
|
||||
|
||||
if (drv_name) {
|
||||
ts->ubus.name = drv_name;
|
||||
ts->ubus.dev_name = drv_name;
|
||||
} else {
|
||||
ts->ubus.name = SIW_TOUCH_NAME;
|
||||
ts->ubus.dev_name = SIW_TOUCH_NAME;
|
||||
}
|
||||
|
||||
memset((void *)&ts->udev, 0, sizeof(ts->udev));
|
||||
ts->udev.bus = &ts->ubus;
|
||||
ts->udev.release = siw_touch_uevent_release;
|
||||
|
||||
ret = subsys_system_register(&ts->ubus, NULL);
|
||||
if (ret < 0) {
|
||||
t_dev_err(ts->dev, "bus is not registered, %d\n", ret);
|
||||
goto out_subsys;
|
||||
}
|
||||
|
||||
ret = device_register(&ts->udev);
|
||||
if (ret < 0) {
|
||||
t_dev_err(ts->dev, "device is not registered, %d\n",ret);
|
||||
goto out_dev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_dev:
|
||||
bus_unregister(&ts->ubus);
|
||||
|
||||
out_subsys:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void siw_touch_free_uevent(void *ts_data)
|
||||
{
|
||||
struct siw_ts *ts = ts_data;
|
||||
|
||||
device_unregister(&ts->udev);
|
||||
|
||||
bus_unregister(&ts->ubus);
|
||||
}
|
||||
#else /* __SIW_SUPPORT_UEVENT */
|
||||
void siw_touch_send_uevent(void *ts_data, int type)
|
||||
{
|
||||
|
||||
}
|
||||
int siw_touch_init_uevent(void *ts_data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void siw_touch_free_uevent(void *ts_data)
|
||||
{
|
||||
|
||||
}
|
||||
#endif /* __SIW_SUPPORT_UEVENT */
|
||||
|
||||
#define SIW_TOUCH_PHYS_NAME_SIZE 128
|
||||
|
||||
int siw_touch_init_input(void *ts_data)
|
||||
{
|
||||
struct siw_ts *ts = ts_data;
|
||||
struct device *dev = ts->dev;
|
||||
struct touch_device_caps *caps = &ts->caps;
|
||||
struct input_dev *input = NULL;
|
||||
char *input_name = NULL;
|
||||
char *phys_name = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (ts->input) {
|
||||
t_dev_err(dev, "input device has been already allocated!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phys_name = touch_kzalloc(dev, SIW_TOUCH_PHYS_NAME_SIZE, GFP_KERNEL);
|
||||
if (!phys_name) {
|
||||
t_dev_err(dev, "failed to allocate memory for phys_name\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_phys;
|
||||
}
|
||||
|
||||
input = input_allocate_device();
|
||||
if (!input) {
|
||||
t_dev_err(dev, "failed to allocate memory for input device\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_input;
|
||||
}
|
||||
|
||||
input_name = touch_idrv_name(ts);
|
||||
input_name = (input_name) ? input_name : SIW_TOUCH_INPUT;
|
||||
|
||||
snprintf(phys_name, SIW_TOUCH_PHYS_NAME_SIZE,
|
||||
"%s/%s - %s",
|
||||
dev_name(dev->parent),
|
||||
dev_name(dev),
|
||||
input_name);
|
||||
|
||||
if (touch_flags(ts) & TOUCH_USE_INPUT_PARENT) {
|
||||
input->dev.parent = dev;
|
||||
} else {
|
||||
/*
|
||||
* To fix sysfs location
|
||||
* With no parent, sysfs folder is created at
|
||||
* '/sys/devices/virtual/input/{input_name}'
|
||||
*/
|
||||
input->dev.parent = NULL;
|
||||
}
|
||||
|
||||
input->name = (const char *)input_name;
|
||||
input->phys = (const char *)phys_name;;
|
||||
memcpy((void *)&input->id, (void *)&ts->i_id, sizeof(input->id));
|
||||
|
||||
set_bit(EV_SYN, input->evbit);
|
||||
set_bit(EV_ABS, input->evbit);
|
||||
set_bit(EV_KEY, input->evbit);
|
||||
set_bit(BTN_TOUCH, input->keybit);
|
||||
set_bit(BTN_TOOL_FINGER, input->keybit);
|
||||
set_bit(INPUT_PROP_DIRECT, input->propbit);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
|
||||
caps->max_x, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
|
||||
caps->max_y, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_PRESSURE, 0,
|
||||
caps->max_pressure, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0,
|
||||
caps->max_width, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0,
|
||||
caps->max_width, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_ORIENTATION, 0,
|
||||
caps->max_orientation, 0, 0);
|
||||
|
||||
/* Initialize multi-touch slot */
|
||||
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))
|
||||
ret = input_mt_init_slots(input, caps->max_id);
|
||||
#else
|
||||
ret = input_mt_init_slots(input, caps->max_id, 0);
|
||||
#endif
|
||||
if (ret < 0) {
|
||||
t_dev_err(dev, "failed to initialize input device, %d\n", ret);
|
||||
goto out_slot;
|
||||
}
|
||||
|
||||
ret = input_register_device(input);
|
||||
if (ret < 0) {
|
||||
t_dev_err(dev, "failed to register input device, %d\n", ret);
|
||||
goto out_reg;
|
||||
}
|
||||
|
||||
input_set_drvdata(input, ts);
|
||||
ts->input = input;
|
||||
|
||||
t_dev_info(&input->dev, "input device[%s] registered (%d, %d, %d, %d, %d, %d, %d)\n",
|
||||
input->phys,
|
||||
caps->max_x,
|
||||
caps->max_y,
|
||||
caps->max_pressure,
|
||||
caps->max_width,
|
||||
caps->max_width,
|
||||
caps->max_orientation,
|
||||
caps->max_id);
|
||||
|
||||
return 0;
|
||||
|
||||
out_reg:
|
||||
input_mt_destroy_slots(input);
|
||||
|
||||
out_slot:
|
||||
input_free_device(input);
|
||||
|
||||
out_input:
|
||||
touch_kfree(dev, phys_name);
|
||||
|
||||
out_phys:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void siw_touch_free_input(void *ts_data)
|
||||
{
|
||||
struct siw_ts *ts = ts_data;
|
||||
struct device *dev = ts->dev;
|
||||
struct input_dev *input = ts->input;
|
||||
|
||||
if (input) {
|
||||
char *phys_name = (char *)input->phys;
|
||||
|
||||
ts->input = NULL;
|
||||
|
||||
t_dev_info(&input->dev, "input device[%s] released\n",
|
||||
input->phys);
|
||||
|
||||
input_unregister_device(input);
|
||||
input_mt_destroy_slots(input);
|
||||
input_free_device(input);
|
||||
|
||||
touch_kfree(dev, phys_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
drivers/input/touchscreen/siw/siw_touch_event.h
Normal file
27
drivers/input/touchscreen/siw/siw_touch_event.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* SiW touch event
|
||||
*
|
||||
* Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr
|
||||
* Copyright (C) 2018 Sony Mobile Communications Inc.
|
||||
* Author: Hyunho Kim <kimhh@siliconworks.co.kr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __SIW_TOUCH_EVENT_H
|
||||
#define __SIW_TOUCH_EVENT_H
|
||||
|
||||
extern void siw_touch_report_event(void *ts_data);
|
||||
extern void siw_touch_report_all_event(void *ts_data);
|
||||
extern void siw_touch_send_uevent(void *ts_data, int type);
|
||||
|
||||
extern int siw_touch_init_uevent(void *ts_data);
|
||||
extern void siw_touch_free_uevent(void *ts_data);
|
||||
|
||||
extern int siw_touch_init_input(void *ts_data);
|
||||
extern void siw_touch_free_input(void *ts_data);
|
||||
|
||||
#endif /* __SIW_TOUCH_EVENT_H */
|
||||
|
||||
447
drivers/input/touchscreen/siw/siw_touch_gpio.c
Normal file
447
drivers/input/touchscreen/siw/siw_touch_gpio.c
Normal file
|
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
* siw_touch_gpio.c - SiW touch gpio driver
|
||||
*
|
||||
* Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr
|
||||
* Copyright (C) 2018 Sony Mobile Communications Inc.
|
||||
* Author: Hyunho Kim <kimhh@siliconworks.co.kr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/async.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/memory.h>
|
||||
/*#include <linux/regulator/machine.h>*/
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "siw_touch.h"
|
||||
#include "siw_touch_gpio.h"
|
||||
#include "siw_touch_sys.h"
|
||||
|
||||
#if 1
|
||||
static int siw_touch_gpio_do_init(struct device *dev,
|
||||
int pin, const char *name)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (gpio_is_valid(pin)) {
|
||||
t_dev_dbg_gpio(dev, "gpio_request: pin %d, name %s\n", pin, name);
|
||||
ret = gpio_request(pin, name);
|
||||
if (ret) {
|
||||
t_dev_err(dev, "gpio_request[%s] failed, %d",
|
||||
name, ret);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void siw_touch_gpio_do_free(struct device *dev, int pin)
|
||||
{
|
||||
if (gpio_is_valid(pin)) {
|
||||
t_dev_dbg_gpio(dev, "gpio_free: pin %d\n", pin);
|
||||
gpio_free(pin);
|
||||
}
|
||||
}
|
||||
|
||||
static void siw_touch_gpio_do_dir_input(struct device *dev, int pin)
|
||||
{
|
||||
if (gpio_is_valid(pin)) {
|
||||
t_dev_dbg_gpio(dev, "set pin(%d) input mode", pin);
|
||||
gpio_direction_input(pin);
|
||||
}
|
||||
}
|
||||
|
||||
static void siw_touch_gpio_do_dir_output(struct device *dev, int pin, int value)
|
||||
{
|
||||
if (gpio_is_valid(pin)) {
|
||||
t_dev_dbg_gpio(dev, "set pin(%d) output mode(%d)", pin, value);
|
||||
gpio_direction_output(pin, value);
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* just test */
|
||||
static int siw_touch_gpio_init(struct device *dev, int pin, const char *name){ return 0; }
|
||||
static void siw_touch_gpio_do_free(struct device *dev, int pin){ }
|
||||
static void siw_touch_gpio_do_dir_input(struct device *dev, int pin)
|
||||
{
|
||||
if (gpio_is_valid(pin)) {
|
||||
gpio_request(pin, NULL);
|
||||
t_dev_dbg_gpio(dev, "set pin(%d) input mode", pin);
|
||||
gpio_direction_input(pin);
|
||||
gpio_free(pin);
|
||||
}
|
||||
}
|
||||
|
||||
static void siw_touch_gpio_do_dir_output(struct device *dev, int pin, int value)
|
||||
{
|
||||
if (gpio_is_valid(pin)) {
|
||||
gpio_request(pin, NULL);
|
||||
t_dev_dbg_gpio(dev, "set pin(%d) output mode(%d)", pin, value);
|
||||
gpio_direction_output(pin, value);
|
||||
gpio_free(pin);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void siw_touch_gpio_do_set_pull(struct device *dev, int pin, int value)
|
||||
{
|
||||
if (gpio_is_valid(pin)) {
|
||||
t_dev_dbg_gpio(dev, "set pin(%d) pull(%d)", pin, value);
|
||||
|
||||
siw_touch_sys_gpio_set_pull(pin, value);
|
||||
}
|
||||
}
|
||||
|
||||
int siw_touch_gpio_init(struct device *dev,
|
||||
int pin, const char *name)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
struct siw_touch_fquirks *fquirks = touch_fquirks(ts);
|
||||
int ret = 0;
|
||||
|
||||
if (fquirks->gpio_init) {
|
||||
ret = fquirks->gpio_init(dev, pin, name);
|
||||
if (ret != -EAGAIN) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = siw_touch_gpio_do_init(dev, pin, name);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void siw_touch_gpio_free(struct device *dev, int pin)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
struct siw_touch_fquirks *fquirks = touch_fquirks(ts);
|
||||
int ret;
|
||||
|
||||
if (fquirks->gpio_free) {
|
||||
ret = fquirks->gpio_free(dev, pin);
|
||||
if (ret != -EAGAIN) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
siw_touch_gpio_do_free(dev, pin);
|
||||
}
|
||||
|
||||
void siw_touch_gpio_direction_input(struct device *dev, int pin)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
struct siw_touch_fquirks *fquirks = touch_fquirks(ts);
|
||||
int ret;
|
||||
|
||||
if (fquirks->gpio_dir_input) {
|
||||
ret = fquirks->gpio_dir_input(dev, pin);
|
||||
if (ret != -EAGAIN) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
siw_touch_gpio_do_dir_input(dev, pin);
|
||||
}
|
||||
|
||||
void siw_touch_gpio_direction_output(struct device *dev, int pin, int value)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
struct siw_touch_fquirks *fquirks = touch_fquirks(ts);
|
||||
int ret;
|
||||
|
||||
if (fquirks->gpio_dir_output) {
|
||||
ret = fquirks->gpio_dir_output(dev, pin, value);
|
||||
if (ret != -EAGAIN) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
siw_touch_gpio_do_dir_output(dev, pin, value);
|
||||
}
|
||||
|
||||
void siw_touch_gpio_set_pull(struct device *dev, int pin, int value)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
struct siw_touch_fquirks *fquirks = touch_fquirks(ts);
|
||||
int ret;
|
||||
|
||||
if (fquirks->gpio_set_pull) {
|
||||
ret = fquirks->gpio_set_pull(dev, pin, value);
|
||||
if (ret != -EAGAIN) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
siw_touch_gpio_do_set_pull(dev, pin, value);
|
||||
}
|
||||
|
||||
#if defined(__SIW_SUPPORT_PWRCTRL)
|
||||
static int siw_touch_power_do_init(struct device *dev)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
int vdd_pin = -1;
|
||||
int vio_pin = -1;
|
||||
void *vdd, *vio;
|
||||
|
||||
if (!(touch_flags(ts) & TOUCH_USE_PWRCTRL)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
vdd_pin = touch_vdd_pin(ts);
|
||||
vio_pin = touch_vio_pin(ts);
|
||||
|
||||
t_dev_dbg_gpio(dev, "power init\n");
|
||||
|
||||
if (gpio_is_valid(vdd_pin)) {
|
||||
gpio_request(vdd_pin, "touch-vdd");
|
||||
} else {
|
||||
vdd = regulator_get(dev, "vdd");
|
||||
if (IS_ERR(vdd)) {
|
||||
t_dev_info(dev, "regulator \"vdd\" not exist\n");
|
||||
} else {
|
||||
touch_set_vdd(ts, vdd);
|
||||
}
|
||||
}
|
||||
|
||||
if (gpio_is_valid(vio_pin)) {
|
||||
gpio_request(vio_pin, "touch-vio");
|
||||
} else {
|
||||
vio = regulator_get(dev, "vio");
|
||||
if (IS_ERR(vio)) {
|
||||
t_dev_info(dev, "regulator \"vio\" not exist\n");
|
||||
} else {
|
||||
touch_set_vio(ts, vio);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int siw_touch_power_do_free(struct device *dev)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
int vdd_pin = -1;
|
||||
int vio_pin = -1;
|
||||
void *vdd, *vio;
|
||||
|
||||
if (!(touch_flags(ts) & TOUCH_USE_PWRCTRL)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
vdd_pin = touch_vdd_pin(ts);
|
||||
vio_pin = touch_vio_pin(ts);
|
||||
|
||||
t_dev_dbg_gpio(dev, "power free\n");
|
||||
|
||||
if (gpio_is_valid(vdd_pin)) {
|
||||
gpio_free(vdd_pin);
|
||||
} else {
|
||||
vdd = touch_get_vdd(ts);
|
||||
if (!IS_ERR(vdd)) {
|
||||
regulator_put(vdd);
|
||||
}
|
||||
}
|
||||
|
||||
if (gpio_is_valid(vio_pin)) {
|
||||
gpio_free(vio_pin);
|
||||
} else {
|
||||
vio = touch_get_vio(ts);
|
||||
if (!IS_ERR(vio)) {
|
||||
regulator_put(vio);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void siw_touch_power_do_vdd(struct device *dev, int value)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
int vdd_pin = -1;
|
||||
void *vdd = NULL;
|
||||
char *op;
|
||||
int ret = 0;
|
||||
|
||||
if (!(touch_flags(ts) & TOUCH_USE_PWRCTRL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
vdd_pin = touch_vdd_pin(ts);
|
||||
vdd = touch_get_vdd(ts);
|
||||
|
||||
if (gpio_is_valid(vdd_pin)) {
|
||||
op = "gpio out control";
|
||||
ret = gpio_direction_output(vdd_pin, value);
|
||||
} else if (!IS_ERR_OR_NULL(vdd)) {
|
||||
if (value) {
|
||||
op = "enable regulator";
|
||||
ret = regulator_enable((struct regulator *)vdd);
|
||||
} else {
|
||||
op = "disable regulator";
|
||||
ret = regulator_disable((struct regulator *)vdd);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
t_dev_err(dev, "vdd - %s, %d", op, ret);
|
||||
}
|
||||
|
||||
static void siw_touch_power_do_vio(struct device *dev, int value)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
int vio_pin = -1;
|
||||
void *vio = NULL;
|
||||
char *op;
|
||||
int ret = 0;
|
||||
|
||||
if (!(touch_flags(ts) & TOUCH_USE_PWRCTRL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
vio_pin = touch_vio_pin(ts);
|
||||
vio = touch_get_vio(ts);
|
||||
|
||||
if (gpio_is_valid(vio_pin)) {
|
||||
op = "gpio out control";
|
||||
ret = gpio_direction_output(vio_pin, value);
|
||||
} else if (!IS_ERR_OR_NULL(vio)) {
|
||||
if (value) {
|
||||
op = "enable regulator";
|
||||
ret = regulator_enable((struct regulator *)vio);
|
||||
} else {
|
||||
op = "disable regulator";
|
||||
ret = regulator_disable((struct regulator *)vio);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
t_dev_err(dev, "vio - %s, %d", op, ret);
|
||||
}
|
||||
#else /* __SIW_SUPPORT_PWRCTRL */
|
||||
static int siw_touch_power_do_init(struct device *dev)
|
||||
{
|
||||
t_dev_dbg_gpio(dev, "power init, nop ...\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int siw_touch_power_do_free(struct device *dev)
|
||||
{
|
||||
t_dev_dbg_gpio(dev, "power free, nop ...\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void siw_touch_power_do_vdd(struct device *dev, int value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void siw_touch_power_do_vio(struct device *dev, int value)
|
||||
{
|
||||
|
||||
}
|
||||
#endif /* __SIW_SUPPORT_PWRCTRL */
|
||||
|
||||
int siw_touch_power_init(struct device *dev)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
struct siw_touch_fquirks *fquirks = touch_fquirks(ts);
|
||||
int ret = 0;
|
||||
|
||||
if (fquirks->power_init) {
|
||||
ret = fquirks->power_init(dev);
|
||||
if (ret != -EAGAIN) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = siw_touch_power_do_init(dev);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int siw_touch_power_free(struct device *dev)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
struct siw_touch_fquirks *fquirks = touch_fquirks(ts);
|
||||
int ret = 0;
|
||||
|
||||
if (fquirks->power_free) {
|
||||
ret = fquirks->power_free(dev);
|
||||
if (ret != -EAGAIN) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = siw_touch_power_do_free(dev);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void siw_touch_power_vdd(struct device *dev, int value)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
struct siw_touch_fquirks *fquirks = touch_fquirks(ts);
|
||||
int ret = 0;
|
||||
|
||||
if (fquirks->power_vdd) {
|
||||
ret = fquirks->power_vdd(dev, value);
|
||||
if (ret != -EAGAIN) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
siw_touch_power_do_vdd(dev, value);
|
||||
}
|
||||
|
||||
void siw_touch_power_vio(struct device *dev, int value)
|
||||
{
|
||||
struct siw_ts *ts = to_touch_core(dev);
|
||||
struct siw_touch_fquirks *fquirks = touch_fquirks(ts);
|
||||
int ret = 0;
|
||||
|
||||
if (fquirks->power_vio) {
|
||||
ret = fquirks->power_vio(dev, value);
|
||||
if (ret != -EAGAIN) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
siw_touch_power_do_vio(dev, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue