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:
Danny Trunk 2021-10-18 21:42:31 +02:00 • committed by Sorayukii
commit 566c39486d
37 changed files with 35711 additions and 0 deletions

View file

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

View file

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

View 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

View 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/

View 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

View 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

View 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");

View 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 */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

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

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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);
}
}

View 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 */

View 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