From 566c39486d6fb141f61974578d60c071ad058976 Mon Sep 17 00:00:00 2001 From: Danny Trunk Date: Mon, 18 Oct 2021 21:42:31 +0200 Subject: [PATCH] drivers/input/touchscreen: Import siw driver from 52.1.A.3.49 Change-Id: Ida8e1eb66ce130322d2e20095a959516503d4f7c Signed-off-by: Sorayukii --- drivers/input/touchscreen/Kconfig | 1 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/siw/Kconfig | 21 + drivers/input/touchscreen/siw/Makefile | 24 + drivers/input/touchscreen/siw/mon/Kconfig | 14 + drivers/input/touchscreen/siw/mon/Makefile | 8 + .../input/touchscreen/siw/mon/siw_touch_mon.c | 857 ++ .../input/touchscreen/siw/mon/siw_touch_mon.h | 245 + .../touchscreen/siw/mon/siw_touch_mon_prt.c | 1017 ++ drivers/input/touchscreen/siw/siw_touch.c | 1962 ++++ drivers/input/touchscreen/siw/siw_touch.h | 1835 ++++ drivers/input/touchscreen/siw/siw_touch_bus.c | 648 ++ drivers/input/touchscreen/siw/siw_touch_bus.h | 160 + .../input/touchscreen/siw/siw_touch_bus_i2c.c | 482 + .../input/touchscreen/siw/siw_touch_bus_i2c.h | 20 + drivers/input/touchscreen/siw/siw_touch_cfg.h | 188 + drivers/input/touchscreen/siw/siw_touch_dbg.h | 206 + .../input/touchscreen/siw/siw_touch_event.c | 517 ++ .../input/touchscreen/siw/siw_touch_event.h | 27 + .../input/touchscreen/siw/siw_touch_gpio.c | 447 + .../input/touchscreen/siw/siw_touch_gpio.h | 150 + drivers/input/touchscreen/siw/siw_touch_hal.c | 8150 +++++++++++++++++ drivers/input/touchscreen/siw/siw_touch_hal.h | 631 ++ .../input/touchscreen/siw/siw_touch_hal_abt.c | 2368 +++++ .../input/touchscreen/siw/siw_touch_hal_prd.c | 6750 ++++++++++++++ .../input/touchscreen/siw/siw_touch_hal_reg.h | 310 + .../touchscreen/siw/siw_touch_hal_sysfs.c | 1103 +++ .../touchscreen/siw/siw_touch_hal_watch.c | 3561 +++++++ drivers/input/touchscreen/siw/siw_touch_irq.c | 310 + drivers/input/touchscreen/siw/siw_touch_irq.h | 43 + .../input/touchscreen/siw/siw_touch_misc.c | 329 + .../input/touchscreen/siw/siw_touch_notify.c | 304 + drivers/input/touchscreen/siw/siw_touch_of.c | 458 + drivers/input/touchscreen/siw/siw_touch_sys.c | 284 + drivers/input/touchscreen/siw/siw_touch_sys.h | 35 + .../input/touchscreen/siw/siw_touch_sysfs.c | 1932 ++++ drivers/input/touchscreen/siw/touch_sw49501.c | 313 + 37 files changed, 35711 insertions(+) create mode 100644 drivers/input/touchscreen/siw/Kconfig create mode 100644 drivers/input/touchscreen/siw/Makefile create mode 100644 drivers/input/touchscreen/siw/mon/Kconfig create mode 100644 drivers/input/touchscreen/siw/mon/Makefile create mode 100644 drivers/input/touchscreen/siw/mon/siw_touch_mon.c create mode 100644 drivers/input/touchscreen/siw/mon/siw_touch_mon.h create mode 100644 drivers/input/touchscreen/siw/mon/siw_touch_mon_prt.c create mode 100644 drivers/input/touchscreen/siw/siw_touch.c create mode 100644 drivers/input/touchscreen/siw/siw_touch.h create mode 100644 drivers/input/touchscreen/siw/siw_touch_bus.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_bus.h create mode 100644 drivers/input/touchscreen/siw/siw_touch_bus_i2c.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_bus_i2c.h create mode 100644 drivers/input/touchscreen/siw/siw_touch_cfg.h create mode 100644 drivers/input/touchscreen/siw/siw_touch_dbg.h create mode 100644 drivers/input/touchscreen/siw/siw_touch_event.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_event.h create mode 100644 drivers/input/touchscreen/siw/siw_touch_gpio.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_gpio.h create mode 100644 drivers/input/touchscreen/siw/siw_touch_hal.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_hal.h create mode 100644 drivers/input/touchscreen/siw/siw_touch_hal_abt.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_hal_prd.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_hal_reg.h create mode 100644 drivers/input/touchscreen/siw/siw_touch_hal_sysfs.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_hal_watch.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_irq.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_irq.h create mode 100644 drivers/input/touchscreen/siw/siw_touch_misc.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_notify.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_of.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_sys.c create mode 100644 drivers/input/touchscreen/siw/siw_touch_sys.h create mode 100644 drivers/input/touchscreen/siw/siw_touch_sysfs.c create mode 100644 drivers/input/touchscreen/siw/touch_sw49501.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index dcd18ecc2b2e..fe0aba53ca3c 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -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 diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index e89d41c346bb..899ebacecfdf 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -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 diff --git a/drivers/input/touchscreen/siw/Kconfig b/drivers/input/touchscreen/siw/Kconfig new file mode 100644 index 000000000000..ef5605b6b28d --- /dev/null +++ b/drivers/input/touchscreen/siw/Kconfig @@ -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 + diff --git a/drivers/input/touchscreen/siw/Makefile b/drivers/input/touchscreen/siw/Makefile new file mode 100644 index 000000000000..abed7f29b37e --- /dev/null +++ b/drivers/input/touchscreen/siw/Makefile @@ -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/ + diff --git a/drivers/input/touchscreen/siw/mon/Kconfig b/drivers/input/touchscreen/siw/mon/Kconfig new file mode 100644 index 000000000000..49c07769a42e --- /dev/null +++ b/drivers/input/touchscreen/siw/mon/Kconfig @@ -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 + diff --git a/drivers/input/touchscreen/siw/mon/Makefile b/drivers/input/touchscreen/siw/mon/Makefile new file mode 100644 index 000000000000..eaa7bfe4345b --- /dev/null +++ b/drivers/input/touchscreen/siw/mon/Makefile @@ -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 + diff --git a/drivers/input/touchscreen/siw/mon/siw_touch_mon.c b/drivers/input/touchscreen/siw/mon/siw_touch_mon.c new file mode 100644 index 000000000000..e65cdc2e3814 --- /dev/null +++ b/drivers/input/touchscreen/siw/mon/siw_touch_mon.c @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); + + + diff --git a/drivers/input/touchscreen/siw/mon/siw_touch_mon.h b/drivers/input/touchscreen/siw/mon/siw_touch_mon.h new file mode 100644 index 000000000000..d14e39c70c58 --- /dev/null +++ b/drivers/input/touchscreen/siw/mon/siw_touch_mon.h @@ -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 + * + * 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 +#include +#include + +/* + * 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); +} + +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); +} + +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< +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siw_touch_mon.h" + + +extern void *siw_mon_get_buf_cache(unsigned char *src, int len); +extern void siw_mon_put_buf_cache(void *buf); + +struct siw_mon_txt_ptr { + int cnt, limit; + char *pbuf; +}; + +struct siw_mon_event_txt { + struct list_head e_link; +// unsigned long id; /* From pointer, most of the time */ + ktime_t time; +// int length; + int type; + struct siw_mon_data data; +}; + +//#define __SIW_MON_EVENT_NO_LIMIT + +enum { + SIW_MON_EVENT_MAX = ((PAGE_SIZE<<2) / sizeof(struct siw_mon_data)), +}; + +enum { + SIW_MON_PRINT_SIZE = 250 /* with 5 ISOs segs */ +}; + +enum { + SLAB_NAME_SZ = 30, +}; + +struct siw_mon_reader_txt { + struct kmem_cache *e_slab; + int nevents; + struct list_head e_list; + struct siw_mon_reader r; /* In C, parent class can be placed anywhere */ + + wait_queue_head_t wait; + int printf_size; + char *printf_buf; + struct mutex printf_lock; + + char slab_name[SLAB_NAME_SZ]; + + atomic_t read_running; +}; + + +#define __siw_mon_prt_snprintf(_p, _margin, _fmt, _args...) \ + ({ \ + int _n_size = 0; \ + if (_p->limit >= (_p->cnt + _margin)) \ + _n_size = snprintf(_p->pbuf + _p->cnt, _p->limit - _p->cnt,\ + (const char *)_fmt, ##_args); \ + _n_size; \ + }) + +#define siw_mon_prt_snprintf(_p, _fmt, _args...) \ + __siw_mon_prt_snprintf(_p, __siw_mon_prt_margin, _fmt, ##_args) + +#define siw_mon_prt_snprintf_finish(_p) \ + __siw_mon_prt_snprintf(_p, __siw_mon_prt_margin_fin, "\n") + + +#define SIW_MON_TIME_FORMAT "%5ld.%06ld" +#define SIW_MON_TIME_FORMAT_NOP " . " + + +#define SIW_MON_IDX_MAIN_FORMAT_4 "[%4d, %d]" +#define SIW_MON_IDX_MAIN_FORMAT_3 "[%3d, %d]" +#define SIW_MON_IDX_MAIN_FORMAT_2 "[%2d, %d]" +#define SIW_MON_IDX_MAIN_FORMAT_1 "[%d, %d]" + +#define siw_mon_prt_idx_main_snprintf(_p, _cur, _tot) \ + ({ \ + int _n_size = 0; \ + if (_tot > 999) { \ + _n_size = siw_mon_prt_snprintf(_p, \ + SIW_MON_IDX_MAIN_FORMAT_4, \ + _cur, _tot); \ + } else if (_tot > 99) { \ + _n_size = siw_mon_prt_snprintf(_p, \ + SIW_MON_IDX_MAIN_FORMAT_3, \ + _cur, _tot); \ + } else if (_tot > 9) { \ + _n_size = siw_mon_prt_snprintf(_p, \ + SIW_MON_IDX_MAIN_FORMAT_2, \ + _cur, _tot); \ + } else { \ + _n_size = siw_mon_prt_snprintf(_p, \ + SIW_MON_IDX_MAIN_FORMAT_1, \ + _cur, _tot); \ + } \ + _n_size; \ + }) + +#define SIW_MON_IDX_SUB_FORMAT_4 "(%4d, %d)" +#define SIW_MON_IDX_SUB_FORMAT_3 "(%3d, %d)" +#define SIW_MON_IDX_SUB_FORMAT_2 "(%2d, %d)" +#define SIW_MON_IDX_SUB_FORMAT_1 "(%d, %d)" + +#define siw_mon_prt_idx_sub_snprintf(_p, _cur, _tot) \ + ({ \ + int _n_size = 0; \ + if (_tot > 999) { \ + _n_size = siw_mon_prt_snprintf(_p, \ + SIW_MON_IDX_SUB_FORMAT_4, \ + _cur, _tot); \ + } else if (_tot > 99) { \ + _n_size = siw_mon_prt_snprintf(_p, \ + SIW_MON_IDX_SUB_FORMAT_3, \ + _cur, _tot); \ + } else if (_tot > 9) { \ + _n_size = siw_mon_prt_snprintf(_p, \ + SIW_MON_IDX_SUB_FORMAT_2, \ + _cur, _tot); \ + } else { \ + _n_size = siw_mon_prt_snprintf(_p, \ + SIW_MON_IDX_SUB_FORMAT_1, \ + _cur, _tot); \ + } \ + _n_size; \ + }) + + +#define SIW_MON_UEVENT_STR "@UEVENT" + + +enum { + SIW_MON_PRT_MARGIN = 128, + SIW_MON_PRT_MARGIN_FIN = 4, + // + SIW_MON_BUF_MAX = 256, + SIW_MON_PRT_MAX = 16, +}; + +static int __siw_mon_prt_margin = SIW_MON_PRT_MARGIN; +static int __siw_mon_prt_margin_fin = SIW_MON_PRT_MARGIN_FIN; + +#if defined(__SIW_MON_EVENT_NO_LIMIT) +static int __siw_mon_buf_max = 0; //0 = limit off, else limit on +#else /* __SIW_MON_EVENT_NO_LIMIT */ +static int __siw_mon_buf_max = SIW_MON_BUF_MAX; +#endif /* __SIW_MON_EVENT_NO_LIMIT */ + +static int __siw_mon_prt_max = SIW_MON_PRT_MAX; + +int __used siw_mon_buf_max(void) +{ + return __siw_mon_buf_max; +} + +int __used siw_mon_prt_max(void) +{ + return __siw_mon_prt_max; +} + +enum { + NO_OF_DEBUF_FILES = 32, +}; + +struct siw_mon_debugfs_ctrl { + struct dentry *root; + struct dentry *file[NO_OF_DEBUF_FILES]; + unsigned long fbit; +}; + +static struct siw_mon_debugfs_ctrl *__siw_mon_debugfs_ctrl = NULL; + +#if 0 +#define SIW_MON_PRT_ATTR_TAG "prt_attr: " + +static int __used siw_mon_prt_add_file_list(struct siw_mon_debugfs_ctrl *ctrl, + struct dentry *f) +{ + int i; + + if (!f) + goto out; + + for (i = 0; i < NO_OF_DEBUF_FILES; i++) { + if (!test_bit(i, &ctrl->fbit)) { + ctrl->file[i] = f; + set_bit(i, &ctrl->fbit); + return i; + } + } + +out: + return -ENOMEM; +} + +static int __used siw_mon_prt_del_file_list(struct siw_mon_debugfs_ctrl *ctrl, + struct dentry *f) +{ + int i; + + if (!f) + goto out; + + for (i = 0; i < NO_OF_DEBUF_FILES; i++) { + if (ctrl->file[i] == f) { + ctrl->file[i] = NULL; + clear_bit(i, &ctrl->fbit); + return i; + } + } + +out: + return -ENOMEM; +} + +static struct dentry *siw_mon_prt_create_file( + struct siw_mon_debugfs_ctrl *ctrl, + const char *name, + umode_t mode, u32 *value) +{ + struct dentry *f; + + f = debugfs_create_u32(name, mode, ctrl->root, value); + if (!f) { + mon_pr_err(SIW_MON_PRT_ATTR_TAG \ + "unable to create siw mon attr file[%s]\n", name); + } + siw_mon_prt_add_file_list(ctrl, f); + + return f; +} + +static int siw_mon_prt_init_files(struct siw_mon_debugfs_ctrl *ctrl) +{ + /* ignore error */ + + siw_mon_prt_create_file(ctrl, "_attr_prt_max", 0666, &__siw_mon_prt_max); + + return 0; +} +#else +#define siw_mon_prt_init_files(_ctrl) { } +#endif + +#if defined(__SIW_MON_USE_BUF_SLAB) +static void siw_mon_prt_submit_memmove(struct siw_mon_data *data) +{ + switch (data->type) { + case SIW_MON_BUS: + #if defined(__SIW_MON_USE_BUF_SLAB_BUS) + { + struct siw_mon_data_bus *bus = &data->d.bus; + // unsigned char *buf; + + if (bus->tx_buf) { + bus->tx_buf = siw_mon_get_buf_cache(bus->tx_buf, + siw_mon_buf_size_cur(bus->tx_size)); + } + + if (bus->rx_buf) { + bus->rx_buf = siw_mon_get_buf_cache(bus->rx_buf, + siw_mon_buf_size_cur(bus->rx_size)); + } + } + #endif /* __SIW_MON_USE_BUF_SLAB_BUS */ + break; + case SIW_MON_EVT: + { + // struct siw_mon_data_evt *evt = &data->d.evt; + } + break; + case SIW_MON_OPS: + #if defined(__SIW_MON_USE_BUF_SLAB_BUS) + { + struct siw_mon_data_ops *ops = &data->d.ops; + unsigned char *buf = (unsigned char *)ops->data[0]; + + if (siw_mon_txt_sock_str_cmp(ops->ops) > 0) { + buf = siw_mon_get_buf_cache(buf, + siw_mon_buf_size_cur(ops->data[1])); + ops->data[0] = (size_t)buf; + } + } + #endif + break; + default: + break; + } +} +#else /* __SIW_MON_USE_BUF_SLAB */ +#define siw_mon_prt_submit_memmove(_data) { } +#endif /* __SIW_MON_USE_BUF_SLAB */ + +#define SIW_MON_PRT_SUBMIT_TAG "prt_submit: " + +static void siw_mon_prt_submit(void *r_data, struct siw_mon_data *data, + unsigned long *flags) +{ + struct siw_mon_reader_txt *rp = r_data; + struct siw_mon_event_txt *etxt; + ktime_t kt = ktime_get(); + + mon_pr_test(SIW_MON_PRT_SUBMIT_TAG "start\n"); + +#if defined(__SIW_MON_EVENT_NO_LIMIT) + /* + * I'm not sure this is safe way... + */ + if (rp->nevents >= SIW_MON_EVENT_MAX) { + unsigned long p_flags; + if (flags) { + p_flags = *flags; + spin_unlock_irqrestore(&rp->r.mterm->lock, p_flags); + } + while (rp->nevents >= SIW_MON_EVENT_MAX) { + wake_up(&rp->wait); + usleep_range(1000, 1000); + } + if (flags) { + spin_lock_irqsave(&rp->r.mterm->lock, p_flags); + *flags = p_flags; + } + } +#else /* __SIW_MON_EVENT_NO_LIMIT */ + if (rp->nevents >= SIW_MON_EVENT_MAX) { + mon_pr_test(SIW_MON_PRT_SUBMIT_TAG "max events\n"); + rp->r.mterm->cnt_text_lost++; + return; + } +#endif /* __SIW_MON_EVENT_NO_LIMIT */ + + if (!atomic_read(&rp->read_running)) { + mon_pr_test(SIW_MON_PRT_SUBMIT_TAG "read_running off\n"); + rp->r.mterm->cnt_text_lost++; + return; + } + + if ((etxt = kmem_cache_zalloc(rp->e_slab, GFP_ATOMIC)) == NULL) { + mon_pr_err(SIW_MON_PRT_SUBMIT_TAG \ + "failed to get memory cache from etxt slab\n"); + rp->r.mterm->cnt_text_lost++; + return; + } + + memcpy(&etxt->time, &kt, sizeof(ktime_t)); + etxt->type = data->type; + siw_mon_prt_submit_memmove(data); + memcpy(&etxt->data, data, sizeof(struct siw_mon_data)); + + rp->nevents++; + list_add_tail(&etxt->e_link, &rp->e_list); + wake_up(&rp->wait); + + mon_pr_test(SIW_MON_PRT_SUBMIT_TAG "done\n"); +} + +/* + * Slab interface: constructor. + */ +static void siw_mon_txt_ctor(void *mem) +{ + /* + * Nothing to initialize. No, really! + * So, we fill it with garbage to emulate a reused object. + */ + memset(mem, 0xe5, sizeof(struct siw_mon_event_txt)); +} + +#define SIW_MON_PRT_OPEN_TAG "prt_open: " + +/* + */ +static int siw_mon_prt_open(struct inode *inode, struct file *file) +{ + struct siw_mon_term *mterm; + struct siw_mon_reader_txt *rp; + int ret = 0; + + mutex_lock(&__siw_mon_lock); + + if (inode == NULL) { + mon_pr_err(SIW_MON_PRT_OPEN_TAG \ + "NULL inode\n"); + ret = -EINVAL; + goto out_alloc; + } + + mterm = inode->i_private; + if (mterm == NULL) { + mon_pr_err(SIW_MON_PRT_OPEN_TAG \ + "NULL mterm\n"); + ret = -EINVAL; + goto out_alloc; + } + + rp = kzalloc(sizeof(struct siw_mon_reader_txt), GFP_KERNEL); + if (rp == NULL) { + mon_pr_err(SIW_MON_PRT_OPEN_TAG \ + "unabled to allocate rp for %s\n", mterm->name); + ret = -ENOMEM; + goto out_alloc; + } + INIT_LIST_HEAD(&rp->e_list); + init_waitqueue_head(&rp->wait); + mutex_init(&rp->printf_lock); + + rp->printf_size = SIW_MON_PRINT_SIZE; + rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL); + if (rp->printf_buf == NULL) { + ret = -ENOMEM; + goto out_alloc_pr; + } + + rp->r.mterm = mterm; + rp->r.r_data = rp; + rp->r.submit = siw_mon_prt_submit; + + snprintf(rp->slab_name, SLAB_NAME_SZ, "siwmon_text_%p", rp); + rp->e_slab = kmem_cache_create(rp->slab_name, + sizeof(struct siw_mon_event_txt), + sizeof(long), + 0, + siw_mon_txt_ctor); + if (rp->e_slab == NULL) { + mon_pr_err(SIW_MON_PRT_OPEN_TAG \ + "failed to create memory cache for rp slab\n"); + ret = -ENOMEM; + goto out_slab; + } + + siw_mon_reader_add(mterm, &rp->r); + + atomic_set(&rp->read_running, 1); + + file->private_data = rp; + + mon_pr_dbg(SIW_MON_PRT_OPEN_TAG \ + "open file - mterm[%s]\n", mterm->name); + + mutex_unlock(&__siw_mon_lock); + return 0; + +// out_busy: +// kmem_cache_destroy(rp->e_slab); +out_slab: + kfree(rp->printf_buf); +out_alloc_pr: + kfree(rp); +out_alloc: + mutex_unlock(&__siw_mon_lock); + return ret; +} + +/* + * [31:16] : original total size + * [15:00] : adjusted total size by print limit + * See 'siw_mon_submit_bus', 'siw_mon_submit_ops' + */ +static int __siw_mon_txt_read_get_len(int size, int *total) +{ + int tot; + int len; + + len = siw_mon_buf_size_tot(size); //original total size + tot = (len) ? len : size; + + if (total) + *total = tot; + + return siw_mon_buf_size_cur(size); //adjusted total size by print limit +} + +static int __siw_mon_txt_read_priv_idx(struct siw_mon_txt_ptr *p, + int priv, int total) +{ + int priv_main; + int priv_sub; + int cur, tot; + int last = 0; + + if (!priv) + goto out; + + priv_main = siw_mon_priv_main(priv); + priv_sub = siw_mon_priv_sub(priv); + + if (priv_main) { + cur = siw_mon_priv_cur(priv_main); + tot = siw_mon_priv_tot(priv_main); + + p->cnt += siw_mon_prt_idx_main_snprintf(p, cur, tot); + } + + if (priv_sub) { + cur = siw_mon_priv_cur(priv_sub); + tot = siw_mon_priv_tot(priv_sub); + + p->cnt += siw_mon_prt_idx_sub_snprintf(p, cur, tot); + + if (total && ((cur + 1) == tot)) { + if ((tot * siw_mon_prt_max()) < total) { + last = 1; + } + } + } + +out: + return last; +} + +static int __siw_mon_txt_read_prt_buf(struct siw_mon_txt_ptr *p, + unsigned char *buf, int size, + char *dir, int total) +{ + int i; + int prt = 0; + + if (!buf) + goto out; + + if (!size) + goto out; + + if (dir) + p->cnt += siw_mon_prt_snprintf(p, " %s(%3d)", dir, total); + + for (i = 0; i < size; i++) { + p->cnt += siw_mon_prt_snprintf(p, " %02X", buf[i]); + } + + prt = 1; + +out: + return prt; +} + +static void __siw_mon_txt_read_bus(struct siw_mon_reader_txt *rp, + struct siw_mon_txt_ptr *p, struct siw_mon_event_txt *etxt) + +{ + struct siw_mon_data_bus *bus = &etxt->data.d.bus; + int total; + int len; + int last = 0; + int prt = 0; + + p->cnt += siw_mon_prt_snprintf(p, "%s", bus->dir); + + if (bus->tx_buf) { + len = __siw_mon_txt_read_get_len(bus->tx_size, &total); + last += __siw_mon_txt_read_priv_idx(p, bus->priv, total); + prt += __siw_mon_txt_read_prt_buf(p, bus->tx_buf, len, "T", total); + siw_mon_put_buf_cache(bus->tx_buf); + } + + if (bus->rx_buf) { + len = __siw_mon_txt_read_get_len(bus->rx_size, &total); + last += __siw_mon_txt_read_priv_idx(p, bus->priv, total); + prt += __siw_mon_txt_read_prt_buf(p, bus->rx_buf, len, "R", total); + siw_mon_put_buf_cache(bus->rx_buf); + } + + if (prt && last) { + p->cnt += siw_mon_prt_snprintf(p, " ..."); + } +} + +static void __siw_mon_txt_read_evt(struct siw_mon_reader_txt *rp, + struct siw_mon_txt_ptr *p, struct siw_mon_event_txt *etxt) + +{ + struct siw_mon_data_evt *evt = &etxt->data.d.evt; + char *type = evt->type; + + if (!strncmp(SIW_MON_UEVENT_STR, evt->type, sizeof(SIW_MON_UEVENT_STR))) { + type = &evt->type[1]; + } + + p->cnt += siw_mon_prt_snprintf(p, "%s(%02X) %20s(%02X) %d", + type, evt->type_v, + evt->code, evt->code_v, + evt->value); +} + +static int __siw_mon_txt_read_ops_sock(struct siw_mon_reader_txt *rp, + struct siw_mon_txt_ptr *p, struct siw_mon_event_txt *etxt) +{ + struct siw_mon_data_ops *ops = &etxt->data.d.ops; + char *title; + unsigned char *buf; + int size, total, len; + int last = 0; + int idx = 0; + int prt = 0; + + idx = siw_mon_txt_sock_str_cmp(ops->ops); + if (idx < 0) + return idx; + + title = (idx == SIW_MON_SOCK_S) ? "SOCK_S" : "SOCK_R"; + p->cnt += siw_mon_prt_snprintf(p, "%s", title); + + buf = (unsigned char *)ops->data[0]; + size = ops->data[1]; + + len = __siw_mon_txt_read_get_len(size, &total); + last += __siw_mon_txt_read_priv_idx(p, ops->priv, total); + prt += __siw_mon_txt_read_prt_buf(p, buf, len, NULL, 0); + + if (prt && last) { + p->cnt += siw_mon_prt_snprintf(p, " ..."); + } + + siw_mon_put_buf_cache(buf); + + return 0; +} + +static void __siw_mon_txt_read_ops(struct siw_mon_reader_txt *rp, + struct siw_mon_txt_ptr *p, struct siw_mon_event_txt *etxt) + +{ + struct siw_mon_data_ops *ops = &etxt->data.d.ops; + int len = min(siw_mon_prt_max(), ops->len); + int i; + + if (!__siw_mon_txt_read_ops_sock(rp, p, etxt)) { + return; + } + + p->cnt += siw_mon_prt_snprintf(p, "%s ", ops->ops); + + if (!len) + return; + + for (i = 0; i < len; i++) { + p->cnt += siw_mon_prt_snprintf(p, " %08zX", (size_t)ops->data[i]); + } +} + +static void __siw_mon_txt_read_x(struct siw_mon_reader_txt *rp, + struct siw_mon_txt_ptr *p, struct siw_mon_event_txt *etxt) + +{ + p->cnt += siw_mon_prt_snprintf(p, "(can't do analysis about this)"); +} + +static void siw_mon_txt_read_data(struct siw_mon_reader_txt *rp, + struct siw_mon_txt_ptr *p, struct siw_mon_event_txt *etxt) +{ + switch (etxt->type) { + case SIW_MON_BUS: + __siw_mon_txt_read_bus(rp, p, etxt); + break; + case SIW_MON_EVT: + __siw_mon_txt_read_evt(rp, p, etxt); + break; + case SIW_MON_OPS: + __siw_mon_txt_read_ops(rp, p, etxt); + break; + default: + __siw_mon_txt_read_x(rp, p, etxt); + break; + } + + p->cnt += siw_mon_prt_snprintf_finish(p); +} + +static void siw_mon_txt_read_head(struct siw_mon_reader_txt *rp, + struct siw_mon_txt_ptr *p, const struct siw_mon_event_txt *etxt) +{ + struct device *dev = etxt->data.dev; + struct timeval tval; + char *type_str; + + switch (etxt->type) { + case SIW_MON_BUS: type_str = "B"; break; + case SIW_MON_EVT: type_str = "E"; break; + case SIW_MON_OPS: type_str = "O"; break; + default: type_str = "x"; break; + } + + if (etxt->data.inc_cnt) { + tval = ktime_to_timeval(etxt->time); + + p->cnt += siw_mon_prt_snprintf(p, + "[" SIW_MON_TIME_FORMAT "] " "%s[%8s](%08X)(%4d) ", + tval.tv_sec, tval.tv_usec, + type_str, dev_name(dev), + etxt->data.cnt_events, + etxt->data.ret); + } else { + p->cnt += siw_mon_prt_snprintf(p, + "[" SIW_MON_TIME_FORMAT_NOP "] " "%s[%8s](%08X)(%4d) ", + type_str, dev_name(dev), + etxt->data.cnt_events, + etxt->data.ret); + } +} + +/* + * Fetch next event from the circular buffer. + */ +static struct siw_mon_event_txt *siw_mon_prt_fetch( + struct siw_mon_reader_txt *rp, + struct siw_mon_term *mterm) +{ + struct list_head *p; + unsigned long flags; + + spin_lock_irqsave(&mterm->lock, flags); + if (list_empty(&rp->e_list)) { + spin_unlock_irqrestore(&mterm->lock, flags); + return NULL; + } + p = rp->e_list.next; + list_del(p); + --rp->nevents; + spin_unlock_irqrestore(&mterm->lock, flags); + return list_entry(p, struct siw_mon_event_txt, e_link); +} + + +static struct siw_mon_event_txt *siw_mon_prt_read_wait( + struct siw_mon_reader_txt *rp, + struct file *file) +{ + struct siw_mon_term *mterm = rp->r.mterm; + DECLARE_WAITQUEUE(waita, current); + struct siw_mon_event_txt *etxt; + + add_wait_queue(&rp->wait, &waita); + set_current_state(TASK_INTERRUPTIBLE); + while ((etxt = siw_mon_prt_fetch(rp, mterm)) == NULL) { + if (file->f_flags & O_NONBLOCK) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&rp->wait, &waita); + return ERR_PTR(-EWOULDBLOCK); + } + /* + * We do not count nwaiters, because ->release is supposed + * to be called when all openers are gone only. + */ + schedule(); + if (signal_pending(current)) { + remove_wait_queue(&rp->wait, &waita); + return ERR_PTR(-EINTR); + } + set_current_state(TASK_INTERRUPTIBLE); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&rp->wait, &waita); + return etxt; +} + +#define SIW_MON_PRT_READ_TAG "prt_read: " + +/* + * For simplicity, we read one record in one system call and throw out + * what does not fit. This means that the following does not work: + * dd if=/dbg/usbmon/0t bs=10 + * Also, we do not allow seeks and do not bother advancing the offset. + */ +static ssize_t siw_mon_prt_read(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct siw_mon_reader_txt *rp = file->private_data; + struct siw_mon_event_txt *etxt; + struct siw_mon_txt_ptr ptr; + + mon_pr_test(SIW_MON_PRT_READ_TAG "start\n"); + + if (!rp) { + mon_pr_err(SIW_MON_PRT_READ_TAG "NULL rp\n"); + return -EINVAL; + } + + if (IS_ERR(etxt = siw_mon_prt_read_wait(rp, file))) { + mon_pr_test(SIW_MON_PRT_READ_TAG \ + "read_wait stop, %d\n", (int)PTR_ERR(etxt)); + atomic_set(&rp->read_running, 0); + return PTR_ERR(etxt); + } + + mutex_lock(&rp->printf_lock); + ptr.cnt = 0; + ptr.pbuf = rp->printf_buf; + ptr.limit = rp->printf_size; + + siw_mon_txt_read_head(rp, &ptr, etxt); + siw_mon_txt_read_data(rp, &ptr, etxt); + + if (copy_to_user(buf, rp->printf_buf, ptr.cnt)) + ptr.cnt = -EFAULT; + + mutex_unlock(&rp->printf_lock); + kmem_cache_free(rp->e_slab, etxt); + + mon_pr_test(SIW_MON_PRT_READ_TAG "done, %d\n", ptr.cnt); + + return ptr.cnt; +} + +#define SIW_MON_PRT_RELEASE_TAG "prt_release: " + +static int siw_mon_prt_release(struct inode *inode, struct file *file) +{ + struct siw_mon_reader_txt *rp = file->private_data; + struct siw_mon_term *mterm; + /* unsigned long flags; */ + struct list_head *p; + struct siw_mon_event_txt *etxt; + int ret = 0; + + mutex_lock(&__siw_mon_lock); + + if (!rp) { + mon_pr_err(SIW_MON_PRT_RELEASE_TAG \ + "NULL rp\n"); + ret = -EINVAL; + goto out; + } + + atomic_set(&rp->read_running, 0); + + if (inode == NULL) { + mon_pr_err(SIW_MON_PRT_RELEASE_TAG \ + "NULL inode\n"); + ret = -EINVAL; + goto out; + } + + mterm = inode->i_private; + if (mterm == NULL) { + mon_pr_err(SIW_MON_PRT_RELEASE_TAG \ + "NULL mterm\n"); + ret = -EINVAL; + goto out; + } + + if (mterm->nreaders <= 0) { + mon_pr_err(SIW_MON_PRT_RELEASE_TAG \ + "consistency error on close\n"); + goto out; + } + siw_mon_reader_del(mterm, &rp->r); + + /* + * In theory, e_list is protected by mbus->lock. However, + * after mon_reader_del has finished, the following is the case: + * - we are not on reader list anymore, so new events won't be added; + * - whole mbus may be dropped if it was orphaned. + * So, we better not touch mbus. + */ + /* spin_lock_irqsave(&mterm->lock, flags); */ + while (!list_empty(&rp->e_list)) { + p = rp->e_list.next; + etxt = list_entry(p, struct siw_mon_event_txt, e_link); + list_del(p); + --rp->nevents; + kmem_cache_free(rp->e_slab, etxt); + } + /* spin_unlock_irqrestore(&mterm->lock, flags); */ + + kmem_cache_destroy(rp->e_slab); + kfree(rp->printf_buf); + kfree(rp); + + file->private_data = NULL; + + mon_pr_dbg(SIW_MON_PRT_RELEASE_TAG \ + "release file - mterm[%s]\n", mterm->name); + + mon_pr_test(SIW_MON_PRT_RELEASE_TAG \ + "release file - mterm[%s]\n", mterm->name); + +out: + mutex_unlock(&__siw_mon_lock); + return ret; +} + +static const struct file_operations mon_fops_prt = { + .owner = THIS_MODULE, + .open = siw_mon_prt_open, + .llseek = no_llseek, + .read = siw_mon_prt_read, + .release = siw_mon_prt_release, +}; + +#define SIW_MON_PRT_ADD_TAG "prt_add: " + +int siw_mon_prt_add(struct siw_mon_term *mterm, char *name) +{ + struct dentry *mroot; + struct dentry *mfile; + enum { NAMESZ = 10 }; + char fname[NAMESZ]; + int ret = 0; + + if (__siw_mon_debugfs_ctrl == NULL) { + mon_pr_warn(SIW_MON_PRT_ADD_TAG \ + "unable to create file : NULL ctrl\n"); + return 0; + } + mroot = __siw_mon_debugfs_ctrl->root; + + if (mroot == NULL) { + mon_pr_warn(SIW_MON_PRT_ADD_TAG \ + "unable to create file : NULL root\n"); + return 0; + } + if (mterm == NULL) { + mon_pr_warn(SIW_MON_PRT_ADD_TAG \ + "unable to create file : NULL mterm\n"); + return 0; + } + + ret = snprintf(fname, NAMESZ, "%s", name); + if (ret <= 0 || ret >= NAMESZ) { + mon_pr_err(SIW_MON_PRT_ADD_TAG \ + "failed to arrange file name[%s]\n", name); + goto out; + } + + mfile = debugfs_create_file(fname, 0444, + mroot, mterm, + &mon_fops_prt); + if (mfile == NULL) { + mon_pr_err(SIW_MON_PRT_ADD_TAG \ + "failed to create SiW debug file : %s\n", fname); + goto out; + } + + mterm->mfile = mfile; + + return 1; + +out: + return 0; +} + +void siw_mon_prt_del(struct siw_mon_term *mterm) +{ + if (mterm->mfile != NULL) { + debugfs_remove(mterm->mfile); + mterm->mfile = NULL; + } +} + +int siw_mon_prt_init(void) +{ + struct siw_mon_debugfs_ctrl *ctrl; + struct dentry *mroot = NULL; + char *name = SIW_MON_NAME; + + ctrl = kzalloc(sizeof(struct siw_mon_debugfs_ctrl), GFP_KERNEL); + if (!ctrl) { + mon_pr_err("unable to create siw mon ctrl\n"); + return -ENOMEM; + } + + mroot = debugfs_create_dir((const char *)name, NULL); + if (!mroot) { + mon_pr_err("unable to create siw mon dir[%s]\n", name); + return -ENOMEM; + } + mon_pr_dbg("siw mon dir created : %s\n", mroot->d_iname); + + ctrl->root = mroot; + + __siw_mon_debugfs_ctrl = ctrl; + + siw_mon_prt_init_files(ctrl); + + return 0; +} + +void siw_mon_prt_exit(void) +{ + struct siw_mon_debugfs_ctrl *ctrl = __siw_mon_debugfs_ctrl; + struct dentry *mroot; + + if (!ctrl) + return; + + mroot = ctrl->root; + if (!mroot) + return; + + mon_pr_dbg("siw mon dir removed : %s\n", mroot->d_iname); + + debugfs_remove_recursive(mroot); + + kfree(ctrl); + __siw_mon_debugfs_ctrl = NULL; +} + diff --git a/drivers/input/touchscreen/siw/siw_touch.c b/drivers/input/touchscreen/siw/siw_touch.c new file mode 100644 index 000000000000..699f596cc608 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch.c @@ -0,0 +1,1962 @@ +/* + * siw_touch.c - SiW touch core driver + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "siw_touch.h" +#include "siw_touch_hal.h" +#include "siw_touch_bus.h" +#include "siw_touch_event.h" +#include "siw_touch_gpio.h" +#include "siw_touch_irq.h" +#include "siw_touch_sys.h" + + +#if defined(SOMC_TOUCH_BRINGUP) +#include +#endif + +#if !defined(__SIW_CONFIG_OF) +//#pragma message("[SiW - Warning] No COFIG_OF") +#endif + +extern int siw_touch_init_sysfs(struct siw_ts *ts); +extern void siw_touch_free_sysfs(struct siw_ts *ts); + +extern int siw_touch_add_sysfs(struct siw_ts *ts); +extern void siw_touch_del_sysfs(struct siw_ts *ts); + +extern int siw_touch_parse_data(struct siw_ts *ts); + +#if defined(SOMC_TOUCH_BRINGUP) +void siw_init_late_session_work(struct work_struct *work); +#endif + +#if 0 +u32 t_pr_dbg_mask = DBG_NONE | DBG_INFO; +u32 t_dev_dbg_mask = DBG_NONE | DBG_BASE | DBG_INFO | DBG_GPIO | DBG_OF; +#else +u32 t_pr_dbg_mask = DBG_NONE; +u32 t_dev_dbg_mask = DBG_NONE; +/* + * DBG_NONE | DBG_BASE | DBG_IRQ | DBG_NOTI | DBG_EVENT = 201326721(0xC000081) + */ +#endif + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/pr_dbg_mask + * (2) insmod {Siw Touch Module Name}.ko pr_dbg_mask= + */ +module_param_named(pr_dbg_mask, t_pr_dbg_mask, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/dev_dbg_mask + * (2) insmod {Siw Touch Module Name}.ko dev_dbg_mask= + */ +module_param_named(dev_dbg_mask, t_dev_dbg_mask, uint, S_IRUGO|S_IWUSR|S_IWGRP); + + +u32 t_dbg_flag = 0; +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/dbg_flag + * (2) insmod {Siw Touch Module Name}.ko dbg_flag= + */ +module_param_named(dbg_flag, t_dbg_flag, uint, S_IRUGO|S_IWUSR|S_IWGRP); + + +u32 t_mfts_lpwg = 0; +u32 t_lpwg_mode = LPWG_NONE; +u32 t_lpwg_screen = 1; +u32 t_lpwg_sensor = PROX_FAR; +u32 t_lpwg_qcover = 0; + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/mfts_lpwg_mode + * (2) insmod {Siw Touch Module Name}.ko mfts_lpwg_mode= + */ +module_param_named(mfts_lpwg_mode, t_mfts_lpwg, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/lpwg_mode + * (2) insmod {Siw Touch Module Name}.ko lpwg_mode= + */ +module_param_named(lpwg_mode, t_lpwg_mode, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/lpwg_screen + * (2) insmod {Siw Touch Module Name}.ko lpwg_screen= + */ +module_param_named(lpwg_screen, t_lpwg_screen, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/lpwg_sensor + * (2) insmod {Siw Touch Module Name}.ko lpwg_sensor= + */ +module_param_named(lpwg_sensor, t_lpwg_sensor, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/lpwg_qcover + * (2) insmod {Siw Touch Module Name}.ko lpwg_qcover= + */ +module_param_named(lpwg_qcover, t_lpwg_qcover, uint, S_IRUGO|S_IWUSR|S_IWGRP); + + +static int siw_setup_names(struct siw_ts *ts, struct siw_touch_pdata *pdata) +{ + struct device *dev = ts->dev; + char *name; + + /* + * Mandatory + */ + name = pdata_chip_id(pdata); + if (name == NULL) { + return -EFAULT; + } + ts->chip_id = name; + t_dev_info(dev, "chip id : %s\n", name); + + name = pdata_chip_name(pdata); + if (name == NULL) { + return -EFAULT; + } + ts->chip_name = name; + t_dev_info(dev, "chip name : %s\n", name); + + /* + * Optional + */ + name = pdata_drv_name(pdata); + if (name == NULL) { + name = SIW_TOUCH_NAME; + } + ts->drv_name = name; + t_dev_info(dev, "drv name : %s\n", name); + + name = pdata_idrv_name(pdata); + if (name == NULL) { + name = SIW_TOUCH_INPUT; + } + ts->idrv_name = name; + t_dev_info(dev, "idrv name : %s\n", name); + + if (!pdata_test_quirks(pdata, CHIP_QUIRK_NOT_SUPPORT_WATCH)) { + name = pdata_ext_watch_name(pdata); + if (name == NULL) { + name = SIW_TOUCH_EXT_WATCH; + } + ts->ext_watch_name = name; + t_dev_info(dev, "watch name : %s\n", name); + } + + return 0; +} + +int siw_setup_params(struct siw_ts *ts, struct siw_touch_pdata *pdata) +{ + struct device *dev = ts->dev; + int max_finger = 0; + int type = 0; + u32 mode_allowed = 0; + int ret = 0; + + max_finger = pdata_max_finger(pdata); + if ((max_finger < 0) || (max_finger > MAX_FINGER)) { + t_dev_err(dev, "invalid max finger, %d\n", max_finger); + return -EFAULT; + } + ts->max_finger = max_finger; + t_dev_info(dev, "max finger : %d\n", max_finger); + + type = pdata_chip_type(pdata); + if (!type) { + return -EFAULT; + } + ts->chip_type = type; + t_dev_info(dev, "chip type : 0x%04X\n", type); + + mode_allowed = pdata->mode_allowed; + if (!mode_allowed) { + return -EFAULT; + } +#if defined(__SIW_CONFIG_SYSTEM_PM) + mode_allowed &= ~(LCD_MODE_BIT_U0|LCD_MODE_BIT_U3_PARTIAL); + mode_allowed &= ~(LCD_MODE_BIT_U2|LCD_MODE_BIT_U2_UNBLANK); +#endif /* __SIW_CONFIG_SYSTEM_PM */ + ts->mode_allowed = mode_allowed; + t_dev_info(dev, "mode bit : 0x%08X\n", mode_allowed); + + ret = siw_setup_names(ts, pdata); + if (ret < 0) { + return ret; + } + + return 0; +} + +/* + * SiW Operations + */ +static void siw_setup_reg_quirks(struct siw_ts *ts) +{ + if (ts->pdata->reg_quirks) { + struct device *dev = ts->dev; + // struct siw_hal_reg *reg = siw_ops_reg(ts); + u32 *curr_reg; + u32 *copy_reg; + struct siw_hal_reg *reg_org; + struct siw_hal_reg_quirk *reg_quirks = ts->pdata->reg_quirks; + int cnt = sizeof(struct siw_hal_reg)>>2; + char *name = touch_chip_name(ts); + u32 new_addr, old_addr; + int total = 0; + int missing = 0; + int show_log = 1; + int found = 0; + int i; + + reg_org = ts->ops_ext->reg; + + while (1) { + old_addr = reg_quirks->old_addr; + new_addr = reg_quirks->new_addr; + + if (old_addr == (1<<31)) { + t_dev_info(dev, "%s reg quirks: ...\n", + name); + show_log = 0; + reg_quirks++; + continue; + } + + if ((old_addr == ~0) || (new_addr == ~0)) { + break; + } + + found = 0; + copy_reg = (u32 *)reg_org; + curr_reg = (u32 *)siw_ops_reg(ts); + for (i = 0; i < cnt; i++) { + if ((*copy_reg) == old_addr) { + (*curr_reg) = new_addr; + found = 1; + + break; + } + copy_reg++; + curr_reg++; + } + if (found) { + if (show_log) { + t_dev_info(dev, "%s reg quirks: [%d] %04Xh -> %04Xh\n", + name, total, + old_addr, new_addr); + } + } else { + t_dev_warn(dev, "%s reg quirks: [%d] %04Xh not found\n", + name, total, + old_addr); + missing++; + } + total++; + + reg_quirks++; + } + t_dev_info(dev, "%s reg quirks: t %d, m %d\n", + name, total, missing); + } +} + +void *siw_setup_operations(struct siw_ts *ts, struct siw_touch_operations *ops_ext) +{ + if (!ops_ext) + return NULL; + + ts->ops_ext = ops_ext; + memcpy(&ts->ops_in, ops_ext, sizeof(struct siw_touch_operations)); + + ts->ops_in.reg = &ts->__reg; + memcpy(ts->ops_in.reg, ops_ext->reg, sizeof(struct siw_hal_reg)); + + ts->ops = &ts->ops_in; + + siw_setup_reg_quirks(ts); + + return ts->ops; +} + +/** + * siw_touch_set() - set touch data + * @dev: device to use + * @cmd: set command + * @buf: data to store + * + * Return: + * On success, the total number of bytes of data stored to device. + * Otherwise, it returns zero or minus value + */ +int siw_touch_set(struct device *dev, u32 cmd, void *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int ret = 0; + + if (!buf) + t_dev_err(dev, "NULL buf\n"); + + mutex_lock(&ts->lock); + ret = siw_ops_set(ts, cmd, buf); + mutex_unlock(&ts->lock); + + return ret; +} + +/** + * siw_touch_get() - get touch data + * @dev: device to use + * @cmd: set command + * @buf: data to store + * + * Return: + * On success, the total number of bytes of data loaded from device. + * Otherwise, it returns zero or minus value + */ +int siw_touch_get(struct device *dev, u32 cmd, void *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int ret = 0; + + if (!buf) + t_dev_err(dev, "NULL buf\n"); + + mutex_lock(&ts->lock); + ret = siw_ops_get(ts, cmd, buf); + mutex_unlock(&ts->lock); + + return ret; +} + +/** + * siw_touch_suspend() - touch suspend + * @dev: device to use + * + */ +static void siw_touch_suspend(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + int ret = 0; + + t_dev_info(dev, "touch core pm suspend start\n"); + + cancel_delayed_work_sync(&ts->init_work); + cancel_delayed_work_sync(&ts->upgrade_work); + atomic_set(&ts->state.uevent, UEVENT_IDLE); + + mutex_lock(&ts->lock); + siw_touch_report_all_event(ts); + atomic_set(&ts->state.fb, FB_SUSPEND); + /* if need skip, return value is not 0 in pre_suspend */ + ret = siw_ops_suspend(ts); + mutex_unlock(&ts->lock); + + t_dev_info(dev, "touch core pm suspend end(%d)\n", ret); + + if (ret == 1) { + mod_delayed_work(ts->wq, &ts->init_work, 0); + } +} + +/** + * siw_touch_resume() - touch resume + * @dev: device to use + * + */ +static void siw_touch_resume(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + int ret = 0; + + t_dev_info(dev, "touch core pm resume start\n"); + + mutex_lock(&ts->lock); + atomic_set(&ts->state.fb, FB_RESUME); + /* if need skip, return value is not 0 in pre_resume */ + ret = siw_ops_resume(ts); + mutex_unlock(&ts->lock); + + t_dev_info(dev, "touch core pm resume end(%d)\n", ret); + + if (ret == 0) { + mod_delayed_work(ts->wq, &ts->init_work, 0); + } +} + +/** + * siw_touch_suspend_call() - Helper function for touch suspend + * @dev: device to use + * + */ +void siw_touch_suspend_call(struct device *dev) +{ +#if !defined(__SIW_CONFIG_EARLYSUSPEND) && !defined(__SIW_CONFIG_FB) + siw_touch_suspend(dev); +#endif +} + +/** + * siw_touch_resume_call() - Helper function for touch resume + * @dev: device to use + * + */ +void siw_touch_resume_call(struct device *dev) +{ +#if !defined(__SIW_CONFIG_EARLYSUSPEND) && !defined(__SIW_CONFIG_FB) + siw_touch_resume(dev); +#endif +} + +#if defined(__SIW_CONFIG_EARLYSUSPEND) +/** + * touch pm control using early pm + * + */ +static void siw_touch_early_suspend(struct early_suspend *h) +{ + struct siw_ts *ts = + container_of(h, struct siw_ts, early_suspend); + struct device *dev = ts->dev; + + t_dev_info(ts->dev, "early suspend\n"); + + siw_touch_suspend(dev); +} + +static void siw_touch_early_resume(struct early_suspend *h) +{ + struct siw_ts *ts = + container_of(h, struct siw_ts, early_suspend); + struct device *dev = ts->dev; + + t_dev_info(ts->dev, "early resume\n"); + + siw_touch_resume(dev); +} + +static int __used siw_touch_init_pm(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + + t_dev_dbg_pm(dev, "init pm\n"); + + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1; + ts->early_suspend.suspend = siw_touch_early_suspend; + ts->early_suspend.resume = siw_touch_early_resume; + register_early_suspend(&ts->early_suspend); + return 0; +} + +static int __used siw_touch_free_pm(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + + t_dev_dbg_pm(dev, "free pm\n"); + + unregister_early_suspend(&ts->early_suspend); + + return 0; +} +#elif defined(__SIW_CONFIG_FB) +/** + * touch pm control using FB notifier + * + */ +static int siw_touch_fb_notifier_callback( + struct notifier_block *self, + unsigned long event, void *data) +{ + struct siw_ts *ts = + container_of(self, struct siw_ts, fb_notif); + struct fb_event *ev = (struct fb_event *)data; + int *blank; + + if (!ev || !ev->data) { + return 0; + } + + blank = (int *)ev->data; + +#if defined(__SIW_CONFIG_SYSTEM_PM) + if (event == FB_EARLY_EVENT_BLANK) { + if (*blank == FB_BLANK_UNBLANK) { + t_dev_info(ts->dev, "fb_unblank(early)\n"); + } else if (*blank == FB_BLANK_POWERDOWN) { + t_dev_info(ts->dev, "fb_blank(early)\n"); + siw_touch_suspend(ts->dev); + } + } + + if (event == FB_EVENT_BLANK) { + if (*blank == FB_BLANK_UNBLANK) { + t_dev_info(ts->dev, "fb_unblank\n"); + siw_touch_resume(ts->dev); + } else if (*blank == FB_BLANK_POWERDOWN) { + t_dev_info(ts->dev, "fb_blank\n"); + } + } +#else /* __SIW_CONFIG_SYSTEM_PM */ + if (event == FB_EVENT_BLANK) { + if (*blank == FB_BLANK_UNBLANK) { + t_dev_info(ts->dev, "fb_unblank\n"); + siw_touch_resume(ts->dev); + } else if (*blank == FB_BLANK_POWERDOWN) { + t_dev_info(ts->dev, "fb_blank\n"); + siw_touch_suspend(ts->dev); + } + } +#endif /* __SIW_CONFIG_SYSTEM_PM */ + + return 0; +} + +static int __used siw_touch_init_pm(struct siw_ts *ts) +{ + t_dev_dbg_pm(ts->dev, "fb_register_client - fb_notif\n"); + + ts->fb_notif.notifier_call = siw_touch_fb_notifier_callback; + return fb_register_client(&ts->fb_notif); +} + +static int __used siw_touch_free_pm(struct siw_ts *ts) +{ + t_dev_dbg_pm(ts->dev, "fb_unregister_client - fb_notif\n"); + + fb_unregister_client(&ts->fb_notif); + + return 0; +} +#elif defined(__SIW_CONFIG_USER_FB) +static int __used siw_touch_init_pm(struct siw_ts *ts) +{ + t_dev_dbg_pm(ts->dev, "sys_fb_register_client\n"); + + return siw_touch_sys_fb_register_client(ts->dev); +} + +static int __used siw_touch_free_pm(struct siw_ts *ts) +{ + t_dev_dbg_pm(ts->dev, "sys_fb_unregister_client\n"); + + siw_touch_sys_fb_unregister_client(ts->dev); + + return 0; +} +#else +//#pragma message("[SiW - Warning] No core pm operation") +static int __used __siw_touch_init_pm_none(struct siw_ts *ts, int init) +{ + t_dev_dbg_pm(ts->dev, "pm %s none\n", (init) ? "free" : "init"); + return 0; +} +#define siw_touch_init_pm(_ts) __siw_touch_init_pm_none(_ts, 0) +#define siw_touch_free_pm(_ts) __siw_touch_init_pm_none(_ts, 1) +#endif + +#if defined(__SIW_SUPPORT_ASC) +/** + * siw_touch_get_max_delta - + * @ts : touch core info + * + */ +static void siw_touch_get_max_delta(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + struct asc_info *asc = &(ts->asc); + int ret; + + if (asc->use_delta_chk == DELTA_CHK_OFF) { + t_dev_warn(dev, "DELTA_CHK is OFf\n"); + return; + } + + if (atomic_read(&ts->state.fb) != FB_RESUME) { + t_dev_warn(dev, "fb state is not FB_RESUME\n"); + return; + } + + if (atomic_read(&ts->state.core) != CORE_NORMAL) { + t_dev_warn(dev, "core state is not CORE_NORMAL\n"); + return; + } + + mutex_lock(&ts->lock); + ret = siw_ops_asc(ts, ASC_READ_MAX_DELTA, 0); + mutex_unlock(&ts->lock); + if (ret < 0) { + t_dev_err(dev, "delta change failed, %d\n", ret); + return; + } + + asc->delta = ret; + asc->delta_updated = true; + + t_dev_info(dev, "delta = %d\n", asc->delta); +} + +static const char *asc_str[] = { + "NORMAL", + "ACUTE", + "OBTUSE", +}; + +/** + * siw_touch_change_sensitivity - change touch sensitivity + * @ts : touch core info + * @target : target sensitivity + * + */ +void siw_touch_change_sensitivity(struct siw_ts *ts, + int target) +{ + struct device *dev = ts->dev; + struct asc_info *asc = &(ts->asc); + int ret; + + if (atomic_read(&ts->state.fb) != FB_RESUME) { + t_dev_warn(dev, "fb state is not FB_RESUME\n"); + return; + } + + if (atomic_read(&ts->state.core) != CORE_NORMAL) { + t_dev_warn(dev, "core state is not CORE_NORMAL\n"); + return; + } + + if (asc->curr_sensitivity == target) { + return; + } + + t_dev_info(dev, "sensitivity(curr->next) = (%s -> %s)\n", + asc_str[asc->curr_sensitivity], + asc_str[target]); + + mutex_lock(&ts->lock); + ret = siw_ops_asc(ts, ASC_WRITE_SENSITIVITY, target); + mutex_unlock(&ts->lock); + if (ret < 0) { + t_dev_err(dev, "sensitivity change failed, %d\n", ret); + return; + } + + asc->curr_sensitivity = target; +} + +static void siw_touch_update_sensitivity(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + struct asc_info *asc = &(ts->asc); + int target = NORMAL_SENSITIVITY; + + if (asc->use_delta_chk == DELTA_CHK_OFF) { + t_dev_warn(dev, "DELTA_CHK is OFf\n"); + return; + } + + if (asc->delta_updated == false) { + t_dev_warn(dev, "delta is not updated.\n"); + return; + } + + if (asc->delta < asc->low_delta_thres) { + target = ACUTE_SENSITIVITY; + } else if (asc->delta > asc->high_delta_thres) { + target = OBTUSE_SENSITIVITY; + } else { + target = NORMAL_SENSITIVITY; + } + + asc->delta_updated = false; + siw_touch_change_sensitivity(ts, target); +} +#endif /* __SIW_SUPPORT_ASC */ + +#define SIW_TOUCH_LPWG_LOCK_NAME "touch_lpwg" + +static void __used siw_touch_init_locks(struct siw_ts *ts) +{ + t_dev_dbg_base(ts->dev, "touch init locks\n"); + + mutex_init(&ts->lock); + mutex_init(&ts->reset_lock); + mutex_init(&ts->probe_lock); +#if defined(__SIW_SUPPORT_WAKE_LOCK) + wake_lock_init(&ts->lpwg_wake_lock, + WAKE_LOCK_SUSPEND, SIW_TOUCH_LPWG_LOCK_NAME); +#endif +} + +static void __used siw_touch_free_locks(struct siw_ts *ts) +{ + t_dev_dbg_base(ts->dev, "free locks\n"); + + mutex_destroy(&ts->lock); + mutex_destroy(&ts->reset_lock); + mutex_destroy(&ts->probe_lock); +#if defined(__SIW_SUPPORT_WAKE_LOCK) + wake_lock_destroy(&ts->lpwg_wake_lock); +#endif +} + +static void siw_touch_initialize(struct siw_ts *ts) +{ + /* lockscreen */ + siw_touch_report_all_event(ts); +} + +static void siw_touch_init_work_func(struct work_struct *work) +{ + struct siw_ts *ts = + container_of(to_delayed_work(work), + struct siw_ts, init_work); + struct device *dev = ts->dev; + int do_fw_upgarde = 0; + int ret = 0; + + t_dev_dbg_base(dev, "init work start\n"); + + if (atomic_read(&ts->state.core) == CORE_PROBE) { + do_fw_upgarde |= !!(ts->role.use_fw_upgrade); + } + + mutex_lock(&ts->lock); + siw_touch_initialize(ts); + ret = siw_ops_init(ts); + if (!ret) { + siw_touch_irq_control(dev, INTERRUPT_ENABLE); + } + mutex_unlock(&ts->lock); + if (ret == -ETDSENTESD) { + /* boot fail detected, but skip(postpone) fw_upgrade */ + return; + } else if (ret == -ETDBOOTFAIL) { + /* boot fail detected, do fw_upgrade */ + do_fw_upgarde |= 0x2; + } else if (ret < 0) { + if (atomic_read(&ts->state.core) == CORE_PROBE) { + t_dev_err(dev, "%s init work failed(%d), try again\n", + touch_chip_name(ts), ret); + atomic_set(&ts->state.core, CORE_NORMAL); + siw_touch_qd_init_work_now(ts); + return; + } + + t_dev_err(dev, "%s init work failed, %d\n", + touch_chip_name(ts), ret); + return; + } + + if (do_fw_upgarde) { + t_dev_info(dev, "Touch F/W upgrade triggered(%Xh)\n", do_fw_upgarde); + siw_touch_qd_upgrade_work_now(ts); + return; + } + +#if defined(__SIW_SUPPORT_ASC) + if (ts->asc.use_asc == ASC_ON) { + if (atomic_read(&ts->state.core) == CORE_UPGRADE) { + mutex_lock(&ts->lock); + ret = siw_ops_asc(ts, ASC_GET_FW_SENSITIVITY, 0); + mutex_unlock(&ts->lock); + if (ret < 0) { + t_dev_warn(dev, "sensitivity change failed, %d\n", ret); + } + } + + siw_touch_qd_toggle_delta_work_jiffies(ts, 0); + } +#endif /* __SIW_SUPPORT_ASC */ + + atomic_set(&ts->state.core, CORE_NORMAL); + + t_dev_dbg_base(dev, "init work done\n"); +} + +static void siw_touch_upgrade_work_func(struct work_struct *work) +{ + struct siw_ts *ts = + container_of(to_delayed_work(work), + struct siw_ts, upgrade_work); + struct device *dev = ts->dev; + int ret = 0; +#if defined(SOMC_TOUCH_BRINGUP) + incell_pw_status pwr_status = { false, false }; + + if (incell_get_power_status(&pwr_status)) { + t_dev_err(ts->dev, "failed get power status\n"); + return; + } + + if (!pwr_status.display_power || !pwr_status.touch_power) { + t_dev_err(dev, "FW upgrade skipped: invalid power status\n"); + return; + } +#endif + + t_dev_info(dev, "FW upgrade work func\n"); + + atomic_set(&ts->state.core, CORE_UPGRADE); + ts->role.use_fw_upgrade = 0; + + mutex_lock(&ts->lock); + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + +#if defined(SOMC_TOUCH_BRINGUP) + incell_power_lock_ctrl(INCELL_DISPLAY_POWER_LOCK, &pwr_status); + ret = siw_ops_upgrade(ts); + incell_power_lock_ctrl(INCELL_DISPLAY_POWER_UNLOCK, &pwr_status); +#else + ret = siw_ops_upgrade(ts); +#endif + mutex_unlock(&ts->lock); + + /* init force_upgrade */ + ts->force_fwup = FORCE_FWUP_CLEAR; + ts->test_fwpath[0] = '\0'; + + if (ret < 0) { + if (ret == -EPERM) { + t_dev_err(dev, "FW upgrade skipped\n"); + } else { + t_dev_err(dev, "FW upgrade halted, %d\n", ret); + } + // siw_touch_qd_init_work_now(ts); + // atomic_set(&ts->state.core, CORE_NORMAL); + // return; + } + +#if 1 + siw_ops_reset(ts, HW_RESET_ASYNC); +#else + siw_ops_power(ts, POWER_OFF); + touch_msleep(1); + siw_ops_power(ts, POWER_ON); + + siw_touch_qd_init_work_now(ts); +#endif +} + +static void siw_touch_fb_work_func(struct work_struct *work) +{ + struct siw_ts *ts = + container_of(to_delayed_work(work), + struct siw_ts, fb_work); + + if (atomic_read(&ts->state.fb) == FB_SUSPEND) { + siw_touch_suspend(ts->dev); + siwmon_submit_ops_step_core(ts->dev, "FB suspend", 0); + } else if (atomic_read(&ts->state.fb) == FB_RESUME) { + siw_touch_resume(ts->dev); + siwmon_submit_ops_step_core(ts->dev, "FB Resume", 0); + } +} + +#if defined(__SIW_CONFIG_USE_SYS_PANEL_RESET) +static void siw_touch_sys_reset_work_func(struct work_struct *work) +{ + struct siw_ts *ts = + container_of(to_delayed_work(work), + struct siw_ts, sys_reset_work); + struct device *dev = ts->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + u32 lcd_mode = (u32)chip->lcd_mode; + int ret = 0; + + t_dev_info(dev, "sys reset start\n"); + + siw_touch_notify(ts, NOTIFY_TOUCH_RESET, NULL); + + /* + * direct call for LCD reset + */ + ret = siw_touch_sys_panel_reset(dev); + if (ret < 0) { + t_dev_err(dev, "failed to reset panel\n"); + } + + siw_touch_notify(ts, LCD_EVENT_LCD_MODE, (void *)&lcd_mode); +} + +static int siw_touch_reset(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + + siw_touch_qd_sys_reset_work_now(ts); + + return 0; +} +#else /* __SIW_CONFIG_USE_SYS_PANEL_RESET */ +static void siw_touch_sys_reset_work_func(struct work_struct *work) +{ + +} + +static int siw_touch_reset(struct siw_ts *ts) +{ + return siw_ops_reset(ts, HW_RESET_ASYNC); +} +#endif /* __SIW_CONFIG_USE_SYS_PANEL_RESET */ + + +#if defined(__SIW_SUPPORT_ASC) +static void siw_touch_toggle_delta_check_work_func( + struct work_struct *work) +{ + struct siw_ts *ts = + container_of(to_delayed_work(work), + struct siw_ts, toggle_delta_work); + struct device *dev = ts->dev; + struct asc_info *asc = &(ts->asc); + int connect_status = atomic_read(&ts->state.connect); + int wireless_status = atomic_read(&ts->state.wireless); + int call_status = atomic_read(&ts->state.incoming_call); + int onhand_status = atomic_read(&ts->state.onhand); + int delta = DELTA_CHK_OFF; + int target = NORMAL_SENSITIVITY; + + if (asc->use_asc == ASC_OFF) { + t_dev_info(dev, "ASC is off\n"); + return; + } + + t_dev_info(dev, "connect = %d, wireless = %d, call = %d, onhand = %d\n", + connect_status, wireless_status, + call_status, onhand_status); + + if ((connect_status != CONNECT_INVALID) || + (wireless_status != 0) || + (call_status != INCOMING_CALL_IDLE)) { + /* */ + } else { + delta = DELTA_CHK_ON; + + switch (onhand_status) { + case IN_HAND_ATTN : + case IN_HAND_NO_ATTN : + break; + case NOT_IN_HAND : + target = ACUTE_SENSITIVITY; + break; + default: + delta = -1; + break; + } + } + + if (delta < 0) { + t_dev_info(dev, "Unknown onhand_status\n"); + return; + } + + asc->use_delta_chk = delta; + siw_touch_change_sensitivity(ts, target); + + t_dev_info(dev, "curr_sensitivity = %s, use_delta_chk = %d\n", + asc_str[asc->curr_sensitivity], asc->use_delta_chk); + + siwmon_submit_ops_step_core(dev, "Delta work done", 0); +} + +static void siw_touch_finger_input_check_work_func( + struct work_struct *work) +{ + struct siw_ts *ts = + container_of(to_delayed_work(work), + struct siw_ts, finger_input_work); + + if ((ts->tcount == 1) && (!ts->asc.delta_updated)) + siw_touch_get_max_delta(ts); + + if ((ts->tcount == 0) && (ts->asc.delta_updated)) + siw_touch_update_sensitivity(ts); + + return; +} +#endif /* __SIW_SUPPORT_ASC */ + +static int siw_touch_mon_chk_pause(struct siw_ts *ts) +{ + struct siw_ts_thread *ts_thread = &ts->mon_thread; + int curr_state; + + mutex_lock(&ts_thread->lock); + curr_state = (atomic_read(&ts->state.mon_ignore)) ? \ + TS_THREAD_PAUSE : TS_THREAD_ON; + atomic_set(&ts_thread->state, curr_state); + mutex_unlock(&ts_thread->lock); + + return (curr_state == TS_THREAD_PAUSE); +} + +static int siw_touch_mon_can_handler(struct siw_ts *ts) +{ + if (touch_get_dev_data(ts) == NULL) { + return 0; + } + + if (atomic_read(&ts->state.core) != CORE_NORMAL) { + return 0; + } + + return 1; +} + +static int siw_touch_mon_thread(void *d) +{ + struct siw_ts *ts = d; + struct siw_ts_thread *ts_thread = &ts->mon_thread; + struct device *dev = ts->dev; + siw_mon_handler_t handler; + unsigned long timeout; + int ret = 0; + + handler = ts_thread->handler; + timeout = ts_thread->interval * HZ; + + atomic_set(&ts_thread->state, TS_THREAD_ON); + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout_interruptible(timeout); + + if (kthread_should_stop()) { + t_dev_info(dev, "stopping mon thread[%s]\n", + ts_thread->thread->comm); + set_current_state(TASK_RUNNING); + break; + } + + set_current_state(TASK_RUNNING); + + if (siw_touch_mon_chk_pause(ts)) { + continue; + } + + if (siw_touch_mon_can_handler(ts)) { + ret = handler(dev, 0); + } + + siw_touch_mon_chk_pause(ts); + } + + atomic_set(&ts_thread->state, TS_THREAD_OFF); + + return 0; +} + +static int siw_touch_mon_hold_set(struct device *dev, int pause) +{ + struct siw_ts *ts = to_touch_core(dev); + struct siw_ts_thread *ts_thread = &ts->mon_thread; + char *name = (pause) ? "pause" : "resume"; + int prev_state = (pause) ? TS_THREAD_ON : TS_THREAD_PAUSE; + + if (ts_thread->thread == NULL) { + return -EINVAL; + } + + if (atomic_read(&ts_thread->state) != prev_state) { + return -EINVAL; + } + + t_dev_info(dev, "mon thread %s\n", name); + + atomic_set(&ts->state.mon_ignore, !!pause); + + return 0; +} + +static void siw_touch_mon_hold(struct device *dev, int pause) +{ + struct siw_ts *ts = to_touch_core(dev); + struct siw_ts_thread *ts_thread = &ts->mon_thread; + char *name = (pause) ? "pause" : "resume"; + int new_state = (pause) ? TS_THREAD_PAUSE : TS_THREAD_ON; + int ret = 0; + + if (!(touch_flags(ts) & TOUCH_USE_MON_THREAD)) { + return; + } + + mutex_lock(&ts_thread->lock); + ret = siw_touch_mon_hold_set(dev, pause); + mutex_unlock(&ts_thread->lock); + if (ret < 0) { + return; + } + + while (1) { + wake_up_process(ts_thread->thread); + + touch_msleep(1); + + if (atomic_read(&ts_thread->state) == new_state) + break; + + t_dev_info(dev, + "waiting for mon thread %s\n", name); + } +} + +void siw_touch_mon_pause(struct device *dev) +{ + siw_touch_mon_hold(dev, 1); +} + +void siw_touch_mon_resume(struct device *dev) +{ + siw_touch_mon_hold(dev, 0); +} + +#define siw_touch_kthread_run(__dev, __threadfn, __data, __name) \ +({ \ + struct task_struct *__k; \ + __k = kthread_run(__threadfn, __data, "%s", __name); \ + if (IS_ERR(__k)) \ + t_dev_err(__dev, "kthread_run failed : %s\n", __name); \ + else \ + t_dev_dbg_base(__dev, "kthread[%s] is running\n", __k->comm); \ + __k; \ +}) + +static int __used siw_touch_init_thread(struct siw_ts *ts) +{ + struct siw_touch_fquirks *fquirks = touch_fquirks(ts); + struct device *dev = ts->dev; + struct siw_ts_thread *ts_thread = NULL; + struct task_struct *thread; + siw_mon_handler_t handler; + char *thread_name; + int interval; + int ret = 0; + + if (t_dbg_flag & DBG_FLAG_SKIP_MON_THREAD) { + ts->flags &= ~TOUCH_USE_MON_THREAD; + } + if (t_dbg_flag & DBG_FLAG_TEST_MON_THREAD) { + ts->flags |= TOUCH_USE_MON_THREAD; + } + + if (!(touch_flags(ts) & TOUCH_USE_MON_THREAD)) { + goto out; + } + + ts_thread = &ts->mon_thread; + + if (ts_thread->thread != NULL) { + goto out; + } + + mutex_init(&ts_thread->lock); + + if (fquirks->mon_handler) { + handler = fquirks->mon_handler; + interval = fquirks->mon_interval; + } else { + handler = ts->ops->mon_handler; + interval = ts->ops->mon_interval; + } + + if (!handler) { + t_dev_warn(dev, "No mon_handler defined!\n"); + goto out; + } + + if (!interval) { + t_dev_warn(dev, "mon_interval is zero!\n"); + goto out; + } + +/* + do { + touch_msleep(10); + } while (atomic_read(&ts->state.core) != CORE_NORMAL); +*/ + + atomic_set(&ts_thread->state, TS_THREAD_OFF); + ts_thread->interval = interval; + ts_thread->handler = handler; + + thread_name = touch_drv_name(ts); + thread_name = (thread_name) ? thread_name : SIW_TOUCH_NAME; + thread = siw_touch_kthread_run(dev, + siw_touch_mon_thread, ts, + thread_name); + if (IS_ERR(thread)) { + ret = PTR_ERR(thread); + goto out; + } + ts_thread->thread = thread; + t_dev_info(dev, "mon thread[%s, %d] begins\n", + thread->comm, ts->ops->mon_interval); + +out: + return ret; +} + +static void __used siw_touch_free_thread(struct siw_ts *ts) +{ +// struct device *dev = ts->dev; + struct siw_ts_thread *ts_thread = NULL; +// char comm[TASK_COMM_LEN] = { 0, }; + + if (!(touch_flags(ts) & TOUCH_USE_MON_THREAD)) { + return; + } + + ts_thread = &ts->mon_thread; + + if (ts_thread->thread == NULL) { + return; + } + +// memcpy(comm, ts_thread->thread->comm, TASK_COMM_LEN); + kthread_stop(ts_thread->thread); + + ts_thread->thread = NULL; + + mutex_destroy(&ts_thread->lock); +} + +static int __used siw_touch_init_works(struct siw_ts *ts) +{ + ts->wq = create_singlethread_workqueue("touch_wq"); + if (!ts->wq) { + t_dev_err(ts->dev, "failed to create workqueue\n"); + return -ENOMEM; + } + + INIT_DELAYED_WORK(&ts->init_work, siw_touch_init_work_func); + INIT_DELAYED_WORK(&ts->upgrade_work, siw_touch_upgrade_work_func); + INIT_DELAYED_WORK(&ts->fb_work, siw_touch_fb_work_func); +#if defined(__SIW_SUPPORT_ASC) + INIT_DELAYED_WORK(&ts->toggle_delta_work, + siw_touch_toggle_delta_check_work_func); + INIT_DELAYED_WORK(&ts->finger_input_work, + siw_touch_finger_input_check_work_func); +#endif /* __SIW_SUPPORT_ASC */ + INIT_DELAYED_WORK(&ts->notify_work, siw_touch_atomic_notifer_work_func); + INIT_DELAYED_WORK(&ts->sys_reset_work, siw_touch_sys_reset_work_func); + +#if defined(SOMC_TOUCH_BRINGUP) + INIT_DELAYED_WORK(&ts->session.work, siw_init_late_session_work); +#endif + + return 0; +} + +static void __used siw_touch_free_works(struct siw_ts *ts) +{ + if (ts->wq) { + cancel_delayed_work(&ts->sys_reset_work); + cancel_delayed_work(&ts->notify_work); + #if defined(__SIW_SUPPORT_ASC) + cancel_delayed_work(&ts->finger_input_work); + cancel_delayed_work(&ts->toggle_delta_work); + #endif /* __SIW_SUPPORT_ASC */ + cancel_delayed_work(&ts->fb_work); + cancel_delayed_work(&ts->upgrade_work); + cancel_delayed_work(&ts->init_work); + +#if defined(SOMC_TOUCH_BRINGUP) + cancel_delayed_work(&ts->session.work); +#endif + + destroy_workqueue(ts->wq); + ts->wq = NULL; + } +} + +static irqreturn_t __used siw_touch_irq_handler(int irq, void *dev_id) +{ + struct siw_ts *ts = (struct siw_ts *)dev_id; + struct device *dev = ts->dev; + + t_dev_dbg_irq(dev, "irq_handler\n"); + + if (atomic_read(&ts->state.pm) >= DEV_PM_SUSPEND) { + t_dev_info(dev, "interrupt in suspend[%d]\n", + atomic_read(&ts->state.pm)); + atomic_set(&ts->state.pm, DEV_PM_SUSPEND_IRQ); + #if defined(__SIW_SUPPORT_WAKE_LOCK) + wake_lock_timeout(&ts->lpwg_wake_lock, msecs_to_jiffies(1000)); + #endif + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static int _siw_touch_do_irq_thread(struct siw_ts *ts) +{ + int ret = 0; + + ts->intr_status = 0; + + if (atomic_read(&ts->state.core) != CORE_NORMAL) { + goto out; + } + + ret = siw_ops_irq_handler(ts); + if (ret < 0) { + t_dev_dbg_irq(ts->dev, "Err in irq_handler of %s, %d", + touch_chip_name(ts), ret); + if (ret == -ERESTART) { + if (t_dbg_flag & DBG_FLAG_SKIP_IRQ_RESET) { + return ret; + } + + if (atomic_read(&ts->state.pm) == DEV_PM_RESUME) { + #if defined(__SIW_SUPPORT_WAKE_LOCK) + wake_lock_timeout(&ts->lpwg_wake_lock, msecs_to_jiffies(1000)); + #endif + } + + siw_touch_reset(ts); + } + return ret; + } + + if (ts->intr_status & TOUCH_IRQ_FINGER) { + siw_touch_report_event(ts); + + #if defined(__SIW_SUPPORT_ASC) + if (ts->asc.use_delta_chk == DELTA_CHK_ON) { + siw_touch_qd_finger_input_work_jiffies(ts, 0); + } + #endif + } + + if (ts->intr_status & TOUCH_IRQ_KNOCK) + siw_touch_send_uevent(ts, TOUCH_UEVENT_KNOCK); + + if (ts->intr_status & TOUCH_IRQ_PASSWD) + siw_touch_send_uevent(ts, TOUCH_UEVENT_PASSWD); + + if (ts->intr_status & TOUCH_IRQ_SWIPE_RIGHT) + siw_touch_send_uevent(ts, TOUCH_UEVENT_SWIPE_RIGHT); + + if (ts->intr_status & TOUCH_IRQ_SWIPE_LEFT) + siw_touch_send_uevent(ts, TOUCH_UEVENT_SWIPE_LEFT); + + if (ts->intr_status & TOUCH_IRQ_GESTURE) + siw_touch_send_uevent(ts, ts->intr_gesture); + +out: + return ret; +} + +static irqreturn_t __used siw_touch_irq_thread(int irq, void *dev_id) +{ + struct siw_ts *ts = (struct siw_ts *)dev_id; + struct device *dev = ts->dev; + int ret = 0; + + t_dev_dbg_irq(dev, "irq_thread\n"); + + if (t_dbg_flag & DBG_FLAG_SKIP_IRQ) { + goto out; + } + + if (ts->ops->irq_dbg_handler) { + ret = ts->ops->irq_dbg_handler(dev); + goto out; + } + + mutex_lock(&ts->lock); + ret = _siw_touch_do_irq_thread(ts); + mutex_unlock(&ts->lock); + +out: + return IRQ_HANDLED; +} + +static int __used siw_touch_verify_pdata(struct siw_ts *ts) +{ + struct siw_touch_operations *ops = ts->ops; + + if (0 || + !ops->probe || + !ops->remove || + !ops->suspend || + !ops->resume || + !ops->init || + !ops->reset || + !ops->ic_info || + !ops->tc_driving || + !ops->chk_status || + !ops->irq_handler || + !ops->irq_abs || + !ops->irq_lpwg || + !ops->power || + !ops->upgrade || + !ops->asc || + 0) + return -EPERM; + + return 0; +} + +static struct siw_touch_pdata *siw_touch_probe_common(struct siw_ts *ts) +{ + struct siw_touch_pdata *pdata = NULL; + struct device *dev = ts->dev; + int ret = 0; + + pdata = (struct siw_touch_pdata *)ts->pdata; + if (!pdata) { + t_dev_err(dev, "NULL core pdata\n"); + goto out; + } + + t_dev_info(dev, "%s quirks = 0x%08X\n", + touch_chip_name(ts), (u32)touch_get_quirks(ts)); + + atomic_set(&ts->state.core, CORE_EARLY_PROBE); + + if (!ts->ops) { + t_dev_warn(dev, "%s ops is NULL : default ops selected\n", + touch_chip_name(ts)); + siw_setup_operations(ts, siw_hal_get_default_ops(0)); + } + + ret = siw_ops_early_probe(ts); + if (ret) { + t_dev_err(dev, "failed to early_probe, %d\n", ret); + goto out; + } + + atomic_set(&ts->state.core, CORE_PROBE); + + ret = siw_touch_verify_pdata(ts); + if (ret) { + t_dev_err(dev, "failed to check functions, %d\n", ret); + goto out; + } + + ret = siw_touch_parse_data(ts); + if (ret < 0) { + t_dev_err(dev, "failed to parse touch data, %d\n", ret); + goto out; + } + + ret = siw_touch_bus_alloc_buffer(ts); + if (ret < 0) { + t_dev_err(dev, "failed to alloc bus buffer, %d\n", ret); + goto out; + } + + ret = siw_touch_bus_pin_get(ts); + if (ret < 0) { + t_dev_err(dev, "failed to setup bus pin, %d\n", ret); + goto out_bus_pin; + } + + ret = siw_touch_bus_init(ts->dev); + if (ret < 0) { + t_dev_err(dev, "failed to setup bus, %d\n", ret); + goto out_bus_init; + } + + siw_touch_init_locks(ts); + + ret = siw_touch_init_works(ts); + if (ret) { + t_dev_err(dev, "failed to initialize works, %d\n", ret); + goto out_init_works; + } + + ret = siw_touch_init_input(ts); + if (ret) { + t_dev_err(dev, "failed to register input device, %d\n", ret); + goto out_init_input; + } + + ret = siw_touch_init_uevent(ts); + if (ret) { + t_dev_err(dev, "failed to initialize uevent, %d\n", ret); + goto out_init_uevent; + } + + ret = siw_touch_init_sysfs(ts); + if (ret) { + t_dev_err(dev, "failed to initialize sysfs, %d\n", ret); + goto out_init_sysfs; + } + + ret = siw_touch_init_notify(ts); + if (ret) { + t_dev_err(dev, "failed to initialize notifier, %d\n", ret); + goto out_init_notify; + } + + return pdata; + +out_init_notify: + siw_touch_free_sysfs(ts); + +out_init_sysfs: + siw_touch_free_uevent(ts); + +out_init_uevent: + siw_touch_free_input(ts); + +out_init_input: + siw_touch_free_works(ts); + +out_init_works: + siw_touch_free_locks(ts); + +out_bus_init: + siw_touch_bus_pin_put(ts); + +out_bus_pin: + siw_touch_bus_free_buffer(ts); + +out: + return NULL; +} + +static void siw_touch_remove_common(struct siw_ts *ts) +{ +// struct siw_touch_pdata *pdata = (struct siw_touch_pdata *)ts->pdata; + + siw_touch_free_notify(ts); + + siw_touch_free_sysfs(ts); + + siw_touch_free_uevent(ts); + + siw_touch_free_input(ts); + + siw_touch_free_works(ts); + + siw_touch_free_locks(ts); + + siw_touch_bus_pin_put(ts); + + siw_touch_bus_free_buffer(ts); +} + +static int __siw_touch_probe_init(void *data) +{ + struct siw_ts *ts = data; + struct device *dev = ts->dev; + const char *irq_name = NULL; + int ret = 0; + + ret = siw_ops_probe(ts); + if (ret) { + t_dev_err(dev, "failed to probe, %d\n", ret); + goto out; + } + + ret = siw_touch_add_sysfs(ts); + if (ret) { + t_dev_err(dev, "failed to add sysfs, %d\n", ret); + goto out_add_sysfs; + } + + ret = siw_touch_init_pm(ts); + if (ret) { + t_dev_err(dev, "failed to init pm\n"); + goto out_init_pm; + } + + if (ts->is_charger) { + goto skip_charger; + } + + siw_ops_power(ts, POWER_OFF); + siw_ops_power(ts, POWER_ON); + +#if defined(__SIW_TEST_IRQ_OFF) + ts->irq = 0; +#else /* __SIW_TEST_IRQ_OFF */ + irq_name = (const char *)touch_drv_name(ts); + irq_name = (irq_name) ? irq_name : SIW_TOUCH_NAME; + ret = siw_touch_request_irq(ts, + siw_touch_irq_handler, + siw_touch_irq_thread, + touch_irqflags(ts), + irq_name); + if (ret) { + t_dev_err(dev, "failed to request thread irq(%d), %d\n", + ts->irq, ret); + goto out_request_irq; + } +// irq_set_irq_type(ts->irq, IRQF_ONESHOT | IRQ_TYPE_EDGE_FALLING); +#endif /* __SIW_TEST_IRQ_OFF */ + + siw_touch_disable_irq(dev, ts->irq); +// t_dev_dbg_irq(dev, "disable irq until init completed\n"); + + ret = siw_touch_init_thread(ts); + if (ret) { + t_dev_err(dev, "failed to create thread\n"); + goto out_init_thread; + } + + t_dev_dbg_base(dev, "hw_reset_delay : %d ms\n", ts->caps.hw_reset_delay); + siw_touch_qd_init_work_hw(ts); + +skip_charger: + ts->init_late_done = 1; + + return 0; + +out_init_thread: + siw_touch_free_irq(ts); + +out_request_irq: + siw_ops_power(ts, POWER_OFF); + + siw_touch_free_pm(ts); + +out_init_pm: + siw_touch_del_sysfs(ts); + +out_add_sysfs: + siw_ops_remove(ts); + +out: + return ret; +} + +static void __siw_touch_probe_free(void *data) +{ + struct siw_ts *ts = data; + + if (!ts->init_late_done) { + return; + } + + if (ts->is_charger) { + goto skip_charger; + } + + cancel_delayed_work_sync(&ts->upgrade_work); + cancel_delayed_work_sync(&ts->init_work); + + siw_touch_free_thread(ts); + + siw_touch_free_irq(ts); + + siw_ops_power(ts, POWER_OFF); + +skip_charger: + siw_touch_free_pm(ts); + + siw_touch_del_sysfs(ts); + + siw_ops_remove(ts); +} + +static int siw_touch_probe_init(void *data) +{ + struct siw_ts *ts = data; + int ret = 0; + + mutex_lock(&ts->probe_lock); + ret = __siw_touch_probe_init(data); + mutex_unlock(&ts->probe_lock); + + return ret; +} + +static void siw_touch_probe_free(void *data) +{ + struct siw_ts *ts = data; + + mutex_lock(&ts->probe_lock); + __siw_touch_probe_free(ts); + mutex_unlock(&ts->probe_lock); +} + +static int siw_touch_probe_post(struct siw_ts *ts) +{ + if (t_dbg_flag & DBG_FLAG_SKIP_INIT_LATE) { + ts->flags &= ~TOUCH_USE_PROBE_INIT_LATE; + } + if (t_dbg_flag & DBG_FLAG_TEST_INIT_LATE) { + ts->flags |= TOUCH_USE_PROBE_INIT_LATE; + } + + if (touch_flags(ts) & TOUCH_USE_PROBE_INIT_LATE) { + /* + * Postpone actual init control + * This is related to LCD_EVENT_TOUCH_INIT_LATE and + * shall be controlled by MIPI via notifier + */ + ts->init_late = siw_touch_probe_init; + return 0; + } + + ts->init_late = NULL; + + touch_msleep(200); + + return siw_touch_probe_init(ts); +} + +static void siw_touch_remove_post(struct siw_ts *ts) +{ + siw_touch_probe_free(ts); +} + +int siw_touch_probe(struct siw_ts *ts) +{ + struct siw_touch_pdata *pdata = NULL; + struct device *dev = ts->dev; + int ret = 0; + + t_dev_info(dev, "SiW Touch Probe\n"); + + ts->is_charger = !!(siw_touch_get_boot_mode() == SIW_TOUCH_CHARGER_MODE); + + if (ts->is_charger) { + t_dev_info(dev, "Probe - Charger mode\n"); + } + + /* set defalut lpwg value because of AAT */ + ts->role.mfts_lpwg = t_mfts_lpwg; + + ts->lpwg.mode = t_lpwg_mode; + ts->lpwg.screen = t_lpwg_screen; + ts->lpwg.sensor = t_lpwg_sensor; + ts->lpwg.qcover = t_lpwg_qcover; + + pdata = siw_touch_probe_common(ts); + if (!pdata) { + return -EINVAL; + } + + ret = siw_touch_probe_post(ts); + if (ret) { + goto out_probe_post; + } + + siw_touch_blocking_notifier_call( + LCD_EVENT_TOUCH_DRIVER_REGISTERED, + NULL); + + t_dev_info(dev, "probe(%s) done\n", + (ts->is_charger) ? "charger" : "normal"); + + return 0; + +out_probe_post: + siw_touch_remove_common(ts); + + return ret; +} + +int siw_touch_remove(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + + siw_touch_blocking_notifier_call( + LCD_EVENT_TOUCH_DRIVER_UNREGISTERED, + NULL); + + if (ts->is_charger) { + t_dev_info(dev, "Remove - Charger mode\n"); + } + + siw_touch_remove_post(ts); + + siw_touch_remove_common(ts); + + t_dev_info(dev, "SiW Touch Removed\n"); + + return 0; +} + +int siw_touch_init_late(struct siw_ts *ts, int value) +{ + struct device *dev = ts->dev; + int ret = 0; + +#if defined(SOMC_TOUCH_BRINGUP) + ret = -EINVAL; +#endif + + if (!value) { + goto out; + } + + if (!(touch_flags(ts) & TOUCH_USE_PROBE_INIT_LATE)) { + t_dev_info(dev, "init_late not enabled\n"); + goto out; + } + + if (ts->init_late == NULL) { + goto out; + } + + t_dev_info(dev, "trigger init_late\n"); + + ret = ts->init_late(ts); + if (ret < 0) { + atomic_set(&ts->state.core, CORE_PROBE); + t_dev_err(dev, "init_late failed, %d\n", ret); + goto out; + } + + t_dev_info(dev, "init_late done\n"); + + ts->init_late = NULL; + + value = 0xAA55; + siw_touch_atomic_notifier_call( + LCD_EVENT_TOUCH_INIT_LATE, + &value); + +out: + return ret; +} + + +#if defined(CONFIG_TOUCHSCREEN_SIWMON) || defined(CONFIG_TOUCHSCREEN_SIWMON_MODULE) + +struct siw_mon_operations *siw_mon_ops; +EXPORT_SYMBOL(siw_mon_ops); + +int siw_mon_register(struct siw_mon_operations *ops) +{ + if (siw_mon_ops) + return -EBUSY; + + siw_mon_ops = ops; + t_pr_dbg(DBG_BASE, "siw mon ops assigned\n"); + mb(); + return 0; +} +EXPORT_SYMBOL_GPL(siw_mon_register); + +void siw_mon_deregister(void) +{ + if (siw_mon_ops == NULL) { + t_pr_err("monitor was not registered\n"); + return; + } + siw_mon_ops = NULL; + t_pr_dbg(DBG_BASE, "siw mon ops released\n"); + mb(); +} +EXPORT_SYMBOL_GPL(siw_mon_deregister); + +#endif /* CONFIG_TOUCHSCREEN_SIWMON */ + +#if defined(SOMC_TOUCH_BRINGUP) + +#define SIW_RETRY_SESSION_COUNT 30 + +void siw_init_late_session_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct siw_probe_session *session = + container_of(dwork, struct siw_probe_session, work); + struct siw_ts *ts = container_of(session, struct siw_ts, session); + int trigger = 0xA55A; + incell_pw_status status = { false, false }; + int ret = 0; + + if (ts->session.done) + goto out; + + t_dev_info(ts->dev, "session_work ready\n"); + + if (incell_get_power_status(&status)) { + t_dev_err(ts->dev, "failed get power status\n"); + goto init_fail; + } + + if (status.display_power && status.touch_power) { + incell_power_lock_ctrl(INCELL_DISPLAY_POWER_LOCK, &status); + mutex_lock(&ts->lock); + ret = siw_touch_init_late(ts, trigger); + mutex_unlock(&ts->lock); + incell_power_lock_ctrl(INCELL_DISPLAY_POWER_UNLOCK, &status); + + if (ret) { + t_dev_err(ts->dev, "stop session_work\n"); + goto init_fail; + } + + ts->session.done = true; + goto out; + } +init_fail: + ts->session.retry++; + if (ts->session.retry > SIW_RETRY_SESSION_COUNT) { + t_dev_err(ts->dev, "stop session_work\n"); + goto out; + } + t_dev_err(ts->dev, "display status=%s, touch status=%s " + "will retring late session...\n", + status.touch_power ? "enable" : "disable", + status.display_power ? "enable" : "disable"); + schedule_delayed_work(&ts->session.work, 1 * HZ); + +out: + return; + +} +#endif + + +__siw_setup_u32("siw_pr_dbg_mask=", siw_setup_pr_dbg_mask, t_pr_dbg_mask); +__siw_setup_u32("siw_dev_dbg_mask=", siw_setup_dev_dbg_mask, t_dev_dbg_mask); + +__siw_setup_u32("siw_mfts_lpwg=", siw_setup_mfts_lpwg, t_mfts_lpwg); +__siw_setup_u32("siw_lpwg_mode=", siw_setup_lpwg_mode, t_lpwg_mode); +__siw_setup_u32("siw_lpwg_screen=", siw_setup_lpwg_screen, t_lpwg_screen); +__siw_setup_u32("siw_lpwg_sensor=", siw_setup_lpwg_sensor, t_lpwg_sensor); + + diff --git a/drivers/input/touchscreen/siw/siw_touch.h b/drivers/input/touchscreen/siw/siw_touch.h new file mode 100644 index 000000000000..eb8ebc574bce --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch.h @@ -0,0 +1,1835 @@ +/* + * SiW touch core driver + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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_H +#define __SIW_TOUCH_H + +#include "siw_touch_cfg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__SIW_CONFIG_EARLYSUSPEND) +#include +#endif + +#if defined(__SIW_CONFIG_FB) +#include +#endif + +#if defined(__SIW_SUPPORT_WAKE_LOCK) +#include +#endif + +#include + +#include "siw_touch_hal_reg.h" + +#include "siw_touch_dbg.h" + + +#define SIW_DRV_VERSION "v2.20b" + + +enum { + SIW_TOUCH_NORMAL_MODE = 0, + SIW_TOUCH_CHARGER_MODE = 1, +}; + +enum { + SIW_TOUCH_MAX_BUF_SIZE = (32<<10), //(64<<10), + SIW_TOUCH_MAX_BUF_IDX = 4, + /* */ + SIW_TOUCH_MAX_XFER_COUNT = 10, +}; + +enum _SIW_BUS_IF { + BUS_IF_I2C = 0, + BUS_IF_SPI, + BUS_IF_MAX, +}; + +enum { + DRIVER_FREE = 0, + DRIVER_INIT = 1, +}; + +enum { + POWER_OFF = 0, + POWER_SLEEP, + POWER_WAKE, + POWER_ON, + POWER_SLEEP_STATUS, + POWER_HW_RESET, +}; + +enum { + UEVENT_IDLE = 0, + UEVENT_BUSY, +}; + +enum { + PROX_NEAR = 0, + PROX_FAR, +}; + +enum { + HOLE_FAR = 0, + HOLE_NEAR, +}; + +enum { + DEV_PM_RESUME = 0, + DEV_PM_SUSPEND, + DEV_PM_SUSPEND_IRQ, +}; + +enum { + FB_RESUME = 0, + FB_SUSPEND, +}; + +enum { + SP_DISCONNECT = 0, + SP_CONNECT, +}; + +/* Deep Sleep or not */ +enum { + IC_NORMAL = 0, + IC_DEEP_SLEEP, +}; + +/* TCI */ +enum { + ENABLE_CTRL = 0, + TAP_COUNT_CTRL, + MIN_INTERTAP_CTRL, + MAX_INTERTAP_CTRL, + TOUCH_SLOP_CTRL, + TAP_DISTANCE_CTRL, + INTERRUPT_DELAY_CTRL, + ACTIVE_AREA_CTRL, + ACTIVE_AREA_RESET_CTRL, + ACTIVE_AREA_QOPEN_CTRL, + ACTIVE_AREA_QCLOSE_CTRL, +}; + +enum { + LPWG_NONE = 0, + LPWG_DOUBLE_TAP, + LPWG_PASSWORD, +}; + +enum { + LPWG_SLEEP = 0, + LPWG_WAKE, + LPWG_KNOCK, + LPWG_PASSWD, + LPWG_PARTIAL, + LPWG_NORMAL, +}; + +enum { + LPWG_CMD_MODE = 0, + LPWG_CMD_AREA, +}; + +enum { + TCI_1 = 0, + TCI_2, +}; + +enum { + LPWG_ENABLE = 1, + LPWG_LCD, + LPWG_ACTIVE_AREA, + LPWG_TAP_COUNT, + LPWG_LENGTH_BETWEEN_TAP, + LPWG_EARLY_SUSPEND, + LPWG_SENSOR_STATUS, + LPWG_DOUBLE_TAP_CHECK, + LPWG_UPDATE_ALL, + LPWG_READ, + LPWG_REPLY, + LPWG_EXT_TCI_INFO_STORE = 0x80, + LPWG_EXT_TCI_QOPEN_AREA_STORE, + LPWG_EXT_TCI_QCLOSE_AREA_STORE, + LPWG_EXT_SWIPE_INFO_STORE, + LPWG_EXT_STORE_END, + LPWG_EXT_TCI_INFO_SHOW = 0x90, + LPWG_EXT_TCI_AREA_SHOW, + LPWG_EXT_SWIPE_INFO_SHOW, + LPWG_EXT_SHOW_END, +}; + +enum { + TCON_GLOVE = 1, + TCON_GRAB, +}; + +enum { + LOCKSCREEN_UNLOCK = 0, + LOCKSCREEN_LOCK, +}; + +enum { + IME_OFF = 0, + IME_ON, + IME_SWYPE, +}; + +enum { + QUICKCOVER_OPEN = 0, + QUICKCOVER_CLOSE, +}; + +enum { + INCOMING_CALL_IDLE, + INCOMING_CALL_RINGING, + INCOMING_CALL_OFFHOOK, +}; + +enum { + MFTS_NONE = 0, + MFTS_FOLDER, + MFTS_FLAT, + MFTS_CURVED, +}; + +enum { /* Command lists */ + CMD_VERSION, + CMD_ATCMD_VERSION, +}; + +enum { + INTERRUPT_DISABLE = 0, + INTERRUPT_ENABLE, +}; + +enum { + CORE_NONE = 0, + CORE_EARLY_PROBE, + CORE_PROBE, + CORE_CHARGER_LOGO, + CORE_MFTS, + CORE_UPGRADE, + CORE_NORMAL, +}; + +// QCT USB connection +enum { + CONNECT_INVALID = 0, + CONNECT_SDP, + CONNECT_DCP, + CONNECT_CDP, + CONNECT_PROPRIETARY, + CONNECT_FLOATED, + CONNECT_HUB, /* SHOULD NOT change the value */ +}; + +// MTK USB connection +enum { + CONNECT_CHARGER_UNKNOWN = 0, + CONNECT_STANDARD_HOST, /* USB : 450mA */ + CONNECT_CHARGING_HOST, + CONNECT_NONSTANDARD_CHARGER, /* AC : 450mA~1A */ + CONNECT_STANDARD_CHARGER, /* AC : ~1A */ + CONNECT_APPLE_2_1A_CHARGER, /* 2.1A apple charger */ + CONNECT_APPLE_1_0A_CHARGER, /* 1A apple charger */ + CONNECT_APPLE_0_5A_CHARGER, /* 0.5A apple charger */ + CONNECT_WIRELESS_CHARGER, + CONNECT_DISCONNECTED, +}; + +enum { + EARJACK_NONE = 0, + EARJACK_NORMAL, + EARJACK_DEBUG, +}; + +enum { + DEBUG_TOOL_DISABLE = 0, + DEBUG_TOOL_ENABLE, + DEBUG_TOOL_MAX, +}; + +enum { + DEBUG_OPTION_DISABLE = 0, + DEBUG_OPTION_1 = 1, //BIT0(1<<0) + DEBUG_OPTION_2 = 2, //BIT1(1<<1) + DEBUG_OPTION_1_2 = 3, //BIT1(1<<1) + BIT0(1<<0) + DEBUG_OPTION_3 = 4, //BIT2(1<<2) + DEBUG_OPTION_1_3 = 5, //BIT2(1<<2) + BIT0(1<<0) + DEBUG_OPTION_2_3 = 6, //BIT2(1<<2) + BIT1(1<<1) + DEBUG_OPTION_1_2_3 = 7, //BIT2(1<<2) + BIT1(1<<1) + BIT0(1<<0) + DEBUG_OPTION_4 = 8, //BIT3(1<<3) + DEBUG_OPTION_1_4 = 9, //BIT3(1<<3) + BIT0(1<<0) + DEBUG_OPTION_2_4 = 10, //BIT3(1<<3) + BIT1(1<<1) + DEBUG_OPTION_1_2_4 = 11, //BIT3(1<<3) + BIT1(1<<1) + BIT0(1<<0) + DEBUG_OPTION_3_4 = 12, //BIT3(1<<3) + BIT2(1<<2) + DEBUG_OPTION_1_3_4 = 13, //BIT3(1<<3) + BIT2(1<<2) + BIT0(1<<0) + DEBUG_OPTION_2_3_4 = 14, //BIT3(1<<3) + BIT2(1<<2) + BIT1(1<<1) + DEBUG_OPTION_1_2_3_4 = 15, //BIT3(1<<3) + BIT2(1<<2) + BIT1(1<<1) + BIT0(1<<0) +}; + +enum { + ASC_OFF = 0, + ASC_ON, +}; + +enum { + IN_HAND_ATTN = 0, + NOT_IN_HAND, + IN_HAND_NO_ATTN, +}; + +enum { + ASC_READ_MAX_DELTA = 0, + ASC_GET_FW_SENSITIVITY, + ASC_WRITE_SENSITIVITY, +}; + +enum { + DELTA_CHK_OFF = 0, + DELTA_CHK_ON, +}; + +enum { + NORMAL_SENSITIVITY = 0, + ACUTE_SENSITIVITY, + OBTUSE_SENSITIVITY, +}; + +enum { + NORMAL_BOOT = 0, + MINIOS_AAT, + MINIOS_MFTS_FOLDER, + MINIOS_MFTS_FLAT, + MINIOS_MFTS_CURVED, +}; + +enum { + QUICKCOVER_OPT_BASE = QUICKCOVER_CLOSE + 1, + /* */ + QUICKCOVER_OPT_BIT_SKIP_CLOSE = BIT(0), +}; + +struct state_info { + atomic_t core; + atomic_t pm; + atomic_t fb; + atomic_t sleep; + atomic_t uevent; + atomic_t irq_enable; + atomic_t connect; /* connection using USB port */ + atomic_t wireless; /* connection using wirelees_charger */ + atomic_t earjack; /* connection using earjack */ + atomic_t lockscreen; + atomic_t ime; + atomic_t quick_cover; + atomic_t quick_cover_opt; + atomic_t incoming_call; + atomic_t mfts; + atomic_t sp_link; + atomic_t debug_tool; + atomic_t debug_option_mask; + atomic_t onhand; + atomic_t hw_reset; + atomic_t mon_ignore; + atomic_t glove; + atomic_t grab; +}; + +struct touch_pins { + int reset_pin; + int reset_pin_pol; + int irq_pin; + int maker_id_pin; + int vdd_pin; + int vio_pin; + /* */ + void *vdd; + void *vio; +}; + +struct touch_device_caps { + u32 max_x; + u32 max_y; + u32 max_pressure; + u32 max_width; + u32 max_orientation; + u32 max_id; + u32 hw_reset_delay; + u32 sw_reset_delay; +// u32 button_support; +// u32 number_of_button; +// u32 button_name[MAX_BUTTON]; +}; + +struct touch_operation_role { + bool use_lpwg; + bool use_firmware; + bool use_fw_upgrade; + u32 use_lpwg_test; + u32 mfts_lpwg; +}; + +struct touch_quick_cover { + u32 x1; + u32 y1; + u32 x2; + u32 y2; +}; + +struct touch_pinctrl { + struct pinctrl *ctrl; + struct pinctrl_state *active; + struct pinctrl_state *suspend; +}; + +struct touch_data { + u16 id; + u16 x; + u16 y; + u16 width_major; + u16 width_minor; + s16 orientation; + u16 pressure; + /* finger, palm, pen, glove, hover */ + u16 type; + u16 event; +}; + +struct point { + int x; + int y; +}; + +struct lpwg_info { + u8 mode; + u8 screen; + u8 sensor; + u8 qcover; + u8 code_num; + struct point area[2]; + struct point code[MAX_LPWG_CODE]; +}; + +struct active_area { + u16 x1; + u16 y1; + u16 x2; + u16 y2; +}; + +struct reset_area { + u32 x1; + u32 y1; + u32 x2; + u32 y2; +}; + +struct tci_info { + u16 tap_count; + u16 min_intertap; + u16 max_intertap; + u16 touch_slop; + u16 tap_distance; + u16 intr_delay; +}; + +struct tci_ctrl { + u32 mode; + struct active_area area; + struct reset_area qcover_open; + struct reset_area qcover_close; + u8 double_tap_check; + struct tci_info info[2]; +}; + +struct asc_info { + u32 use_asc; + u8 curr_sensitivity; + bool use_delta_chk; + bool delta_updated; + u32 delta; + u32 low_delta_thres; + u32 high_delta_thres; +}; + +typedef int (*siw_mon_handler_t)(struct device *dev, u32 opt); + +struct siw_touch_operations { + /* Register Map */ + struct siw_hal_reg *reg; + /* Func. */ + int (*early_probe)(struct device *dev); + int (*probe)(struct device *dev); + int (*remove)(struct device *dev); + int (*suspend)(struct device *dev); + int (*resume)(struct device *dev); + int (*init)(struct device *dev); + int (*reset)(struct device *dev, int ctrl); + int (*ic_info)(struct device *dev); + int (*tc_con)(struct device *dev, u32 code, void *param); + int (*tc_driving)(struct device *dev, int mode); + int (*chk_status)(struct device *dev); + int (*irq_dbg_handler)(struct device *dev); + int (*irq_handler)(struct device *dev); + int (*irq_abs)(struct device *dev); + int (*irq_lpwg)(struct device *dev); + int (*power)(struct device *dev, int power_mode); + int (*upgrade)(struct device *dev); + int (*lpwg)(struct device *dev, u32 code, void *param); + int (*asc)(struct device *dev, u32 code, u32 value); + int (*notify)(struct device *dev, ulong event, void *data); + int (*set)(struct device *dev, u32 cmd, void *buf); + int (*get)(struct device *dev, u32 cmd, void *buf); + /* */ + int (*sysfs)(struct device *dev, int on_off); + /* */ + siw_mon_handler_t mon_handler; + int mon_interval; + /* */ + int (*abt_sysfs)(struct device *dev, int on_off); + int (*prd_sysfs)(struct device *dev, int on_off); + int (*watch_sysfs)(struct device *dev, int on_off); +}; + +enum { + MON_FLAG_RST_ONLY = (1<<8), +}; + +struct siw_touch_bus_info { + u32 bus_type; + u32 buf_size; + /* */ + u32 spi_mode; + u32 bits_per_word; + u32 max_freq; + /* */ + int bus_tx_hdr_size; + int bus_rx_hdr_size; + int bus_tx_dummy_size; + int bus_rx_dummy_size; +}; + +struct siw_touch_buf { + u8 *buf; + dma_addr_t dma; + int size; +}; + +struct siw_touch_second_screen { + int bound_i; + int bound_j; +}; + +struct siw_touch_fquirks { //function quirks + int (*parse_dts)(void *data); + /* */ + int (*gpio_init)(struct device *dev, int pin, const char *name); + int (*gpio_free)(struct device *dev, int pin); + int (*gpio_dir_input)(struct device *dev, int pin); + int (*gpio_dir_output)(struct device *dev, int pin, int value); + int (*gpio_set_pull)(struct device *dev, int pin, int value); + /* */ + int (*power_init)(struct device *dev); + int (*power_free)(struct device *dev); + int (*power_vdd)(struct device *dev, int value); + int (*power_vio)(struct device *dev, int value); + /* */ + int (*fwup_check)(struct device *dev, u8 *fw_buf); + int (*fwup_upgrade)(struct device *dev, u8 *fw_buf, int fw_size, int retry); + /* */ + int (*mon_handler)(struct device *dev, u32 opt); + int mon_interval; +}; + +enum { + TOUCH_IRQ_NONE = 0, + TOUCH_IRQ_FINGER = (1 << 0), + TOUCH_IRQ_KNOCK = (1 << 1), + TOUCH_IRQ_PASSWD = (1 << 2), + TOUCH_IRQ_SWIPE_RIGHT = (1 << 3), + TOUCH_IRQ_SWIPE_LEFT = (1 << 4), + TOUCH_IRQ_GESTURE = (1 << 5), + TOUCH_IRQ_ERROR = (1 << 15), +}; + +enum { + ABS_MODE = 0, + KNOCK_1, + KNOCK_2, + SWIPE_RIGHT, + SWIPE_LEFT, + /* */ + GESTURE_C, + GESTURE_W, + GESTURE_V, + GESTURE_O, + GESTURE_S, + GESTURE_E, + GESTURE_M, + GESTURE_Z, + /* */ + GESTURE_DIR_RIGHT = 20, + GESTURE_DIR_DOWN, + GESTURE_DIR_LEFT, + GESTURE_DIR_UP, + GESTURE_FAIL = 100, + /* */ + CUSTOM_DEBUG = 200, + KNOCK_OVERTAP, +}; + +enum _SIW_TOUCH_UEVENT { + TOUCH_UEVENT_KNOCK = 0, + TOUCH_UEVENT_PASSWD, + TOUCH_UEVENT_SWIPE_RIGHT, + TOUCH_UEVENT_SWIPE_LEFT, + TOUCH_UEVENT_GESTURE_C, + TOUCH_UEVENT_GESTURE_W, + TOUCH_UEVENT_GESTURE_V, + TOUCH_UEVENT_GESTURE_O, + TOUCH_UEVENT_GESTURE_S, + TOUCH_UEVENT_GESTURE_E, + TOUCH_UEVENT_GESTURE_M, + TOUCH_UEVENT_GESTURE_Z, + TOUCH_UEVENT_GESTURE_DIR_RIGHT, + TOUCH_UEVENT_GESTURE_DIR_DOWN, + TOUCH_UEVENT_GESTURE_DIR_LEFT, + TOUCH_UEVENT_GESTURE_DIR_UP, + TOUCH_UEVENT_MAX, +}; + +struct siw_touch_uevent_ctrl { + char *str[TOUCH_UEVENT_MAX][2]; + u32 flags; +}; + +struct siw_touch_fw_bin { + u8 *fw_data; + int fw_size; +}; + +struct siw_touch_font_bin { + u8 *font_data; + int font_size; +}; + +struct siw_touch_pdata { + /* Config. */ + char *chip_id; //chip id(fixed) + char *chip_name; //chip name + char *drv_name; //driver name + char *idrv_name; //input driver name + char *ext_watch_name; //ext_watch name + struct module *owner; + u32 max_finger; + u32 chip_type; + u32 mode_allowed; + u32 fw_size; //Pure F/W size, not include config data(1K) + + u32 flags; + unsigned long irqflags; + + unsigned long quirks; +#define _CHIP_QUIRK_NOT_SUPPORT_XFER (1L<<0) + +#define _CHIP_QUIRK_NOT_SUPPORT_ASC (1L<<16) +#define _CHIP_QUIRK_NOT_SUPPORT_LPWG (1L<<17) +#define _CHIP_QUIRK_NOT_SUPPORT_WATCH (1L<<18) + +#define _CHIP_QUIRK_NOT_SUPPORT_IME (1L<<28) + + unsigned long abt_quirks; +#define _ABT_QUIRK_RAW_RETURN_MODE_VAL (1L<<0) + + unsigned long prd_quirks; +#define _PRD_QUIRK_RAW_RETURN_MODE_VAL (1L<<0) + + struct siw_touch_bus_info bus_info; + +#if defined(__SIW_CONFIG_OF) + const struct of_device_id *of_match_table; +#else + struct touch_pins pins; + struct touch_device_caps caps; +#endif + + /* Input Device ID */ + struct input_id i_id; + + /* Hal operations */ + struct siw_touch_operations *ops; + + void *tci_info; + void *tci_qcover_open; + void *tci_qcover_close; + void *swipe_ctrl; + void *watch_win; + + void *reg_quirks; + + struct siw_touch_fquirks fquirks; + + void *uevent_ctrl; + + void *fw_bin; + + void *font_bin; + + int senseless_margin; +}; + +struct siw_touch_chip_data { + const struct siw_touch_pdata *pdata; + void *bus_drv; +}; + +enum { + CHIP_QUIRK_NOT_SUPPORT_XFER = _CHIP_QUIRK_NOT_SUPPORT_XFER, + /* */ + CHIP_QUIRK_NOT_SUPPORT_ASC = _CHIP_QUIRK_NOT_SUPPORT_ASC, + CHIP_QUIRK_NOT_SUPPORT_LPWG = _CHIP_QUIRK_NOT_SUPPORT_LPWG, + CHIP_QUIRK_NOT_SUPPORT_WATCH = _CHIP_QUIRK_NOT_SUPPORT_WATCH, + /* */ + CHIP_QUIRK_NOT_SUPPORT_IME = _CHIP_QUIRK_NOT_SUPPORT_IME, +}; + +enum { + ABT_QUIRK_RAW_RETURN_MODE_VAL = _ABT_QUIRK_RAW_RETURN_MODE_VAL, +}; + +enum { + PRD_QUIRK_RAW_RETURN_MODE_VAL = _PRD_QUIRK_RAW_RETURN_MODE_VAL, +}; + +static inline unsigned long pdata_get_quirks(struct siw_touch_pdata *pdata) +{ + return pdata->quirks; +} + +static inline unsigned long pdata_get_abt_quirks(struct siw_touch_pdata *pdata) +{ + return pdata->abt_quirks; +} + +static inline unsigned long pdata_get_prd_quirks(struct siw_touch_pdata *pdata) +{ + return pdata->prd_quirks; +} + +static inline unsigned long pdata_test_quirks(struct siw_touch_pdata *pdata, + unsigned long quirk_bit) +{ + return (pdata_get_quirks(pdata) & quirk_bit); +} + +static inline unsigned long pdata_test_abt_quirks(struct siw_touch_pdata *pdata, + unsigned long quirk_bit) +{ + return (pdata_get_abt_quirks(pdata) & quirk_bit); +} + +static inline unsigned long pdata_test_prd_quirks(struct siw_touch_pdata *pdata, + unsigned long quirk_bit) +{ + return (pdata_get_prd_quirks(pdata) & quirk_bit); +} + +static inline char *pdata_chip_id(struct siw_touch_pdata *pdata) +{ + return pdata->chip_id; +} + +static inline char *pdata_chip_name(struct siw_touch_pdata *pdata) +{ + return pdata->chip_name; +} + +static inline char *pdata_drv_name(struct siw_touch_pdata *pdata) +{ + return pdata->drv_name; +} + +static inline char *pdata_idrv_name(struct siw_touch_pdata *pdata) +{ + return pdata->idrv_name; +} + +static inline char *pdata_ext_watch_name(struct siw_touch_pdata *pdata) +{ + return pdata->ext_watch_name; +} + +static inline u32 pdata_max_finger(struct siw_touch_pdata *pdata) +{ + return (pdata->max_finger) ? pdata->max_finger : MAX_FINGER; +} + +static inline u32 pdata_chip_type(struct siw_touch_pdata *pdata) +{ + return pdata->chip_type; +} + +static inline u32 pdata_mode_allowed(struct siw_touch_pdata *pdata, u32 mode) +{ + return (pdata->mode_allowed & BIT(mode)); +} + +static inline u32 pdata_fw_size(struct siw_touch_pdata *pdata) +{ + return pdata->fw_size; +} + +static inline u32 pdata_flags(struct siw_touch_pdata *pdata) +{ + return pdata->flags; +} + +static inline unsigned long pdata_irqflags(struct siw_touch_pdata *pdata) +{ + return pdata->irqflags; +} + +static inline u32 pdata_bus_type(struct siw_touch_pdata *pdata) +{ + return pdata->bus_info.bus_type; +} + +static inline u32 pdata_buf_size(struct siw_touch_pdata *pdata) +{ + return pdata->bus_info.buf_size; +} + +static inline u32 pdata_spi_mode(struct siw_touch_pdata *pdata) +{ + return pdata->bus_info.spi_mode; +} + +static inline u32 pdata_bits_per_word(struct siw_touch_pdata *pdata) +{ + return pdata->bus_info.bits_per_word; +} + +static inline u32 pdata_max_freq(struct siw_touch_pdata *pdata) +{ + return pdata->bus_info.max_freq; +} + +static inline int pdata_tx_hdr_size(struct siw_touch_pdata *pdata) +{ + return pdata->bus_info.bus_tx_hdr_size; +} + +static inline int pdata_rx_hdr_size(struct siw_touch_pdata *pdata) +{ + return pdata->bus_info.bus_rx_hdr_size; +} + +static inline int pdata_tx_dummy_size(struct siw_touch_pdata *pdata) +{ + return pdata->bus_info.bus_tx_dummy_size; +} + +static inline int pdata_rx_dummy_size(struct siw_touch_pdata *pdata) +{ + return pdata->bus_info.bus_rx_dummy_size; +} + +static inline void *pdata_tci_info(struct siw_touch_pdata *pdata) +{ + return pdata->tci_info; +} + +static inline void *pdata_tci_qcover_open(struct siw_touch_pdata *pdata) +{ + return pdata->tci_qcover_open; +} + +static inline void *pdata_tci_qcover_close(struct siw_touch_pdata *pdata) +{ + return pdata->tci_qcover_close; +} + +static inline void *pdata_swipe_ctrl(struct siw_touch_pdata *pdata) +{ + return pdata->swipe_ctrl; +} + +static inline void *pdata_watch_win(struct siw_touch_pdata *pdata) +{ + return pdata->watch_win; +} + +#if defined(__SIW_SUPPORT_XFER) +static inline int pdata_xfer_allowed(struct siw_touch_pdata *pdata) +{ + if (pdata_bus_type(pdata) != BUS_IF_SPI) + return 0; + + if (pdata_test_quirks(pdata, CHIP_QUIRK_NOT_SUPPORT_XFER)) + return 0; + + return 1; //allowed +} +#else /* __SIW_SUPPORT_XFER */ +static inline int pdata_xfer_allowed(struct siw_touch_pdata *pdata){ return 0; } +#endif /* __SIW_SUPPORT_XFER */ + +static inline struct siw_touch_fquirks *pdata_fquirks( + struct siw_touch_pdata *pdata) +{ + return &pdata->fquirks; +} + +static inline struct siw_touch_uevent_ctrl *pdata_uevent_ctrl( + struct siw_touch_pdata *pdata) +{ + return pdata->uevent_ctrl; +} + +static inline struct siw_touch_fw_bin *pdata_fw_bin( + struct siw_touch_pdata *pdata) +{ + return pdata->fw_bin; +} + +static inline struct siw_touch_font_bin *pdata_font_bin( + struct siw_touch_pdata *pdata) +{ + return pdata->font_bin; +} + +static inline int pdata_senseless_margin(struct siw_touch_pdata *pdata) +{ + return pdata->senseless_margin; +} + +enum { + TS_THREAD_OFF = 0, + TS_THREAD_ON, + TS_THREAD_PAUSE, +}; + +struct siw_ts_thread { + struct task_struct *thread; + atomic_t state; + int interval; + siw_mon_handler_t handler; + struct mutex lock; +}; + +enum { + DEFAULT_NAME_SZ = PATH_MAX, +}; + +#if defined(SOMC_TOUCH_BRINGUP) +struct siw_probe_session { + struct delayed_work work; + bool started; + bool done; + int retry; +}; +#endif + +struct siw_ts { +// struct platform_device *pdev; + + size_t addr; + + char *chip_id; + char *chip_name; //chip name + char *drv_name; //driver name + char *idrv_name; //input driver name + char *ext_watch_name; //ext_watch name + int max_finger; + int chip_type; + u32 mode_allowed; + + int irq; + unsigned long irqflags; + unsigned long irqflags_curr; + irq_handler_t handler_fn; + irq_handler_t thread_fn; + struct delayed_work work_irq; + + void *bus_dev; /* i2c or spi */ + struct device *dev; /* client device : i2c->dev or spi->dev */ + void *dev_data; /* chip */ + + struct device udev; + struct bus_type ubus; + + struct input_dev *input; + struct siw_touch_pdata *pdata; + + struct kobject kobj; +#if defined(__SIW_SUPPORT_WAKE_LOCK) + struct wake_lock lpwg_wake_lock; +#endif + struct state_info state; + + struct touch_pins pins; + struct touch_device_caps caps; + struct touch_operation_role role; + struct touch_quick_cover qcover; + struct touch_pinctrl pinctrl; + + u32 intr_status; + u32 intr_gesture; + u16 new_mask; + u16 old_mask; + int tcount; + struct touch_data tdata[MAX_FINGER]; + int is_palm; + struct lpwg_info lpwg; + struct tci_ctrl tci; + struct asc_info asc; + + int def_fwcnt; + const char *def_fwpath[4]; + char test_fwpath[DEFAULT_NAME_SZ]; + const char *panel_spec; + const char *panel_spec_mfts; + u32 force_fwup; +#define _FORCE_FWUP_CLEAR 0 +#define _FORCE_FWUP_ON (1<<0) +#define _FORCE_FWUP_SYS_SHOW (1<<2) +#define _FORCE_FWUP_SYS_STORE (1<<3) + + /* __SIW_SUPPORT_WATCH */ + const char *watch_font_image; + /* */ + + /* __SIW_SUPPORT_PRD */ + const char *prd_in_file_path; + const char *prd_in_file_m_path; + const char *prd_out_file_path; + const char *prd_out_file_mo_aat_path; + const char *prd_out_file_mo_mfo_path; + const char *prd_out_file_mo_mfl_path; + const char *prd_out_file_mo_mcv_path; + /* */ + + int buf_size; + struct touch_xfer_msg *xfer; + struct siw_touch_buf tx_buf[SIW_TOUCH_MAX_BUF_IDX]; + struct siw_touch_buf rx_buf[SIW_TOUCH_MAX_BUF_IDX]; + int tx_buf_idx; + int rx_buf_idx; + + struct mutex lock; + struct mutex reset_lock; + struct mutex probe_lock; + struct workqueue_struct *wq; + struct delayed_work init_work; + struct delayed_work upgrade_work; + struct delayed_work notify_work; + struct delayed_work fb_work; + struct delayed_work toggle_delta_work; + struct delayed_work finger_input_work; + struct delayed_work sys_reset_work; + + struct notifier_block blocking_notif; + struct notifier_block atomic_notif; + unsigned long notify_event; + int notify_data; +#if defined(__SIW_CONFIG_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif +#if defined(__SIW_CONFIG_FB) + struct notifier_block fb_notif; +#endif +#if defined(__SIW_CONFIG_USER_FB) + struct notifier_block user_fb_notif; +#endif + + struct siw_ts_thread mon_thread; + + int vdd_id; + int vdd_vol; + int vio_id; + int vio_vol; + + void *abt; + void *prd; + + u32 flags; +#define _IRQ_USE_WAKE (1UL<<0) /* unavailable */ +#define _IRQ_USE_SCHEDULE_WORK (1UL<<1) + +#define _TOUCH_USE_MON_THREAD (1UL<<8) +#define _TOUCH_USE_PINCTRL (1UL<<9) +#define _TOUCH_USE_PWRCTRL (1UL<<10) + +#define _TOUCH_USE_VBLANK (1UL<<12) + +#define _TOUCH_USE_INPUT_PARENT (1UL<<15) + +#define _TOUCH_USE_VIRT_DIR_WATCH (1UL<<16) +#define _TOUCH_USE_DRV_NAME_SYSFS (1UL<<17) +#define _TOUCH_USE_FW_BINARY (1UL<<18) +#define _TOUCH_USE_FONT_BINARY (1UL<<19) + +#define _TOUCH_USE_PROBE_INIT_LATE (1UL<<24) + +#define _TOUCH_SKIP_ESD_EVENT (1UL<<28) +#define _TOUCH_SKIP_RESET_PIN (1UL<<29) + +#define _TOUCH_IGNORE_DT_FLAGS (1UL<<31) + + /* Input Device ID */ + struct input_id i_id; + + /* Hal operations */ + struct siw_touch_operations *ops_ext; + struct siw_touch_operations ops_in; + struct siw_touch_operations *ops; + struct siw_hal_reg __reg; + + /* */ + int (*bus_init)(struct device *dev); + int (*bus_read)(struct device *dev, void *msg); + int (*bus_write)(struct device *dev, void *msg); + int (*bus_xfer)(struct device *dev, void *xfer); + int bus_tx_hdr_size; + int bus_rx_hdr_size; + int bus_tx_dummy_size; + int bus_rx_dummy_size; + + /* */ + atomic_t recur_chk; + + /* */ + int is_charger; + + /* */ + int (*init_late)(void *data); + int init_late_done; + +#if defined(SOMC_TOUCH_BRINGUP) + struct siw_probe_session session; + int irq_enable_status; + int cover_status; + struct device virtdev; +#endif +}; + +enum { + FORCE_FWUP_CLEAR = _FORCE_FWUP_CLEAR, + FORCE_FWUP_ON = _FORCE_FWUP_ON, + FORCE_FWUP_SYS_SHOW = _FORCE_FWUP_SYS_SHOW, + FORCE_FWUP_SYS_STORE = _FORCE_FWUP_SYS_STORE, +}; + +enum { + IRQ_USE_WAKE = _IRQ_USE_WAKE, + IRQ_USE_SCHEDULE_WORK = _IRQ_USE_SCHEDULE_WORK, + /* */ + TOUCH_USE_MON_THREAD = _TOUCH_USE_MON_THREAD, + TOUCH_USE_PINCTRL = _TOUCH_USE_PINCTRL, + TOUCH_USE_PWRCTRL = _TOUCH_USE_PWRCTRL, + /* */ + TOUCH_USE_VBLANK = _TOUCH_USE_VBLANK, + TOUCH_USE_INPUT_PARENT = _TOUCH_USE_INPUT_PARENT, + /* */ + TOUCH_USE_VIRT_DIR_WATCH = _TOUCH_USE_VIRT_DIR_WATCH, + TOUCH_USE_DRV_NAME_SYSFS = _TOUCH_USE_DRV_NAME_SYSFS, + TOUCH_USE_FW_BINARY = _TOUCH_USE_FW_BINARY, + TOUCH_USE_FONT_BINARY = _TOUCH_USE_FONT_BINARY, + /* */ + TOUCH_USE_PROBE_INIT_LATE = _TOUCH_USE_PROBE_INIT_LATE, + /* */ + TOUCH_SKIP_ESD_EVENT = _TOUCH_SKIP_ESD_EVENT, + TOUCH_SKIP_RESET_PIN = _TOUCH_SKIP_RESET_PIN, + /* */ + TOUCH_IGNORE_DT_FLAGS = _TOUCH_IGNORE_DT_FLAGS, +}; + + +/* goes to siw_touch_init_work_func */ +#define __siw_touch_qd_init_work(_ts, _delay) \ + queue_delayed_work(_ts->wq, &_ts->init_work, _delay) +/* goes to siw_touch_upgrade_work_func */ +#define __siw_touch_qd_upgrade_work(_ts, _delay) \ + queue_delayed_work(_ts->wq, &_ts->upgrade_work, _delay) +/* goes to siw_touch_atomic_notifer_work_func */ +#define __siw_touch_qd_notify_work(_ts, _delay) \ + queue_delayed_work(_ts->wq, &_ts->notify_work, _delay) +/* goes to siw_touch_fb_work_func */ +#define __siw_touch_qd_fb_work(_ts, _delay) \ + queue_delayed_work(_ts->wq, &_ts->fb_work, _delay) + +#if defined(__SIW_SUPPORT_ASC) +/* goes to siw_touch_toggle_delta_check_work_func */ +#define __siw_touch_qd_toggle_delta_work(_ts, _delay) \ + queue_delayed_work(_ts->wq, &_ts->toggle_delta_work, _delay) +/* goes to siw_touch_finger_input_check_work_func */ +#define __siw_touch_qd_finger_input_work(_ts, _delay) \ + queue_delayed_work(_ts->wq, &_ts->finger_input_work, _delay) +#endif /* __SIW_SUPPORT_ASC */ + +/* goes to siw_touch_sys_reset_work_func */ +#define __siw_touch_qd_sys_reset_work(_ts, _delay) \ + queue_delayed_work(_ts->wq, &_ts->sys_reset_work, _delay) + + +#define siw_touch_qd_init_work_now(_ts) \ + __siw_touch_qd_init_work(_ts, 0) +#define siw_touch_qd_init_work_jiffies(_ts, _jiffies) \ + __siw_touch_qd_init_work(_ts, msecs_to_jiffies(_jiffies)) +#define siw_touch_qd_init_work_sw(_ts) \ + __siw_touch_qd_init_work(_ts, \ + msecs_to_jiffies(_ts->caps.sw_reset_delay)) +#define siw_touch_qd_init_work_hw(_ts) \ + __siw_touch_qd_init_work(_ts, \ + msecs_to_jiffies(_ts->caps.hw_reset_delay)) + +#define siw_touch_qd_upgrade_work_now(_ts) \ + __siw_touch_qd_upgrade_work(_ts, 0) +#define siw_touch_qd_upgrade_work_jiffies(_ts, _jiffies) \ + __siw_touch_qd_upgrade_work(_ts, msecs_to_jiffies(_jiffies)) + +#define siw_touch_qd_notify_work_now(_ts) \ + __siw_touch_qd_notify_work(_ts, 0) +#define siw_touch_qd_notify_work_jiffies(_ts, _jiffies) \ + __siw_touch_qd_notify_work(_ts, msecs_to_jiffies(_jiffies)) + +#define siw_touch_qd_fb_work_now(_ts) \ + __siw_touch_qd_fb_work(_ts, 0) +#define siw_touch_qd_fb_work_jiffies(_ts, _jiffies) \ + __siw_touch_qd_fb_work(_ts, msecs_to_jiffies(_jiffies)) + +#if defined(__SIW_SUPPORT_ASC) +#define siw_touch_qd_toggle_delta_work_now(_ts) \ + __siw_touch_qd_toggle_delta_work(_ts, 0) +#define siw_touch_qd_toggle_delta_work_jiffies(_ts, _jiffies) \ + __siw_touch_qd_toggle_delta_work(_ts, msecs_to_jiffies(_jiffies)) + +#define siw_touch_qd_finger_input_work_now(_ts) \ + __siw_touch_qd_finger_input_work(_ts, 0) +#define siw_touch_qd_finger_input_work_jiffies(_ts, _jiffies) \ + __siw_touch_qd_finger_input_work(_ts, msecs_to_jiffies(_jiffies)) +#endif /* __SIW_SUPPORT_ASC */ + +#define siw_touch_qd_sys_reset_work_now(_ts) \ + __siw_touch_qd_sys_reset_work(_ts, 0) +#define siw_touch_qd_sys_reset_work_jiffies(_ts, _jiffies) \ + __siw_touch_qd_sys_reset_work(_ts, msecs_to_jiffies(_jiffies)) + + + +struct siw_touch_attribute { + struct attribute attr; + ssize_t (*show)(struct device *dev, char *buf); + ssize_t (*store)(struct device *dev, const char *buf, size_t count); +}; + +#define __TOUCH_ATTR(_name, _attr, _show, _store) \ + struct siw_touch_attribute touch_attr_##_name \ + = __ATTR(_name, _attr, _show, _store) + +#if defined(__SIW_ATTR_PERMISSION_ALL) +#define __TOUCH_DEFAULT_PERM (S_IRUGO | S_IWUGO) +#else +#define __TOUCH_DEFAULT_PERM (S_IRUGO | S_IWUSR | S_IWGRP) +#endif + +#define TOUCH_ATTR(_name, _show, _store) \ + __TOUCH_ATTR(_name, __TOUCH_DEFAULT_PERM, _show, _store) + + +static inline void touch_set_dev_data(struct siw_ts *ts, void *data) +{ +// t_dev_info(ts->dev, "data : 0x%P\n", data); + ts->dev_data = data; +} + +static inline void *touch_get_dev_data(struct siw_ts *ts) +{ + return ts->dev_data; +} + +static inline struct siw_ts *to_touch_core(struct device *dev) +{ + return dev ? (struct siw_ts *)dev_get_drvdata(dev) : NULL; +} + +static inline unsigned long touch_get_quirks(struct siw_ts *ts) +{ + return pdata_get_quirks(ts->pdata); +} + +static inline unsigned long touch_test_quirks(struct siw_ts *ts, + unsigned long quirk_bit) +{ + return pdata_test_quirks(ts->pdata, quirk_bit); +} + +static inline unsigned long touch_test_abt_quirks(struct siw_ts *ts, + unsigned long quirk_bit) +{ + return pdata_test_abt_quirks(ts->pdata, quirk_bit); +} + +static inline unsigned long touch_test_prd_quirks(struct siw_ts *ts, + unsigned long quirk_bit) +{ + return pdata_test_prd_quirks(ts->pdata, quirk_bit); +} + +static inline char *touch_chip_id(struct siw_ts *ts) +{ + return ts->chip_id; +} + +static inline char *touch_chip_name(struct siw_ts *ts) +{ + return ts->chip_name; +} + +static inline char *touch_drv_name(struct siw_ts *ts) +{ + return ts->drv_name; +} + +static inline char *touch_idrv_name(struct siw_ts *ts) +{ + return ts->idrv_name; +} + +static inline char *touch_ext_watch_name(struct siw_ts *ts) +{ + return ts->ext_watch_name; +} + +static inline u32 touch_max_finger(struct siw_ts *ts) +{ + return ts->max_finger; +} + +static inline u32 touch_chip_type(struct siw_ts *ts) +{ + return ts->chip_type; +} + +static inline u32 touch_mode_allowed(struct siw_ts *ts, u32 mode) +{ + return (ts->mode_allowed & BIT(mode)); +} + +static inline u32 touch_mode_not_allowed(struct siw_ts *ts, u32 mode) +{ + int ret; + + ret = !touch_mode_allowed(ts, mode); + if (ret) { + t_dev_warn(ts->dev, "target mode(%d) not supported\n", mode); + } + + return ret; +} + +static inline u32 touch_fw_size(struct siw_ts *ts) +{ + return pdata_fw_size(ts->pdata); +} + +static inline u32 touch_flags(struct siw_ts *ts) +{ + return ts->flags; +} + +static inline unsigned long touch_irqflags(struct siw_ts *ts) +{ + return ts->irqflags; +} + +static inline u32 touch_bus_type(struct siw_ts *ts) +{ + return pdata_bus_type(ts->pdata); +} + +static inline u32 touch_buf_size(struct siw_ts *ts) +{ + return pdata_buf_size(ts->pdata); +} + +static inline u32 touch_spi_mode(struct siw_ts *ts) +{ + return pdata_spi_mode(ts->pdata); +} + +static inline u32 touch_bits_per_word(struct siw_ts *ts) +{ + return pdata_bits_per_word(ts->pdata); +} + +static inline u32 touch_max_freq(struct siw_ts *ts) +{ + return pdata_max_freq(ts->pdata); +} + +static inline int touch_tx_hdr_size(struct siw_ts *ts) +{ + return ts->bus_tx_hdr_size; +} + +static inline int touch_rx_hdr_size(struct siw_ts *ts) +{ + return ts->bus_rx_hdr_size; +} + +static inline int touch_tx_dummy_size(struct siw_ts *ts) +{ + return ts->bus_tx_dummy_size; +} + +static inline int touch_rx_dummy_size(struct siw_ts *ts) +{ + return ts->bus_rx_dummy_size; +} + +static inline int touch_xfer_allowed(struct siw_ts *ts) +{ + return pdata_xfer_allowed(ts->pdata); +} + +static inline struct siw_touch_fquirks *touch_fquirks(struct siw_ts *ts) +{ + return pdata_fquirks(ts->pdata); +} + +static inline struct siw_touch_uevent_ctrl *touch_uevent_ctrl( + struct siw_ts *ts) +{ + return pdata_uevent_ctrl(ts->pdata); +} + +static inline struct siw_touch_fw_bin *touch_fw_bin( + struct siw_ts *ts) +{ + return pdata_fw_bin(ts->pdata); +} + +static inline struct siw_touch_font_bin *touch_font_bin( + struct siw_ts *ts) +{ + return pdata_font_bin(ts->pdata); +} + +static inline int touch_senseless_margin(struct siw_ts *ts) +{ + return pdata_senseless_margin(ts->pdata); +} + + +static inline u32 touch_get_act_buf_size(struct siw_ts *ts) +{ + return ts->buf_size; +} + +static inline void touch_set_act_buf_size(struct siw_ts *ts, int size) +{ + ts->buf_size = size; +} + +static inline void touch_set_caps(struct siw_ts *ts, + struct touch_device_caps *caps_src) +{ + struct touch_device_caps *caps = &ts->caps; + + caps->max_x = caps_src->max_x; + caps->max_y = caps_src->max_y; + caps->max_pressure = caps_src->max_pressure; + caps->max_width = caps_src->max_width; + caps->max_orientation = caps_src->max_orientation; + caps->max_id = caps_src->max_id; + caps->hw_reset_delay = caps_src->hw_reset_delay; + caps->sw_reset_delay = caps_src->sw_reset_delay; +} + +static inline void touch_set_pins(struct siw_ts *ts, + struct touch_pins *pins_src) +{ + struct touch_pins *pins = &ts->pins; + + pins->reset_pin = pins_src->reset_pin; + pins->reset_pin_pol = pins_src->reset_pin_pol; + pins->irq_pin = pins_src->irq_pin; + pins->maker_id_pin = pins_src->maker_id_pin; + /* Power */ + pins->vdd_pin = pins_src->vdd_pin; + pins->vio_pin = pins_src->vio_pin; +} + +static inline int touch_reset_pin(struct siw_ts *ts) +{ + return ts->pins.reset_pin; +} + +static inline int touch_reset_pin_pol(struct siw_ts *ts) +{ + return ts->pins.reset_pin_pol; +} + +static inline int touch_irq_pin(struct siw_ts *ts) +{ + return ts->pins.irq_pin; +} + +static inline int touch_maker_id_pin(struct siw_ts *ts) +{ + return ts->pins.maker_id_pin; +} + +static inline int touch_vdd_pin(struct siw_ts *ts) +{ + return ts->pins.vdd_pin; +} + +static inline int touch_vio_pin(struct siw_ts *ts) +{ + return ts->pins.vio_pin; +} + +static inline void *touch_get_vdd(struct siw_ts *ts) +{ + return ts->pins.vdd; +} + +static inline void touch_set_vdd(struct siw_ts *ts, void *vdd) +{ + ts->pins.vdd = vdd; +} + +static inline void *touch_get_vio(struct siw_ts *ts) +{ + return ts->pins.vio; +} + +static inline void touch_set_vio(struct siw_ts *ts, void *vio) +{ + ts->pins.vio = vio; +} + +static inline int touch_qcover_opt_skip_close(struct siw_ts *ts) +{ + return !!(atomic_read(&ts->state.quick_cover_opt) & QUICKCOVER_OPT_BIT_SKIP_CLOSE); +} + + +static inline void *siw_ops_reg(struct siw_ts *ts) +{ + return ts->ops->reg; +} + +static inline void siw_ops_set_irq_handler(struct siw_ts *ts, void *handler) +{ + ts->ops->irq_handler = handler; +} + +static inline void siw_ops_restore_irq_handler(struct siw_ts *ts) +{ + ts->ops->irq_handler = ts->ops_ext->irq_handler; +} + + +#define siw_ops_is_null(_ts, _ops) !!(_ts->ops->_ops == NULL) + +#define siw_ops_xxx(_ops, _ret, _ts, args...) \ + ({ int _r = 0; \ + do { \ + if (_ts->ops->_ops == NULL) { \ + if ((_ret) < 0) { \ + t_dev_err(ts->dev, "%s isn't assigned\n", #_ops); \ + _r = _ret; \ + } \ + break; \ + } \ + _r = _ts->ops->_ops(_ts->dev, ##args); \ + } while (0); \ + _r; \ + }) + +#define siw_ops_early_probe(_ts, args...) siw_ops_xxx(early_probe, 0, _ts, ##args) +#define siw_ops_probe(_ts, args...) siw_ops_xxx(probe, -ESRCH, _ts, ##args) +#define siw_ops_remove(_ts, args...) siw_ops_xxx(remove, -ESRCH, _ts, ##args) +#define siw_ops_suspend(_ts, args...) siw_ops_xxx(suspend, -ESRCH, _ts, ##args) +#define siw_ops_resume(_ts, args...) siw_ops_xxx(resume, -ESRCH, _ts, ##args) +#define siw_ops_init(_ts, args...) siw_ops_xxx(init, -ESRCH, _ts, ##args) +#define siw_ops_reset(_ts, args...) siw_ops_xxx(reset, -ESRCH, _ts, ##args) +#define siw_ops_ic_info(_ts, args...) siw_ops_xxx(ic_info, -ESRCH, _ts, ##args) +#define siw_ops_tc_con(_ts, args...) siw_ops_xxx(tc_con, 0, _ts, ##args) +#define siw_ops_tc_driving(_ts, args...) siw_ops_xxx(tc_driving, -ESRCH, _ts, ##args) +#define siw_ops_chk_status(_ts, args...) siw_ops_xxx(chk_status, -ESRCH, _ts, ##args) +#define siw_ops_irq_handler(_ts, args...) siw_ops_xxx(irq_handler, -ESRCH, _ts, ##args) +#define siw_ops_irq_abs(_ts, args...) siw_ops_xxx(irq_abs, -ESRCH, _ts, ##args) +#define siw_ops_irq_lpwg(_ts, args...) siw_ops_xxx(irq_lpwg, -ESRCH, _ts, ##args) +#define siw_ops_power(_ts, args...) siw_ops_xxx(power, -ESRCH, _ts, ##args) +#define siw_ops_upgrade(_ts, args...) siw_ops_xxx(upgrade, -ESRCH, _ts, ##args) +#define siw_ops_lpwg(_ts, args...) siw_ops_xxx(lpwg, 0, _ts, ##args) +#define siw_ops_asc(_ts, args...) siw_ops_xxx(asc, -ESRCH, _ts, ##args) +#define siw_ops_notify(_ts, args...) siw_ops_xxx(notify, 0, _ts, ##args) +#define siw_ops_set(_ts, args...) siw_ops_xxx(set, 0, _ts, ##args) +#define siw_ops_get(_ts, args...) siw_ops_xxx(get, 0, _ts, ##args) +#define siw_ops_sysfs(_ts, args...) siw_ops_xxx(sysfs, 0, _ts, ##args) +#define siw_ops_mon_handler(_ts, args...) siw_ops_xxx(mon_handler, -ESRCH, _ts, ##args) + +#define siw_ops_abt_sysfs(_ts, args...) siw_ops_xxx(abt_sysfs, 0, _ts, ##args) +#define siw_ops_prd_sysfs(_ts, args...) siw_ops_xxx(prd_sysfs, 0, _ts, ##args) +#define siw_ops_watch_sysfs(_ts, args...) siw_ops_xxx(watch_sysfs, 0, _ts, ##args) + + +static inline void touch_msleep(unsigned int msecs) +{ + if (!msecs) + return; + + if (msecs >= 20) + msleep(msecs); + else + usleep_range(msecs * 1000, msecs * 1000); +} + +static inline void *touch_kzalloc(struct device *dev, size_t size, gfp_t gfp) +{ + return devm_kzalloc(dev, size, gfp); +} + +static inline void touch_kfree(struct device *dev, void *p) +{ + return devm_kfree(dev, p); +} + +static inline void *touch_getname(void) +{ + void *name = __getname(); + if (name != NULL) { + memset(name, 0, PATH_MAX); + } + return name; +} + +static inline void touch_putname(void *name) +{ + if (name != NULL) { + __putname(name); + } +} + + +struct siw_op_dbg { + char *name; + int (*func)(void *data); + void (*debug)(void *op, void *data); + int flags; +}; + +#define _SIW_OP_DBG(_type, _func, _debug, _flags) \ + [_type] = { \ + .name = #_func, \ + .func = _func, \ + .debug = _debug, \ + .flags = _flags, \ + } + +static inline int __siw_touch_op_dbg(struct siw_op_dbg *op, + void *data) +{ + int ret = 0; + + ret = op->func(data); + if (op->debug) { + op->debug(op, data); + } + + return ret; +} + +#define __siw_snprintf(_buf, _buf_max, _size, _fmt, _args...) \ + ({ \ + int _n_size = 0; \ + if (_size < _buf_max) \ + _n_size = snprintf(_buf + _size, _buf_max - _size,\ + (const char *)_fmt, ##_args); \ + _n_size; \ + }) + + +#define siw_snprintf(_buf, _size, _fmt, _args...) \ + __siw_snprintf(_buf, PAGE_SIZE, _size, _fmt, ##_args) + + +extern u32 t_mfts_lpwg; +extern u32 t_lpwg_mode; +extern u32 t_lpwg_screen; +extern u32 t_lpwg_sensor; +extern u32 t_lpwg_qcover; + +extern int siw_setup_params(struct siw_ts *ts, struct siw_touch_pdata *pdata); + +extern void *siw_setup_operations(struct siw_ts *ts, struct siw_touch_operations *ops_ext); + +extern int siw_touch_set(struct device *dev, u32 cmd, void *buf); +extern int siw_touch_get(struct device *dev, u32 cmd, void *buf); + +extern void siw_touch_suspend_call(struct device *dev); +extern void siw_touch_resume_call(struct device *dev); + +extern void siw_touch_change_sensitivity(struct siw_ts *ts, + int target); + +extern int siw_touch_init_notify(struct siw_ts *ts); +extern void siw_touch_free_notify(struct siw_ts *ts); + +extern void siw_touch_atomic_notifer_work_func(struct work_struct *work); + +extern void siw_touch_mon_pause(struct device *dev); +extern void siw_touch_mon_resume(struct device *dev); + +extern int siw_touch_probe(struct siw_ts *ts); +extern int siw_touch_remove(struct siw_ts *ts); + +extern int siw_touch_init_late(struct siw_ts *ts, int value); + +extern int siw_touch_notify(struct siw_ts *ts, unsigned long event, void *data); + +#if defined(SOMC_TOUCH_BRINGUP) +extern int siw_hal_tc_driving(struct device *dev, int mode); +#endif + +#if defined(CONFIG_TOUCHSCREEN_SIWMON) || defined(CONFIG_TOUCHSCREEN_SIWMON_MODULE) + +struct touch_bus_msg; + +struct siw_mon_operations { + void (*submit_bus)(struct device *dev, char *dir, void *data, int ret); + void (*submit_evt)(struct device *dev, char *type, int type_v, char *code, int code_v, int value, int ret); + void (*submit_ops)(struct device *dev, char *ops, void *data, int size, int ret); +}; + +extern struct siw_mon_operations *siw_mon_ops; + +static inline void siwmon_submit_bus(struct device *dev, char *dir, void *data, int ret) +{ + if (siw_mon_ops && siw_mon_ops->submit_bus) + (*siw_mon_ops->submit_bus)(dev, dir, data, ret); +} + +static inline void siwmon_submit_evt(struct device *dev, char *type, int type_v, char *code, int code_v, int value, int ret) +{ + if (siw_mon_ops && siw_mon_ops->submit_evt) + (*siw_mon_ops->submit_evt)(dev, type, type_v, code, code_v, value, ret); +} + +static inline void siwmon_submit_ops(struct device *dev, char *ops, void *data, int size, int ret) +{ + if (siw_mon_ops && siw_mon_ops->submit_ops) + (*siw_mon_ops->submit_ops)(dev, ops, data, size, ret); +} + +extern int siw_mon_register(struct siw_mon_operations *ops); +extern void siw_mon_deregister(void); + +#else /* CONFIG_TOUCHSCREEN_SIWMON */ + +static inline void siwmon_submit_bus(struct device *dev, char *dir, void *data, int ret){ } +static inline void siwmon_submit_evt(struct device *dev, char *type, int type_v, char *code, int code_v, int value, int ret){ } +static inline void siwmon_submit_ops(struct device *dev, char *ops, void *data, int size, int ret){ } + +#endif /* CONFIG_TOUCHSCREEN_SIWMON */ + +#define siwmon_submit_ops_wh_name(_dev, _fmt, _name, _val, _size, _ret) \ + do { \ + char _mstr[64]; \ + snprintf(_mstr, sizeof(_mstr), _fmt, _name); \ + siwmon_submit_ops(_dev, _mstr, _val, _size, _ret); \ + } while (0) + +#define siwmon_submit_ops_step(_dev, _ops) \ + siwmon_submit_ops(_dev, _ops, NULL, 0, 0) + +#define siwmon_submit_ops_step_core(_dev, _ops, _ret) \ + siwmon_submit_ops(_dev, "[S] " _ops, NULL, 0, _ret) + +#define siwmon_submit_ops_step_chip(_dev, _ops, _ret) \ + siwmon_submit_ops(_dev, "(c) " _ops, NULL, 0, _ret) + +#define siwmon_submit_ops_step_chip_wh_name(_dev, _fmt, _name, _ret) \ + do { \ + char _mstr[64]; \ + snprintf(_mstr, sizeof(_mstr), "(c) " _fmt, _name); \ + siwmon_submit_ops(_dev, _mstr, NULL, 0, _ret); \ + } while (0) + +#if !defined(MODULE) +#define __siw_setup_u32(_name, _fn, _var) \ + static int __init _fn(char *in_str) \ + { \ + _var = (u32)simple_strtol(in_str, NULL, 0); \ + return 1; \ + } \ + __setup(_name, _fn) + +#define __siw_setup_str(_name, _fn, _var) \ + static int __init _fn(char *in_str) \ + { \ + strlcpy(_var, in_str, sizeof(_var)); \ + return 1; \ + } \ + __setup(_name, _fn) +#else /* MODULE */ +#define __siw_setup_u32(_name, _fn, _var) +#define __siw_setup_str(_name, _fn, _var) +#endif /* MODULE */ + +#define siw_chip_module_init(_name, _data, _desc, _author) \ + static int __init siw_touch_driver_init(void)\ + { \ + t_pr_info("%s: %s driver init - %s\n", \ + _data.pdata->drv_name, _name, SIW_DRV_VERSION); \ + return siw_touch_bus_add_driver(&_data); \ + } \ + static void __exit siw_touch_driver_exit(void) \ + { \ + (void)siw_touch_bus_del_driver(&_data);\ + t_pr_info("%s: %s driver exit - %s\n", \ + _data.pdata->drv_name, _name, SIW_DRV_VERSION); \ + } \ + module_init(siw_touch_driver_init); \ + module_exit(siw_touch_driver_exit); \ + MODULE_AUTHOR(_author); \ + MODULE_DESCRIPTION(_desc); \ + MODULE_VERSION(SIW_DRV_VERSION); \ + MODULE_LICENSE("GPL"); + +#endif /* __SIW_TOUCH_H */ + + diff --git a/drivers/input/touchscreen/siw/siw_touch_bus.c b/drivers/input/touchscreen/siw/siw_touch_bus.c new file mode 100644 index 000000000000..3f00f74c6fcd --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_bus.c @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SOMC_TOUCH_BRINGUP +#include +#endif + +#include +#include + +#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; +} + + diff --git a/drivers/input/touchscreen/siw/siw_touch_bus.h b/drivers/input/touchscreen/siw/siw_touch_bus.h new file mode 100644 index 000000000000..43e12b78f1ce --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_bus.h @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include + + +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 */ + diff --git a/drivers/input/touchscreen/siw/siw_touch_bus_i2c.c b/drivers/input/touchscreen/siw/siw_touch_bus_i2c.c new file mode 100644 index 000000000000..25f5427278ad --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_bus_i2c.c @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 */ + + + + diff --git a/drivers/input/touchscreen/siw/siw_touch_bus_i2c.h b/drivers/input/touchscreen/siw/siw_touch_bus_i2c.h new file mode 100644 index 000000000000..ad192c9c7262 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_bus_i2c.h @@ -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 + * + * 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 */ + diff --git a/drivers/input/touchscreen/siw/siw_touch_cfg.h b/drivers/input/touchscreen/siw/siw_touch_cfg.h new file mode 100644 index 000000000000..6ae334423789 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_cfg.h @@ -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 + * + * 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 + +#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 */ + diff --git a/drivers/input/touchscreen/siw/siw_touch_dbg.h b/drivers/input/touchscreen/siw/siw_touch_dbg.h new file mode 100644 index 000000000000..11359a794e72 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_dbg.h @@ -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 + * + * 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 + +#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 */ + diff --git a/drivers/input/touchscreen/siw/siw_touch_event.c b/drivers/input/touchscreen/siw/siw_touch_event.c new file mode 100644 index 000000000000..90a484f6bab7 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_event.c @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + } +} + + + diff --git a/drivers/input/touchscreen/siw/siw_touch_event.h b/drivers/input/touchscreen/siw/siw_touch_event.h new file mode 100644 index 000000000000..80556a15fabc --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_event.h @@ -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 + * + * 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 */ + diff --git a/drivers/input/touchscreen/siw/siw_touch_gpio.c b/drivers/input/touchscreen/siw/siw_touch_gpio.c new file mode 100644 index 000000000000..bbb0edb66fc9 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_gpio.c @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ +#include + +#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); +} + + + diff --git a/drivers/input/touchscreen/siw/siw_touch_gpio.h b/drivers/input/touchscreen/siw/siw_touch_gpio.h new file mode 100644 index 000000000000..2c9c78ca79df --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_gpio.h @@ -0,0 +1,150 @@ +/* + * 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 + * + * 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_GPIO_H +#define __SIW_TOUCH_GPIO_H + +#if 1 +enum { + GPIO_PULL_DOWN = 0, + GPIO_PULL_UP, + GPIO_NO_PULL, +}; + +enum { + GPIO_OUT_ZERO = 0, + GPIO_OUT_ONE, +}; +#else +/****************************************************************************** +* Enumeration for GPIO pin +******************************************************************************/ +/* GPIO MODE CONTROL VALUE*/ +typedef enum { + GPIO_MODE_UNSUPPORTED = -1, + GPIO_MODE_GPIO = 0, + GPIO_MODE_00 = 0, + GPIO_MODE_01 = 1, + GPIO_MODE_02 = 2, + GPIO_MODE_03 = 3, + GPIO_MODE_04 = 4, + GPIO_MODE_05 = 5, + GPIO_MODE_06 = 6, + GPIO_MODE_07 = 7, + GPIO_MODE_MAX, + GPIO_MODE_DEFAULT = GPIO_MODE_01, +} GPIO_MODE; + +/*----------------------------------------------------------------------------*/ +/* GPIO DIRECTION */ +typedef enum { + GPIO_DIR_UNSUPPORTED = -1, + GPIO_DIR_IN = 0, + GPIO_DIR_OUT = 1, + GPIO_DIR_MAX, + GPIO_DIR_DEFAULT = GPIO_DIR_IN, +} GPIO_DIR; + +/*----------------------------------------------------------------------------*/ +/* GPIO PULL ENABLE*/ + +typedef enum { + GPIO_PULL_EN_UNSUPPORTED = -1, + GPIO_NOPULLUP = -4, + GPIO_NOPULLDOWN = -5, + GPIO_PULL_DISABLE = 0, + GPIO_PULL_ENABLE = 1, + GPIO_PULL_EN_MAX, + GPIO_PULL_EN_DEFAULT = GPIO_PULL_ENABLE, +} GPIO_PULL_EN; + +/*----------------------------------------------------------------------------*/ +/* GPIO SMT*/ +typedef enum { + GPIO_SMT_UNSUPPORTED = -1, + GPIO_SMT_DISABLE = 0, + GPIO_SMT_ENABLE = 1, + + GPIO_SMT_MAX, + GPIO_SMT_DEFAULT = GPIO_SMT_ENABLE, +} GPIO_SMT; + +/*----------------------------------------------------------------------------*/ +/* GPIO IES*/ +typedef enum { + GPIO_IES_UNSUPPORTED = -1, + GPIO_IES_DISABLE = 0, + GPIO_IES_ENABLE = 1, + + GPIO_IES_MAX, + GPIO_IES_DEFAULT = GPIO_IES_ENABLE, +} GPIO_IES; + +/*----------------------------------------------------------------------------*/ +/* GPIO PULL-UP/PULL-DOWN*/ +typedef enum { + GPIO_PULL_UNSUPPORTED = -1, + GPIO_PULL_DOWN = 0, + GPIO_PULL_UP = 1, + GPIO_NO_PULL = 2, + GPIO_PULL_MAX, + GPIO_PULL_DEFAULT = GPIO_PULL_DOWN +} GPIO_PULL; + +/*----------------------------------------------------------------------------*/ +/* GPIO INVERSION */ +typedef enum { + GPIO_DATA_INV_UNSUPPORTED = -1, + GPIO_DATA_UNINV = 0, + GPIO_DATA_INV = 1, + + GPIO_DATA_INV_MAX, + GPIO_DATA_INV_DEFAULT = GPIO_DATA_UNINV +} GPIO_INVERSION; + +/*----------------------------------------------------------------------------*/ +/* GPIO OUTPUT */ +typedef enum { + GPIO_OUT_UNSUPPORTED = -1, + GPIO_OUT_ZERO = 0, + GPIO_OUT_ONE = 1, + + GPIO_OUT_MAX, + GPIO_OUT_DEFAULT = GPIO_OUT_ZERO, + GPIO_DATA_OUT_DEFAULT = GPIO_OUT_ZERO, /*compatible with DCT */ +} GPIO_OUT; + +/*----------------------------------------------------------------------------*/ +/* GPIO INPUT */ +typedef enum { + GPIO_IN_UNSUPPORTED = -1, + GPIO_IN_ZERO = 0, + GPIO_IN_ONE = 1, + + GPIO_IN_MAX, +} GPIO_IN; +#endif + +extern int siw_touch_gpio_init(struct device *dev, int pin, const char *name); +extern void siw_touch_gpio_free(struct device *dev, int pin); + +extern void siw_touch_gpio_direction_input(struct device *dev, int pin); +extern void siw_touch_gpio_direction_output(struct device *dev, int pin, int value); +extern void siw_touch_gpio_set_pull(struct device *dev, int pin, int value); + +extern int siw_touch_power_init(struct device *dev); +extern int siw_touch_power_free(struct device *dev); +extern void siw_touch_power_vdd(struct device *dev, int value); +extern void siw_touch_power_vio(struct device *dev, int value); + +#endif /* __SIW_TOUCH_GPIO_H */ + diff --git a/drivers/input/touchscreen/siw/siw_touch_hal.c b/drivers/input/touchscreen/siw/siw_touch_hal.c new file mode 100644 index 000000000000..150dc1d5b480 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_hal.c @@ -0,0 +1,8150 @@ +/* + * siw_touch_hal.c - SiW touch hal driver + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siw_touch.h" +#include "siw_touch_hal.h" +#include "siw_touch_bus.h" +#include "siw_touch_event.h" +#include "siw_touch_gpio.h" +#include "siw_touch_irq.h" +#include "siw_touch_sys.h" + +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +//#define __FW_VERIFY_TEST + +enum { + LPWG_SET_SKIP = -1, +}; + +struct lpwg_mode_ctrl { + int clk; + int qcover; + int lpwg; + int lcd; +}; + + +extern int siw_hal_sysfs(struct device *dev, int on_off); + +/* + * weak(dummy) function for ABT control + * These are deactivated by enabling __SIW_SUPPORT_ABT + * and the actual functions can be found in siw_touch_hal_abt.c + */ +int __weak siw_hal_abt_init(struct device *dev) +{ + t_dev_warn(dev, "ABT disabled\n"); + return 0; +} +int __weak siw_hal_abt_sysfs(struct device *dev, int on_off) +{ + t_dev_warn(dev, "ABT disabled\n"); + return 0; +} + +/* + * weak(dummy) function for PRD control + * These are deactivated by enabling __SIW_SUPPORT_PRD + * and the actual functions can be found in siw_touch_hal_prd.c + */ +int __weak siw_hal_prd_sysfs(struct device *dev, int on_off) +{ + t_dev_warn(dev, "PRD disabled\n"); + return 0; +} + +#if defined(__SIW_SUPPORT_WATCH) +#define t_warn_weak_watch(_dev, fmt, args...) \ + t_dev_warn(_dev, "Watch disabled: "fmt, ##args) +#else +#define t_warn_weak_watch(_dev, fmt, args...) do { }while(0) +#endif +/* + * weak(dummy) function for Watch control + * These are deactivated by enabling __SIW_SUPPORT_WATCH + * and the actual functions can be found in siw_touch_hal_watch.c + */ +int __weak siw_hal_watch_sysfs(struct device *dev, int on_off) +{ + t_warn_weak_watch(dev, "%s\n", __func__); + return 0; +} +int __weak siw_hal_watch_init(struct device *dev) +{ + t_warn_weak_watch(dev, "%s\n", __func__); + return 0; +} +int __weak siw_hal_watch_chk_font_status(struct device *dev) +{ + t_warn_weak_watch(dev, "%s\n", __func__); + return 0; +} +int __weak siw_hal_watch_get_curr_time(struct device *dev, char *buf, int *len) +{ + t_warn_weak_watch(dev, "%s\n", __func__); + return 0; +} +int __weak siw_hal_watch_display_off(struct device *dev) +{ + t_warn_weak_watch(dev, "%s\n", __func__); + return 0; +} +int __weak siw_hal_watch_is_disp_waton(struct device *dev) +{ + t_warn_weak_watch(dev, "%s\n", __func__); + return 0; +} +int __weak siw_hal_watch_is_rtc_run(struct device *dev) +{ + t_warn_weak_watch(dev, "%s\n", __func__); + return 0; +} +void __weak siw_hal_watch_set_rtc_run(struct device *dev) +{ + t_warn_weak_watch(dev, "%s\n", __func__); +} +void __weak siw_hal_watch_set_rtc_clear(struct device *dev) +{ + t_warn_weak_watch(dev, "%s\n", __func__); +} +void __weak siw_hal_watch_set_font_empty(struct device *dev) +{ + t_warn_weak_watch(dev, "%s\n", __func__); +} +void __weak siw_hal_watch_set_cfg_blocked(struct device *dev) +{ + t_warn_weak_watch(dev, "%s\n", __func__); +} +void __weak siw_hal_watch_rtc_on(struct device *dev) +{ + t_warn_weak_watch(dev, "%s\n", __func__); +} + + +static int siw_hal_reset_ctrl(struct device *dev, int ctrl); + +#if defined(SOMC_TOUCH_BRINGUP) +int siw_hal_tc_driving(struct device *dev, int mode); +#else +static int siw_hal_tc_driving(struct device *dev, int mode); +#endif + + +#define t_hal_bus_info(_dev, fmt, args...) \ + __t_dev_info(_dev, "hal(bus) : " fmt, ##args) + +#define t_hal_bus_err(_dev, fmt, args...) \ + __t_dev_err(_dev, "hal(bus) : " fmt, ##args) + +#define t_hal_bus_warn(_abt, fmt, args...) \ + __t_dev_warn(_dev, "hal(bus) : " fmt, ##args) + +#if defined(__SIW_CONFIG_SWIPE) +#define SWIPE_FAIL_NUM 7 +static const char const *siw_hal_swipe_debug_str[SWIPE_FAIL_NUM] = { + "ERROR", + "1FINGER_FAST_RELEASE", + "MULTI_FINGER", + "FAST_SWIPE", + "SLOW_SWIPE", + "OUT_OF_AREA", + "RATIO_FAIL", +}; +#endif /* __SIW_CONFIG_SWIPE */ + +static void siw_hal_deep_sleep(struct device *dev); + +static int siw_hal_lpwg_mode(struct device *dev); + +static void siw_hal_power_init(struct device *dev) +{ + siw_touch_power_init(dev); +} + +static void siw_hal_power_free(struct device *dev) +{ + siw_touch_power_free(dev); +} + +static void siw_hal_power_vdd(struct device *dev, int value) +{ + siw_touch_power_vdd(dev, value); +} + +static void siw_hal_power_vio(struct device *dev, int value) +{ + siw_touch_power_vio(dev, value); +} + +#define SIW_HAL_GPIO_RST "siw_hal_reset" +#define SIW_HAL_GPIO_IRQ "siw_hal_irq" +#define SIW_HAL_GPIO_MAKER "siw_hal_maker_id" + +static int __siw_hal_gpio_skip_reset(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + int reset_pin = touch_reset_pin(ts); + + if (touch_flags(ts) & TOUCH_SKIP_RESET_PIN) { + return 1; + } + + if (!gpio_is_valid(reset_pin)) { + t_dev_err(dev, "reset_pin invalid, %d\n", reset_pin); + return 1; + } + + return 0; +} + +static void siw_hal_set_gpio_reset(struct device *dev, int val) +{ + struct siw_ts *ts = to_touch_core(dev); + int reset_pin = touch_reset_pin(ts); + + if (__siw_hal_gpio_skip_reset(ts)) { + return; + } + + siw_touch_gpio_direction_output(dev, + reset_pin, !!(val)); + t_dev_dbg_gpio(dev, "set %s(%d) : %d\n", + SIW_HAL_GPIO_RST, + reset_pin, !!(val)); +} + +static void siw_hal_init_gpio_reset(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + int reset_pin = touch_reset_pin(ts); +#if defined(SOMC_TOUCH_BRINGUP) + t_pr_err("%s:gpio check start\n",__func__); + if (__siw_hal_gpio_skip_reset(ts)) { + return; + } + + siw_touch_gpio_direction_output(dev, + reset_pin, GPIO_OUT_ONE); + t_dev_dbg_gpio(dev, "SIW:set %s(%d) as output\n", + SIW_HAL_GPIO_RST, reset_pin); + + siw_touch_gpio_set_pull(dev, + reset_pin, GPIO_PULL_UP); + t_dev_dbg_gpio(dev, "SIW:set %s(%d) as pull-up(%d)\n", + SIW_HAL_GPIO_RST, + reset_pin, GPIO_NO_PULL); +#else + int ret = 0; + + if (__siw_hal_gpio_skip_reset(ts)) { + return; + } + + ret = siw_touch_gpio_init(dev, + reset_pin, + SIW_HAL_GPIO_RST); + if (ret) + return; + + siw_touch_gpio_direction_output(dev, + reset_pin, GPIO_OUT_ONE); + t_dev_dbg_gpio(dev, "set %s(%d) as output\n", + SIW_HAL_GPIO_RST, reset_pin); + + siw_touch_gpio_set_pull(dev, + reset_pin, GPIO_PULL_UP); + t_dev_dbg_gpio(dev, "set %s(%d) as pull-up(%d)\n", + SIW_HAL_GPIO_RST, + reset_pin, GPIO_NO_PULL); +#endif +} + +static void siw_hal_trigger_gpio_reset(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + + siw_hal_set_gpio_reset(dev, GPIO_OUT_ZERO); + touch_msleep(chip->drv_reset_low + hal_dbg_delay(chip, HAL_DBG_DLY_HW_RST_0)); + siw_hal_set_gpio_reset(dev, GPIO_OUT_ONE); + + t_dev_info(dev, "trigger gpio reset\n"); +} + +static void siw_hal_free_gpio_reset(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + int reset_pin = touch_reset_pin(ts); + + if (__siw_hal_gpio_skip_reset(ts)) { + return; + } + + siw_touch_gpio_free(dev, reset_pin); +} + +static int __siw_hal_gpio_skip_irq(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + int irq_pin = touch_irq_pin(ts); + + if (!gpio_is_valid(irq_pin)) { + t_dev_err(dev, "irq_pin inavlid, %d\n", irq_pin); + return 1; + } + + return 0; +} + +static void siw_hal_init_gpio_irq(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + int irq_pin = touch_irq_pin(ts); + int ret = 0; + + if (__siw_hal_gpio_skip_irq(ts)) { + return; + } + + ret = siw_touch_gpio_init(dev, + irq_pin, + SIW_HAL_GPIO_IRQ); + if (ret) + return; + + siw_touch_gpio_direction_input(dev, + irq_pin); + t_dev_dbg_gpio(dev, "set %s(%d) as input\n", + SIW_HAL_GPIO_IRQ, + irq_pin); + + siw_touch_gpio_set_pull(dev, + irq_pin, GPIO_PULL_UP); + t_dev_dbg_gpio(dev, "set %s(%d) as pull-up(%d)\n", + SIW_HAL_GPIO_IRQ, + irq_pin, GPIO_PULL_UP); +} + +static void siw_hal_free_gpio_irq(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + int irq_pin = touch_irq_pin(ts); + + if (__siw_hal_gpio_skip_irq(ts)) { + return; + } + + siw_touch_gpio_free(dev, irq_pin); +} + +static void siw_hal_init_gpio_maker_id(struct device *dev) +{ +#if 0 + struct siw_ts *ts = to_touch_core(dev); + int maker_id_pin = touch_maker_id_pin(ts); + int ret = 0; + + if (!gpio_is_valid(maker_id_pin)) { + return; + } + + ret = siw_touch_gpio_init(dev, + maker_id_pin, + SIW_HAL_GPIO_MAKER, ts->addr); + if (ret) + return; + + siw_touch_gpio_direction_input(dev, + maker_id_pin); +#endif +} + +static void siw_hal_free_gpio_maker_id(struct device *dev) +{ +#if 0 + struct siw_ts *ts = to_touch_core(dev); + int maker_id_pin = touch_maker_id_pin(ts); + + if (!gpio_is_valid(maker_id_pin)) { + return; + } + + siw_touch_gpio_free(dev, maker_id_pin); +#endif +} + +static void siw_hal_init_gpios(struct device *dev) +{ + siw_hal_init_gpio_reset(dev); + + siw_hal_init_gpio_irq(dev); + + siw_hal_init_gpio_maker_id(dev); + + siw_hal_trigger_gpio_reset(dev); +} + +static void siw_hal_free_gpios(struct device *dev) +{ + siw_hal_free_gpio_reset(dev); + + siw_hal_free_gpio_irq(dev); + + siw_hal_free_gpio_maker_id(dev); +} + +u32 t_bus_dbg_mask = 0; + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/bus_dbg_mask + * (2) insmod {Siw Touch Module Name}.ko bus_dbg_mask= + */ +module_param_named(bus_dbg_mask, t_bus_dbg_mask, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +#if 1 +#define t_bus_info(_dev, fmt, args...) __t_dev_info(_dev, "bus: " fmt, ##args) +#define t_bus_warn(_dev, fmt, args...) __t_dev_warn(_dev, "bus: " fmt, ##args) +#else +#define t_bus_info(_dev, fmt, args...) __t_dev_none() +#define t_bus_warn(_dev, fmt, args...) __t_dev_none() +#endif + +#define t_bus_err(_dev, fmt, args...) __t_dev_err(_dev, "bus: " fmt, ##args) + +#define t_bus_dbg(condition, _dev, fmt, args...) \ + do { \ + if (unlikely(t_bus_dbg_mask & (condition))) \ + __t_dev_info(_dev, "bus: " fmt, ##args); \ + } while (0) + +#define t_bus_dbg_base(_dev, fmt, args...) \ + t_bus_dbg(DBG_BASE, _dev, fmt, ##args) + +#define t_bus_dbg_trace(_dev, fmt, args...) \ + t_bus_dbg(DBG_TRACE, _dev, fmt, ##args) + +static inline void __siw_hal_bus_dbg(struct device *dev, u32 addr, u8 *buf, int size, + int wr, int xfer) +{ + if (!wr && !xfer && + (size == sizeof(struct siw_hal_touch_info))) { + t_bus_dbg_trace(dev, "%s(%s) 0x%04X, 0x%04X: %02X %02X %02X %02X %02X %02X %02X %02X%s\n", + (wr) ? "wr" : "rd", + (xfer) ? "x" : "s", + (u32)addr, (u32)size, + buf[0], buf[1],buf[2], buf[3], + buf[4], buf[5],buf[6], buf[7], + (size > 8) ? " ..." : ""); + return; + } + + if (size > 4) { + t_bus_dbg_base(dev, "%s(%s) 0x%04X, 0x%04X: %02X %02X %02X %02X %02X %02X %02X %02X%s\n", + (wr) ? "wr" : "rd", + (xfer) ? "x" : "s", + (u32)addr, (u32)size, + buf[0], buf[1],buf[2], buf[3], + buf[4], buf[5],buf[6], buf[7], + (size > 8) ? " ..." : ""); + } else { + t_bus_dbg_base(dev, "%s(%s) 0x%04X, 0x%04X: %02X %02X %02X %02X\n", + (wr) ? "wr" : "rd", + (xfer) ? "x" : "s", + (u32)addr, (u32)size, + buf[0], buf[1],buf[2], buf[3]); + } +} + +static void *__siw_hal_get_curr_buf(struct siw_ts *ts, dma_addr_t *dma, int tx) +{ + struct siw_touch_buf *t_buf; + int *idx; + void *buf = NULL; + + idx = (tx) ? &ts->tx_buf_idx : &ts->rx_buf_idx; + t_buf = (tx) ? &ts->tx_buf[(*idx)] : &ts->rx_buf[(*idx)]; + + buf = t_buf->buf; + if (dma) + *dma = t_buf->dma; + + (*idx)++; + (*idx) %= SIW_TOUCH_MAX_BUF_IDX; + + return buf; +} + +//#define __SIW_CONFIG_CLR_RX_BUFFER + +static int __used __siw_hal_do_reg_read(struct device *dev, u32 addr, void *data, int size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int bus_tx_hdr_size = touch_tx_hdr_size(ts); + int bus_rx_hdr_size = touch_rx_hdr_size(ts); +// int bus_tx_dummy_size = touch_tx_dummy_size(ts); + int bus_rx_dummy_size = (touch_rx_dummy_size(ts) & 0xFFFF); + int bus_rx_dummy_flag = (touch_rx_dummy_size(ts) >> 16); + int bus_rd_hdr_flag = 0; + struct touch_bus_msg _msg = {0, }; + struct touch_bus_msg *msg = &_msg; + int tx_size = bus_tx_hdr_size; + u8 *tx_buf; + u8 *rx_buf; + dma_addr_t tx_dma; + dma_addr_t rx_dma; + int ret = 0; + +#if 0 + if (!addr) { + t_dev_err(dev, "NULL addr\n"); + return -EFAULT; + } +#endif + if (!data) { + t_dev_err(dev, "NULL data(0x%04X, 0x%04X)\n", addr, size); + return -EFAULT; + } + +#if defined(__SIW_I2C_TYPE_1) + if (touch_bus_type(ts) == BUS_IF_I2C) { + struct siw_hal_reg *reg = chip->reg; + + /* + * If 0x200 and burst, change to 0x201 + * If 0x201, change to 0x202 + */ + if (addr == reg->tc_ic_status) { + addr += !!(size > 4); + } else { + addr += !!(addr == reg->tc_status); + } + } +#endif + +#if defined(__SIW_SPI_TYPE_1) + /* + * 0x10 : 128-bit dummy + * size > 4 : burst + */ + if (bus_rx_dummy_flag & SPI_BUS_RX_DUMMY_FLAG_128BIT) { + /* Burst restriction under 128-bit dummy */ + if (size > 4) { + switch (addr & 0xF00) { + case 0xC00: + case 0xD00: + case 0xE00: + break; + default: + bus_rx_dummy_flag &= ~SPI_BUS_RX_DUMMY_FLAG_128BIT; + bus_rx_hdr_size = SPI_BUS_RX_HDR_SZ_32BIT; + bus_rx_dummy_size = SPI_BUS_RX_DUMMY_SZ_32BIT; + break; + } + } + } + if (bus_rx_dummy_flag & SPI_BUS_RX_DUMMY_FLAG_128BIT) { + bus_rd_hdr_flag |= SPI_BUS_RX_DUMMY_FLAG_128BIT; + } +#else + bus_rx_dummy_flag = 0; +#endif + +// t_dev_info(dev, "addr %04Xh, size %d\n", addr, size); + + tx_buf = __siw_hal_get_curr_buf(ts, &tx_dma, 1); + rx_buf = __siw_hal_get_curr_buf(ts, &rx_dma, 0); + +#if defined(__SIW_CONFIG_CLR_RX_BUFFER) + if (touch_bus_type(ts) == BUS_IF_I2C) { + memset(&rx_buf[bus_rx_hdr_size], 0xFF, min(8, size)); + } +#endif + +#if defined(__SIW_BUS_ADDR_16BIT) + tx_buf[0] = ((addr >> 8) & 0xff); + tx_buf[1] = (addr & 0xff); + tx_buf[2] = bus_rd_hdr_flag; /* just prevent 'build warning' */ +#else + tx_buf[0] = bus_rd_hdr_flag | ((size > 4) ? 0x20 : 0x00); + tx_buf[0] |= ((addr >> 8) & 0x0f); + tx_buf[1] = (addr & 0xff); +#endif +// while (bus_tx_dummy_size--) { + while (bus_rx_dummy_size--) { + tx_buf[tx_size++] = 0; + } + + msg->tx_buf = tx_buf; + msg->tx_size = tx_size; + msg->rx_buf = rx_buf; + msg->rx_size = bus_rx_hdr_size + size; + msg->tx_dma = tx_dma; + msg->rx_dma = rx_dma; + msg->bits_per_word = 8; + msg->priv = 0; + + ret = siw_touch_bus_read(dev, msg); + if (ret < 0) { + t_dev_err(dev, "touch bus read error(0x%04X, 0x%04X), %d\n", + (u32)addr, (u32)size, ret); + return ret; + } + + memcpy(data, &rx_buf[bus_rx_hdr_size], size); + + __siw_hal_bus_dbg(dev, addr, (u8 *)data, size, 0, 0); + + return size; +} + +static int __used __siw_hal_reg_read(struct device *dev, u32 addr, void *data, int size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + int ret = 0; + + mutex_lock(&chip->bus_lock); + ret = __siw_hal_do_reg_read(dev, addr, data, size); + mutex_unlock(&chip->bus_lock); + + return ret; +} + +static int __used __siw_hal_do_reg_write(struct device *dev, u32 addr, void *data, int size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int bus_tx_hdr_size = touch_tx_hdr_size(ts); +// int bus_rx_hdr_size = touch_rx_hdr_size(ts); + struct touch_bus_msg _msg = {0, }; + struct touch_bus_msg *msg = &_msg; + u8 *tx_buf; + dma_addr_t tx_dma; + int ret = 0; + +#if 0 + if (!addr) { + t_dev_err(dev, "NULL addr\n"); + return -EFAULT; + } +#endif + if (!data) { + t_dev_err(dev, "NULL data(0x%04X, 0x%04X)\n", addr, size); + return -EFAULT; + } + + tx_buf = __siw_hal_get_curr_buf(ts, &tx_dma, 1); + +#if defined(__SIW_BUS_ADDR_16BIT) + tx_buf[0] = ((addr >> 8) & 0xff); + tx_buf[1] = (addr & 0xff); +#else + tx_buf[0] = (touch_bus_type(ts) == BUS_IF_SPI) ? 0x60 : \ + ((size > 4) ? 0x60 : 0x40); + tx_buf[0] |= ((addr >> 8) & 0x0f); + tx_buf[1] = (addr & 0xff); +#endif + + msg->tx_buf = tx_buf; + msg->tx_size = bus_tx_hdr_size + size; + msg->rx_buf = NULL; + msg->rx_size = 0; + msg->tx_dma = tx_dma; + msg->rx_dma = 0; + msg->bits_per_word = 8; + msg->priv = 0; + + memcpy(&tx_buf[bus_tx_hdr_size], data, size); + + ret = siw_touch_bus_write(dev, msg); + if (ret < 0) { + t_dev_err(dev, "touch bus write error(0x%04X, 0x%04X), %d\n", + (u32)addr, (u32)size, ret); + return ret; + } + + __siw_hal_bus_dbg(dev, addr, (u8 *)data, size, 1, 0); + + return size; +} + +static int __used __siw_hal_reg_write(struct device *dev, u32 addr, void *data, int size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + int ret = 0; + + mutex_lock(&chip->bus_lock); + ret = __siw_hal_do_reg_write(dev, addr, data, size); + mutex_unlock(&chip->bus_lock); + + return ret; +} + +static void __used __siw_hal_do_xfer_dbg(struct device *dev, struct touch_xfer_msg *xfer) +{ + struct touch_xfer_data_t *tx = NULL; + struct touch_xfer_data_t *rx = NULL; + int i; + + for (i = 0; i < xfer->msg_count; i++) { + tx = &xfer->data[i].tx; + rx = &xfer->data[i].rx; + + t_dev_err(dev, "[%d] rx(0x%04X, 0x%04X) tx(0x%04X, 0x%04X)\n", + i, + (u32)rx->addr, (u32)rx->size, + (u32)tx->addr, (u32)tx->size); + } +} + +static int __used __siw_hal_do_xfer_to_single(struct device *dev, struct touch_xfer_msg *xfer) +{ + struct touch_xfer_data_t *tx = NULL; + struct touch_xfer_data_t *rx = NULL; + int i = 0; + int ret = 0; + + for (i = 0; i < xfer->msg_count; i++) { + tx = &xfer->data[i].tx; + rx = &xfer->data[i].rx; + + if (rx->size) { + ret = __siw_hal_do_reg_read(dev, rx->addr, rx->buf, rx->size); + t_dev_dbg_trace(dev, "xfer single [%d/%d] - rd(%04Xh, %d), %d\n", + i, xfer->msg_count, rx->addr, rx->size, ret); + if (ret < 0) { + return ret; + } + } else if (tx->size) { + ret = __siw_hal_do_reg_write(dev, tx->addr, tx->buf, tx->size); + t_dev_dbg_trace(dev, "xfer single [%d/%d] - wr(%04Xh, %d), %d\n", + i, xfer->msg_count, rx->addr, rx->size, ret); + if (ret < 0) { + return ret; + } + } + } + + return 0; +} + +static int __used __siw_hal_do_xfer_msg(struct device *dev, struct touch_xfer_msg *xfer) +{ + struct siw_ts *ts = to_touch_core(dev); + struct touch_xfer_data_t *tx = NULL; + struct touch_xfer_data_t *rx = NULL; + int bus_tx_hdr_size = touch_tx_hdr_size(ts); + int bus_rx_hdr_size = touch_rx_hdr_size(ts); +// int bus_tx_dummy_size = touch_tx_dummy_size(ts); + int bus_rx_dummy_size = (touch_rx_dummy_size(ts) & 0xFFFF); + int bus_rx_dummy_flag = (touch_rx_dummy_size(ts) >> 16); + int bus_dummy; + int buf_size = touch_get_act_buf_size(ts); + int tx_size; + int i = 0; + int ret = 0; + + if (!touch_xfer_allowed(ts)) { + return __siw_hal_do_xfer_to_single(dev, xfer); + } + + t_dev_dbg_base(dev, "xfer: start\n"); + +#if defined(__SIW_SPI_TYPE_1) + /* + * 0x10 : 128-bit dummy + * size > 4 : burst + */ + if (bus_rx_dummy_flag & SPI_BUS_RX_DUMMY_FLAG_128BIT) { + bus_rx_hdr_size = SPI_BUS_RX_HDR_SZ_32BIT; + bus_rx_dummy_size = SPI_BUS_RX_DUMMY_SZ_32BIT; + } +#else + bus_rx_dummy_flag = 0; +#endif + + for (i = 0; i < xfer->msg_count; i++) { + tx = &xfer->data[i].tx; + rx = &xfer->data[i].rx; + + if (rx->size) { + t_dev_dbg_base(dev, "xfer: rd set(%d)\n", i); + #if 0 + if (!rx->addr) { + t_dev_err(dev, "NULL xfer rx->addr(%i)\n", i); + __siw_hal_do_xfer_dbg(dev, xfer); + return -EFAULT; + } + #endif + tx_size = bus_tx_hdr_size; + bus_dummy = bus_rx_dummy_size; + + tx->data[0] = (rx->size > 4) ? 0x20 : 0x00; + tx->data[0] |= ((rx->addr >> 8) & 0x0f); + tx->data[1] = (rx->addr & 0xff); + while (bus_dummy--) { + tx->data[tx_size++] = 0; + } + tx->size = tx_size; + rx->size += bus_rx_hdr_size; + continue; + } + + t_dev_dbg_base(dev, "xfer: wr set(%d)\n", i); + + #if 0 + if (!tx->addr) { + t_dev_err(dev, "NULL xfer tx->addr(%i)\n", i); + __siw_hal_do_xfer_dbg(dev, xfer); + return -EFAULT; + } + #endif + + if (tx->size > (buf_size - bus_tx_hdr_size)) { + t_dev_err(dev, "buffer overflow\n"); + return -EOVERFLOW; + } + + // tx->data[0] = ((tx->size == 1) ? 0x60 : 0x40); + tx->data[0] = 0x60; + tx->data[0] |= ((tx->addr >> 8) & 0x0f); + tx->data[1] = (tx->addr & 0xff); + memcpy(&tx->data[bus_tx_hdr_size], tx->buf, tx->size); + tx->size += bus_tx_hdr_size; + } + + t_dev_dbg_base(dev, "xfer: call bus xfer\n"); + + ret = siw_touch_bus_xfer(dev, xfer); + if (ret < 0) { + t_dev_err(dev, "touch bus xfer error, %d\n", ret); + __siw_hal_do_xfer_dbg(dev, xfer); + return ret; + } + + ret = 0; + for (i = 0; i < xfer->msg_count; i++) { + tx = &xfer->data[i].tx; + rx = &xfer->data[i].rx; + + if (rx->size) { + if (!rx->buf) { + t_dev_err(dev, "NULL xfer->data[%d].rx.buf\n", i); + return -EFAULT; + } + memcpy(rx->buf, rx->data + bus_rx_hdr_size, + (rx->size - bus_rx_hdr_size)); + } + ret += rx->size; + + { + struct touch_xfer_data_t *dbg = (rx->size) ? rx : tx; + int dbg_hdr_size = (rx->size) ? bus_rx_hdr_size : bus_tx_hdr_size; + + __siw_hal_bus_dbg(dev, + dbg->addr, dbg->buf, dbg->size - dbg_hdr_size, !(rx->size), 1); + } + } + + return ret; +} + +int siw_hal_read_value(struct device *dev, u32 addr, u32 *value) +{ + int ret = __siw_hal_reg_read(dev, addr, value, sizeof(u32)); + if (ret < 0) + t_hal_bus_err(dev, "read val err[%03Xh, 0x%X], %d", + addr, *value, ret); + return ret; +} + +int siw_hal_write_value(struct device *dev, u32 addr, u32 value) +{ + int ret = __siw_hal_reg_write(dev, addr, &value, sizeof(u32)); + if (ret < 0) + t_hal_bus_err(dev, "write val err[%03Xh, 0x%X], %d", + addr, value, ret); + return ret; +} + +int siw_hal_reg_read(struct device *dev, u32 addr, void *data, int size) +{ + int ret = __siw_hal_reg_read(dev, addr, data, size); + if (ret < 0) + t_hal_bus_err(dev, "read reg err[%03Xh, 0x%X], %d", + addr, ((u32 *)data)[0], ret); + return ret; +} + +int siw_hal_reg_write(struct device *dev, u32 addr, void *data, int size) +{ + int ret = __siw_hal_reg_write(dev, addr, data, size); + if (ret < 0) + t_hal_bus_err(dev, "write reg err[%03Xh, 0x%X], %d", + addr, ((u32 *)data)[0], ret); + return ret; +} + +void siw_hal_xfer_init(struct device *dev, void *xfer_data) +{ + struct touch_xfer_msg *xfer = xfer_data; + struct siw_touch_chip *chip = to_touch_chip(dev); + + mutex_lock(&chip->bus_lock); + xfer->bits_per_word = 8; + xfer->msg_count = 0; +// mutex_unlock(&chip->bus_lock); +} + +int siw_hal_xfer_msg(struct device *dev, struct touch_xfer_msg *xfer) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + int ret = 0; + +// mutex_lock(&chip->bus_lock); + ret = __siw_hal_do_xfer_msg(dev, xfer); + mutex_unlock(&chip->bus_lock); + + return ret; +} + +void siw_hal_xfer_add_rx(void *xfer_data, u32 reg, void *buf, u32 size) +{ + struct touch_xfer_msg *xfer = xfer_data; + struct touch_xfer_data_t *rx = &xfer->data[xfer->msg_count].rx; + struct touch_xfer_data_t *tx = &xfer->data[xfer->msg_count].tx; + + if (xfer->msg_count >= SIW_TOUCH_MAX_XFER_COUNT) { + t_pr_err("touch xfer msg_count overflow (rx)\n"); + return; + } + + rx->addr = reg; + rx->buf = buf; + rx->size = size; + + tx->addr = 0; + tx->buf = NULL; + tx->size = 0; + + xfer->msg_count++; +} + +void siw_hal_xfer_add_tx(void *xfer_data, u32 reg, void *buf, u32 size) +{ + struct touch_xfer_msg *xfer = xfer_data; + struct touch_xfer_data_t *rx = &xfer->data[xfer->msg_count].rx; + struct touch_xfer_data_t *tx = &xfer->data[xfer->msg_count].tx; + + if (xfer->msg_count >= SIW_TOUCH_MAX_XFER_COUNT) { + t_pr_err("touch xfer msg_count overflow (tx)\n"); + return; + } + + rx->addr = 0; + rx->buf = NULL; + rx->size = 0; + + tx->addr = reg; + tx->buf = buf; + tx->size = size; + + xfer->msg_count++; +} + +static int siw_hal_cmd_write(struct device *dev, u8 cmd) +{ +// struct siw_touch_chip *chip = to_touch_chip(dev); + struct touch_bus_msg msg = {0, }; + u8 input[2] = {0, }; + int ret = 0; + + input[0] = cmd; + input[1] = 0; + + msg.tx_buf = input; + msg.tx_size = 2; + + msg.rx_buf = NULL; + msg.rx_size = 0; + msg.bits_per_word = 8; + + ret = siw_touch_bus_write(dev, &msg); + if (ret < 0) { + t_dev_err(dev, "touch cmd(0x%02X) write error, %d\n", + cmd, ret); + return ret; + } + return 0; +} + +static int siw_hal_condition_wait(struct device *dev, + u16 addr, u32 *value, u32 expect, + u32 mask, u32 delay, u32 retry) +{ + u32 data = 0; + int ret = 0; + + do { + touch_msleep(delay); + + ret = siw_hal_read_value(dev, addr, &data); + switch (expect) { + case FLASH_CODE_DNCHK_VALUE: + case FLASH_CONF_DNCHK_VALUE: + t_dev_dbg_base(dev, + "wait read: addr[%04Xh] data[%08Xh], " + "mask[%08Xh], expect[%08Xh], %d\n", + addr, data, mask, expect, retry); + break; + } + if ((ret >= 0) && ((data & mask) == expect)) { + if (value) + *value = data; + #if 0 + t_dev_info(dev, + "wait done: addr[%04Xh] data[%08Xh], " + "mask[%08Xh], expect[%08Xh], %d\n", + addr, data, mask, expect, retry); + #endif + return 0; + } + } while (--retry); + + if (value) + *value = data; + + t_dev_err(dev, + "wait fail: addr[%04Xh] data[%08Xh], " + "mask[%08Xh], expect[%08Xh]\n", + addr, data, mask, expect); + + return -EPERM; +} + +static void siw_hal_fb_notify_work_func(struct work_struct *fb_notify_work) +{ + struct siw_touch_chip *chip = + container_of(to_delayed_work(fb_notify_work), + struct siw_touch_chip, fb_notify_work); + int type = FB_RESUME; + + switch (chip->lcd_mode) { + case LCD_MODE_U0: + case LCD_MODE_U2: + type = FB_SUSPEND; + break; + } + + siw_touch_notifier_call_chain(NOTIFY_FB, &type); +} + +static void siw_hal_init_works(struct siw_touch_chip *chip) +{ + INIT_DELAYED_WORK(&chip->fb_notify_work, siw_hal_fb_notify_work_func); +} + +static void siw_hal_free_works(struct siw_touch_chip *chip) +{ + cancel_delayed_work(&chip->fb_notify_work); +} + +static void siw_hal_init_locks(struct siw_touch_chip *chip) +{ + mutex_init(&chip->bus_lock); +} + +static void siw_hal_free_locks(struct siw_touch_chip *chip) +{ + mutex_destroy(&chip->bus_lock); +} + + +const struct tci_info siw_hal_tci_info_default[2] = { + [TCI_1] = { + .tap_count = 2, + .min_intertap = 6, + .max_intertap = 70, + .touch_slop = 100, + .tap_distance = 10, + .intr_delay = 0, + }, + [TCI_2] = { + .tap_count = 0, + .min_intertap = 6, + .max_intertap = 70, + .touch_slop = 100, + .tap_distance = 255, + .intr_delay = 20, + }, +}; + +#define siw_prt_tci_info(_dev, _idx, _info) \ + do { \ + t_dev_info(_dev, \ + "tci info[%s] tap_count %d, min_intertap %d, max_intertap %d\n", \ + _idx, _info->tap_count, _info->min_intertap, _info->max_intertap); \ + t_dev_info(_dev, \ + "tci info[%s] touch_slop %d, tap_distance %d, intr_delay %d\n", \ + _idx, _info->touch_slop, _info->tap_distance, _info->intr_delay); \ + } while(0) + +#define siw_prt_area_info(_dev, _name, _area) \ + do { \ + t_dev_info(_dev, "%s %4d %4d %4d %4d\n", \ + _name, _area->x1, _area->y1, _area->x2, _area->y2); \ + } while(0) + +static void siw_hal_prt_tci_info(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct tci_ctrl *tci = &ts->tci; + struct tci_info *info; + struct active_area *area; + struct reset_area *tci_qcover; + + info = &tci->info[TCI_1]; + siw_prt_tci_info(dev, "TCI_1", info); + info = &tci->info[TCI_2]; + siw_prt_tci_info(dev, "TCI_2", info); + + area = &ts->tci.area; + siw_prt_area_info(dev, "tci active area ", area); + + tci_qcover = &ts->tci.qcover_open; + siw_prt_area_info(dev, "tci qcover_open ", tci_qcover); + + tci_qcover = &ts->tci.qcover_close; + siw_prt_area_info(dev, "tci qcover_close", tci_qcover); +} + +static void siw_hal_get_tci_info(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + void *tci_src; + struct reset_area *tci_qcover; + + tci_src = pdata_tci_info(ts->pdata); + if (tci_src == NULL) { + tci_src = (void *)siw_hal_tci_info_default; + } + memcpy(ts->tci.info, tci_src, sizeof(siw_hal_tci_info_default)); + + //Set default + ts->tci.area.x1 = 0; + ts->tci.area.y1 = 0; + ts->tci.area.x2 = ts->caps.max_x; + ts->tci.area.y2 = ts->caps.max_y; + + tci_qcover = pdata_tci_qcover_open(ts->pdata); + if (tci_qcover != NULL) { + memcpy(&ts->tci.qcover_open, tci_qcover, sizeof(struct reset_area)); + } + + tci_qcover = pdata_tci_qcover_close(ts->pdata); + if (tci_qcover != NULL) { + memcpy(&ts->tci.qcover_close, tci_qcover, sizeof(struct reset_area)); + } + + siw_hal_prt_tci_info(dev); +} + +#if defined(__SIW_CONFIG_SWIPE) +const struct siw_hal_swipe_ctrl siw_hal_swipe_info_default = { + .mode = SWIPE_LEFT_BIT | SWIPE_RIGHT_BIT, + .info = { + [SWIPE_R] = { + .distance = 5, + .ratio_thres = 100, + .ratio_distance = 2, + .ratio_period = 5, + .min_time = 0, + .max_time = 150, + .area.x1 = 401, + .area.y1 = 0, + .area.x2 = 1439, + .area.y2 = 159, + }, + [SWIPE_L] = { + .distance = 5, + .ratio_thres = 100, + .ratio_distance = 2, + .ratio_period = 5, + .min_time = 0, + .max_time = 150, + .area.x1 = 401, /* 0 */ + .area.y1 = 0, /* 2060 */ + .area.x2 = 1439, + .area.y2 = 159, /* 2559 */ + }, + }, +}; + +#define siw_prt_swipe_info(_dev, _idx, _info) \ + do { \ + t_dev_info(_dev, \ + "swipe info[%s] distance %d, ratio_thres %d, ratio_distance %d\n", \ + _idx, _info->distance, _info->ratio_thres, _info->ratio_distance); \ + t_dev_info(_dev, \ + "swipe info[%s] ratio_period %d, min_time %d, max_time %d\n", \ + _idx, _info->ratio_period, _info->min_time, _info->max_time); \ + t_dev_info(_dev, \ + "swipe info[%s] area_x1 %d, area_y1 %d, area_x2 %d, area_y2 %d\n", \ + _idx, _info->area.x1, _info->area.y1, _info->area.x2, _info->area.y2); \ + } while(0) + +static void siw_hal_prt_swipe_info(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_swipe_ctrl *swipe = &chip->swipe; + struct siw_hal_swipe_info *info; + + t_dev_info(dev, "swipe mode %08Xh\n", swipe->mode); + info = &swipe->info[SWIPE_R]; + siw_prt_swipe_info(dev, "SWIPE_R", info); + info = &swipe->info[SWIPE_L]; + siw_prt_swipe_info(dev, "SWIPE_L", info); +} + +static void siw_hal_get_swipe_info(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + void *swipe_src; + + swipe_src = pdata_swipe_ctrl(ts->pdata); + if (!swipe_src) + swipe_src = (void *)&siw_hal_swipe_info_default; + + memcpy(&chip->swipe, swipe_src, sizeof(struct siw_hal_swipe_ctrl)); + + siw_hal_prt_swipe_info(dev); +} +#else /* __SIW_CONFIG_SWIPE */ +static void siw_hal_get_swipe_info(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + + memset(&chip->swipe, 0, sizeof(struct siw_hal_swipe_ctrl)); + + t_dev_info(dev, "swipe mode disabled\n"); +} +#endif /* __SIW_CONFIG_SWIPE */ + +static const char *siw_hal_pwr_name[] = { + [POWER_OFF] = "Power off", + [POWER_SLEEP] = "Power sleep", + [POWER_WAKE] = "Power wake", + [POWER_ON] = "Power on", +}; + +static int siw_hal_power(struct device *dev, int ctrl) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + if ((ctrl < 0) || (ctrl > POWER_ON)) { + t_dev_err(dev, "power ctrl: wrong ctrl value, %d\n", ctrl); + return -EINVAL; + } + + t_dev_dbg_pm(dev, "power ctrl: %s - %s\n", + touch_chip_name(ts), siw_hal_pwr_name[ctrl]); + + switch (ctrl) { + case POWER_OFF: + t_dev_dbg_pm(dev, "power ctrl: power off\n"); + atomic_set(&chip->init, IC_INIT_NEED); + + siw_hal_set_gpio_reset(dev, GPIO_OUT_ZERO); + siw_hal_power_vio(dev, 0); + siw_hal_power_vdd(dev, 0); + touch_msleep(chip->drv_reset_low + hal_dbg_delay(chip, HAL_DBG_DLY_HW_RST_0)); + break; + + case POWER_ON: + t_dev_dbg_pm(dev, "power ctrl: power on\n"); + siw_hal_power_vdd(dev, 1); + siw_hal_power_vio(dev, 1); + siw_hal_set_gpio_reset(dev, GPIO_OUT_ONE); + break; + + case POWER_SLEEP: + t_dev_dbg_pm(dev, "power ctrl: sleep\n"); + break; + + case POWER_WAKE: + t_dev_dbg_pm(dev, "power ctrl: wake\n"); + break; + + case POWER_HW_RESET: + t_dev_info(dev, "power ctrl: reset\n"); + siw_hal_reset_ctrl(dev, HW_RESET_ASYNC); + } + + return 0; +} + +enum { + BOOT_CHK_SKIP = (1<<16), +}; + +static int siw_hal_chk_boot_mode(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 boot_failed = 0; + u32 bootmode = 0; + u32 boot_chk_offset_busy = 0; + u32 boot_chk_offset_err = 0; + u32 boot_chk_empty_mask = 0; + int ret = 0; + + switch (chip->opt.t_boot_mode) { + case 2: + boot_chk_empty_mask = (1<<6); + /* fall through */ + case 1: + boot_chk_offset_busy = 0; + boot_chk_offset_err = 2; + break; + default: + boot_chk_offset_busy = 1; + boot_chk_offset_err = 3; + break; + } + + ret = siw_hal_read_value(dev, + reg->spr_boot_status, + &bootmode); + if (ret < 0) { + return ret; + } + + /* maybe nReset is low state */ + if (!bootmode || (bootmode == ~0)) { + return BOOT_CHK_SKIP; + } + + /* booting... need to wait */ + if ((bootmode >> boot_chk_offset_busy) & 0x1) { + return BOOT_CHK_SKIP; + } + + boot_failed |= !!((bootmode >> boot_chk_offset_err) & 0x1); /* CRC error */ + boot_failed |= (!!(bootmode & boot_chk_empty_mask))<<1; + + if (boot_failed) { + t_dev_err(dev, "boot fail: boot sts = %08Xh(%02Xh)\n", + bootmode, boot_failed); + } + + return boot_failed; +} + +static int siw_hal_chk_boot_status(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_status_mask_bit *mask_bit = &chip->status_mask_bit; + u32 boot_failed = 0; + u32 tc_status = 0; + u32 valid_cfg_crc_mask = 0; + u32 valid_code_crc_mask = 0; + int ret = 0; + + valid_cfg_crc_mask = (mask_bit->valid_cfg_crc) ? + mask_bit->valid_cfg_crc : (1<valid_code_crc) ? + mask_bit->valid_code_crc : (1<tc_status, + &tc_status); + if (ret < 0) { + return ret; + } + + /* maybe nReset is low state */ + if (!tc_status || (tc_status == ~0)) { + return BOOT_CHK_SKIP; + } + + if (!(tc_status & valid_cfg_crc_mask)) { + boot_failed |= (1<<5); + } + if (!(tc_status & valid_code_crc_mask)) { + boot_failed |= (1<<4); + } + if (boot_failed) { + t_dev_err(dev, "boot fail: tc_status = %08Xh(%02Xh)\n", + tc_status, boot_failed); + } + + return boot_failed; +} + +enum { + BOOT_CHK_MODE_RETRY = 2, + BOOT_CHK_STS_RETRY = 2, + /* */ + BOOT_CHK_MODE_DELAY = 10, + BOOT_CHK_STS_DELAY = 10, +}; + +static int siw_hal_chk_boot(struct device *dev) +{ + u32 boot_failed = 0; + int retry; + int ret = 0; + + retry = BOOT_CHK_MODE_RETRY; + while (retry--) { + ret = siw_hal_chk_boot_mode(dev); + if (ret < 0) { + return ret; + } + if (ret == BOOT_CHK_SKIP) { + return 0; + } + if (!ret) { + break; + } + touch_msleep(BOOT_CHK_MODE_DELAY); + } + boot_failed |= ret; + + retry = BOOT_CHK_STS_RETRY; + while (retry--) { + ret = siw_hal_chk_boot_status(dev); + if (ret < 0) { + return ret; + } + if (ret == BOOT_CHK_SKIP) { + return boot_failed; + } + if (!ret) { + break; + } + touch_msleep(BOOT_CHK_STS_DELAY); + } + boot_failed |= ret; + + return boot_failed; +} + +#if defined(CONFIG_TOUCHSCREEN_SIW_SW49408) +#define __SIW_SUPPORT_STATUS_ERROR_MEM +#endif + +#if defined(CONFIG_TOUCHSCREEN_SIW_SW49106) || \ + defined(CONFIG_TOUCHSCREEN_SIW_SW49408) || \ + defined(CONFIG_TOUCHSCREEN_SIW_SW49409) || \ + defined(CONFIG_TOUCHSCREEN_SIW_SW49501) +#define __SIW_SUPPORT_STATUS_ERROR_DISP +#endif + +enum { + IC_DEBUG_SIZE = 16, // byte + // + IC_CHK_LOG_MAX = (1<<9), + // + INT_IC_ABNORMAL_STATUS = (1<<0), + // + INT_IC_ERROR_STATUS = ((1<<5) | (1<<3)), +}; + +static const struct siw_hal_status_filter status_filter_type_0[] = { + _STS_FILTER(STS_ID_VALID_DEV_CTL, 1, STS_POS_VALID_DEV_CTL, + 0, "device ctl not set"), + _STS_FILTER(STS_ID_VALID_CODE_CRC, 1, STS_POS_VALID_CODE_CRC_TYPE_0, + 0, "code crc invalid"), + _STS_FILTER(STS_ID_ERROR_ABNORMAL, 1, STS_POS_ERROR_ABNORMAL, + STS_FILTER_FLAG_TYPE_ERROR | STS_FILTER_FLAG_CHK_FAULT, + "abnormal status detected"), + _STS_FILTER(STS_ID_ERROR_SYSTEM, 1, STS_POS_ERROR_SYSTEM, + STS_FILTER_FLAG_TYPE_ERROR | STS_FILTER_FLAG_ESD_SEND, + "system error detected"), + _STS_FILTER(STS_ID_ERROR_MISMTACH, 2, STS_POS_ERROR_MISMTACH, + STS_FILTER_FLAG_TYPE_ERROR, + "display mode mismatch"), + _STS_FILTER(STS_ID_VALID_IRQ_PIN, 1, STS_POS_VALID_IRQ_PIN, + 0, "irq pin invalid"), + _STS_FILTER(STS_ID_VALID_IRQ_EN, 1, STS_POS_VALID_IRQ_EN, + 0, "irq status invalid"), + /* end mask */ + _STS_FILTER(STS_ID_NONE, 0, 0, 0, NULL), +}; + +static const struct siw_hal_status_filter status_filter_type_1[] = { + _STS_FILTER(STS_ID_VALID_DEV_CTL, 1, STS_POS_VALID_DEV_CTL, + 0, "device ctl not set"), + _STS_FILTER(STS_ID_VALID_CODE_CRC, 1, STS_POS_VALID_CODE_CRC, + 0, "code crc invalid"), + _STS_FILTER(STS_ID_VALID_CFG_CRC, 1, STS_POS_VALID_CFG_CRC, + 0, "cfg crc invalid"), + _STS_FILTER(STS_ID_ERROR_ABNORMAL, 1, STS_POS_ERROR_ABNORMAL, + STS_FILTER_FLAG_TYPE_ERROR | STS_FILTER_FLAG_CHK_FAULT, + "abnormal status detected"), + _STS_FILTER(STS_ID_ERROR_SYSTEM, 1, STS_POS_ERROR_SYSTEM, + STS_FILTER_FLAG_TYPE_ERROR | STS_FILTER_FLAG_ESD_SEND, + "system error detected"), + _STS_FILTER(STS_ID_ERROR_MISMTACH, 1, STS_POS_ERROR_MISMTACH, + STS_FILTER_FLAG_TYPE_ERROR, + "display mode mismatch"), + _STS_FILTER(STS_ID_VALID_IRQ_PIN, 1, STS_POS_VALID_IRQ_PIN, + 0, "irq pin invalid"), + _STS_FILTER(STS_ID_VALID_IRQ_EN, 1, STS_POS_VALID_IRQ_EN, + 0, "irq status invalid"), + _STS_FILTER(STS_ID_VALID_TC_DRV, 1, STS_POS_VALID_TC_DRV, + 0, "driving invalid"), +#if defined(__SIW_SUPPORT_STATUS_ERROR_MEM) + _STS_FILTER(STS_ID_ERROR_MEM, 1, STS_POS_ERROR_MEM, + STS_FILTER_FLAG_TYPE_ERROR, + "memory error detected"), +#endif +#if defined(__SIW_SUPPORT_STATUS_ERROR_DISP) + _STS_FILTER(STS_ID_ERROR_DISP, 1, STS_POS_ERROR_DISP, + STS_FILTER_FLAG_TYPE_ERROR | STS_FILTER_FLAG_ESD_SEND, + "display error detected"), +#endif + /* end mask */ + _STS_FILTER(STS_ID_NONE, 0, 0, 0, NULL), +}; + +static const struct siw_hal_status_filter status_filter_type_2[] = { + _STS_FILTER(STS_ID_VALID_DEV_CTL, 1, STS_POS_VALID_DEV_CTL, + 0, "device ctl not set"), + _STS_FILTER(STS_ID_ERROR_ABNORMAL, 1, STS_POS_ERROR_ABNORMAL, + STS_FILTER_FLAG_TYPE_ERROR | STS_FILTER_FLAG_CHK_FAULT, + "re-init required"), + _STS_FILTER(STS_ID_VALID_IRQ_PIN, 1, STS_POS_VALID_IRQ_PIN, + 0, "irq pin invalid"), + _STS_FILTER(STS_ID_VALID_IRQ_EN, 1, STS_POS_VALID_IRQ_EN, + 0, "irq status invalid"), + _STS_FILTER(STS_ID_VALID_TC_DRV, 1, STS_POS_VALID_TC_DRV, + 0, "driving invalid"), + /* end mask */ + _STS_FILTER(STS_ID_NONE, 0, 0, 0, NULL), +}; + +static u32 siw_hal_get_status_mask(struct device *dev, int id) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_status_filter *filter = chip->status_filter; + u32 mask = 0; + + if (filter == NULL) + goto out; + + while (1) { + if (!filter->id || !filter->width) { + break; + } + + if (filter->id == id) { + mask = ((1<width)-1)<pos; + break; + } + filter++; + } + +out: + return mask; +} + +static void siw_hal_chk_report_type(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_fw_info *fw = &chip->fw; + + if (chip->report_type) { + return; + } + + if (!fw->product_id[0]) { + return; + } + + switch (touch_chip_type(ts)) { + default: + chip->report_type = CHIP_REPORT_TYPE_0; + break; + } + + t_dev_info(dev, "report type : %d\n", chip->report_type); +} + +static int siw_hal_chk_status_type(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_fw_info *fw = &chip->fw; + struct siw_hal_status_mask_bit *mask_bit = &chip->status_mask_bit; + + if (chip->status_type) { + return 0; + } + + if (!fw->product_id[0]) { + return -EINVAL; + } + + siw_hal_chk_report_type(dev); + + switch (touch_chip_type(ts)) { + case CHIP_SW42101: + chip->status_type = CHIP_STATUS_TYPE_2; + break; + case CHIP_LG4894: + if (!strncmp(fw->product_id, "L0W53K6P", 8)) { + chip->status_type = CHIP_STATUS_TYPE_0; + break; + } + chip->status_type = CHIP_STATUS_TYPE_1; + break; + default: + chip->status_type = CHIP_STATUS_TYPE_1; + break; + } + + switch (chip->status_type) { + case CHIP_STATUS_TYPE_2: + chip->status_filter = (struct siw_hal_status_filter *)status_filter_type_2; + break; + case CHIP_STATUS_TYPE_0: + chip->status_filter = (struct siw_hal_status_filter *)status_filter_type_0; + break; + default: + chip->status_filter = (struct siw_hal_status_filter *)status_filter_type_1; + break; + } + + mask_bit->valid_dev_ctl = siw_hal_get_status_mask(dev, STS_ID_VALID_DEV_CTL); + mask_bit->valid_code_crc = siw_hal_get_status_mask(dev, STS_ID_VALID_CODE_CRC); + mask_bit->valid_cfg_crc = siw_hal_get_status_mask(dev, STS_ID_VALID_CFG_CRC);; + mask_bit->error_abnormal = siw_hal_get_status_mask(dev, STS_ID_ERROR_ABNORMAL); + mask_bit->error_system = siw_hal_get_status_mask(dev, STS_ID_ERROR_SYSTEM); + mask_bit->error_mismtach = siw_hal_get_status_mask(dev, STS_ID_ERROR_MISMTACH); + mask_bit->valid_irq_pin = siw_hal_get_status_mask(dev, STS_ID_VALID_IRQ_PIN); + mask_bit->valid_irq_en = siw_hal_get_status_mask(dev, STS_ID_VALID_IRQ_EN); + mask_bit->error_mem = siw_hal_get_status_mask(dev, STS_ID_ERROR_MEM); + mask_bit->valid_tv_drv = siw_hal_get_status_mask(dev, STS_ID_VALID_TC_DRV); + mask_bit->error_disp = siw_hal_get_status_mask(dev, STS_ID_ERROR_DISP); + + t_dev_dbg_base(dev, "mask[v_dev] : %08Xh\n", mask_bit->valid_dev_ctl); + t_dev_dbg_base(dev, "mask[v_code] : %08Xh\n", mask_bit->valid_code_crc); + t_dev_dbg_base(dev, "mask[v_cfg] : %08Xh\n", mask_bit->valid_cfg_crc); + t_dev_dbg_base(dev, "mask[e_abn] : %08Xh\n", mask_bit->error_abnormal); + t_dev_dbg_base(dev, "mask[e_sys] : %08Xh\n", mask_bit->error_system); + t_dev_dbg_base(dev, "mask[e_mis] : %08Xh\n", mask_bit->error_mismtach); + t_dev_dbg_base(dev, "mask[v_i_p] : %08Xh\n", mask_bit->valid_irq_pin); + t_dev_dbg_base(dev, "mask[v_i_e] : %08Xh\n", mask_bit->valid_irq_en); + t_dev_dbg_base(dev, "mask[e_mem] : %08Xh\n", mask_bit->error_mem); + t_dev_dbg_base(dev, "mask[v_tc] : %08Xh\n", mask_bit->valid_tv_drv); + t_dev_dbg_base(dev, "mask[e_disp] : %08Xh\n", mask_bit->error_disp); + + chip->status_mask_normal = mask_bit->valid_dev_ctl | + mask_bit->valid_code_crc | + mask_bit->valid_cfg_crc | + mask_bit->valid_irq_pin | + mask_bit->valid_irq_en | + mask_bit->valid_tv_drv | + 0; + + chip->status_mask_logging = mask_bit->error_mismtach | + mask_bit->valid_irq_pin | + mask_bit->valid_irq_en | + mask_bit->valid_tv_drv | + 0; + + chip->status_mask_reset = mask_bit->valid_dev_ctl | + mask_bit->valid_code_crc | + mask_bit->valid_cfg_crc | + mask_bit->error_abnormal | + mask_bit->error_system | + mask_bit->error_mem | + mask_bit->error_disp | + 0; + + chip->status_mask = chip->status_mask_normal | + chip->status_mask_logging | + chip->status_mask_reset | + 0; + + chip->status_mask_ic_abnormal = INT_IC_ABNORMAL_STATUS; + chip->status_mask_ic_error = INT_IC_ERROR_STATUS; + chip->status_mask_ic_disp_err = 0; + + switch (chip->opt.t_sts_mask) { + case 1: + chip->status_mask_ic_abnormal |= (0x3<<6); + chip->status_mask_ic_valid = 0xFFFF; + break; + case 2: + chip->status_mask_ic_abnormal |= (0x3<<1); + chip->status_mask_ic_error = ((1<<7) | (1<<5)); + chip->status_mask_ic_valid = 0x7FFFF; + chip->status_mask_ic_disp_err = (0x3<<8); + break; + default: + chip->status_mask_ic_valid = 0xFF; + break; + } + + t_dev_info(dev, "status type : %d\n", chip->status_type); + t_dev_info(dev, "status mask : %08Xh\n", chip->status_mask); + t_dev_info(dev, " normal : %08Xh\n", chip->status_mask_normal); + t_dev_info(dev, " logging : %08Xh\n", chip->status_mask_logging); + t_dev_info(dev, " reset : %08Xh\n", chip->status_mask_reset); + t_dev_info(dev, " ic abnormal : %08Xh\n", chip->status_mask_ic_abnormal); + t_dev_info(dev, " ic error : %08Xh\n", chip->status_mask_ic_error); + t_dev_info(dev, " ic disp err : %08Xh\n", chip->status_mask_ic_disp_err); + + return 0; +} + +struct siw_ic_info_chip_proto { + int chip_type; + int vchip; + int vproto; +}; + +static const struct siw_ic_info_chip_proto siw_ic_info_chip_protos[] = { + { CHIP_LG4894, 4, 4 }, + { CHIP_LG4895, 8, 4 }, + { CHIP_LG4946, 7, 4 }, + { CHIP_LG4951, 7, 4 }, + { CHIP_SW1828, 9, 4 }, + { CHIP_SW49105, 10, 4 }, + { CHIP_SW49106, 11, 4 }, + { CHIP_SW49406, 7, 4 }, + { CHIP_SW49407, 9, 4 }, + { CHIP_SW49408, 9, 4 }, + { CHIP_SW49501, 14, 4 }, + { CHIP_SW42101, 15, 4 }, + { CHIP_NONE, 0, 0 }, //End mark +}; + +static int siw_hal_ic_info_ver_check(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_fw_info *fw = &chip->fw; + struct siw_ic_info_chip_proto *chip_proto; +// u32 version = fw->v.version_raw; + u32 vchip = fw->v.version.chip; + u32 vproto = fw->v.version.protocol; +// int ret = 0; + + chip_proto = (struct siw_ic_info_chip_proto *)siw_ic_info_chip_protos; + while (1) { + if (chip_proto->chip_type == CHIP_NONE) { + break; + } + + if (touch_chip_type(ts) == chip_proto->chip_type) { + if ((chip_proto->vchip != vchip) || + (chip_proto->vproto != vproto)) { + break; + } + + t_dev_info(dev, "[%s] IC info is good: %d, %d\n", + touch_chip_name(ts), vchip, vproto); + + return 0; + } + + chip_proto++; + } + + t_dev_err(dev, "[%s] IC info is abnormal: %d, %d\n", + touch_chip_name(ts), vchip, vproto); + + return -EINVAL; +} + +static int siw_hal_do_ic_info(struct device *dev, int prt_on) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_fw_info *fw = &chip->fw; + struct touch_xfer_msg *xfer = ts->xfer; + u32 product[2] = {0}; + u32 chip_id = 0; + u32 version = 0; + u32 version_ext = 0; + u32 revision = 0; + u32 bootmode = 0; + u32 boot_chk_offset; + int ret = 0; + + siw_hal_xfer_init(dev, xfer); + + siw_hal_xfer_add_rx(xfer, + reg->spr_chip_id, + (void *)&chip_id, sizeof(chip_id)); + siw_hal_xfer_add_rx(xfer, + reg->tc_version, + (void *)&version, sizeof(version)); + siw_hal_xfer_add_rx(xfer, + reg->info_chip_version, + (void *)&revision, sizeof(revision)); + siw_hal_xfer_add_rx(xfer, + reg->tc_product_id1, + (void *)&product[0], sizeof(product)); + if (chip->opt.f_ver_ext) { + siw_hal_xfer_add_rx(xfer, + reg->tc_version_ext, + (void *)&version_ext, sizeof(version_ext)); + } + siw_hal_xfer_add_rx(xfer, + reg->spr_boot_status, + (void *)&bootmode, sizeof(bootmode)); + + ret = siw_hal_xfer_msg(dev, ts->xfer); + if (ret < 0) { + t_dev_err(dev, "ic_info(1): xfer failed, %d\n", ret); + return ret; + } + + if (chip->opt.f_info_more) { + siw_hal_xfer_init(dev, xfer); + + siw_hal_xfer_add_rx(xfer, + reg->info_fpc_type, + (void *)&fw->fpc, sizeof(fw->fpc)); + siw_hal_xfer_add_rx(xfer, + reg->info_wfr_type, + (void *)&fw->wfr, sizeof(fw->wfr)); + siw_hal_xfer_add_rx(xfer, + reg->info_cg_type, + (void *)&fw->cg, sizeof(fw->cg)); + siw_hal_xfer_add_rx(xfer, + reg->info_lot_num, + (void *)&fw->lot, sizeof(fw->lot)); + siw_hal_xfer_add_rx(xfer, + reg->info_serial_num, + (void *)&fw->sn, sizeof(fw->sn)); + siw_hal_xfer_add_rx(xfer, + reg->info_date, + (void *)&fw->date, sizeof(fw->date)); + siw_hal_xfer_add_rx(xfer, + reg->info_time, + (void *)&fw->time, sizeof(fw->time)); + + ret = siw_hal_xfer_msg(dev, ts->xfer); + if (ret < 0) { + t_dev_err(dev, "ic_info(2): xfer failed, %d\n", ret); + return ret; + } + } + + siw_hal_fw_set_chip_id(fw, chip_id); + siw_hal_fw_set_version(fw, version, version_ext); + siw_hal_fw_set_revision(fw, revision); + siw_hal_fw_set_prod_id(fw, (u8 *)product, sizeof(product)); + + siw_hal_chk_status_type(dev); + + fw->wfr &= WAFER_TYPE_MASK; + + if (fw->version_ext) { + int ferr; + + ferr = siw_hal_fw_chk_version_ext(fw->version_ext, + fw->v.version.ext); + t_dev_info_sel(dev, prt_on, + "[T] chip id %s, version %08X(%u.%02u) (0x%02X) %s\n", + chip->fw.chip_id, + fw->version_ext, + fw->v.version.major, fw->v.version.minor, + fw->revision, + (ferr < 0) ? "(invalid)" : ""); + } else { + t_dev_info_sel(dev, prt_on, + "[T] chip id %s, version v%u.%02u (0x%08X, 0x%02X)\n", + fw->chip_id, + fw->v.version.major, fw->v.version.minor, + version, fw->revision); + } + + switch (chip->opt.t_boot_mode) { + case 2: + /* fall through */ + case 1: + boot_chk_offset = 0; + break; + default: + boot_chk_offset = 1; + break; + } + t_dev_info_sel(dev, prt_on, + "[T] product id %s, flash boot %s(%s), crc %s (0x%08X)\n", + fw->product_id, + ((bootmode >> boot_chk_offset) & 0x1) ? "BUSY" : "idle", + ((bootmode >> (boot_chk_offset + 1)) & 0x1) ? "done" : "booting", + ((bootmode >> (boot_chk_offset + 2)) & 0x1) ? "ERROR" : "ok", + bootmode); + + ret = siw_hal_chk_boot(dev); + if (ret < 0) { + return ret; + } + if (ret) { + int boot_fail_cnt = chip->boot_fail_cnt; + atomic_set(&chip->boot, IC_BOOT_FAIL); + + /* Limit to avoid infinite repetition */ + if (boot_fail_cnt >= BOOT_FAIL_RECOVERY_MAX) { + t_dev_err(dev, "Boot fail can't be recovered(%d) - %02Xh\n", + boot_fail_cnt, ret); + return -EFAULT; + } + + t_dev_err(dev, "Boot fail detected(%d) - %02Xh\n", + boot_fail_cnt, ret); + + chip->boot_fail_cnt++; + + /* return special flag to let the core layer know */ + return -ETDBOOTFAIL; + } + chip->boot_fail_cnt = 0; + + if (chip->opt.f_info_more) { + t_dev_info_sel(dev, prt_on, + "[T] fpc %d, wfr %d, cg %d, lot %d\n", + fw->fpc, fw->wfr, fw->cg, fw->lot); + t_dev_info_sel(dev, prt_on, + "[T] sn %Xh, " + "date %04d.%02d.%02d, time %02d:%02d:%02d site%d\n", + fw->sn, + fw->date & 0xFFFF, ((fw->date>>16) & 0xFF), ((fw->date>>24) & 0xFF), + fw->time & 0xFF, ((fw->time>>8) & 0xFF), ((fw->time>>16) & 0xFF), + ((fw->time>>24) & 0xFF)); + } + + if (strcmp(fw->chip_id, touch_chip_id(ts))) { + t_dev_err(dev, "Invalid chip id(%s), shall be %s\n", + fw->chip_id, touch_chip_id(ts)); + return -EINVAL; + } + + ret = siw_hal_ic_info_ver_check(dev); + if (ret < 0) { +#if 0 +#if 1 + siw_hal_reset_ctrl(dev, HW_RESET_ASYNC); +#else + siw_hal_power(dev, POWER_OFF); + siw_hal_power(dev, POWER_ON); + touch_msleep(ts->caps.hw_reset_delay); +#endif +#endif + } + + return ret; +} + +static int siw_hal_ic_info(struct device *dev) +{ + return siw_hal_do_ic_info(dev, 1); +} + +#if defined(__SIW_CONFIG_FB) && !defined(__SIW_CONFIG_SYSTEM_PM) +static int siw_hal_fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct siw_ts *ts = + container_of(self, struct siw_ts, fb_notif); + struct fb_event *ev = (struct fb_event *)data; + int *blank; + + if (!ev || !ev->data) { + return 0; + } + + blank = (int *)ev->data; + + if (event == FB_EARLY_EVENT_BLANK) { + if (*blank == FB_BLANK_UNBLANK) { + t_dev_info(ts->dev, "FB_UNBLANK(early)\n"); + } else if (*blank == FB_BLANK_POWERDOWN) { + t_dev_info(ts->dev, "FB_BLANK(early)\n"); + } + } + + if (event == FB_EVENT_BLANK) { + if (*blank == FB_BLANK_UNBLANK) { + t_dev_info(ts->dev, "FB_UNBLANK\n"); + } else if (*blank == FB_BLANK_POWERDOWN) { + t_dev_info(ts->dev, "FB_BLANK\n"); + } + } + + return 0; +} + +/* + * Change notifier to siw_hal_fb_notifier_callback + * instead of siw_touch_fb_notifier_callback and + * siw_touch_suspend/resume will be called via + * siw_touch_fb_work_func + * : siw_touch_notify -> siw_touch_qd_fb_work_now + */ +static int siw_hal_fb_notifier_init(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + t_dev_dbg_base(dev, "fb_notif change\n"); + + fb_unregister_client(&ts->fb_notif); + ts->fb_notif.notifier_call = siw_hal_fb_notifier_callback; + fb_register_client(&ts->fb_notif); + + return 0; +} +#else /* __SIW_CONFIG_FB */ +static int siw_hal_fb_notifier_init(struct device *dev) +{ + return 0; +} +#endif /* __SIW_CONFIG_FB */ + +static int siw_hal_init_reg_verify(struct device *dev, + u32 addr, u32 data, int retry, + const char *name) +{ + u32 rdata = ~0; + int i; + int ret = 0; + + for (i = 0; i < retry; i++) { + ret = siw_hal_write_value(dev, addr, data); + if (ret < 0) { + t_dev_err(dev, "failed to write %s, %d\n", + name, ret); + return ret; + } + + ret = siw_hal_read_value(dev, addr, &rdata); + if (ret < 0) { + t_dev_err(dev, "failed to read %s, %d\n", + name, ret); + return ret; + } + + if (data == rdata) { + t_dev_dbg_base(dev, "init reg done: %s(%08Xh)\n", name, rdata); + return 0; + } + } + + t_dev_err(dev, "init reg failed: %08Xh, %08Xh\n", data, rdata); + + return -EFAULT; +} + +static int siw_hal_init_reg_set_pre(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int ret = 0; + + if (chip->opt.f_attn_opt) { + u32 addr = 0; + if (touch_bus_type(ts) == BUS_IF_I2C) { + addr = 0xFE4; + } else { + addr = 0xFE5; + } + if (addr) { + ret = siw_hal_write_value(dev, addr, 0); + if (ret < 0) { + goto out; + } + } + ret = siw_hal_init_reg_verify(dev, + 0xFF3, ABNORMAL_IC_DETECTION, 3, + "spi_tattn_opt"); + if (ret < 0) { + t_dev_err(dev, + "failed to set spi_tattn_opt, %d\n", + ret); + goto out; + } + } + +out: + return ret; +} + +static int siw_hal_init_reg_set_post(struct device *dev) +{ + return 0; +} + +static int siw_hal_init_reg_set(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 data = 1; + int ret = 0; + + siw_hal_init_reg_set_pre(dev); + + ret = siw_hal_write_value(dev, + reg->tc_device_ctl, + 1); + if (ret < 0) { + t_dev_err(dev, "failed to start chip, %d\n", ret); + goto out; + } + + ret = siw_hal_write_value(dev, + reg->tc_interrupt_ctl, + 1); + if (ret < 0) { + t_dev_err(dev, "failed to start chip irq, %d\n", ret); + goto out; + } + +#if 0 + ret = siw_hal_write_value(dev, + reg->spr_charger_status, + chip->charger); + if (ret < 0) { + goto out; + } +#endif + + data = atomic_read(&ts->state.ime); + + ret = siw_hal_write_value(dev, + reg->ime_state, + data); + if (ret < 0) { + goto out; + } + + siw_hal_init_reg_set_post(dev); + +out: + return ret; +} + +#if defined(__SIW_SUPPORT_WATCH) +static int siw_hal_check_watch(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + + siw_hal_watch_chk_font_status(chip->dev); + + if ((chip->lcd_mode == LCD_MODE_U2) && + siw_hal_watch_is_disp_waton(dev) && + siw_hal_watch_is_rtc_run(dev)) { + siw_hal_watch_get_curr_time(dev, NULL, NULL); + } + + return 0; +} + +static int siw_hal_check_mode_type_0(struct device *dev, + int lcd_mode, int chk_mode) +{ +// struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + int ret = 0; + + if (lcd_mode == LCD_MODE_U3) { + goto out; + } + + if (lcd_mode == LCD_MODE_U2) { + if (chk_mode == LCD_MODE_U2_UNBLANK) { + t_dev_info(dev, "U1 -> U2 : watch on\n"); + siw_hal_watch_init(dev); + // knockon mode change + swipe enable + ret = siw_hal_tc_driving(dev, LCD_MODE_U2); + if (!ret) + ret = 1; + } else { + t_dev_info(dev, "U2 mode change\n"); + } + goto out; + } + + if (lcd_mode == LCD_MODE_U2_UNBLANK) { + switch (chk_mode) { + case LCD_MODE_STOP: + t_dev_info(dev, "Skip mode change : LCD_MODE_STOP -> U1\n"); + siw_hal_watch_display_off(dev); + ret = 1; + break; + case LCD_MODE_U2: + t_dev_info(dev, "U2 -> U1 : watch off\n"); + siw_hal_watch_display_off(dev); + // abs mode change + swipe disable + ret = siw_hal_tc_driving(dev, LCD_MODE_U2_UNBLANK); + if (!ret) + ret = 1; + break; + case LCD_MODE_U0: + t_dev_info(dev, "U0 -> U1 mode change\n"); + break; + default: + t_dev_info(dev, "Not Defined Mode, %d\n", chk_mode); + break; + } + goto out; + } + + if (lcd_mode == LCD_MODE_U0) { + t_dev_info(dev, "U0 mode change\n"); + goto out; + } + + t_dev_info(dev, "Not defined mode, %d\n", lcd_mode); + +out: + return ret; +} + +static int siw_hal_check_mode_type_1(struct device *dev, + int lcd_mode, int chk_mode) +{ +// struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + int ret = 0; + + if (lcd_mode == LCD_MODE_U3) { + goto out; + } + + if (lcd_mode == LCD_MODE_U2) { + if (chk_mode == LCD_MODE_U2_UNBLANK) { + t_dev_info(dev, "U1 -> U2 : watch on\n"); + siw_hal_watch_init(dev); + ret = 1; + } else { + t_dev_info(dev, "U2 mode change\n"); + siw_hal_watch_init(dev); + } + goto out; + } + + if (lcd_mode == LCD_MODE_U2_UNBLANK) { + switch (chk_mode) { + case LCD_MODE_U2: + t_dev_info(dev, "U2 -> U1\n"); + break; + case LCD_MODE_U0: + t_dev_info(dev, "U0 -> U1 mode change\n"); + siw_hal_watch_init(dev); + break; + default: + t_dev_info(dev, "Not Defined Mode, %d\n", chk_mode); + break; + } + goto out; + } + + if (lcd_mode == LCD_MODE_U0) { + t_dev_info(dev, "U0 mode change\n"); + goto out; + } + + t_dev_info(dev, "Not defined mode, %d\n", lcd_mode); + +out: + return ret; +} + +static int siw_hal_check_mode(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + int ret = 0; + + switch (chip->opt.t_chk_mode) { + case 1: + ret = siw_hal_check_mode_type_1(dev, + chip->lcd_mode, chip->prev_lcd_mode); + break; + default: + ret = siw_hal_check_mode_type_0(dev, + chip->lcd_mode, chip->driving_mode); + break; + } + + return ret; +} + +static void siw_hal_lcd_event_read_reg(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct touch_xfer_msg *xfer = ts->xfer; + u32 rdata[5] = {0, 0}; + int ret = 0; + + siw_hal_xfer_init(dev, xfer); + + siw_hal_xfer_add_rx(xfer, + reg->tc_ic_status, + (void *)&rdata[0], sizeof(rdata[0])); + siw_hal_xfer_add_rx(xfer, + reg->tc_status, + (void *)&rdata[1], sizeof(rdata[1])); + siw_hal_xfer_add_rx(xfer, + reg->spr_subdisp_status, + (void *)&rdata[2], sizeof(rdata[2])); + siw_hal_xfer_add_rx(xfer, + reg->tc_version, + (void *)&rdata[3], sizeof(rdata[3])); + siw_hal_xfer_add_rx(xfer, + reg->spr_chip_id, + (void *)&rdata[4], sizeof(rdata[4])); + + ret = siw_hal_xfer_msg(dev, xfer); + if (ret < 0) { + t_dev_err(dev, "xfer failed, %d\n", ret); + return; + } + + t_dev_info(dev, + "r[%04X] %08Xh, r[%04X] %08Xh, r[%04X] %08Xh, r[%04X] %08Xh, r[%04X] %08Xh\n", + reg->tc_ic_status, rdata[0], + reg->tc_status, rdata[1], + reg->spr_subdisp_status, rdata[2], + reg->tc_version, rdata[3], + reg->spr_chip_id, rdata[4]); + t_dev_info(dev, + "v%d.%02d\n", + (rdata[3] >> 8) & 0xF, rdata[3] & 0xFF); +} +#else /* __SIW_SUPPORT_WATCH */ +static int siw_hal_check_watch(struct device *dev){ return 0; } +static int siw_hal_check_mode(struct device *dev){ return 0; } +static void siw_hal_lcd_event_read_reg(struct device *dev){ } +#endif /* __SIW_SUPPORT_WATCH */ + +#define DIC_ERR_TYPE 0x10 + +static int siw_hal_send_esd_notifier(struct device *dev, int type) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int esd = type; + int ret; + + if (touch_flags(ts) & TOUCH_SKIP_ESD_EVENT) { + if (type < DIC_ERR_TYPE) { + t_dev_info(dev, "skip sending ESD notifier\n"); + return 0; + } + } + + t_dev_info(dev, "trigger ESD notifier, %Xh\n", type); + + ret = siw_touch_atomic_notifier_call(LCD_EVENT_TOUCH_ESD_DETECTED, (void*)&esd); + if (ret) + t_dev_err(dev, "check the value, %d\n", ret); + + /* Sending Event done */ + return 1; +} + +static int siw_hal_init_quirk(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + u8 temp[10+4]; + int ret = 0; + + switch (touch_chip_type(ts)) { + case CHIP_SW42101: + memset(temp, 0, sizeof(temp)); + ret = siw_hal_reg_read(dev, 0x150, temp, 10); + if (ret < 0) { + t_dev_err(dev, "init quirk for SW42101 failed, %d\n", ret); + return ret; + } + t_dev_info(dev, "init quirk for SW42101: %s\n", temp); + break; + } + + return 0; +} + +static int siw_hal_init(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int init_retry = CHIP_INIT_RETRY_MAX; + int i; + int ret = 0; + + atomic_set(&chip->boot, IC_BOOT_DONE); + + if (atomic_read(&ts->state.core) == CORE_PROBE) { + siw_hal_fb_notifier_init(dev); + init_retry = CHIP_INIT_RETRY_PROBE; + } else { + ret = siw_hal_init_quirk(dev); + if (ret < 0) { + goto out; + } + } + + t_dev_dbg_base(dev, "charger_state = 0x%02X\n", chip->charger); + + if (atomic_read(&ts->state.debug_tool) == DEBUG_TOOL_ENABLE) { + siw_hal_abt_init(dev); + } + + for (i = 0; i < init_retry; i++) { + ret = siw_hal_ic_info(dev); + if (ret >= 0) { + break; + } + /* + * When boot fail detected + * + * 1. At the fisrt detection, + * it sends esd noti for LCD recovery(full reset procedure) + * and skip fw_upgrade. + * 2. LCD driver is suppsed to send lcd mode notifier + * back to touch side after its recovery. + * 3. The lcd mode notifier restarts init work again + * via siw_touch_resume. + * 4. If boot fail detected again(counted by boot_fail_cnt) + * it goes to fw_upgrade stage. + * (See siw_touch_init_work_func in siw_touch.c) + */ + if (ret == -ETDBOOTFAIL) { + /* For the probe stage */ + if (atomic_read(&ts->state.core) == CORE_PROBE) { + break; + } + + /* Don't do recovery twice continuously */ + if (atomic_read(&chip->esd_noti_sent)) { + break; + } + + /* At the first boot fail */ + if (chip->boot_fail_cnt > 1) { + break; + } + + if (siw_hal_send_esd_notifier(dev, 2)) { + ret = -ETDSENTESD; + break; + } + } + + t_dev_dbg_base(dev, "retry getting ic info (%d)\n", i); + + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + siw_hal_power(dev, POWER_OFF); + siw_hal_power(dev, POWER_ON); + touch_msleep(ts->caps.hw_reset_delay); + } + if (ret < 0) { + goto out; + } + + atomic_set(&chip->esd_noti_sent, 0); + + siw_hal_init_reg_set(dev); + + siw_hal_watch_rtc_on(dev); + + atomic_set(&chip->init, IC_INIT_DONE); + atomic_set(&ts->state.sleep, IC_NORMAL); + + ret = siw_hal_lpwg_mode(dev); + if (ret < 0) { + t_dev_err(dev, "failed to lpwg_control, %d\n", ret); + goto out; + } + + ret = siw_hal_check_watch(dev); + if (ret < 0) { + t_dev_err(dev, "failed to check watch, %d\n", ret); + goto out; + } + +out: + if (ret) { + t_dev_err(dev, "%s init failed, %d\n", + touch_chip_name(ts), ret); + } else { + t_dev_info(dev, "%s init done\n", + touch_chip_name(ts)); + } + + siwmon_submit_ops_step_chip_wh_name(dev, "%s init done", + touch_chip_name(ts), ret); + + return ret; +} + +static int siw_hal_reinit(struct device *dev, + int pwr_con, + int delay, + int irq_enable, + int (*do_call)(struct device *dev)) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + int ret = 0; + + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + + atomic_set(&chip->init, IC_INIT_NEED); + + if (pwr_con) { + siw_hal_power(dev, POWER_OFF); + siw_hal_power(dev, POWER_ON); + } else { + siw_hal_set_gpio_reset(dev, GPIO_OUT_ZERO); + touch_msleep(chip->drv_reset_low + hal_dbg_delay(chip, HAL_DBG_DLY_HW_RST_0)); + siw_hal_set_gpio_reset(dev, GPIO_OUT_ONE); + } + + touch_msleep(delay); + + if (do_call) { + ret = do_call(dev); + } + + if (irq_enable) + siw_touch_irq_control(dev, INTERRUPT_ENABLE); + + return ret; +} + +#define SIW_SW_RST_CTL_T2 0xFE0 + +static int siw_hal_sw_reset_type_2(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + t_dev_info(dev, "SW Reset(2)\n"); + + siw_hal_write_value(dev, SIW_SW_RST_CTL_T2, 0); + + touch_msleep(1 + hal_dbg_delay(chip, HAL_DBG_DLY_SW_RST_0)); + + siw_hal_write_value(dev, SIW_SW_RST_CTL_T2, 1); + + touch_msleep(ts->caps.sw_reset_delay + \ + hal_dbg_delay(chip, HAL_DBG_DLY_SW_RST_1)); + + return 0; +} + +static int siw_hal_sw_reset_type_1(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + t_dev_info(dev, "SW Reset(1)\n"); + + siw_hal_cmd_write(dev, CMD_ENA); + siw_hal_cmd_write(dev, CMD_RESET_LOW); + + touch_msleep(1 + hal_dbg_delay(chip, HAL_DBG_DLY_SW_RST_0)); + + siw_hal_cmd_write(dev, CMD_RESET_HIGH); + siw_hal_cmd_write(dev, CMD_DIS); + + touch_msleep(ts->caps.sw_reset_delay + \ + hal_dbg_delay(chip, HAL_DBG_DLY_SW_RST_1)); + + return 0; +} + +static int siw_hal_sw_reset_default(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 chk_resp; + u32 data; + int ret = 0; + + /****************************************************** + * Siliconworks does not recommend to use SW reset * + * due to ist limitation in stability in LG4894. * + ******************************************************/ + t_dev_info(dev, "SW Reset\n"); + ret = siw_hal_write_value(dev, + reg->spr_rst_ctl, + 7); + ret = siw_hal_write_value(dev, + reg->spr_rst_ctl, + 0); + + /* Boot Start */ + ret = siw_hal_write_value(dev, + reg->spr_boot_ctl, + 1); + + /* firmware boot done check */ + chk_resp = FLASH_BOOTCHK_VALUE; + ret = siw_hal_condition_wait(dev, reg->tc_flash_dn_status, &data, + chk_resp, ~0, + 10 + hal_dbg_delay(chip, HAL_DBG_DLY_SW_RST_0), + 200); + if (ret < 0) { + t_dev_err(dev, "failed : boot check(%Xh), %Xh\n", + chk_resp, data); + goto out; + } + +out: + return ret; +} + +static int siw_hal_sw_reset(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int ret = 0; + + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + + atomic_set(&chip->init, IC_INIT_NEED); + + switch (chip->opt.t_sw_rst) { + case 2: + ret = siw_hal_sw_reset_type_2(dev); + break; + + case 1: + ret = siw_hal_sw_reset_type_1(dev); + break; + + default: + ret = siw_hal_sw_reset_default(dev); + break; + } + + siw_touch_qd_init_work_sw(ts); + + return ret; +} + +static int siw_hal_hw_reset(struct device *dev, int ctrl) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + t_dev_info(dev, "HW Reset(%s)\n", + (ctrl == HW_RESET_ASYNC) ? "Async" : "Sync"); + + if (ctrl == HW_RESET_ASYNC) { + siw_hal_reinit(dev, 0, hal_dbg_delay(chip, HAL_DBG_DLY_HW_RST_1), 0, NULL); + siw_touch_qd_init_work_hw(ts); + return 0; + } + + siw_hal_reinit(dev, 0, ts->caps.hw_reset_delay, 1, siw_hal_init); + + return 0; +} + +static int siw_hal_reset_ctrl(struct device *dev, int ctrl) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int ret = -EINVAL; + + mutex_lock(&ts->reset_lock); + + t_dev_info(dev, "%s reset control(%d)\n", + touch_chip_name(ts), ctrl); + + siw_hal_watch_set_rtc_clear(dev); + + switch (ctrl) { + case SW_RESET: + ret = siw_hal_sw_reset(dev); + break; + + case HW_RESET_ASYNC: + case HW_RESET_SYNC: + ret = siw_hal_hw_reset(dev, ctrl); + break; + + default: + t_dev_err(dev, "unknown reset type, %d\n", ctrl); + break; + } + + mutex_unlock(&ts->reset_lock); + + return ret; +} + +static int siw_hal_fw_rd_value(struct device *dev, + u32 addr, u32 *value) +{ + u32 data; + int ret; + + ret = siw_hal_read_value(dev, addr, &data); + if (ret < 0) { + return ret; + } + + t_dev_dbg_base(dev, "FW upgrade: reg rd: addr[%04Xh], value[%08Xh], %d\n", + addr, data, ret); + + if (value) + *value = data; + + return 0; +} + +static int siw_hal_fw_wr_value(struct device *dev, + u32 addr, u32 value) +{ + int ret; + + ret = siw_hal_write_value(dev, addr, value); + if (ret < 0) { + return ret; + } + + t_dev_dbg_base(dev, "FW upgrade: reg wr: addr[%04Xh], value[%08Xh], %d\n", + addr, value, ret); + + return 0; +} + +static int siw_hal_fw_wr_seq(struct device *dev, + u32 addr, u8 *data, int size) +{ + int ret; + + ret = siw_hal_reg_write(dev, addr, (void *)data, size); + if (ret < 0) { + return ret; + } + + t_dev_dbg_base(dev, "FW upgrade: reg wr: addr[%04Xh], data[%02X ...], %d\n", + addr, data[0], ret); + + return 0; +} + +static int siw_hal_fw_sram_wr_enable(struct device *dev, int onoff) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 data; + int ret = 0; + +#if 0 + ret = siw_hal_fw_rd_value(dev, reg->spr_sram_ctl, &data); + if (ret < 0) { + goto out; + } + + if (onoff) + data |= 0x01; + else + data &= ~0x01; + + ret = siw_hal_fw_wr_value(dev, reg->spr_sram_ctl, data); + if (ret < 0) { + goto out; + } +#else +// data = !!onoff; + data = (onoff) ? 0x03 : 0x00; + ret = siw_hal_fw_wr_value(dev, reg->spr_sram_ctl, data); + if (ret < 0) { + goto out; + } +#endif + +out: + return ret; +} + +#define FW_DN_LOG_UNIT (8<<10) + +static int siw_hal_fw_upgrade_fw_core(struct device *dev, u8 *dn_buf, int dn_size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + int is_i2c = !!(touch_bus_type(ts) == BUS_IF_I2C); + u8 *fw_data; + int fw_size; + int fw_pos, curr_size; + int fw_size_org = dn_size; + int fw_dn_size = 0, fw_dn_percent; + int ret = 0; + + fw_data = dn_buf; + fw_size = dn_size; + fw_pos = 0; + while (fw_size) { + t_dev_dbg_base(dev, "FW upgrade: fw_pos[%06Xh ...] = %02X %02X %02X %02X ...\n", + fw_pos, + fw_data[0], fw_data[1], fw_data[2], fw_data[3]); + + curr_size = min(fw_size, MAX_RW_SIZE); + + /* code sram base address write */ + ret = siw_hal_fw_wr_value(dev, reg->spr_code_offset, fw_pos>>2); + if (ret < 0) { + goto out; + } + + ret = siw_hal_fw_wr_seq(dev, reg->code_access_addr, + (void *)fw_data, curr_size); + if (ret < 0) { + goto out; + } + + fw_data += curr_size; + fw_pos += curr_size; + fw_size -= curr_size; + + /* + * Show progress log for slow I2C case + */ + if (!is_i2c) { + continue; + } + + fw_dn_size += curr_size; + if (!fw_size || !(fw_dn_size & (FW_DN_LOG_UNIT-1))) { + fw_dn_percent = (fw_dn_size * 100); + fw_dn_percent /= fw_size_org; + + t_dev_info(dev, "FW upgrade: downloading...(%d%c)\n", + fw_dn_percent, '%'); + } + } + +out: + return ret; +} + +static int siw_hal_fw_upgrade_conf_core(struct device *dev, + u32 addr, u8 *dn_buf, int dn_size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + int ret; + + /* conf sram base address write */ + ret = siw_hal_fw_wr_value(dev, + reg->serial_data_offset, + addr); + if (ret < 0) { + goto out; + } + + /* Conf data download to conf sram */ + ret = siw_hal_fw_wr_seq(dev, + reg->data_i2cbase_addr, + (void *)dn_buf, dn_size); + if (ret < 0) { + goto out; + } + +out: + return ret; +} + +#if defined(__SIW_FW_TYPE_1) +/* + * Common CONF + Specific CONF(s) + */ +enum { + POW_C_CONF = 9, + POW_S_CONF = 10, +}; + +enum { + NUM_C_CONF = 1, + MIN_S_CONF = 1, + MAX_S_CONF = 31, +}; + +enum { + MIN_S_CONF_IDX = 1, + MAX_S_CONF_IDX = (MAX_S_CONF + 1), +}; + +#define FW_BOOT_LOADER_INIT (0x74696E69) //"init" +#define FW_BOOT_LOADER_CODE (0x544F4F42) //"BOOT" + +#define FW_BOOT_CODE_ADDR (0x044) +#define FW_S_CONF_IDX_ADDR (0x260) +#define FW_S_CONF_DN_ADDR (0x267) + +#define FW_TYPE_STR "FW_TYPE_1" + +#define FLASH_CONF_DNCHK_VALUE_TYPE_X (FLASH_CONF_DNCHK_VALUE | 0x0C) +#define FLASH_CONF_SIZE_TYPE_X (1<<9) + +#define FW_POST_QUIRK_DELAY 20 +#define FW_POST_QUIRK_COUNT 200 + +static int siw_hal_fw_upgrade_fw_post_quirk(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 chk_resp, data; + u32 boot_code_addr = chip->fw.boot_code_addr; + int ret; + + ret = siw_hal_fw_wr_value(dev, boot_code_addr, FW_BOOT_LOADER_INIT); + if (ret < 0) { + goto out; + } + + /* Set Serial Dump Done */ + ret = siw_hal_fw_wr_value(dev, reg->spr_boot_ctl, 1); + if (ret < 0) { + goto out; + } + + /* Release CM3 core */ + ret = siw_hal_fw_wr_value(dev, reg->spr_rst_ctl, 0); + if (ret < 0) { + goto out; + } + + /* firmware boot done check */ + chk_resp = FW_BOOT_LOADER_CODE; + ret = siw_hal_condition_wait(dev, boot_code_addr, &data, + chk_resp, ~0, + FW_POST_QUIRK_DELAY + hal_dbg_delay(chip, HAL_DBG_DLY_FW_0), + FW_POST_QUIRK_COUNT); + if (ret < 0) { + t_dev_err(dev, "FW upgrade: failed - boot check(%Xh), %08Xh\n", + chk_resp, data); + goto out; + } + + t_dev_info(dev, "FW upgrade: boot check done\n"); + +out: + return ret; +} + +static int siw_hal_fw_upgrade_conf_quirk(struct device *dev, + u8 *fw_buf, int fw_size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int fw_size_max; + u32 conf_dn_addr; + u32 data; + int ret = 0; + + if (!chip->fw.conf_index) { + goto out; + } + + fw_size_max = touch_fw_size(ts); + + conf_dn_addr = chip->fw.conf_dn_addr; + ret = siw_hal_fw_rd_value(dev, conf_dn_addr, &data); + if (ret < 0) { + goto out; + } + + conf_dn_addr = (data & 0xFFFF); + t_dev_dbg_base(dev, "FW upgrade: s_conf_dn_addr %04Xh (%08Xh)\n", + conf_dn_addr, data); + + data = fw_size_max + \ + (NUM_C_CONF<fw.conf_index - 1)<ts; + struct siw_hal_fw_info *fw = &chip->fw; + + fw->conf_index = 0; + fw->conf_idx_addr = FW_S_CONF_IDX_ADDR; + fw->conf_dn_addr = FW_S_CONF_DN_ADDR; + fw->boot_code_addr = FW_BOOT_CODE_ADDR; + + switch (touch_chip_type(ts)) { + case CHIP_SW49501: + fw->conf_idx_addr = 0x316; + fw->conf_dn_addr = 0x31D; + fw->boot_code_addr = 0x03F; + break; + } + + t_dev_info(dev, "FW upgrade: idx %Xh, dn %Xh, code %Xh\n", + fw->conf_idx_addr, fw->conf_dn_addr, fw->boot_code_addr); +} + +static int siw_hal_fw_size_check(struct device *dev, int fw_size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int fw_size_max = touch_fw_size(ts); + int size_min = (fw_size_max + (NUM_C_CONF< size_max)) { + t_dev_err(dev, "FW upgrade: wrong file size - %Xh,\n", + fw_size); + t_dev_err(dev, " shall be '%Xh <= x <= %Xh'\n", + size_min, size_max); + return -EFAULT; + } + + return 0; +} + +static int siw_hal_fw_size_check_post(struct device *dev, int fw_size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int fw_size_max = touch_fw_size(ts); + int required_size; + u32 index = 0; + u32 conf_idx_addr = chip->fw.conf_idx_addr; + int ret = 0; + +#if (S_CFG_DBG_IDX != 0) + index = S_CFG_DBG_IDX; + t_dev_warn(dev, "FW upgrade: conf_index fixed for debugging: %d\n", index); +#else + ret = siw_hal_read_value(dev, conf_idx_addr, &index); +#endif + if (ret < 0) { + t_dev_err(dev, "FW upgrade: failed - conf_index(%04Xh) read, %d\n", + conf_idx_addr, ret); + return ret; + } + if ((index < MIN_S_CONF_IDX) || (index > MAX_S_CONF_IDX)) { + t_dev_err(dev, "FW upgrade: failed - wrong cfg index, %d\n", index); + return -EFAULT; + } + t_dev_info(dev, "FW upgrade: conf_index: %d\n", index); + + required_size = fw_size_max + (NUM_C_CONF<fw.conf_index = index; + + return 0; +} +#else /* __FW_TYPE_1 */ +#define FW_TYPE_STR "FW_TYPE_0" + +#define FLASH_CONF_DNCHK_VALUE_TYPE_X (FLASH_CONF_DNCHK_VALUE) +#define FLASH_CONF_SIZE_TYPE_X FLASH_CONF_SIZE + +#define FW_POST_QUIRK_DELAY 20 +#define FW_POST_QUIRK_COUNT 200 + +static int siw_hal_fw_upgrade_fw_post_quirk(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 chk_resp, data; + int ret; + + /* Release CM3 core */ + ret = siw_hal_fw_wr_value(dev, reg->spr_rst_ctl, 0); + if (ret < 0) { + goto out; + } + + /* Set Serial Dump Done */ + ret = siw_hal_fw_wr_value(dev, reg->spr_boot_ctl, 1); + if (ret < 0) { + goto out; + } + + /* firmware boot done check */ + chk_resp = FLASH_BOOTCHK_VALUE; + ret = siw_hal_condition_wait(dev, reg->tc_flash_dn_status, &data, + chk_resp, ~0, + FW_POST_QUIRK_DELAY + hal_dbg_delay(chip, HAL_DBG_DLY_FW_0), + FW_POST_QUIRK_COUNT); + if (ret < 0) { + t_dev_err(dev, "FW upgrade: failed - boot check(%Xh), %08Xh\n", + chk_resp, data); + goto out; + } + + t_dev_info(dev, "FW upgrade: boot check done\n"); + +out: + return ret; +} + +static int siw_hal_fw_upgrade_conf_quirk(struct device *dev, + u8 *fw_buf, int fw_size) +{ + return 0; +} + +static int siw_hal_fw_size_check(struct device *dev, int fw_size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int fw_size_max = touch_fw_size(ts); + + chip->fw.conf_index = 0; + + if ((fw_size != fw_size_max) && + (fw_size != (fw_size_max + FLASH_CONF_SIZE))) + { + t_dev_err(dev, "FW upgrade: wrong file size - %Xh,\n", + fw_size); + t_dev_err(dev, " shall be '%Xh' or '%Xh + %Xh'\n", + fw_size_max, fw_size_max, FLASH_CONF_SIZE); + return -EFAULT; + } + + return 0; +} + +static int siw_hal_fw_size_check_post(struct device *dev, int fw_size) +{ + return 0; +} +#endif /* __FW_TYPE_1 */ + +enum { + BIN_VER_OFFSET_POS = 0xE8, + BIN_VER_EXT_OFFSET_POS = 0xDC, + BIN_PID_OFFSET_POS = 0xF0, +}; + +static int siw_hal_fw_compare(struct device *dev, u8 *fw_buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_fw_info *fw = &chip->fw; + struct siw_touch_fquirks *fquirks = touch_fquirks(ts); + struct siw_hal_tc_version_bin *bin_ver; + int fw_max_size = touch_fw_size(ts); + u32 bin_ver_offset = 0; + u32 bin_ver_ext_offset = 0; + u32 bin_pid_offset = 0; + u32 dev_major = 0; + u32 dev_minor = 0; + char pid[12] = {0, }; + u32 bin_major = 0; + u32 bin_minor = 0; +// u32 bin_raw = 0; + u32 bin_raw_ext = 0; + int bin_diff = 0; + int update = 0; +// int ret = 0; + + if (fquirks->fwup_check) { + update = fquirks->fwup_check(dev, fw_buf); + if (update != -EAGAIN) { + if (update < 0) { + return update; + } + goto out; + } + update = 0; + } + + if (fw->version_ext) { + dev_major = fw->version_ext >> 8; + dev_minor = fw->version_ext & 0xFF; + } else { + dev_major = fw->v.version.major; + dev_minor = fw->v.version.minor; + } + + if (atomic_read(&chip->boot) == IC_BOOT_FAIL) { + update |= (1<<7); + goto out; + } + + if (!dev_major && !dev_minor){ + t_dev_err(dev, "fw can not be 0.0!! Check your panel connection!!\n"); + return 0; + } + + bin_ver_offset = *((u32 *)&fw_buf[BIN_VER_OFFSET_POS]); + if (!bin_ver_offset) { + t_dev_err(dev, "FW compare: zero ver offset\n"); + return -EINVAL; + } + + if (chip->opt.f_ver_ext) { + bin_ver_ext_offset = *((u32 *)&fw_buf[BIN_VER_EXT_OFFSET_POS]); + } else { + bin_ver_ext_offset = 0; + } + + if ((fw->version_ext && !bin_ver_ext_offset) || + (!fw->version_ext && bin_ver_ext_offset)) { + if (!ts->force_fwup) { + t_dev_warn(dev, + "FW compare: different version format, " + "use force update %s", + (fw->version_ext) ? "(ext)" : ""); + return -EINVAL; + } + bin_diff = 1; + } + + bin_pid_offset = *((u32 *)&fw_buf[BIN_PID_OFFSET_POS]); + if (!bin_pid_offset) { + t_dev_err(dev, "FW compare: zero pid offset\n"); + return -EINVAL; + } + + memcpy(pid, &fw_buf[bin_pid_offset], 8); + t_dev_dbg_base(dev, "pid %s\n", pid); + + if ((bin_ver_offset > fw_max_size) || + (bin_ver_ext_offset > fw_max_size) || + (bin_pid_offset > fw_max_size)) { + t_dev_err(dev, "FW compare: invalid offset - ver %06Xh, ver_ext %06Xh pid %06Xh, max %06Xh\n", + bin_ver_offset, bin_ver_ext_offset, bin_pid_offset, fw_max_size); + return -EINVAL; + } + + t_dev_dbg_base(dev, "ver %06Xh, ver_ext %06Xh, pid %06Xh\n", + bin_ver_offset, bin_ver_ext_offset, bin_pid_offset); + + bin_ver = (struct siw_hal_tc_version_bin *)&fw_buf[bin_ver_offset]; + bin_major = bin_ver->major; + bin_minor = bin_ver->minor; + + if (bin_ver_ext_offset) { + if (!bin_ver->ext) { + t_dev_err(dev, "FW compare: (no ext flag in binary)\n"); + return -EINVAL; + } + + memcpy(&bin_raw_ext, &fw_buf[bin_ver_ext_offset], sizeof(bin_raw_ext)); + bin_major = bin_raw_ext >> 8; + bin_minor = bin_raw_ext & 0xFF; + + t_dev_info(dev, + "FW compare: bin-ver: %08X (%s)(%d)\n", + bin_raw_ext, pid, bin_diff); + + if (siw_hal_fw_chk_version_ext(bin_raw_ext, + bin_ver->ext) < 0) { + t_dev_err(dev, "FW compare: (invalid extension in binary)\n"); + return -EINVAL; + } + } else { + t_dev_info(dev, + "FW compare: bin-ver: %d.%02d (%s)(%d)\n", + bin_major, bin_minor, pid, bin_diff); + } + + if (fw->version_ext) { + t_dev_info(dev, "FW compare: dev-ver: %08X (%s)\n", + fw->version_ext, fw->product_id); + } else { + t_dev_info(dev, "FW compare: dev-ver: %d.%02d (%s)\n", + dev_major, dev_minor, fw->product_id); + } + + if (ts->force_fwup) { + update |= (1<<0); + } else { + if (bin_major > dev_major) { + update |= (1<<1); + } else if (bin_major == dev_major) { + if (bin_minor > dev_minor) { + update |= (1<<2); + } + } + } + + if (memcmp(pid, fw->product_id, 8)) { + t_dev_err(dev, + "FW compare: bin-pid[%s] != dev-pid[%s], halted (up %02X, fup %02X)\n", + pid, fw->product_id, update, ts->force_fwup); + return -EINVAL; + } + +out: + t_dev_info(dev, + "FW compare: up %02X, fup %02X\n", + update, ts->force_fwup); + + return update; +} + +#if defined(__FW_VERIFY_TEST) +static int __siw_hal_fw_up_verify(struct device *dev, u8 *chk_buf, int chk_size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u8 *fw_rd_data, *fw_data; + u8 *r_data, *w_data; + int fw_size; + int fw_pos, curr_size; + int i; + int ret = 0; + + fw_size = chk_size; + + fw_rd_data = kmalloc(fw_size, GFP_KERNEL); + if (!fw_rd_data) { + t_dev_err(dev, "FW upgrade: failed to allocate verifying memory\n"); + ret = -ENOMEM; + goto out; + } + + fw_data = fw_rd_data; + fw_pos = 0; + while (fw_size && fw_data) { + curr_size = min(fw_size, MAX_RW_SIZE); + + /* code sram base address write */ + ret = siw_hal_write_value(dev, reg->spr_code_offset, fw_pos>>2); + if (ret < 0) { + goto out_free; + } + + ret = siw_hal_reg_read(dev, reg->code_access_addr, + (void *)fw_data, curr_size); + if (ret < 0) { + goto out_free; + } + + fw_data += curr_size; + fw_pos += curr_size; + fw_size -= curr_size; + } + + r_data = fw_rd_data; + w_data = chk_buf; + fw_size = chk_size; + for (i = 0; i < fw_size; i++) { + if ((*r_data) != (*w_data)) { + t_dev_err(dev, "* Err [%06X] rd(%02X) != wr(%02X)\n", + i, (*r_data), (*w_data)); + ret = -EFAULT; + } else { + #if 0 + t_dev_info(dev, " OK! [%06X] rd(%02X) == wr(%02X)\n", + i, (*r_data), (*w_data)); + #endif + } + + r_data++; + w_data++; + } + + if (ret >= 0) { + t_dev_info(dev, "FW dn verified\n"); + } + +out_free: + kfree(fw_rd_data); + +out: + return ret; +} +#else /* __FW_VERIFY_TEST */ +static int __siw_hal_fw_up_verify(struct device *dev, u8 *chk_buf, int chk_size) +{ + return 0; +} +#endif /* __FW_VERIFY_TEST */ + +static int siw_hal_fw_upgrade_fw_pre(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + int ret; + + /* Reset CM3 core */ + ret = siw_hal_fw_wr_value(dev, reg->spr_rst_ctl, 2); + if (ret < 0) { + goto out; + } + + /* Disable SRAM write protection */ + ret = siw_hal_fw_sram_wr_enable(dev, 1); + if (ret < 0) { + goto out; + } + +out: + return ret; +} + +#define FW_POST_DELAY 20 +#define FW_POST_COUNT 200 + +static int siw_hal_fw_upgrade_fw_post(struct device *dev, int fw_size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 dn_cmd, chk_resp, data; + int ret; + + /* Enable SRAM write protection */ + ret = siw_hal_fw_sram_wr_enable(dev, 0); + if (ret < 0) { + goto out; + } + + ret = siw_hal_fw_upgrade_fw_post_quirk(dev); + if (ret < 0) { + goto out; + } + + ret = siw_hal_fw_size_check_post(dev, fw_size); + if (ret < 0) { + goto out; + } + + /* Firmware Download Start */ + dn_cmd = (FLASH_KEY_CODE_CMD << 16) | 1; + ret = siw_hal_fw_wr_value(dev, reg->tc_flash_dn_ctl, dn_cmd); + if (ret < 0) { + goto out; + } + + touch_msleep(ts->caps.hw_reset_delay); + + /* download check */ + chk_resp = FLASH_CODE_DNCHK_VALUE; + ret = siw_hal_condition_wait(dev, reg->tc_flash_dn_status, &data, + chk_resp, 0xFFFF, + FW_POST_DELAY + hal_dbg_delay(chip, HAL_DBG_DLY_FW_1), + FW_POST_COUNT); + if (ret < 0) { + t_dev_err(dev, "FW upgrade: failed - code check(%Xh), %08Xh\n", + chk_resp, data); + goto out; + } + t_dev_info(dev, "FW upgrade: code check done\n"); + +out: + return ret; +} + +static int siw_hal_fw_upgrade_fw(struct device *dev, + u8 *fw_buf, int fw_size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + u8 *fw_data; + int fw_size_max; + int ret = 0; + + /* + * Stage 1-1 : download code data + */ + fw_size_max = touch_fw_size(ts); + + ret = siw_hal_fw_upgrade_fw_pre(dev); + if (ret < 0) { + goto out; + } + + /* + * [Caution] + * The size for F/W upgrade is fw_size_max, not fw->size + * because the fw file can have config area. + */ + fw_data = fw_buf; + ret = siw_hal_fw_upgrade_fw_core(dev, fw_data, fw_size_max); + if (ret < 0) { + goto out; + } + + ret = __siw_hal_fw_up_verify(dev, fw_data, fw_size_max); + if (ret < 0) { + goto out; + } + + /* + * Stage 1-2: upgrade code data + */ + ret = siw_hal_fw_upgrade_fw_post(dev, fw_size); + if (ret < 0) { + goto out; + } + +out: + return ret; +} + +static int siw_hal_fw_upgrade_conf_pre(struct device *dev, u32 *value) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + int data = 0; + int ret; + + ret = siw_hal_fw_rd_value(dev, reg->tc_confdn_base_addr, &data); + if (ret < 0) { + goto out; + } + +out: + if (value) + *value = data; + + return ret; +} + +#define CONF_POST_DELAY 20 +#define CONF_POST_COUNT 200 + +static int siw_hal_fw_upgrade_conf_post(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 dn_cmd, chk_resp, data; + int ret; + + /* Conf Download Start */ + dn_cmd = (FLASH_KEY_CONF_CMD << 16) | 2; + ret = siw_hal_fw_wr_value(dev, reg->tc_flash_dn_ctl, dn_cmd); + if (ret < 0) { + goto out; + } + + /* Conf check */ + chk_resp = FLASH_CONF_DNCHK_VALUE_TYPE_X; + ret = siw_hal_condition_wait(dev, reg->tc_flash_dn_status, &data, + chk_resp, 0xFFFF, + CONF_POST_DELAY + hal_dbg_delay(chip, HAL_DBG_DLY_FW_2), + CONF_POST_COUNT); + if (ret < 0) { + t_dev_err(dev, "FW upgrade: failed - conf check(%Xh), %X\n", + chk_resp, data); + ret = -EPERM; + goto out; + } + + t_dev_info(dev, "FW upgrade: conf check done\n"); + + /* Release & Reset CM3 */ + ret = siw_hal_fw_wr_value(dev, reg->spr_rst_ctl, 1); + if (ret < 0) { + goto out; + } + +out: + return ret; +} + +static int siw_hal_fw_upgrade_conf(struct device *dev, + u8 *fw_buf, int fw_size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int fw_size_max; + u32 conf_dn_addr; + u32 data; + int ret; + + fw_size_max = touch_fw_size(ts); + + /* + * Stage 2-1: download config data + */ + ret = siw_hal_fw_upgrade_conf_pre(dev, &data); + if (ret < 0) { + goto out; + } + + conf_dn_addr = ((data >> 16) & 0xFFFF); + + t_dev_dbg_base(dev, "FW upgrade: conf_dn_addr %04Xh (%08Xh)\n", + conf_dn_addr, data); +#if 0 + if (conf_dn_addr >= (0x1200) || conf_dn_addr < (0x8C0)) { + t_dev_err(dev, "FW upgrade: failed - conf base invalid\n"); + ret = -EPERM; + goto out; + } +#endif + + /* C_CFG */ + ret = siw_hal_fw_upgrade_conf_core(dev, conf_dn_addr, + (u8 *)&fw_buf[fw_size_max], FLASH_CONF_SIZE_TYPE_X); + if (ret < 0) { + goto out; + } + + ret = siw_hal_fw_upgrade_conf_quirk(dev, fw_buf, fw_size); + if (ret < 0) { + goto out; + } + + /* + * Stage 2-2: upgrade config data + */ + ret = siw_hal_fw_upgrade_conf_post(dev); + if (ret < 0) { + goto out; + } + +out: + return ret; +} + +static int siw_hal_fw_upgrade(struct device *dev, + u8 *fw_buf, int fw_size, int retry) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_touch_fquirks *fquirks = touch_fquirks(ts); + int fw_size_max; + u32 include_conf; + int ret = 0; + + t_dev_info(dev, "===== FW upgrade: start (%d) =====\n", retry); + + if (fquirks->fwup_upgrade) { + ret = fquirks->fwup_upgrade(dev, fw_buf, fw_size, retry); + if (ret < 0) { + goto out; + } + goto out_done; + } + + fw_size_max = touch_fw_size(ts); + + ret = siw_hal_fw_size_check(dev, fw_size); + if (ret < 0) { + goto out; + } + + include_conf = !!(fw_size > fw_size_max); + t_dev_info(dev, "FW upgrade:%s include conf data\n", + (include_conf) ? "" : " not"); + + t_dev_dbg_base(dev, "FW upgrade: fw size %08Xh, fw_size_max %08Xh\n", + fw_size, fw_size_max); + + ret = siw_hal_fw_upgrade_fw(dev, fw_buf, fw_size); + if (ret < 0) { + goto out; + } + + if (include_conf) { + ret = siw_hal_fw_upgrade_conf(dev, fw_buf, fw_size); + if (ret < 0) { + goto out; + } + } + +out_done: + t_dev_info(dev, "===== FW upgrade: done (%d) =====\n", retry); + +out: + return ret; +} + +static int siw_hal_fw_do_get_fw_abs(const struct firmware **fw_p, + const char *name, + struct device *dev) +{ +// struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct firmware *fw = NULL; + struct file *filp = NULL; + char *buf = NULL; + loff_t size; + int rd_size; + int ret = 0; + + fw = kzalloc(sizeof(*fw), GFP_KERNEL); + if (fw == NULL) { + dev_err(dev, "can't allocate fw(struct firmware)\n"); + return -ENOMEM; + } + + filp = filp_open(name, O_RDONLY, 0); + if (IS_ERR(filp)) { + dev_err(dev, "can't open %s\n", name); + return (int)PTR_ERR(filp); + } + + size = vfs_llseek(filp, 0, SEEK_END); + if (size < 0) { + t_dev_err(dev, "invalid file size, %d\n", (int)size); + ret = -EINVAL; + goto out; + } + + buf = kzalloc((size_t)size, GFP_KERNEL); + if (buf == NULL) { + t_dev_err(dev, "can't allocate firm buf\n"); + ret = -ENOMEM; + goto out; + } + + rd_size = kernel_read(filp, 0, + (char *)buf, + (unsigned long)size); + if (rd_size != (int)size) { + t_dev_err(dev, "can't read[%d], %d\n", + (int)size, (int)rd_size); + ret = (rd_size < 0) ? rd_size : -EFAULT; + goto out; + } + + fw->data = buf; + fw->size = size; + + if (fw_p) { + *fw_p = fw; + } + + filp_close(filp, 0); + + return 0; + +out: + if (buf) + kfree(buf); + + if (fw) + kfree(fw); + + filp_close(filp, 0); + + return ret; +} + +static int siw_hal_fw_do_get_file(const struct firmware **fw_p, + const char *name, + struct device *dev, + int abs_path) +{ + if (abs_path) { + return siw_hal_fw_do_get_fw_abs(fw_p, name, dev); + } + + return request_firmware(fw_p, name, dev); +} + +static int siw_hal_fw_get_file(const struct firmware **fw_p, + char *fwpath, + struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + const struct firmware *fw = NULL; + char *src_path; + int src_len; + int abs_path = 0; + int ret = 0; + + if (atomic_read(&ts->state.fb) >= FB_SUSPEND) { + t_dev_warn(dev, "state.fb is not FB_RESUME\n"); + ret = -EPERM; + goto out; + } + + if (ts->test_fwpath[0]) { + src_path = (char *)ts->test_fwpath; + } else if (ts->def_fwcnt) { + src_path = (char *)ts->def_fwpath[0]; + } else { + t_dev_err(dev, "no target fw defined\n"); + ret = -ENOENT; + goto out; + } + + /* + * Absolute path option + * ex) echo {root}/.../target_fw_img > fw_upgrade + * ++++++~~~~~~~~~~~~~~~~~~ + * flag | + * absolute path + */ + src_len = strlen(src_path); + if (strncmp(src_path, "{root}", 6) == 0) { + abs_path = 1; + src_path += 6; + src_len -= 6; + } + chip->fw_abs_path = abs_path; + + strncpy(fwpath, src_path, src_len); + fwpath[src_len] = 0; + + t_dev_info(dev, "target fw: %s (%s)\n", + fwpath, + (abs_path) ? "abs" : "rel"); + + ret = siw_hal_fw_do_get_file(&fw, + (const char *)fwpath, + dev, abs_path); + if (ret < 0) { + if (ret == -ENOENT) { + t_dev_err(dev, "can't find fw: %s\n", fwpath); + } else { + t_dev_err(dev, "can't %s fw: %s, %d\n", + (abs_path) ? "read" : "request", + fwpath, ret); + } + goto out; + } + + if (fw_p) { + *fw_p = fw; + } + +out: + return ret; +} + +static void siw_hal_fw_release_firm(struct device *dev, + const struct firmware *fw) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + + if (chip->fw_abs_path) { + chip->fw_abs_path = 0; + kfree(fw->data); + kfree(fw); + return; + } + + release_firmware(fw); +} + +/* + * FW upgrade option + * + * 1. If TOUCH_USE_FW_BINARY used + * 1-1 Default upgrade (through version comparison) + * do upgarde using binary header link + * 1-2 echo {bin} > fw_upgrade + * do force-upgrade using binary header link (same as 1-1) + * 1-3 echo /.../fw_img > fw_upgrade + * do force-upgrade using request_firmware (relative path) + * 1-4 echo {root}/.../fw_img > fw_upgrade + * do force-upgrade using normal file open control (absolute path) + * + * 2. Else + * 2-1 Default upgrade (through version comparison) + * do upgarde using request_firmware (relative path) + * 2-2 echo /.../fw_img > fw_upgrade + * do force-upgrade using request_firmware (relative path) + * 2-3 echo {root}/.../fw_img > fw_upgrade + * do force-upgrade using normal file open control (absolute path) + */ +static int siw_hal_upgrade_not_allowed(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + if (atomic_read(&chip->boot) == IC_BOOT_FAIL) { + return 0; + } + + if (atomic_read(&chip->init) == IC_INIT_NEED) { + t_dev_warn(dev, "Not Ready, Need IC init (fw)\n"); + return 1; + } + + if (atomic_read(&ts->state.fb) >= FB_SUSPEND) { + t_dev_warn(dev, "state.fb is not FB_RESUME\n"); + return 1; + } + + return 0; +} + +static int siw_hal_upgrade(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_touch_fw_bin *fw_bin = NULL; + const struct firmware *fw = NULL; + char *fwpath = NULL; + u8 *fw_buf = NULL; + int fw_max_size = touch_fw_size(ts); + int fw_size = 0; + int fw_up_binary = 0; + int i = 0; + int ret_val = 0; + int ret = 0; + + chip->fw_abs_path = 0; + + t_dev_info(dev, "fw type: %s\n", FW_TYPE_STR); + + if (siw_hal_upgrade_not_allowed(dev)) { + return -EPERM; + } + + fwpath = touch_getname(); + if (fwpath == NULL) { + t_dev_err(dev, "failed to allocate name buffer - fwpath\n"); + return -ENOMEM; + } + + if (touch_flags(ts) & TOUCH_USE_FW_BINARY) { + fw_up_binary = 1; + + if (ts->force_fwup & FORCE_FWUP_SYS_STORE) { + switch (ts->test_fwpath[0]) { + case 0: + /* fall through */ + case ' ': /* ignore space */ + break; + + default: + /* if target string is not "{bin}" */ + if (strncmp(ts->test_fwpath, "{bin}", 5) != 0) { + fw_up_binary = 0; + } + break; + } + } + } + + if (fw_up_binary) { + t_dev_info(dev, "getting fw from binary header data\n"); + fw_bin = touch_fw_bin(ts); + if (fw_bin != NULL) { + fw_buf = fw_bin->fw_data; + fw_size = fw_bin->fw_size; + } else { + t_dev_warn(dev, "empty fw info\n"); + } + } else { + t_dev_info(dev, "getting fw from file\n"); + ret = siw_hal_fw_get_file(&fw, fwpath, dev); + if (ret < 0) { + goto out; + } + fw_buf = (u8 *)fw->data; + fw_size = (int)fw->size; + } + +// ret = -EINVAL; + ret = -EPERM; + + if ((fw_buf == NULL) || !fw_size) { + t_dev_err(dev, "invalid fw info\n"); + goto out; + } + + if (fw_size < fw_max_size) { + t_dev_err(dev, "invalid fw size: %Xh < %Xh\n", + fw_size, fw_max_size); + goto out; + } + + t_dev_info(dev, "fw size: %d\n", fw_size); + + ret_val = siw_hal_fw_compare(dev, fw_buf); + if (ret_val < 0) { + ret = ret_val; + } else if (ret_val) { + touch_msleep(200); + for (i = 0; i < 2 && ret; i++) { + ret = siw_hal_fw_upgrade(dev, fw_buf, fw_size, i); + } + } + + if (fw) { + siw_hal_fw_release_firm(dev, fw); + } + +out: + if (ret) { + siwmon_submit_ops_step_chip_wh_name(dev, "%s - FW upgrade halted", + touch_chip_name(ts), ret); + } else { + siwmon_submit_ops_step_chip_wh_name(dev, "%s - FW upgrade done", + touch_chip_name(ts), ret); + } + + touch_putname(fwpath); + + return ret; +} + +#if defined(__SIW_CONFIG_KNOCK) +static void siw_hal_set_tci_debug_type_1(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 wdata = chip->tci_debug_type; + int ret = 0; + + t_dev_info(dev, "TCI-Debug: %s\n", (wdata) ? "Enable" : "Disable"); + + ret = siw_hal_write_value(dev, reg->tci_debug_fail_ctrl, wdata); + if (ret < 0) { + t_dev_err(dev, "TCI-Debug: ctrl failed, %d\n", ret); + } +} + +static void siw_hal_set_tci_debug(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + + switch (chip->opt.t_chk_tci_debug) { + case 1: + siw_hal_set_tci_debug_type_1(dev); + break; + default: + break; + } +} + +static int siw_hal_tci_knock(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct tci_info *info1 = &ts->tci.info[TCI_1]; + struct tci_info *info2 = &ts->tci.info[TCI_2]; + u32 lpwg_data[7]; + int ret = 0; + + siw_hal_set_tci_debug(dev); + + lpwg_data[0] = ts->tci.mode; + lpwg_data[1] = info1->tap_count | (info2->tap_count << 16); + lpwg_data[2] = info1->min_intertap | (info2->min_intertap << 16); + lpwg_data[3] = info1->max_intertap | (info2->max_intertap << 16); + lpwg_data[4] = info1->touch_slop | (info2->touch_slop << 16); + lpwg_data[5] = info1->tap_distance | (info2->tap_distance << 16); + lpwg_data[6] = info1->intr_delay | (info2->intr_delay << 16); + + t_dev_dbg_base(dev, "lpwg_data[0] : %08Xh\n", lpwg_data[0]); + t_dev_dbg_base(dev, "lpwg_data[1] : %08Xh\n", lpwg_data[1]); + t_dev_dbg_base(dev, "lpwg_data[2] : %08Xh\n", lpwg_data[2]); + t_dev_dbg_base(dev, "lpwg_data[3] : %08Xh\n", lpwg_data[3]); + t_dev_dbg_base(dev, "lpwg_data[4] : %08Xh\n", lpwg_data[4]); + t_dev_dbg_base(dev, "lpwg_data[5] : %08Xh\n", lpwg_data[5]); + t_dev_dbg_base(dev, "lpwg_data[6] : %08Xh\n", lpwg_data[6]); + + ret = siw_hal_reg_write(dev, + reg->tci_enable_w, + (void *)lpwg_data, sizeof(lpwg_data)); + + return ret; +} + +static int siw_hal_tci_password(struct device *dev) +{ +// struct siw_touch_chip *chip = to_touch_chip(dev); + + siw_hal_set_tci_debug(dev); + + return siw_hal_tci_knock(dev); +} + +static int siw_hal_do_tci_active_area(struct device *dev, + u32 x1, u32 y1, u32 x2, u32 y2) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + int ret = 0; + + ret = siw_hal_write_value(dev, + reg->act_area_x1_w, + x1); + if (ret < 0) { + goto out; + } + ret = siw_hal_write_value(dev, + reg->act_area_y1_w, + y1); + if (ret < 0) { + goto out; + } + ret = siw_hal_write_value(dev, + reg->act_area_x2_w, + x2); + if (ret < 0) { + goto out; + } + ret = siw_hal_write_value(dev, + reg->act_area_y2_w, + y2); + if (ret < 0) { + goto out; + } + +out: + return ret; +} + +static int siw_hal_tci_active_area(struct device *dev, + u32 x1, u32 y1, u32 x2, u32 y2, int type) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + int margin = touch_senseless_margin(ts); + u32 area[4] = { 0, }; + int i; + + t_dev_info(dev, "tci_active_area[%d]: x1[%Xh], y1[%Xh], x2[%Xh], y2[%Xh]\n", + type, x1, y1, x2, y2); + + area[0] = (x1 + margin) & 0xFFFF; + area[1] = (y1 + margin) & 0xFFFF; + area[2] = (x2 - margin) & 0xFFFF; + area[3] = (y2 - margin) & 0xFFFF; + + for (i = 0; i < ARRAY_SIZE(area); i++) { + area[i] = (area[i]) | (area[i]<<16); + } + + return siw_hal_do_tci_active_area(dev, area[0], area[1], area[2], area[3]); +} + +static int siw_hal_tci_area_set(struct device *dev, int cover_status) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct reset_area *qcover; + const char *msg; + int qcover_invalid = 0; + + if (touch_mode_not_allowed(ts, LCD_MODE_U3_QUICKCOVER)) { + return 0; + } + + qcover = (cover_status == QUICKCOVER_CLOSE) ? + &ts->tci.qcover_close : &ts->tci.qcover_open; + msg = (cover_status == QUICKCOVER_CLOSE) ? + "close" : "open"; + + if (!qcover->x2 || !qcover->y2) { + /* deactivated */ + return 0; + } + + qcover_invalid |= (qcover->x1 >= qcover->x2); + qcover_invalid |= (qcover->y1 >= qcover->y2); + + if (!qcover_invalid) { + siw_hal_tci_active_area(dev, + qcover->x1, qcover->y1, + qcover->x2, qcover->y2, + 0); + } + + t_dev_info(dev, + "lpwg active area - qcover %s %4d %4d %4d %4d%s\n", + msg, qcover->x1, qcover->y1, qcover->x2, qcover->y2, + (qcover_invalid) ? " (invalid)": ""); + + return 0; +} + +static int siw_hal_tci_control(struct device *dev, int type) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct tci_ctrl *tci = &ts->tci; + struct active_area *area = &tci->area; + struct tci_info *info1 = &tci->info[TCI_1]; + struct tci_info *info2 = &tci->info[TCI_2]; + u32 reg_w = ~0; + u32 data; + int ret = 0; + + switch (type) { + case ENABLE_CTRL: + reg_w = reg->tci_enable_w; + data = tci->mode; + break; + + case TAP_COUNT_CTRL: + reg_w = reg->tap_count_w; + data = info1->tap_count | (info2->tap_count << 16); + break; + + case MIN_INTERTAP_CTRL: + reg_w = reg->min_intertap_w; + data = info1->min_intertap | (info2->min_intertap << 16); + break; + + case MAX_INTERTAP_CTRL: + reg_w = reg->max_intertap_w; + data = info1->max_intertap | (info2->max_intertap << 16); + break; + + case TOUCH_SLOP_CTRL: + reg_w = reg->touch_slop_w; + data = info1->touch_slop | (info2->touch_slop << 16); + break; + + case TAP_DISTANCE_CTRL: + reg_w = reg->tap_distance_w; + data = info1->tap_distance | (info2->tap_distance << 16); + break; + + case INTERRUPT_DELAY_CTRL: + reg_w = reg->int_delay_w; + data = info1->intr_delay | (info2->intr_delay << 16); + break; + + case ACTIVE_AREA_CTRL: + ret = siw_hal_tci_active_area(dev, + area->x1, area->y1, + area->x2, area->y2, + type); + break; + + default: + break; + } + + if (reg_w != ~0) { + ret = siw_hal_write_value(dev, + reg_w, + data); + } + + return ret; +} + +static int siw_hal_lpwg_control(struct device *dev, int mode) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct tci_info *info1 = &ts->tci.info[TCI_1]; + int ret = 0; + + switch (mode) { + case LPWG_DOUBLE_TAP: + ts->tci.mode = 0x01; + info1->intr_delay = 0; + info1->tap_distance = 10; + + if (touch_senseless_margin(ts)) { + ret = siw_hal_tci_control(dev, ACTIVE_AREA_CTRL); + if (ret < 0) { + break; + } + } + + ret = siw_hal_tci_knock(dev); + break; + + case LPWG_PASSWORD: + ts->tci.mode = 0x01 | (0x01 << 16); + info1->intr_delay = ts->tci.double_tap_check ? 68 : 0; + info1->tap_distance = 7; + + if (touch_senseless_margin(ts)) { + ret = siw_hal_tci_control(dev, ACTIVE_AREA_CTRL); + if (ret < 0) { + break; + } + } + + ret = siw_hal_tci_password(dev); + break; + + default: + ts->tci.mode = 0; + ret = siw_hal_tci_control(dev, ENABLE_CTRL); + break; + } + + t_dev_info(dev, "lpwg_control mode = %d\n", mode); + + return ret; +} +#else /* __SIW_SUPPORT_KNOCK */ +static int siw_hal_tci_area_set(struct device *dev, int cover_status) +{ + return 0; +} +static int siw_hal_lpwg_control(struct device *dev, int mode) +{ + return 0; +} +#endif /* __SIW_SUPPORT_KNOCK */ + +#define SIW_OSC_CTL_T2 0xFE1 +#define SIW_CLK_CTL_T2 0xFE2 + +struct siw_clock_setup { + const int count; + const u32 *data; +}; + +static int siw_hal_clock_osc_base(struct device *dev, + const struct siw_clock_setup *setup, int onoff) +{ + u32 value; + int i; + + if (setup == NULL) { + t_dev_info(dev, "osc %s\n", + (onoff) ? "on" : "off"); + siw_hal_write_value(dev, SIW_OSC_CTL_T2, !!onoff); + return 0; + } + + for (i = 0; i < setup->count; i++) { + value = setup->data[i]; + t_dev_info(dev, "osc %s : %Xh\n", + (onoff) ? "on" : "off", value); + siw_hal_write_value(dev, SIW_OSC_CTL_T2, value); + touch_msleep(1); + } + + return 0; +} + +static const u32 osc_setup_data_on_base[3] = { + 0x01, 0x03, 0x07, +}; + +static const u32 osc_setup_data_off_base[3] = { + 0x03, 0x01, 0x00, +}; + +static const struct siw_clock_setup osc_setup_spi_on_base = { + .count = 3, + .data = osc_setup_data_on_base, +}; + +static const struct siw_clock_setup osc_setup_spi_off_base = { + .count = 3, + .data = osc_setup_data_off_base, +}; + +static const struct siw_clock_setup osc_setup_i2c_on_base = { + .count = 1, + .data = &osc_setup_data_on_base[2], +}; + +static const struct siw_clock_setup osc_setup_i2c_off_base = { + .count = 1, + .data = &osc_setup_data_off_base[0], +}; + +static int siw_hal_clock_type_2_osc(struct device *dev, bool onoff) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int is_spi = !!(touch_bus_type(ts) == BUS_IF_SPI); + const struct siw_clock_setup *osc_setup = NULL; + + switch (touch_chip_type(ts)) { + case CHIP_SW49501: + if (onoff) { + osc_setup = (is_spi) ? &osc_setup_spi_on_base : \ + &osc_setup_i2c_on_base; + } else { + osc_setup = (is_spi) ? &osc_setup_spi_off_base : \ + &osc_setup_i2c_off_base; + } + break; + } + + if (onoff) { + /* I2C needs touch reset. */ + if (!is_spi) { + return 0; + } + } + + siw_hal_clock_osc_base(dev, osc_setup, onoff); + + return 0; +} + +static int siw_hal_clock_type_2_clk(struct device *dev, bool onoff) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int is_spi = !!(touch_bus_type(ts) == BUS_IF_SPI); + + if (!is_spi) { + return 0; + } + + siw_hal_write_value(dev, SIW_CLK_CTL_T2, !!onoff); + + return 0; +} + +static int siw_hal_clock_type_2(struct device *dev, bool onoff) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + if (onoff) { + /* + * [Notice] + * I2C needs touch reset. + */ + siw_hal_clock_type_2_osc(dev, onoff); + siw_hal_clock_type_2_clk(dev, onoff); + atomic_set(&ts->state.sleep, IC_NORMAL); + } else { + if (chip->lcd_mode == LCD_MODE_U0) { + siw_hal_clock_type_2_clk(dev, onoff); + siw_hal_clock_type_2_osc(dev, onoff); + atomic_set(&ts->state.sleep, IC_DEEP_SLEEP); + } + } + + t_dev_info(dev, "siw_hal_clock(2) -> %s\n", + (onoff) ? "ON" : (!chip->lcd_mode) ? "OFF" : "SKIP"); + + return 0; +} + +static int siw_hal_clock_type_1(struct device *dev, bool onoff) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + siw_hal_cmd_write(dev, CMD_ENA); + + if (onoff) { + siw_hal_cmd_write(dev, CMD_OSC_ON); + siw_hal_cmd_write(dev, CMD_CLK_ON); + atomic_set(&ts->state.sleep, IC_NORMAL); + } else { + if (chip->lcd_mode == LCD_MODE_U0) { + siw_hal_cmd_write(dev, CMD_CLK_OFF); + siw_hal_cmd_write(dev, CMD_OSC_OFF); + atomic_set(&ts->state.sleep, IC_DEEP_SLEEP); + } + } + + siw_hal_cmd_write(dev, CMD_DIS); + + t_dev_info(dev, "siw_hal_clock(1) -> %s\n", + (onoff) ? "ON" : (!chip->lcd_mode) ? "OFF" : "SKIP"); + + return 0; +} + +static int siw_hal_clock(struct device *dev, bool onoff) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int state = atomic_read(&ts->state.sleep); + int ret = 0; + + if (onoff) { + if (state != IC_DEEP_SLEEP) { + return 0; + } + } else { + if (state == IC_DEEP_SLEEP) { + t_dev_info(dev, "already sleep state\n"); + return 0; + } + } + + siw_touch_sys_osc(dev, onoff); + + switch (chip->opt.t_clock) { + case 2: + ret = siw_hal_clock_type_2(dev, onoff); + break; + case 1: + ret = siw_hal_clock_type_1(dev, onoff); + break; + default: + atomic_set(&ts->state.sleep, + (onoff) ? IC_NORMAL : IC_DEEP_SLEEP); + t_dev_info(dev, "sleep state -> %s\n", + (onoff) ? "IC_NORMAL" : "IC_DEEP_SLEEP"); + break; + } + + return ret; +} + +#if defined(__SIW_CONFIG_SWIPE) +static int siw_hal_swipe_active_area(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_swipe_info *left = &chip->swipe.info[SWIPE_L]; + struct siw_hal_swipe_info *right = &chip->swipe.info[SWIPE_R]; + u32 active_area[4] = {0x0, }; + int ret = 0; + + active_area[0] = (right->area.x1) | (left->area.x1 << 16); + active_area[1] = (right->area.y1) | (left->area.y1 << 16); + active_area[2] = (right->area.x2) | (left->area.x2 << 16); + active_area[3] = (right->area.y2) | (left->area.y2 << 16); + + ret = siw_hal_reg_write(dev, + reg->swipe_act_area_x1_w, + (void *)active_area, sizeof(active_area)); + + return ret; +} + +static int siw_hal_swipe_control(struct device *dev, int type) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_swipe_info *left = &chip->swipe.info[SWIPE_L]; + struct siw_hal_swipe_info *right = &chip->swipe.info[SWIPE_R]; + u32 reg_w = ~0; + u32 data = 0; + int ret = 0; + + switch (type) { + case SWIPE_ENABLE_CTRL: + case SWIPE_DISABLE_CTRL: + reg_w = reg->swipe_enable_w; + data = (type == SWIPE_ENABLE_CTRL) ? + chip->swipe.mode : 0; + break; + case SWIPE_DIST_CTRL: + reg_w = reg->swipe_dist_w; + data = (right->distance) | (left->distance << 16); + break; + case SWIPE_RATIO_THR_CTRL: + reg_w = reg->swipe_ratio_thr_w; + data = (right->ratio_thres) | (left->ratio_thres << 16); + break; + case SWIPE_RATIO_PERIOD_CTRL: + reg_w = reg->swipe_ratio_period_w; + data = (right->ratio_period) | (left->ratio_period << 16); + break; + case SWIPE_RATIO_DIST_CTRL: + reg_w = reg->swipe_ratio_dist_w; + data = (right->ratio_distance) | + (left->ratio_distance << 16); + break; + case SWIPE_TIME_MIN_CTRL: + reg_w = reg->swipe_time_min_w; + data = (right->min_time) | (left->min_time << 16); + break; + case SWIPE_TIME_MAX_CTRL: + reg_w = reg->swipe_time_max_w; + data = (right->max_time) | (left->max_time << 16); + break; + case SWIPE_AREA_CTRL: + ret = siw_hal_swipe_active_area(dev); + break; + default: + break; + } + + if (reg_w != ~0) { + ret = siw_hal_write_value(dev, reg_w, data); + } + + return ret; +} + +static int siw_hal_swipe_mode(struct device *dev, int mode) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_swipe_info *left = &chip->swipe.info[SWIPE_L]; + struct siw_hal_swipe_info *right = &chip->swipe.info[SWIPE_R]; + u32 swipe_data[11] = {0x0, }; + int ret = 0; + + if (!chip->swipe.mode || (mode != LCD_MODE_U2)) { + ret = siw_hal_swipe_control(dev, SWIPE_DISABLE_CTRL); + t_dev_dbg_base(dev, "swipe disabled\n"); + goto out; + } + + swipe_data[0] = chip->swipe.mode; + swipe_data[1] = (right->distance) | (left->distance << 16); + swipe_data[2] = (right->ratio_thres) | (left->ratio_thres << 16); + swipe_data[3] = (right->ratio_distance) | (left->ratio_distance << 16); + swipe_data[4] = (right->ratio_period) | (left->ratio_period << 16); + swipe_data[5] = (right->min_time) | (left->min_time << 16); + swipe_data[6] = (right->max_time) | (left->max_time << 16); + swipe_data[7] = (right->area.x1) | (left->area.x1 << 16); + swipe_data[8] = (right->area.y1) | (left->area.y1 << 16); + swipe_data[9] = (right->area.x2) | (left->area.x2 << 16); + swipe_data[10] = (right->area.y2) | (left->area.y2 << 16); + + ret = siw_hal_reg_write(dev, + reg->swipe_enable_w, + (void *)swipe_data, sizeof(swipe_data)); + if (ret >= 0) { + t_dev_info(dev, "swipe enabled\n"); + } + +out: + return ret; +} +#else /* __SIW_CONFIG_SWIPE */ +static int siw_hal_swipe_mode(struct device *dev, int mode) +{ + return 0; +} +#endif /* __SIW_CONFIG_SWIPE */ + +static int siw_hal_tc_con_type_g(struct device *dev, u32 addr, int value, char *name) +{ + int ret = 0; + + ret = siw_hal_write_value(dev, addr, value); + if (ret < 0) { + t_dev_err(dev, "failed to set %s[%04Xh], %d\n", + name, addr, ret); + goto out; + } + + t_dev_info(dev, "%s[%04Xh]: %s(%08Xh)\n", + name, addr, + (value & 0x1) ? "ON" : "OFF", + value); + +out: + return ret; +} + +static int siw_hal_tc_con_glove(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 addr = reg->glove_en; + int value = atomic_read(&ts->state.glove); + int ret = 0; + + if (chip->opt.f_glove_en) { + ret = siw_hal_tc_con_type_g(dev, addr, value, "glove_en"); + } + + return ret; +} + +static int siw_hal_tc_con_grab(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 addr = reg->grab_en; + int value = atomic_read(&ts->state.grab); + int ret = 0; + + if (chip->opt.f_grab_en) { + ret = siw_hal_tc_con_type_g(dev, addr, value, "grab_en"); + } + + return ret; +} + +static int siw_hal_tc_con(struct device *dev, u32 code, void *param) +{ +// struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + int ret = 0; + + switch (code) { + case TCON_GLOVE: + ret = siw_hal_tc_con_glove(dev); + break; + case TCON_GRAB: + ret = siw_hal_tc_con_grab(dev); + break; + } + + return ret; +} + +static inline int __used siw_hal_tc_driving_u0(struct device *dev) +{ + return TC_DRIVE_CTL_START; +} + +static inline int __used siw_hal_tc_driving_u2(struct device *dev) +{ + return (TC_DRIVE_CTL_DISP_U2 | TC_DRIVE_CTL_START); //0x101 +} + +static inline int __used siw_hal_tc_driving_u3(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int ctrl = (TC_DRIVE_CTL_DISP_U3 | TC_DRIVE_CTL_MODE_6LHB | TC_DRIVE_CTL_START); + + if (touch_flags(ts) & TOUCH_USE_VBLANK) + ctrl &= ~TC_DRIVE_CTL_MODE_6LHB; + + if (atomic_read(&ts->state.debug_option_mask) & DEBUG_OPTION_1) + ctrl &= ~TC_DRIVE_CTL_MODE_6LHB; + +#if defined(SOMC_TOUCH_BRINGUP) /* Use Doze/Active Mode */ + if (!atomic_read(&ts->state.glove)) { + ctrl |= 0x10; /* 0x185 → 0x195 */ + } +#endif + + return ctrl; +} + +static inline int __used siw_hal_tc_driving_u3_partial(struct device *dev) +{ + return (TC_DRIVE_CTL_PARTIAL | siw_hal_tc_driving_u3(dev)); +} + +static inline int __used siw_hal_tc_driving_u3_qcover(struct device *dev) +{ + return (TC_DRIVE_CTL_QCOVER | siw_hal_tc_driving_u3(dev)); +} + +static inline int siw_hal_tc_driving_stop(struct device *dev) +{ + return TC_DRIVE_CTL_STOP; +} + +static void siw_hal_chk_dbg_report(struct device *dev, u32 status, int irq) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + u32 debug_mask = (status >> 16) & 0x0F; + u32 ic_debug[4]; + u32 debug_info = 0; + u32 debug_len = 0; + u32 debug_type = 0; + int ret = 0; + + if (!chip->opt.f_dbg_report) { + return; + } + + /* + * 0x03 : Error report + * 0x04 : Debug report + */ + switch (debug_mask) { + case 0x03: + case 0x04: + break; + default: + return; + } + + ret = siw_hal_reg_read(dev, 0x23E, ic_debug, sizeof(ic_debug)); + if (ret < 0) { + return; + } + + debug_info = ic_debug[3]; + debug_len = ((debug_info>>24) & 0xFF); + debug_type = (debug_info & ((1<<24)-1)); + + t_dev_info(dev, + "[%d] ic debug: s %08Xh / m %Xh, l %Xh, t %Xh (%08Xh)\n", + irq, status, debug_mask, debug_len, debug_type, debug_info); + + t_dev_info(dev, + "[%d] ic debug: log %08Xh %08Xh %08Xh\n", + irq, ic_debug[0], ic_debug[1], ic_debug[2]); +} + +static int siw_hal_tc_driving_post(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_fw_info *fw = &chip->fw; + u32 addr[4] = { 0, }; + u32 rdata; + int ret = 0; + + switch (chip->opt.t_chk_mipi) { + case 1: + if (chip->driving_mode != LCD_MODE_U3) { + break; + } + + rdata = 0; + switch (fw->v.version.major) { + case 0 : + case 1 : + rdata = !!(fw->v.version.minor > 14); + break; + } + + addr[0] = (rdata) ? 0x284 : 0; + break; + default: + goto out; + } + + if (addr[0]) { + rdata = 0; + ret = siw_hal_read_value(dev, addr[0], &rdata); + if (ret < 0) { + goto out; + } + if (rdata) { + t_dev_err(dev, "!! [Warning] !!\n"); + t_dev_err(dev, " Check MIPI script (%d)\n", rdata); + t_dev_err(dev, " Maybe, MIPI(VIDEO) vs. FW(CMD) or vice versa.\n"); + } + } + +out: + return 0; +} + +#if defined(SOMC_TOUCH_BRINGUP) +int siw_hal_tc_driving(struct device *dev, int mode) +#else +static int siw_hal_tc_driving(struct device *dev, int mode) +#endif +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 tc_status = 0; + u32 running_status = 0; + u32 ctrl = 0; + u32 rdata; + int re_init = 0; + int ret = 0; + + if (atomic_read(&ts->state.sleep) == IC_DEEP_SLEEP) { + t_dev_warn(dev, "can not control tc driving - deep sleep state\n"); + return 0; + } + + if (touch_mode_not_allowed(ts, mode)) { + return -EPERM; + } + + if (atomic_read(&ts->recur_chk)) { + /* keep the last value for retry case */ + mode = chip->driving_mode; + t_dev_info(dev, "keep the last mode(%d) for retry\n", mode); + } + + chip->driving_mode = mode; + + switch (mode) { + case LCD_MODE_U0: + ctrl = siw_hal_tc_driving_u0(dev); + break; + + case LCD_MODE_U2_UNBLANK: + case LCD_MODE_U2: + ctrl = siw_hal_tc_driving_u2(dev); + break; + + case LCD_MODE_U3: + ctrl = siw_hal_tc_driving_u3(dev); + break; + + case LCD_MODE_U3_PARTIAL: + ctrl = siw_hal_tc_driving_u3_partial(dev); + break; + + case LCD_MODE_U3_QUICKCOVER: + ctrl = siw_hal_tc_driving_u3_qcover(dev); + break; + + case LCD_MODE_STOP: + ctrl = siw_hal_tc_driving_stop(dev); + break; + + default: + t_dev_err(dev, "mode(%d) not supported\n", mode); + return -ESRCH; + } + + /* swipe set */ + ret = siw_hal_swipe_mode(dev, mode); + if (ret < 0) { + t_dev_warn(dev, "swipe mode err, %d", ret); + } + + if ((mode == LCD_MODE_U0) || + (mode == LCD_MODE_U2)) { + touch_msleep(chip->drv_opt_delay); + } + + touch_msleep(hal_dbg_delay(chip, HAL_DBG_DLY_TC_DRIVING_0)); + + t_dev_info(dev, "current driving mode is %s\n", + siw_lcd_driving_mode_str(mode)); + + ret = siw_hal_read_value(dev, + reg->spr_subdisp_status, + &rdata); + t_dev_info(dev, "DDI Display Mode[%04Xh] = 0x%08X\n", + reg->spr_subdisp_status, rdata); + + ret = siw_hal_write_value(dev, + reg->tc_drive_ctl, + ctrl); + t_dev_info(dev, "TC Driving[%04Xh] wr 0x%08X\n", + reg->tc_drive_ctl, ctrl); + + rdata = chip->drv_delay + hal_dbg_delay(chip, HAL_DBG_DLY_TC_DRIVING_1); + touch_msleep(rdata); + t_dev_dbg_base(dev, "waiting %d msecs\n", rdata); + + if (siw_touch_boot_mode_tc_check(dev)) { + goto out; + } + + if (mode == LCD_MODE_U3_PARTIAL) { + goto out; + } + + siw_hal_tc_driving_post(dev); + + ret = siw_hal_read_value(dev, + reg->tc_status, + &tc_status); + if (ret < 0) { + t_dev_err(dev, "failed to get tc_status\n"); + atomic_set(&ts->recur_chk, 0); + return ret; + } + + siw_hal_chk_dbg_report(dev, tc_status, 0); + + running_status = tc_status & 0x1F; + + re_init = 0; + if (mode == LCD_MODE_STOP) { + re_init = !!running_status; + } else { + if (!running_status || + (running_status == 0x10) || + (running_status == 0x0F)){ + re_init = 1; + } + } + + if (re_init) { + ret = siw_hal_read_value(dev, + reg->spr_subdisp_status, + &rdata); + + if (atomic_read(&ts->recur_chk)) { + t_dev_err(dev, "command failed: mode %d, tc_status %08Xh, DDI %08Xh\n", + mode, tc_status, rdata); + atomic_set(&ts->recur_chk, 0); + return -EFAULT; + } + + t_dev_err(dev, "command missed: mode %d, tc_status %08Xh, DDI %08Xh\n", + mode, tc_status, rdata); + + atomic_set(&ts->recur_chk, 1); + + ret = siw_hal_reinit(dev, 1, 100 + hal_dbg_delay(chip, HAL_DBG_DLY_HW_RST_2), 1, siw_hal_init); + if (ret < 0) { + return ret; + } + } else { + t_dev_info(dev, "command done: mode %d, running_sts %02Xh\n", + mode, running_status); + } + +out: + siw_hal_tc_con_glove(dev); + siw_hal_tc_con_grab(dev); + + atomic_set(&ts->recur_chk, 0); + + return 0; +} + +static void siw_hal_deep_sleep(struct device *dev) +{ + t_dev_info(dev, "deep sleep\n"); + + siw_hal_tc_driving(dev, LCD_MODE_STOP); + siw_hal_clock(dev, 0); +} + +#if defined(__SIW_CONFIG_KNOCK) +static const char *siw_hal_tci_debug_type_1_str[] = { + "SUCCESS", + "DISTANCE_INTER_TAP", + "DISTANCE_TOUCH_SLOP", + "MIN_TIMEOUT_INTER_TAP", + "MAX_TIMEOUT_INTER_TAP", + "LONG_PRESS_TIME_OUT", + "MULTI_FINGER", + "DELAY_TIME", + "PALM_STATE", + "OUT_OF_AREA", +}; + +#define TCI_FAIL_TYPE_1_NUM ARRAY_SIZE(siw_hal_tci_debug_type_1_str) + +static void siw_hal_debug_tci_type_1(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + char **str = (char **)siw_hal_tci_debug_type_1_str; + u32 rdata = 0; + u32 buffer = 0; + u32 index = 0; + int ret = 0; + + ret = siw_hal_read_value(dev, + reg->tci_debug_fail_status, + &rdata); + if (ret < 0) { + t_dev_err(dev, "failed to read tci debug fail status, %d\n", ret); + return; + } + + ret = siw_hal_read_value(dev, + reg->tci_debug_fail_buffer, + &buffer); + if (ret < 0) { + t_dev_err(dev, "failed to read tci debug fail buffer, %d\n", ret); + return; + } + + t_dev_info(dev, + "Status[%04Xh] = %08Xh, Buffer[%04Xh] = %08Xh\n", + reg->tci_debug_fail_status, rdata, + reg->tci_debug_fail_buffer, buffer); + + /* Knock On fail */ + if (rdata & 0x01) { + index = (buffer & 0xFFFF); + t_dev_info(dev, "TCI-Debug: on fail reason: %s(%08Xh)\n", + (index < TCI_FAIL_TYPE_1_NUM) ? str[index] : "(unknown)", + buffer); + } + + /* Knock Code fail */ + if (rdata & 0x02) { + index = ((buffer>>16) & 0xFFFF); + t_dev_info(dev, "TCI-Debug: code fail reason: %s(%08Xh)\n", + (index < TCI_FAIL_TYPE_1_NUM) ? str[index] : "(unknown)", + buffer); + } +} + +static void siw_hal_debug_tci(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + + switch (chip->opt.t_chk_tci_debug) { + case 1: + siw_hal_debug_tci_type_1(dev); + break; + default: + break; + } +} +#else /* __SIW_CONFIG_KNOCK */ +static void siw_hal_debug_tci(struct device *dev) +{ + +} +#endif /* __SIW_CONFIG_KNOCK */ + +#if defined(__SIW_CONFIG_SWIPE) +static void siw_hal_debug_swipe(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u8 debug_reason_buf[SWIPE_MAX_NUM][SWIPE_DEBUG_MAX_NUM]; + u32 rdata[5] = {0 , }; + u8 count[2] = {0, }; + u8 count_max = 0; + u32 i, j = 0; + u8 buf = 0; + int ret = 0; + + if (!chip->swipe_debug_type) + return; + + ret = siw_hal_reg_read(dev, + reg->swipe_debug_r, + (void *)&rdata, sizeof(rdata)); + + count[SWIPE_R] = (rdata[0] & 0xFFFF); + count[SWIPE_L] = ((rdata[0] >> 16) & 0xFFFF); + count_max = (count[SWIPE_R] > count[SWIPE_L]) ? + count[SWIPE_R] : count[SWIPE_L]; + + if (count_max == 0) + return; + + if (count_max > SWIPE_DEBUG_MAX_NUM) { + count_max = SWIPE_DEBUG_MAX_NUM; + if (count[SWIPE_R] > SWIPE_DEBUG_MAX_NUM) + count[SWIPE_R] = SWIPE_DEBUG_MAX_NUM; + if (count[SWIPE_L] > SWIPE_DEBUG_MAX_NUM) + count[SWIPE_L] = SWIPE_DEBUG_MAX_NUM; + } + + for (i = 0; i < ((count_max-1)>>2)+1; i++) { + memcpy(&debug_reason_buf[SWIPE_R][i<<2], &rdata[i+1], sizeof(u32)); + memcpy(&debug_reason_buf[SWIPE_L][i<<2], &rdata[i+3], sizeof(u32)); + } + + for (i = 0; i < SWIPE_MAX_NUM; i++) { + for (j = 0; j < count[i]; j++) { + buf = debug_reason_buf[i][j]; + t_dev_info(dev, "SWIPE_%s - DBG[%d/%d]: %s\n", + i == SWIPE_R ? "Right" : "Left", + j + 1, count[i], + (buf > 0 && buf < SWIPE_FAIL_NUM) ? + siw_hal_swipe_debug_str[buf] : + siw_hal_swipe_debug_str[0]); + } + } +} +#else /* __SIW_CONFIG_SWIPE */ +static void siw_hal_debug_swipe(struct device *dev) +{ + +} +#endif /* __SIW_CONFIG_SWIPE */ + +static void siw_hal_lpwg_ctrl_init(struct lpwg_mode_ctrl *ctrl) +{ + ctrl->clk = LPWG_SET_SKIP; + ctrl->qcover = LPWG_SET_SKIP; + ctrl->lpwg = LPWG_SET_SKIP; + ctrl->lcd = LPWG_SET_SKIP; +} + +static int siw_hal_lpwg_ctrl(struct device *dev, + struct lpwg_mode_ctrl *ctrl) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int ret = 0; + + if (ctrl->clk != LPWG_SET_SKIP) { + if (atomic_read(&ts->state.sleep) == IC_DEEP_SLEEP) { + siw_hal_clock(dev, ctrl->clk); + } + } + + if (ctrl->qcover != LPWG_SET_SKIP) { + ret = siw_hal_tci_area_set(dev, ctrl->qcover); + if (ret < 0) { + goto out; + } + } + + if (ctrl->lpwg != LPWG_SET_SKIP) { + ret = siw_hal_lpwg_control(dev, ctrl->lpwg); + if (ret < 0) { + goto out; + } + } + + if (ctrl->lcd != LPWG_SET_SKIP) { + ret = siw_hal_tc_driving(dev, ctrl->lcd); + } + +out: + return ret; +} + +static int siw_hal_lpwg_ctrl_skip(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + t_dev_info(dev, "skip lpwg_mode\n"); + + if (atomic_read(&ts->state.sleep) == IC_DEEP_SLEEP) { + siw_hal_clock(dev, 1); + } + + siw_hal_debug_tci(dev); + siw_hal_debug_swipe(dev); + + return 0; +} + +static int siw_hal_lpwg_mode_suspend(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct lpwg_mode_ctrl ctrl; + int changed = 0; + int ret = 0; + + siw_hal_lpwg_ctrl_init(&ctrl); + + t_dev_info(dev, "lpwg suspend: mode %d, screen %d\n", + ts->lpwg.mode, ts->lpwg.screen); + + if (ts->role.mfts_lpwg) { + t_dev_info(dev, "lpwg suspend: mfts_lpwg\n"); + + ctrl.lpwg = LPWG_DOUBLE_TAP, + ctrl.lcd = chip->lcd_mode; + goto out_con; + } + + if (ts->lpwg.mode == LPWG_NONE) { + if (ts->lpwg.screen) { + siw_hal_clock(dev, 1); + } else { + siw_hal_deep_sleep(dev); + } + goto out; + } + + if (ts->lpwg.screen) { + siw_hal_lpwg_ctrl_skip(dev); + goto out; + } + +#if defined(__SIW_CONFIG_PROX_ON_SUSPEND) + if (ts->lpwg.sensor == PROX_NEAR) { + t_dev_info(dev, "lpwg suspend: (ts->lpwg.sensor == PROX_NEAR)\n"); + siw_hal_deep_sleep(dev); + goto out; + } +#endif + + if (ts->lpwg.qcover == HOLE_NEAR) { + /* knock on/code disable */ + ctrl.clk = 1; + ctrl.qcover = QUICKCOVER_CLOSE; + ctrl.lpwg = LPWG_NONE; + ctrl.lcd = chip->lcd_mode; + goto out_con; + } + + /* knock on/code */ + ctrl.clk = 1; + ctrl.qcover = QUICKCOVER_OPEN; + ctrl.lpwg = ts->lpwg.mode; + ctrl.lcd = chip->lcd_mode; + +out_con: + ret = siw_hal_lpwg_ctrl(dev, &ctrl); + changed = 1; + +out: + t_dev_info(dev, "lpwg suspend(%d, %d): lcd_mode %d, driving_mode %d\n", + changed, ret, + chip->lcd_mode, chip->driving_mode); + + return ret; +} + +static int siw_hal_lpwg_mode_resume(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct lpwg_mode_ctrl ctrl; + char *str = ""; + int mode_allowed_partial = !!touch_mode_allowed(ts, LCD_MODE_U3_PARTIAL); + int mode_allowed_qcover = !!touch_mode_allowed(ts, LCD_MODE_U3_QUICKCOVER); + int qcover_near = (ts->lpwg.qcover == HOLE_NEAR); + int lcd_mode = chip->lcd_mode; + int changed = 0; + int ret = 0; + + siw_hal_lpwg_ctrl_init(&ctrl); + + t_dev_info(dev, "lpwg resume: mode %d, screen %d\n", + ts->lpwg.mode, ts->lpwg.screen); + + siw_touch_report_all_event(ts); //clear (?) + +#if defined(__SIW_CONFIG_PROX_ON_RESUME) + if (ts->lpwg.sensor == PROX_NEAR) { + ctrl.lcd = LCD_MODE_STOP; + + t_dev_info(dev, "lpwg resume: sensor\n"); + goto out_con; + } +#endif + + if (ts->lpwg.screen) { + if (mode_allowed_qcover) { + if (qcover_near && touch_qcover_opt_skip_close(ts)) { + str = "(skip qcover)"; + goto lpwg_resume_screen_done; + } + lcd_mode = (qcover_near) ? LCD_MODE_U3_QUICKCOVER : lcd_mode; + + ctrl.qcover = (qcover_near) ? QUICKCOVER_CLOSE : QUICKCOVER_OPEN; + } + +lpwg_resume_screen_done: + ctrl.lpwg = LPWG_NONE; + ctrl.lcd = lcd_mode; + + t_dev_info(dev, "lpwg resume: screen %s\n", str); + goto out_con; + } + + if (ts->lpwg.mode == LPWG_NONE) { + ctrl.lpwg = LPWG_NONE; + ctrl.lcd = LCD_MODE_STOP; + + t_dev_info(dev, "lpwg resume: mode (LPWG_NONE)\n"); + goto out_con; + } + + if (mode_allowed_qcover) { + if (qcover_near) { + if (touch_qcover_opt_skip_close(ts)) { + str = "(skip)"; + } else { + ctrl.qcover = QUICKCOVER_CLOSE; + } + + ctrl.lpwg = ts->lpwg.mode; + + if (mode_allowed_partial) { + lcd_mode = LCD_MODE_U3_PARTIAL; + } + + ctrl.lcd = lcd_mode; + + t_dev_info(dev, "lpwg resume: qcover %s\n", str); + goto out_con; + } + } + + t_dev_info(dev, "lpwg resume: %s\n", + (mode_allowed_partial) ? "partial" : "default"); + + if (mode_allowed_qcover) { + ctrl.qcover = QUICKCOVER_OPEN; + } + ctrl.lpwg = ts->lpwg.mode; + if (mode_allowed_partial) { + lcd_mode = LCD_MODE_U3_PARTIAL; + } + ctrl.lcd = lcd_mode; + +out_con: + ret = siw_hal_lpwg_ctrl(dev, &ctrl); + changed = 1; + + t_dev_info(dev, "lpwg resume(%d, %d): lcd_mode %d, driving_mode %d\n", + changed, ret, + chip->lcd_mode, chip->driving_mode); + + return ret; +} + +static int siw_hal_lpwg_mode(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + if (atomic_read(&chip->init) == IC_INIT_NEED) { + t_dev_warn(dev, "Not Ready, Need IC init (lpwg_mode)\n"); + return 0; + } + + if (atomic_read(&ts->state.fb) == FB_SUSPEND) { + return siw_hal_lpwg_mode_suspend(dev); + } + + return siw_hal_lpwg_mode_resume(dev); +} + +#define TCI_INFO_CNT (sizeof(struct tci_info) / 2) + +#define siw_prt_tci_control_info(_dev, _idx, _info) \ + do { \ + t_dev_info(_dev, \ + "tci info[%s] tap_count %d, min_intertap %d, max_intertap %d\n", \ + _idx, _info->tap_count, _info->min_intertap, _info->max_intertap); \ + t_dev_info(_dev, \ + "tci info[%s] touch_slop %d, tap_distance %d, intr_delay %d\n", \ + _idx, _info->touch_slop, _info->tap_distance, _info->intr_delay); \ + } while(0) + +static int siw_hal_show_ic_tci_info(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; + struct tci_info *info; + u16 lpwg_data[TCI_INFO_CNT + 1][2]; + u16 lpwg_data_tci_1[TCI_INFO_CNT]; + u16 lpwg_data_tci_2[TCI_INFO_CNT]; + int i = 0; + int ret = 0; + + t_dev_info(dev, "[ IC tci info ]\n"); + + ret = siw_hal_reg_read(dev, + reg->tci_enable_w, + (void *)lpwg_data, sizeof(lpwg_data)); + if(ret < 0) { + t_dev_err(dev, "tci info read fail\n"); + + goto out; + } + + for (i = 0; i < TCI_INFO_CNT; i++) { + lpwg_data_tci_1[i] = lpwg_data[i + 1][0]; + lpwg_data_tci_2[i] = lpwg_data[i + 1][1]; + } + + info = (struct tci_info *)lpwg_data_tci_1; + siw_prt_tci_control_info(dev, "TCI_1", info); + + info = (struct tci_info *)lpwg_data_tci_2; + siw_prt_tci_control_info(dev, "TCI_2", info); + + out: + return ret; +} + +static void siw_hal_show_driver_tci_info(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct tci_ctrl *tci = &ts->tci; + struct tci_info *info; + + t_dev_info(dev, "[ driver tci info ]\n"); + + info = &tci->info[TCI_1]; + siw_prt_tci_control_info(dev, "TCI_1", info); + + info = &tci->info[TCI_2]; + siw_prt_tci_control_info(dev, "TCI_2", info); +} + +static int siw_hal_lpwg_ext_tci_info(struct device *dev, void *param) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u16 *info_addr; + int *data = (int *)param; + int tci_type = 0; + int index = 0; + u16 value = 0; + u16 write_en = 0; + u16 value_2 = 0; + u32 send_buf = 0; + int ret = 0; + + tci_type = data[0]; + index = data[1]; + value = data[2]; + write_en = data[3]; + + if((tci_type > TCI_2) || (0 > tci_type)) { + t_dev_err(dev, "invaild tci_type value [%d]\n", tci_type); + goto out_invaild; + } + + if((index > (TCI_INFO_CNT - 1)) || (0 > index)) { + t_dev_err(dev, "invaild index value [%d]\n", index); + goto out_invaild; + } + + info_addr = (u16 *)&ts->tci.info[!tci_type]; + value_2 = info_addr[index]; + + send_buf = (value << (tci_type ? 16 : 0)) | (value_2 << (tci_type ? 0 : 16)); + + if (write_en == 1) { + t_dev_info(dev, "tci info write addr[%Xh] value[%Xh]\n", + reg->tap_count_w + index, send_buf); + + ret = siw_hal_write_value(dev, + reg->tap_count_w + index, send_buf); + if (ret < 0) { + t_dev_err(dev, "tci info write fail, so don't setting driver tci info\n"); + goto out_write_fail; + } + } + + info_addr = (u16 *)&ts->tci.info[tci_type]; + info_addr[index] = value; + + siw_hal_show_ic_tci_info(dev); + +out_write_fail: + siw_hal_show_driver_tci_info(dev); + +out_invaild: + return ret; +} + +#define TCI_AREA_CNT (sizeof(struct active_area) / 2) + +#define __siw_prt_tci_area(_dev, _fmt, _name, _area, _shift) \ + do { \ + t_dev_info(_dev, \ + _fmt "x1:%4d, y1:%4d, x2:%4d, y2:%4d\n", \ + _name, \ + ((_area->x1 >> _shift) & 0xFFFF), \ + ((_area->y1 >> _shift) & 0xFFFF), \ + ((_area->x2 >> _shift) & 0xFFFF), \ + ((_area->y2 >> _shift )& 0xFFFF)); \ + } while(0) + +#define siw_prt_tci_area_drv(_dev, _name, _area, _shift) \ + do { \ + __siw_prt_tci_area(_dev, "tci %s area ", _name, _area, _shift); \ + } while(0) + +static void siw_hal_show_driver_tci_area(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct active_area *area; + struct reset_area *tci_qcover; + + t_dev_info(dev, "[ driver tci area ]\n"); + + area = &ts->tci.area; + siw_prt_tci_area_drv(dev, "[active ]", area, 0); + + tci_qcover = &ts->tci.qcover_open; + siw_prt_tci_area_drv(dev, "[qcover open ]", tci_qcover, 0); + + tci_qcover = &ts->tci.qcover_close; + siw_prt_tci_area_drv(dev, "[qcover close]", tci_qcover, 0); +} + +#define siw_prt_tci_area_ic(_dev, _name, _area, _shift) \ + do { \ + __siw_prt_tci_area(_dev, "tci area %s ", _name, _area, _shift); \ + } while (0) + +static int siw_hal_show_ic_tci_area(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; + struct reset_area *area; + u32 area_data_buf[TCI_AREA_CNT] = {0, }; + int ret = 0; + + t_dev_info(dev, "[ IC tci area ]\n"); + + ret = siw_hal_reg_read(dev, + reg->act_area_x1_w, + (void *)area_data_buf, sizeof(area_data_buf)); + if(ret < 0) { + t_dev_err(dev, "tci area read fail\n"); + + goto out; + } + + area = (struct reset_area *)area_data_buf; + siw_prt_tci_area_ic(dev, "[TCI_1]", area, 0); + siw_prt_tci_area_ic(dev, "[TCI_2]", area, 16); + + out: + return ret; +} + +//#define __SKIP_LPWG_UPDATE_ALL_FOR_SAME_INPUT + +static int siw_hal_lpwg(struct device *dev, u32 code, void *param) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct tci_ctrl *tci = &ts->tci; + struct active_area *area = &tci->area; + struct reset_area *area_qcover; + struct lpwg_info *lpwg = &ts->lpwg; + int *value = (int *)param; + int changed = 0; + int ret = 0; + +// if (!touch_test_quirks(ts, CHIP_QUIRK_SUPPORT_LPWG)) { + if (!ts->role.use_lpwg) { + t_dev_warn(dev, "LPWG control not supported in %s\n", + touch_chip_name(ts)); + return 0; + } + + touch_msleep(hal_dbg_delay(chip, HAL_DBG_DLY_LPWG)); + + switch (code) { + case LPWG_ACTIVE_AREA: + area->x1 = value[0]; + area->x2 = value[1]; + area->y1 = value[2]; + area->y2 = value[3]; + t_dev_info(dev, "LPWG_ACTIVE_AREA: x1[%d], y1[%d], x2[%d], y2[%d]\n", + area->x1, area->y1, area->x2, area->y2); + break; + + case LPWG_TAP_COUNT: + tci->info[TCI_2].tap_count = value[0]; + break; + + case LPWG_DOUBLE_TAP_CHECK: + tci->double_tap_check = value[0]; + break; + + case LPWG_UPDATE_ALL: + changed = (lpwg->mode != value[0]) | + ((lpwg->screen != value[1])<<1) | + ((lpwg->sensor != value[2])<<2) | + ((lpwg->qcover != value[3])<<3); + + lpwg->mode = value[0]; + lpwg->screen = value[1]; + lpwg->sensor = value[2]; + lpwg->qcover = value[3]; + + t_lpwg_mode = lpwg->mode; + t_lpwg_screen = lpwg->screen; + t_lpwg_sensor = lpwg->sensor; + t_lpwg_qcover = lpwg->qcover; + + t_dev_info(dev, + "LPWG_UPDATE_ALL: mode[%d], screen[%s], sensor[%s], qcover[%s] (%02Xh)\n", + lpwg->mode, + lpwg->screen ? "ON" : "OFF", + lpwg->sensor ? "FAR" : "NEAR", + lpwg->qcover ? "CLOSE" : "OPEN", + changed); + +#if defined(__SKIP_LPWG_UPDATE_ALL_FOR_SAME_INPUT) + if (!changed) { + t_dev_info(dev, "LPWG_UPDATE_ALL: not changed, skip\n"); + break; + } +#endif + + ret = siw_hal_lpwg_mode(dev); + break; + + case LPWG_REPLY: + break; + + case LPWG_EXT_TCI_INFO_STORE: + t_dev_info(dev, + "LPWG_EXT_TCI_INFO_STORE: tci[%s], index[%d], value[%d], write_en[%s]\n", + value[0] ? "TCI_2" : "TCI_1", + value[1], + value[2], + value[3] ? "YES" : "NO"); + + ret = siw_hal_lpwg_ext_tci_info(dev, param); + break; + + case LPWG_EXT_TCI_QOPEN_AREA_STORE: + area_qcover = &tci->qcover_open; + + area_qcover->x1 = value[0]; + area_qcover->x2 = value[1]; + area_qcover->y1 = value[2]; + area_qcover->y2 = value[3]; + + t_dev_info(dev, + "LPWG_EXT_TCI_QOPEN_AREA_STORE: x1[%d], y1[%d], x2[%d], y2[%d]\n", + area_qcover->x1, area_qcover->y1, area_qcover->x2, area_qcover->y2); + break; + + case LPWG_EXT_TCI_QCLOSE_AREA_STORE: + area_qcover = &tci->qcover_close; + + area_qcover->x1 = value[0]; + area_qcover->x2 = value[1]; + area_qcover->y1 = value[2]; + area_qcover->y2 = value[3]; + + t_dev_info(dev, + "LPWG_EXT_TCI_QCLOSE_AREA_STORE: x1[%d], y1[%d], x2[%d], y2[%d]\n", + area_qcover->x1, area_qcover->y1, area_qcover->x2, area_qcover->y2); + break; + + case LPWG_EXT_SWIPE_INFO_STORE: + t_dev_info(dev, "LPWG_EXT_SWIPE_INFO_STORE\n"); + break; + + case LPWG_EXT_TCI_INFO_SHOW: + t_dev_info(dev, "LPWG_EXT_TCI_INFO_SHOW\n"); + + ret = siw_hal_show_ic_tci_info(dev); + + siw_hal_show_driver_tci_info(dev); + break; + + case LPWG_EXT_TCI_AREA_SHOW: + t_dev_info(dev, "LPWG_EXT_TCI_AREA_SHOW\n"); + + ret = siw_hal_show_ic_tci_area(dev); + + siw_hal_show_driver_tci_area(dev); + break; + + case LPWG_EXT_SWIPE_INFO_SHOW: + break; + } + + return ret; +} + +#if defined(__SIW_SUPPORT_ASC) +static int siw_hal_asc(struct device *dev, u32 code, u32 value) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_asc_info *asc = &chip->asc; + u32 rdata = 0; + u32 wdata = 0; + u32 asc_ret = 0; + size_t mon_data[6] = { 0, }; + int mon_cnt = 0; + int ret = 0; + + mon_data[mon_cnt++] = code; + + switch (code) { + case ASC_READ_MAX_DELTA: + ret = siw_hal_reg_read(dev, + reg->max_delta, + (void *)&rdata, sizeof(rdata)); + mon_data[mon_cnt++] = (size_t)reg->max_delta; + mon_data[mon_cnt++] = (size_t)rdata; + if (ret < 0) { + break; + } + + asc_ret = rdata; + + break; + + case ASC_GET_FW_SENSITIVITY: + /* fall through */ + case ASC_WRITE_SENSITIVITY: + ret = siw_hal_reg_read(dev, + reg->touch_max_r, + (void *)&rdata, sizeof(rdata)); + mon_data[mon_cnt++] = (size_t)reg->touch_max_r; + mon_data[mon_cnt++] = (size_t)rdata; + if (ret < 0) { + break; + } + + asc->normal_s = rdata; + asc->acute_s = (rdata / 10) * 6; + asc->obtuse_s = rdata; + + if (code == ASC_GET_FW_SENSITIVITY) { + t_dev_info(dev, + "max_r(%04Xh) = %d, n_s %d, a_s = %d, o_s = %d\n", + reg->touch_max_r, + rdata, + asc->normal_s, + asc->acute_s, + asc->obtuse_s); + break; + } + + switch (value) { + case NORMAL_SENSITIVITY : + wdata = asc->normal_s; + break; + case ACUTE_SENSITIVITY : + wdata = asc->acute_s; + break; + case OBTUSE_SENSITIVITY : + wdata = asc->obtuse_s; + break; + default: + wdata = rdata; + break; + } + + ret = siw_hal_write_value(dev, + reg->touch_max_w, + wdata); + mon_data[mon_cnt++] = (size_t)reg->touch_max_w; + mon_data[mon_cnt++] = (size_t)wdata; + if (ret < 0) { + break; + } + + t_dev_info(dev, "max_w(%04Xh) changed (%d -> %d)\n", + reg->touch_max_w, + rdata, wdata); + break; + default: + break; + } + + siwmon_submit_ops_wh_name(dev, "%s asc done", + touch_chip_name(ts), + mon_data, mon_cnt, asc_ret); + + return asc_ret; +} +#else /* __SIW_SUPPORT_ASC */ +static int siw_hal_asc(struct device *dev, u32 code, u32 value) +{ + return 0; +} +#endif /* __SIW_SUPPORT_ASC */ + + +#define siw_chk_sts_snprintf(_dev, _buf, _buf_max, _size, _fmt, _args...) \ + ({ \ + int _n_size = 0; \ + _n_size = __siw_snprintf(_buf, _buf_max, _size, _fmt, ##_args); \ + t_dev_dbg_trace(_dev, _fmt, ##_args); \ + _n_size; \ + }) + +static int siw_hal_check_fault_type(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + u32 addr = 0; + int fault_type = NON_FAULT_INT; + int ret = 0; + + switch (chip->opt.t_chk_fault) { + case 1 : + addr = 0x283; + break; + default : + return NON_FAULT_INT; + } + + ret = siw_hal_read_value(dev, addr, (u32 *)&fault_type); + if (ret < 0) { + return NON_FAULT_INT; + } + + return fault_type; +} + +static u32 siw_hal_check_sys_error_type(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + u32 addr = 0; + u32 fault_type = NON_FAULT_U32; + int ret = 0; + + switch (chip->opt.t_chk_sys_error) { + case 1: + addr = 0x020; + break; + case 2: + addr = 0x021; + break; + case 3: + addr = 0x01C; + break; + default : + return NON_FAULT_U32; + } + + ret = siw_hal_read_value(dev, addr, (u32 *)&fault_type); + if (ret < 0) { + return NON_FAULT_U32; + } + + return fault_type; +} + +static u32 siw_hal_check_sys_fault_type(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + u32 addr = 0; + u32 fault_type = NON_FAULT_U32; + int ret = 0; + + switch (chip->opt.t_chk_sys_fault) { + case 1: + addr = 0xFF4; + break; + default : + return NON_FAULT_U32; + } + + ret = siw_hal_read_value(dev, addr, (u32 *)&fault_type); + if (ret < 0) { + return NON_FAULT_U32; + } + + return fault_type; +} + +static int siw_hal_check_status_type_x(struct device *dev, + u32 status, u32 ic_status, int irq) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_status_filter *filter = chip->status_filter; + u32 check_mask, detected; + int type_error, type_esd, type_fault; + u32 dbg_mask = 0; + u32 log_flag = 0; + u32 esd_send = 0; + u32 tc_disp_err = 0; + u32 ic_abnormal, ic_error, ic_disp_err; + int log_max = IC_CHK_LOG_MAX; + char log[IC_CHK_LOG_MAX] = {0, }; + int fault_val; + int len = 0; + int ret = 0; + + if (filter == NULL) { + return -EINVAL; + } + + if (ic_status & ~chip->status_mask_ic_valid) { + t_dev_err(dev, "[%d] status %08Xh, ic_status %08Xh, ic_status invalid\n", + irq, status, ic_status); + + if (chip->lcd_mode != LCD_MODE_U0) { + if (siw_hal_send_esd_notifier(dev, 8)) { + atomic_set(&chip->esd_noti_sent, 1); + return -ETDSENTESDIRQ; + } + } + return -ERESTART; + } + + while (1) { + if (!filter->id || !filter->width) { + break; + } + + type_error = !!(filter->flag & STS_FILTER_FLAG_TYPE_ERROR); + type_esd = !!(filter->flag & STS_FILTER_FLAG_ESD_SEND); + type_fault = !!(filter->flag & STS_FILTER_FLAG_CHK_FAULT); + + check_mask = ((1<width)-1)<pos; + + detected = (type_error) ? (status & check_mask) : !(status & check_mask); + + if (check_mask && detected) { + log_flag |= check_mask; + esd_send |= (type_esd) ? check_mask : 0; + + fault_val = (type_fault) ? siw_hal_check_fault_type(dev) : -1; + + if (type_fault && (fault_val >= 0)) { + len += siw_chk_sts_snprintf(dev, log, log_max, len, + "[b%d] %s(%Xh) ", filter->pos, filter->str, fault_val); + } else { + len += siw_chk_sts_snprintf(dev, log, log_max, len, + "[b%d] %s ", filter->pos, filter->str); + } + + tc_disp_err |= !!(filter->id == STS_ID_ERROR_DISP); + } + + filter++; + } + + if (log_flag) { + t_dev_err(dev, "[%d] status %08Xh, ic_status %08Xh, (%08Xh) %s\n", + irq, status, ic_status, log_flag, log); + } + + ic_abnormal = ic_status & chip->status_mask_ic_abnormal; + ic_error = ic_status & chip->status_mask_ic_error; + ic_disp_err = ic_status & chip->status_mask_ic_disp_err; + + if (ic_abnormal || ic_error || ic_disp_err) { + u32 err_val[3] = { ic_abnormal, ic_error, ic_disp_err }; + char *err_str[3] = { + "esd", + "watchdog", + "dic" + }; + u32 sys_error, sys_fault; + int log_add = !log_flag; + int err_pre, i; + + sys_error = siw_hal_check_sys_error_type(dev); + sys_fault = siw_hal_check_sys_fault_type(dev); + + log_add |= (!!((sys_error != NON_FAULT_U32) || (sys_fault != NON_FAULT_U32)))<<1; + + len = siw_chk_sts_snprintf(dev, log, log_max, 0, + "[%d] ", irq); + + err_pre = 0; + for (i = 0; i < ARRAY_SIZE(err_val) ; i++) { + if (!err_val[i]) { + continue; + } + + if (err_pre) { + len += siw_chk_sts_snprintf(dev, log, log_max, len, " & "); + } + len += siw_chk_sts_snprintf(dev, log, log_max, len, "%s", err_str[i]); + err_pre |= err_val[i]; + } + + if (log_add) { + len += siw_chk_sts_snprintf(dev, log, log_max, len, + " - "); + + if (log_add & 0x01) { + len += siw_chk_sts_snprintf(dev, log, log_max, len, + "status %08Xh, ic_status %08Xh%s", + status, ic_status, + (log_add & 0x02) ? ", " : " "); + } + + if (log_add & 0x02) { + len += siw_chk_sts_snprintf(dev, log, log_max, len, + "sys_error %08Xh, sys_fault %08Xh", + sys_error, sys_fault); + } + } + + t_dev_err(dev, "%s\n", log); + + esd_send |= (ic_abnormal | ic_disp_err); + if (!esd_send) { + ret = -ERESTART; //touch reset + } + } + + if (esd_send) { + ret = -ERESTART; + if (chip->lcd_mode != LCD_MODE_U0) { + int esd_type; + + esd_type = (ic_disp_err || tc_disp_err)? DIC_ERR_TYPE : 0; + + if (siw_hal_send_esd_notifier(dev, esd_type | 1)) { + atomic_set(&chip->esd_noti_sent, 1); + return -ETDSENTESDIRQ; + } + } + } + + if (ret == -ERESTART) { + return ret; + } + + dbg_mask = ((status>>16) & 0xF); + switch (dbg_mask) { + case 0x2 : + t_dev_info(dev, "[%d] TC Driving OK\n", irq); + ret = -ERANGE; + break; + case 0x3 : + /* fall through */ + case 0x4 : + t_dev_dbg_trace(dev, "[%d] dbg_mask %Xh\n", + irq, dbg_mask); + ret = -ERANGE; + break; + } + + return ret; +} + +#if defined(CONFIG_TOUCHSCREEN_SIW_SW1828) || \ + defined(CONFIG_TOUCHSCREEN_SIW_SW42101) +#define STS_RET_ERR ERESTART +#else +#define STS_RET_ERR ERANGE +#endif + +static int siw_hal_do_check_status(struct device *dev, + u32 status, u32 ic_status, int irq) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + u32 reset_clr_bit = 0; + u32 logging_clr_bit = 0; + u32 int_norm_mask = 0; + u32 status_mask = 0; + int ret_pre = 0; + int ret = 0; + + if (!status && !ic_status) { + t_dev_err(dev, "all low detected\n"); + return -STS_RET_ERR; + } + if ((status == ~0) && (ic_status == ~0)) { + t_dev_err(dev, "all high detected\n"); + return -STS_RET_ERR; + } + + ret = siw_hal_chk_status_type(dev); + if (ret < 0) { + return ret; + } + + reset_clr_bit = chip->status_mask_reset; + logging_clr_bit = chip->status_mask_logging; + int_norm_mask = chip->status_mask_normal; + + status_mask = status ^ int_norm_mask; + + if (!(irq & 0x80)) { + t_dev_dbg_trace(dev, "[%d] h/w:%Xh, f/w:%Xh(%Xh)\n", + irq, ic_status, status, status_mask); + } + irq &= 0x01; + + if (status_mask & reset_clr_bit) { + t_dev_err(dev, + "[%d] need reset : status %08Xh, ic_status %08Xh, chk %08Xh (%08Xh)\n", + irq, status, ic_status, status_mask & reset_clr_bit, reset_clr_bit); + ret_pre = -ERESTART; + } else if (status_mask & logging_clr_bit) { + t_dev_err(dev, + "[%d] need logging : status %08Xh, ic_status %08Xh, chk %08Xh (%08Xh)\n", + irq, status, ic_status, status_mask & logging_clr_bit, logging_clr_bit); + ret_pre = -ERANGE; + } + + switch (chip->status_type) { + case CHIP_STATUS_TYPE_2: + case CHIP_STATUS_TYPE_1: + case CHIP_STATUS_TYPE_0: + ret = siw_hal_check_status_type_x(dev, status, ic_status, irq); + siw_hal_chk_dbg_report(dev, status, irq); + break; + default: + t_dev_warn(dev, "unknown status type, %d\n", chip->status_type); + break; + } + + if (ret == -ETDSENTESDIRQ) { + return ret; + } + + if (ret_pre) { + if (ret != -ERESTART) { + ret = ret_pre; + } + } + + return ret; +} + +static int siw_hal_check_status(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + u32 ic_status = chip->info.ic_status; + u32 status = chip->info.device_status; + + return siw_hal_do_check_status(dev, status, ic_status, 1); +} + +static int siw_hal_irq_abs_data_type_1(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_touch_data_type_1 *data; + struct touch_data *tdata; + u32 touch_count = chip->info.touch_cnt; + int finger_index = 0; + int i = 0; + int ret = 0; + + data = (struct siw_hal_touch_data_type_1 *)chip->info.data; + + touch_count = chip->info.touch_cnt; + ts->new_mask = 0; + + /* check if palm detected */ + if (data->track_id == PALM_ID) { + if (data->event == TOUCHSTS_DOWN) { + ts->is_palm = 1; + t_dev_info(dev, "Palm Detected\n"); + } else if (data->event == TOUCHSTS_UP) { + ts->is_palm = 0; + t_dev_info(dev, "Palm Released\n"); + } + ts->tcount = 0; + ts->intr_status = TOUCH_IRQ_FINGER; + return ret; + } + + for (i = 0; i < touch_count; i++, data++) { + if (data->track_id >= touch_max_finger(ts)) { + continue; + } + + if ((data->event == TOUCHSTS_DOWN) || + (data->event == TOUCHSTS_MOVE)) { + ts->new_mask |= (1 << data->track_id); + tdata = ts->tdata + data->track_id; + + tdata->id = data->track_id; + tdata->type = data->tool_type; + tdata->event = data->event; + tdata->x = data->x; + tdata->y = data->y; + tdata->pressure = data->pressure; + tdata->width_major = data->width_major; + tdata->width_minor = data->width_minor; + + if (data->width_major == data->width_minor) + tdata->orientation = 1; + else + tdata->orientation = (s8)data->angle; + + finger_index++; + + t_dev_dbg_abs(dev, + "touch data [id %d, t %d, e %d, x %d, y %d, z %d - %d, %d, %d]\n", + tdata->id, + tdata->type, + tdata->event, + tdata->x, + tdata->y, + tdata->pressure, + tdata->width_major, + tdata->width_minor, + tdata->orientation); + } + } + + ts->tcount = finger_index; + ts->intr_status = TOUCH_IRQ_FINGER; + + return ret; +} + +static int siw_hal_irq_abs_data_type_0(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_touch_data *data; + struct touch_data *tdata; + u32 touch_count = chip->info.touch_cnt; + int finger_index = 0; + int i = 0; + int ret = 0; + + data = (struct siw_hal_touch_data *)chip->info.data; + + touch_count = chip->info.touch_cnt; + ts->new_mask = 0; + + /* check if palm detected */ + if (data->track_id == PALM_ID) { + if (data->event == TOUCHSTS_DOWN) { + ts->is_palm = 1; + t_dev_info(dev, "Palm Detected\n"); + } else if (data->event == TOUCHSTS_UP) { + ts->is_palm = 0; + t_dev_info(dev, "Palm Released\n"); + } + ts->tcount = 0; + ts->intr_status = TOUCH_IRQ_FINGER; + return ret; + } + + for (i = 0; i < touch_count; i++, data++) { + if (data->track_id >= touch_max_finger(ts)) { + continue; + } + + if ((data->event == TOUCHSTS_DOWN) || + (data->event == TOUCHSTS_MOVE)) { + ts->new_mask |= (1 << data->track_id); + tdata = ts->tdata + data->track_id; + + tdata->id = data->track_id; + tdata->type = data->tool_type; + tdata->event = data->event; + tdata->x = data->x; + tdata->y = data->y; + tdata->pressure = data->pressure; + tdata->width_major = data->width_major; + tdata->width_minor = data->width_minor; + + if (data->width_major == data->width_minor) + tdata->orientation = 1; + else + tdata->orientation = (s8)data->angle; + + finger_index++; + + t_dev_dbg_abs(dev, + "touch data [id %d, t %d, e %d, x %d, y %d, z %d - %d, %d, %d]\n", + tdata->id, + tdata->type, + tdata->event, + tdata->x, + tdata->y, + tdata->pressure, + tdata->width_major, + tdata->width_minor, + tdata->orientation); + } + } + + ts->tcount = finger_index; + ts->intr_status = TOUCH_IRQ_FINGER; + + return ret; +} + +static int siw_hal_irq_abs_data(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + int ret = 0; + + switch (chip->report_type) { + case CHIP_REPORT_TYPE_1: + ret = siw_hal_irq_abs_data_type_1(dev); + break; + case CHIP_REPORT_TYPE_0: + ret = siw_hal_irq_abs_data_type_0(dev); + break; + default: + t_dev_warn(dev, "unknown report type, %d\n", chip->report_type); + break; + } + + return ret; +} + +static int siw_hal_irq_abs(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + /* check if touch cnt is valid */ + if (chip->info.touch_cnt == 0 || chip->info.touch_cnt > ts->caps.max_id) { + struct siw_hal_touch_data *data = chip->info.data; + + t_dev_dbg_abs(dev, "Invalid touch count, %d(%d)\n", + chip->info.touch_cnt, ts->caps.max_id); + + /* debugging */ + t_dev_dbg_abs(dev, "t %d, ev %d, id %d, x %d, y %d, p %d, a %d, w %d %d\n", + data->tool_type, data->event, data->track_id, + data->x, data->y, data->pressure, data->angle, + data->width_major, data->width_minor); + + return -ERANGE; + } + + return siw_hal_irq_abs_data(dev); +} + +static int siw_hal_get_tci_data(struct device *dev, int count) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + u8 i = 0; + u32 rdata[MAX_LPWG_CODE]; + + if (!count) + return 0; + + ts->lpwg.code_num = count; + + memcpy(&rdata, chip->info.data, sizeof(u32) * count); + + for (i = 0; i < count; i++) { + ts->lpwg.code[i].x = rdata[i] & 0xffff; + ts->lpwg.code[i].y = (rdata[i] >> 16) & 0xffff; + + if (ts->lpwg.mode == LPWG_PASSWORD) + t_dev_info(dev, "LPWG data xxxx, xxxx\n"); + else + t_dev_info(dev, "LPWG data %d, %d\n", + ts->lpwg.code[i].x, ts->lpwg.code[i].y); + } + ts->lpwg.code[count].x = -1; + ts->lpwg.code[count].y = -1; + + return 0; +} + +static int siw_hal_get_swipe_data(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + u32 rdata[3]; + int count = 1; + + /* swipe_info */ + /* start (X, Y), end (X, Y), time = 2bytes * 5 = 10 bytes */ + memcpy(&rdata, chip->info.data, sizeof(u32) * 3); + + t_dev_info(dev, + "Swipe Gesture: start(%4d, %4d) end(%4d, %4d) swipe_time(%dms)\n", + rdata[0] & 0xffff, rdata[0] >> 16, + rdata[1] & 0xffff, rdata[1] >> 16, + rdata[2] & 0xffff); + + ts->lpwg.code_num = count; + ts->lpwg.code[0].x = rdata[1] & 0xffff; + ts->lpwg.code[0].x = rdata[1] >> 16; + + ts->lpwg.code[count].x = -1; + ts->lpwg.code[count].y = -1; + + return 0; +} + +static int siw_hal_irq_lpwg_base(struct siw_ts *ts, int type) +{ + struct device *dev = ts->dev; + int ret = 0; + + switch (type) { + case KNOCK_1: + if (ts->lpwg.mode == LPWG_NONE) { + break; + } + t_dev_info(dev, "LPWG: TOUCH_IRQ_KNOCK\n"); + siw_hal_get_tci_data(dev, + ts->tci.info[TCI_1].tap_count); + ts->intr_status = TOUCH_IRQ_KNOCK; + break; + case KNOCK_2: + if (ts->lpwg.mode != LPWG_PASSWORD) { + break; + } + t_dev_info(dev, "LPWG: TOUCH_IRQ_PASSWD\n"); + siw_hal_get_tci_data(dev, + ts->tci.info[TCI_2].tap_count); + ts->intr_status = TOUCH_IRQ_PASSWD; + break; + case SWIPE_RIGHT: + t_dev_info(dev, "LPWG: SWIPE_RIGHT\n"); + siw_hal_get_swipe_data(dev); + ts->intr_status = TOUCH_IRQ_SWIPE_RIGHT; + break; + case SWIPE_LEFT: + t_dev_info(dev, "LPWG: SWIPE_LEFT\n"); + siw_hal_get_swipe_data(dev); + ts->intr_status = TOUCH_IRQ_SWIPE_LEFT; + break; + default: + // t_dev_warn(dev, "unkown lpwg: %d\n", type); + break; + } + + return ret; +} + +static int siw_hal_irq_lpwg_gesture(struct siw_ts *ts, int type) +{ + struct device *dev = ts->dev; + int index = 0; + int ret = 0; + + t_dev_info(dev, "lpwg gesture: %d\n", type); + + ts->intr_status = TOUCH_IRQ_GESTURE; + ts->intr_gesture = TOUCH_UEVENT_GESTURE_C + index; + + return ret; +} + +static int siw_hal_irq_lpwg_dir(struct siw_ts *ts, int type) +{ + struct device *dev = ts->dev; + int index = 0; + int ret = 0; + + t_dev_info(dev, "lpwg dir: %d\n", type); + + ts->intr_status = TOUCH_IRQ_GESTURE; + ts->intr_gesture = TOUCH_UEVENT_GESTURE_DIR_RIGHT + index; + + return ret; +} + +static int siw_hal_irq_lpwg_custom_debug(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + + t_dev_info(dev, "LPWG: CUSTOM_DEBUG\n"); + siw_hal_debug_tci(dev); + siw_hal_debug_swipe(dev); + + return 0; +} + +static int siw_hal_irq_lpwg_knock_overtap(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + + t_dev_info(dev, "LPWG: overtap\n"); +// siw_hal_get_tci_data(dev, 1); + siw_hal_get_tci_data(dev, ts->tci.info[TCI_2].tap_count + 1); + ts->intr_status = TOUCH_IRQ_PASSWD; + + return 0; +} + +static int siw_hal_irq_lpwg(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + u32 type = chip->info.wakeup_type; + int ret = 0; + + if (!type || (type > KNOCK_OVERTAP)) { + goto out; + } + + if (type == CUSTOM_DEBUG) { + ret = siw_hal_irq_lpwg_custom_debug(ts); + } else if (type == KNOCK_OVERTAP) { + ret = siw_hal_irq_lpwg_knock_overtap(ts); + } else if (type <= SWIPE_LEFT) { + ret = siw_hal_irq_lpwg_base(ts, type); + } else if (type <= GESTURE_Z) { + ret = siw_hal_irq_lpwg_gesture(ts, type); + } else if ((type >= GESTURE_DIR_RIGHT) || (type <= GESTURE_DIR_UP)) { + ret = siw_hal_irq_lpwg_dir(ts, type); + } else { + goto out; + } + + return ret; + +out: + t_dev_err(dev, "LPWG: unknown type, %d\n", type); + + return -EINVAL; +} + +static int siw_hal_irq_handler(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + int size = 0; + int ret = 0; + + if (atomic_read(&chip->init) == IC_INIT_NEED) { + t_dev_warn(dev, "Not Ready, Need IC init (irq)\n"); + return 0; + } + +#if defined(__SIW_SUPPORT_PM_QOS) + pm_qos_update_request(&chip->pm_qos_req, 10); +#endif + size = 12 + (sizeof(struct siw_hal_touch_data) * touch_max_finger(ts)); + ret = siw_hal_reg_read(dev, + reg->tc_ic_status, + (void *)&chip->info, size); +#if defined(__SIW_SUPPORT_PM_QOS) + pm_qos_update_request(&chip->pm_qos_req, PM_QOS_DEFAULT_VALUE); +#endif + if (ret < 0) { + goto out; + } + + ret = siw_hal_check_status(dev); + if (ret < 0) { + goto out; + } + + t_dev_dbg_irq(dev, "hal irq handler: wakeup_type %d\n", + chip->info.wakeup_type); + + if (chip->info.wakeup_type == ABS_MODE) { + ret = siw_hal_irq_abs(dev); + if (ret) { + t_dev_err(dev, "siw_hal_irq_abs failed(%d), %d\n", + chip->info.touch_cnt, ret); + goto out; + } + } else { + ret = siw_hal_irq_lpwg(dev); + if (ret) { + t_dev_err(dev, "siw_hal_irq_lpwg failed, %d\n", ret); + goto out; + } + } + + if (atomic_read(&ts->state.debug_option_mask) & DEBUG_OPTION_2) { + /* */ + } + +out: + return ret; +} + +static void siw_hal_connect(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + int charger_state = atomic_read(&ts->state.connect); + int wireless_state = atomic_read(&ts->state.wireless); + +#if 1 + if (wireless_state) { + chip->charger = CONNECT_WIRELESS; + } else { + if ((charger_state < 0) || + (charger_state > CONNECT_OTG)) { + t_dev_err(dev, "invalid charger status, %d\n", charger_state); + return; + } + chip->charger = charger_state; + } +#else + chip->charger = 0; + switch (charger_state) { + case CONNECT_INVALID: + chip->charger = CONNECT_NONE; + break; + case CONNECT_DCP: + /* fall through */ + case CONNECT_PROPRIETARY: + chip->charger = CONNECT_TA; + break; + case CONNECT_HUB: + chip->charger = CONNECT_OTG; + break; + default: + chip->charger = CONNECT_USB; + break; + } + +#if 0 + /* code for TA simulator */ + if (atomic_read(&ts->state.debug_option_mask) & DEBUG_OPTION_4) { + t_dev_info(dev, "TA simulator mode, set CONNECT_TA\n"); + chip->charger = CONNECT_TA; + } +#endif + + /* wireless */ + chip->charger |= (wireless_state) ? CONNECT_WIRELESS : 0; +#endif + + t_dev_info(dev, + "charger_state = %Xh (%Xh, %Xh)\n", + chip->charger, charger_state, wireless_state); + + if (atomic_read(&ts->state.pm) > DEV_PM_RESUME) { + t_dev_warn(dev, "DEV_PM_SUSPEND - Don't try SPI\n"); + return; + } + + siw_hal_write_value(dev, + reg->spr_charger_status, + chip->charger); + + t_dev_info(dev, "charger_state set done\n"); +} + +static int siw_hal_lcd_mode(struct device *dev, u32 mode) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + if (touch_mode_not_allowed(ts, mode)) { + return -EPERM; + } + + if ((chip->lcd_mode == LCD_MODE_U2) && + siw_hal_watch_is_disp_waton(dev)) { + siw_hal_watch_get_curr_time(dev, NULL, NULL); + } + + if (chip->opt.f_u2_blank_chg) { + if (mode == LCD_MODE_U2_UNBLANK) { + mode = LCD_MODE_U2; + } + } + + chip->prev_lcd_mode = chip->lcd_mode; + chip->lcd_mode = mode; + + t_dev_info(dev, "lcd_mode: %d (prev: %d)\n", + mode, chip->prev_lcd_mode); + + return 0; +} + +static int siw_hal_usb_status(struct device *dev, u32 mode) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + t_dev_info(dev, "TA Type: %d\n", atomic_read(&ts->state.connect)); + + siw_hal_connect(dev); + + return 0; +} + +static int siw_hal_wireless_status(struct device *dev, u32 onoff) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + t_dev_info(dev, "Wireless charger: 0x%02X\n", atomic_read(&ts->state.wireless)); + + siw_hal_connect(dev); + + return 0; +} + +static int siw_hal_earjack_status(struct device *dev, u32 onoff) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + t_dev_info(dev, "Earjack Type: 0x%02X\n", atomic_read(&ts->state.earjack)); + + return 0; +} + +#if defined(__SIW_SUPPORT_ABT) +extern void siw_hal_switch_to_abt_irq_handler(struct siw_ts *ts); + +static int siw_hal_debug_tool(struct device *dev, u32 value) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + if (value >= DEBUG_TOOL_MAX) { + t_dev_err(dev, + "wrong index, debug tool select failed, %d\n", + value); + return -EINVAL; + } + + mutex_lock(&ts->lock); + switch (value) { + case DEBUG_TOOL_ENABLE: + siw_hal_switch_to_abt_irq_handler(ts); + break; + default: + siw_ops_restore_irq_handler(ts); + t_dev_info(dev, "restore irq handler\n"); + break; + } + mutex_unlock(&ts->lock); + + return 0; +} +#else /* __SIW_SUPPORT_ABT */ +static int siw_hal_debug_tool(struct device *dev, u32 value) +{ + t_dev_info(dev, "Nop ...\n"); + return 0; +} +#endif /* __SIW_SUPPORT_ABT */ + +static int siw_hal_notify(struct device *dev, ulong event, void *data) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + char *noti_str = "(unknown)"; + u32 value = 0; + int ret = 0; + + if (data) { + value = *((u32 *)data); + } + + switch (event) { + case LCD_EVENT_TOUCH_RESET_START: + case LCD_EVENT_TOUCH_RESET_END: + break; + default: + touch_msleep(hal_dbg_delay(chip, HAL_DBG_DLY_NOTIFY)); + break; + } + + t_dev_dbg_noti(dev, "notify event(%d)\n", (u32)event); + + switch (event) { + case NOTIFY_TOUCH_RESET: + atomic_set(&chip->init, IC_INIT_NEED); + atomic_set(&ts->state.hw_reset, event); + + #if 0 + ret = !!(atomic_read(&ts->state.debug_option_mask) & DEBUG_OPTION_1); + t_dev_info(dev, "notify: reset, %d\n", ret); + #else + t_dev_info(dev, "notify: reset\n"); + #endif + + siw_hal_watch_set_rtc_clear(dev); + + #if 0 + siw_hal_watch_set_font_empty(dev); + siw_hal_watch_set_cfg_blocked(dev); + #endif + + noti_str = "TOUCH_RESET"; + break; + case LCD_EVENT_TOUCH_RESET_START: + atomic_set(&chip->init, IC_INIT_NEED); + atomic_set(&ts->state.hw_reset, event); + + t_dev_info(dev, "notify: lcd_event: touch reset start\n"); + siw_touch_irq_control(ts->dev, INTERRUPT_DISABLE); + siw_hal_set_gpio_reset(dev, GPIO_OUT_ZERO); + touch_msleep(chip->drv_reset_low + hal_dbg_delay(chip, HAL_DBG_DLY_HW_RST_0)); + + noti_str = "TOUCH_RESET_START"; + break; + case LCD_EVENT_TOUCH_RESET_END: + atomic_set(&ts->state.hw_reset, event); + + t_dev_info(dev, "notify: lcd_event: touch reset end\n"); + siw_hal_set_gpio_reset(dev, GPIO_OUT_ONE); + + siw_touch_qd_init_work_hw(ts); + + noti_str = "TOUCH_RESET_END"; + break; + case LCD_EVENT_LCD_MODE: + noti_str = "LCD_MODE"; + + t_dev_info(dev, "notify: lcd_event: lcd mode\n"); + if (data == NULL) { + t_dev_err(dev, "data is null, cancled\n"); + break; + } + + ret = siw_hal_lcd_mode(dev, value); + if (ret < 0) { + break; + } + ret = siw_hal_check_mode(dev); + if (!ret) { + queue_delayed_work(ts->wq, &chip->fb_notify_work, 0); + } + ret = 0; + break; + + case LCD_EVENT_READ_REG: + t_dev_info(dev, "notify: lcd event: read reg\n"); + siw_hal_lcd_event_read_reg(dev); + + noti_str = "READ_REG"; + break; + + case NOTIFY_CONNECTION: + t_dev_info(dev, "notify: connection\n"); + ret = siw_hal_usb_status(dev, value); + + noti_str = "CONNECTION"; + break; + case NOTIFY_WIRELEES: + t_dev_info(dev, "notify: wireless\n"); + ret = siw_hal_wireless_status(dev, value); + + noti_str = "WIRELESS"; + break; + case NOTIFY_EARJACK: + t_dev_info(dev, "notify: earjack\n"); + ret = siw_hal_earjack_status(dev, value); + + noti_str = "EARJACK"; + break; + case NOTIFY_IME_STATE: +#if 0 + t_dev_info(dev, "notify: ime state\n"); + ret = siw_hal_write_value(dev, + reg->ime_state, + value); +#else + t_dev_info(dev, "notify: do nothing for ime\n"); +#endif + + noti_str = "IME_STATE"; + break; + case NOTIFY_DEBUG_TOOL: + ret = siw_hal_debug_tool(dev, value); + t_dev_info(dev, "notify: debug tool\n"); + + noti_str = "DEBUG_TOOL"; + break; + case NOTIFY_CALL_STATE: + t_dev_info(dev, "notify: call state\n"); + ret = siw_hal_write_value(dev, + reg->call_state, + value); + + noti_str = "CALL_STATE"; + break; + case LCD_EVENT_TOUCH_DRIVER_REGISTERED: + case LCD_EVENT_TOUCH_DRIVER_UNREGISTERED: + if (0) { + /* from siw_touch_probe */ + t_dev_info(dev, "notify: driver %s\n", + (event == LCD_EVENT_TOUCH_DRIVER_REGISTERED) ? + "registered" : "unregistered"); + } + noti_str = "DRV"; + break; + case LCD_EVENT_TOUCH_WATCH_LUT_UPDATE: + t_dev_info(dev, "notify: WATCH_LUT_UPDATE(%lu)\n", event); + noti_str = "WATCH_LUT"; + break; + case LCD_EVENT_TOUCH_WATCH_POS_UPDATE: + t_dev_info(dev, "notify: WATCH_POS_UPDATE(%lu)\n", event); + noti_str = "WATCH_POS"; + break; + case LCD_EVENT_TOUCH_PROXY_STATUS: + t_dev_info(dev, "notify: PROXY_STATUS(%lu)\n", event); + noti_str = "PROXY"; + break; + case LCD_EVENT_TOUCH_ESD_DETECTED: + t_dev_info(dev, "notify: ESD_DETECTED(%lu, %d)\n", event, value); + noti_str = "ESD"; + break; + default: + t_dev_err(dev, "notify: %lu is not supported\n", event); + break; + } + + siwmon_submit_evt(dev, "NOTIFY", 0, noti_str, event, value, ret); + + return ret; +} + +enum { + SIW_GET_CHIP_NAME = (1<<0), + SIW_GET_VERSION = (1<<1), + SIW_GET_REVISION = (1<<2), + SIW_GET_PRODUCT = (1<<3), + /* */ + SIW_GET_OPT1 = (1<<8), + /* */ + SIW_GET_VER_SIMPLE = (1<<16), + /* */ +#if defined(SOMC_TOUCH_BRINGUP) + SIW_GET_ALL = 0xFFFF & ~SIW_GET_PRODUCT, //Disable PID flag +#else + SIW_GET_ALL = 0xFFFF, +#endif + +}; + +static int siw_hal_get_cmd_version(struct device *dev, char *buf, int flag) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_fw_info *fw = &chip->fw; + int offset = 0; +// int ret = 0; + + if (!flag) + return 0; + + if (flag & SIW_GET_CHIP_NAME) { + offset += siw_snprintf(buf, offset, + "chip : %s\n", + touch_chip_name(ts)); + } + + if (flag & (SIW_GET_VERSION|SIW_GET_VER_SIMPLE)) { + char *ver_tag = (flag & SIW_GET_VER_SIMPLE) ? "" : "version : "; + if (fw->version_ext) { + offset += siw_snprintf(buf, offset, + "%s%08X(%u.%02u)\n", + ver_tag, + fw->version_ext, + fw->v.version.major, fw->v.version.minor); + } else { + offset += siw_snprintf(buf, offset, + "%sv%u.%02u\n", + ver_tag, + fw->v.version.major, fw->v.version.minor); + } + } + + if (flag & SIW_GET_REVISION) { + if (chip->fw.revision == 0xFF) { + offset += siw_snprintf(buf, offset, + "revision : Flash Erased(0xFF)\n"); + } else { + offset += siw_snprintf(buf, offset, + "revision : %d\n", fw->revision); + } + } + + if (flag & SIW_GET_PRODUCT) { + offset += siw_snprintf(buf, offset, + "product id : %s\n", fw->product_id); + } + + if (flag & SIW_GET_OPT1) { + if (chip->opt.f_info_more) { + offset += siw_snprintf(buf, offset, + "fpc : %d\n", fw->fpc); + offset += siw_snprintf(buf, offset, + "wfr : %d\n", fw->wfr); + offset += siw_snprintf(buf, offset, + "cg : %d\n", fw->cg); + offset += siw_snprintf(buf, offset, + "lot : %d\n", fw->lot); + offset += siw_snprintf(buf, offset, + "serial : 0x%X\n", fw->sn); + offset += siw_snprintf(buf, offset, + "date : %04d.%02d.%02d %02d:%02d:%02d Site%d\n", + fw->date & 0xFFFF, + ((fw->date>>16) & 0xFF), ((fw->date>>24) & 0xFF), + fw->time & 0xFF, + ((fw->time>>8) & 0xFF), ((fw->time>>16) & 0xFF), + ((fw->time>>24) & 0xFF)); + } + } + + return offset; +} + +static int siw_hal_set(struct device *dev, u32 cmd, void *buf) +{ + return 0; +} + +static int siw_hal_get(struct device *dev, u32 cmd, void *buf) +{ + int ret = 0; + + t_dev_dbg_base(dev, "cmd %d\n", cmd); + + switch (cmd) { + case CMD_VERSION: + ret = siw_hal_get_cmd_version(dev, (char *)buf, SIW_GET_ALL); + break; + + case CMD_ATCMD_VERSION: + ret = siw_hal_get_cmd_version(dev, (char *)buf, SIW_GET_VER_SIMPLE); + break; + + default: + break; + } + + return ret; +} + +u32 t_mon_dbg_mask = 0; + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/mon_dbg_mask + * (2) insmod {Siw Touch Module Name}.ko mon_dbg_mask= + */ +module_param_named(mon_dbg_mask, t_mon_dbg_mask, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +#if 1 +#define t_mon_info(_dev, fmt, args...) __t_dev_info(_dev, "mon: " fmt, ##args) +#define t_mon_warn(_dev, fmt, args...) __t_dev_warn(_dev, "mon: " fmt, ##args) +#else +#define t_mon_info(_dev, fmt, args...) __t_dev_none() +#define t_mon_warn(_dev, fmt, args...) __t_dev_none() +#endif + +#define t_mon_err(_dev, fmt, args...) __t_dev_err(_dev, "mon: " fmt, ##args) + +#define t_mon_dbg(condition, _dev, fmt, args...) \ + do { \ + if (unlikely(t_mon_dbg_mask & (condition))) \ + __t_dev_info(_dev, "mon: " fmt, ##args); \ + } while (0) + +#define t_mon_dbg_base(_dev, fmt, args...) \ + t_mon_dbg(DBG_BASE, _dev, fmt, ##args) + +#define t_mon_dbg_trace(_dev, fmt, args...) \ + t_mon_dbg(DBG_TRACE, _dev, fmt, ##args) + +static int siw_hal_mon_handler_chk_frame(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + u32 frame_addr = 0; + u32 frame_s = 0; + u32 frame_e = 0; + u32 delay = 0; + int cnt = 0; + int i; + int ret = 0; + + switch (chip->opt.t_chk_frame) { + case 1: + frame_addr = 0x24F; + cnt = 20; + delay = 1; + break; + default: + return 0; + } + + ret = siw_hal_read_value(dev, + frame_addr, + (void *)&frame_s); + if (ret < 0){ + goto out; + } + + for (i = 0; i < cnt; i++) { + touch_msleep(delay); + + ret = siw_hal_read_value(dev, + frame_addr, + (void *)&frame_e); + if (ret < 0){ + goto out; + } + + if (frame_e != frame_s) { + t_mon_dbg_trace(dev, "frame ok: %d(%d), %d x %d ms\n", + frame_e, frame_s, i, delay); + return 0; + } + } + + t_mon_err(dev, "frame not changed: %d, %d x %d ms\n", + frame_s, cnt, delay); + + ret = -ERESTART; + +out: + return ret; +} + +static int siw_hal_mon_handler_chk_status(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 dbg_mask = (t_mon_dbg_mask & DBG_TRACE) ? 0 : 0x80; + u32 ic_status; + u32 status; + int ret = 0; + + ret = siw_hal_reg_read(dev, + reg->tc_ic_status, + (void *)&ic_status, sizeof(ic_status)); + if (ret < 0){ + goto out; + } + + ret = siw_hal_reg_read(dev, + reg->tc_status, + (void *)&status, sizeof(status)); + if (ret < 0){ + goto out; + } + + status |= 0x8000; //Valid IRQ + ret = siw_hal_do_check_status(dev, status, ic_status, dbg_mask); + if (ret < 0) { + if (ret == -ERESTART) { + goto out; + } + ret = 0; + } + +out: + return ret; +} + +static int siw_hal_mon_handler_chk_id(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_fw_info *fw = &chip->fw; + u32 chip_id; + int ret = 0; + + ret = siw_hal_read_value(dev, + reg->spr_chip_id, + &chip_id); + if (ret < 0) { + goto out; + } + + if (fw->chip_id_raw != chip_id) { + ret = -ERESTART; + goto out; + } + +out: + return ret; +} + +struct siw_mon_hanlder_op { + unsigned int step; + unsigned int delay; //msec + unsigned int retry; + char *name; + int (*func)(struct device *dev); +}; + +#define SIW_MON_HANDLER_OP_SET(_step, _delay, _retry, _name, _func) \ + [_step] = { \ + .step = _step, \ + .delay = _delay, \ + .retry = _retry, \ + .name = _name, \ + .func = _func, \ + } + +static const struct siw_mon_hanlder_op siw_mon_hanlder_ops[] = { + SIW_MON_HANDLER_OP_SET(0, 10, 3, "id", siw_hal_mon_handler_chk_id), + SIW_MON_HANDLER_OP_SET(1, 10, 3, "status", siw_hal_mon_handler_chk_status), + SIW_MON_HANDLER_OP_SET(2, 10, 3, "frame", siw_hal_mon_handler_chk_frame), +}; + +static int siw_hal_mon_hanlder_do_op(struct device *dev, + const struct siw_mon_hanlder_op *op, char *p_name) +{ + unsigned int delay = op->delay; + unsigned int retry = op->retry; + unsigned int i; + int ret = 0; + + for (i = 0; i < retry; i++) { + ret = op->func(dev); + if (ret >= 0) { + t_mon_dbg_trace(dev, + "%s : [%d] %s check done\n", + p_name, op->step, op->name); + break; + } + + t_mon_err(dev, + "%s : [%d] %s check failed(%d), %d (%d)\n", + p_name, op->step, op->name, i, ret, op->delay); + + touch_msleep(delay); + } + + return ret; +} + +static int siw_hal_mon_handler_skip(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + if (atomic_read(&ts->state.sleep) != IC_NORMAL) { + return 1; + } + + if (atomic_read(&ts->state.fb) != FB_RESUME) { + return 1; + } + + if (atomic_read(&ts->state.pm) != DEV_PM_RESUME) { + return 1; + } + + if (atomic_read(&ts->state.core) != CORE_NORMAL) { + return 1; + } + + if (atomic_read(&chip->init) != IC_INIT_DONE) { + return 1; + } + + if (chip->lcd_mode != LCD_MODE_U3) { + return 1; + } + + if (chip->driving_mode != LCD_MODE_U3) { + return 1; + } + + return 0; +} + +static void siw_hal_mon_handler_self_reset(struct device *dev, char *title) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + const struct siw_mon_hanlder_op *ops = siw_mon_hanlder_ops; + unsigned int ops_num = ARRAY_SIZE(siw_mon_hanlder_ops); + char name[32]; + int step; + int ret = 0; + + if (siw_hal_mon_handler_skip(dev)) { + return; + } + + mutex_lock(&ts->lock); + + snprintf(name, sizeof(name), "%s self-reset", title); + + for (step = 0 ; stepstep >= ops_num) || + (ops->name == NULL) || + (ops->func == NULL)) { + break; + } + + ret = siw_hal_mon_hanlder_do_op(dev, ops, name); + if (ret < 0){ + break; + } + } + + if (ret < 0) { + t_mon_err(dev, + "%s : recovery begins(hw reset)\n", + name); + + siw_hal_reset_ctrl(dev, HW_RESET_ASYNC); + } else { + t_mon_dbg_trace(dev, + "%s : check ok\n", + name); + } + + mutex_unlock(&ts->lock); +} + +static int siw_hal_mon_handler(struct device *dev, u32 opt) +{ + char *name; + + name = (opt & MON_FLAG_RST_ONLY) ? "reset cond" : "mon handler"; + + t_mon_dbg_trace(dev, "%s begins\n", name); + + siw_hal_mon_handler_self_reset(dev, name); + + if (opt & MON_FLAG_RST_ONLY) { + goto out; + } + + /* + * For other controls + */ + +out: + t_mon_dbg_trace(dev, "%s ends\n", name); + + return 0; +} + +static int siw_hal_early_probe(struct device *dev) +{ + return 0; +} + +enum { + IC_TEST_ADDR_NOT_VALID = 0x8000, +}; + +int siw_hal_ic_test_unit(struct device *dev, u32 data) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 data_rd; + int ret; + + if (!reg->spr_chip_test) { + t_dev_warn(dev, "ic test addr not valid, skip\n"); + return IC_TEST_ADDR_NOT_VALID; + } + + ret = siw_hal_write_value(dev, + reg->spr_chip_test, + data); + if (ret < 0) { + t_dev_err(dev, "ic test wr err, %08Xh, %d\n", data, ret); + goto out; + } + + ret = siw_hal_read_value(dev, + reg->spr_chip_test, + &data_rd); + if (ret < 0) { + t_dev_err(dev, "ic test rd err: %08Xh, %d\n", data, ret); + goto out; + } + + if (data != data_rd) { + t_dev_err(dev, "ic test cmp err, %08Xh, %08Xh\n", data, data_rd); + ret = -EFAULT; + goto out; + } + +out: + return ret; +} + +static int siw_hal_ic_test(struct device *dev) +{ + u32 data[] = { + 0x5A5A5A5A, + 0xA5A5A5A5, + 0xF0F0F0F0, + 0x0F0F0F0F, + 0xFF00FF00, + 0x00FF00FF, + 0xFFFF0000, + 0x0000FFFF, + 0xFFFFFFFF, + 0x00000000, + }; + int i; + int ret = 0; + + for (i = 0; i < ARRAY_SIZE(data); i++) { + ret = siw_hal_ic_test_unit(dev, data[i]); + if ((ret == IC_TEST_ADDR_NOT_VALID) || (ret < 0)) { + break; + } + } + + if (ret >= 0) { + t_dev_dbg_base(dev, "ic bus r/w test done\n"); + } + + return ret; +} + +static int siw_hal_get_product_id(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_fw_info *fw = &chip->fw; + u8 product_id[8+4] = { 0, }; //dummy 4-byte + int ret; + + ret = siw_hal_reg_read(dev, + reg->tc_product_id1, + (void *)product_id, 8); + if (ret < 0) { + t_dev_err(dev, + "failed to read product id, %d\n", + ret); + return ret; + } + + siw_hal_fw_set_prod_id(fw, (u8 *)product_id, 8); + + t_dev_dbg_base(dev, "product id - %s\n", fw->product_id); + + return 0; +} + +static int siw_hal_chipset_check(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int ret = 0; + + touch_msleep(ts->caps.hw_reset_delay); + + ret = siw_hal_init_quirk(dev); + if (ret < 0) { + goto out; + } + + ret = siw_hal_ic_test(dev); + if (ret < 0) { + goto out; + } + + ret = siw_hal_chk_boot(dev); + if (ret < 0) { + goto out; + } + if (ret) { + t_dev_err(dev, "Boot fail detected\n"); + return 0; + } + + /* + * Preceding process + * : get reference value to cope with product variation + */ + ret = siw_hal_get_product_id(dev); + if (ret < 0) { + goto out; + } + +out: + return ret; +} + +static int siw_hal_chipset_option(struct siw_touch_chip *chip) +{ + struct device *dev = chip->dev; + struct siw_touch_chip_opt *opt = &chip->opt; + struct siw_ts *ts = chip->ts; + + chip->drv_reset_low = 1; + chip->drv_delay = 20; + chip->drv_opt_delay = 0; + + switch (touch_chip_type(ts)) { + case CHIP_LG4894: + chip->drv_delay = 60; + + opt->t_chk_tci_debug = 1; + break; + + case CHIP_LG4895: + chip->drv_opt_delay = 200; + + opt->f_u2_blank_chg = 1; + opt->t_clock = 1; + opt->t_sw_rst = 1; + break; + + case CHIP_LG4946: + opt->f_info_more = 1; + opt->f_glove_en = 1; + opt->t_chk_mode = 1; + opt->t_clock = 1; + opt->t_sw_rst = 1; + break; + + case CHIP_SW1828 : + chip->drv_reset_low = 10; //following LGD CAS recommendation + + opt->f_ver_ext = 1; + opt->f_dbg_report = 1; + opt->t_chk_frame = 1; + break; + + case CHIP_SW49105: + opt->f_attn_opt = 1; + opt->t_sw_rst = 2; + opt->t_chk_sys_error = 1; + opt->t_chk_sys_fault = 1; + break; + + case CHIP_SW49106: + opt->f_attn_opt = 1; + opt->t_boot_mode = 1; + opt->t_sts_mask = 1; + opt->t_sw_rst = 2; + opt->t_chk_tci_debug = 1; + break; + + case CHIP_SW49406: + opt->f_attn_opt = 1; + break; + + case CHIP_SW49407: + opt->f_attn_opt = 1; + opt->f_glove_en = 1; + opt->f_grab_en = 1; + opt->f_dbg_report = 1; + opt->f_u2_blank_chg = 1; + opt->t_sw_rst = 2; + opt->t_clock = 2; + opt->t_chk_mipi = 1; + opt->t_chk_sys_error = 1; + opt->t_chk_sys_fault = 1; + opt->t_chk_fault = 1; + break; + + case CHIP_SW49408: + opt->f_attn_opt = 1; + opt->t_chk_mode = 1; + opt->t_sw_rst = 2; + opt->t_clock = 2; + opt->t_chk_sys_error = 2; + opt->t_chk_sys_fault = 1; + break; + + case CHIP_SW49409: + opt->t_sw_rst = 2; + opt->t_clock = 2; + opt->t_chk_sys_error = 3; + opt->t_chk_sys_fault = 1; + break; + + case CHIP_SW49501: + opt->f_glove_en = 1; + opt->f_grab_en = 1; + opt->f_dbg_report = 1; + opt->t_boot_mode = 2; + opt->t_sts_mask = 2; + opt->t_sw_rst = 2; + opt->t_clock = 2; + opt->t_chk_tci_debug = 1; + break; + } + + t_dev_info(dev, "[opt summary]\n"); + t_dev_info(dev, " f_info_more : %d\n", opt->f_info_more); + t_dev_info(dev, " f_ver_ext : %d\n", opt->f_ver_ext); + t_dev_info(dev, " f_attn_opt : %d\n", opt->f_attn_opt); + t_dev_info(dev, " f_glove_en : %d\n", opt->f_glove_en); + t_dev_info(dev, " f_grab_en : %d\n", opt->f_grab_en); + t_dev_info(dev, " f_dbg_report : %d\n", opt->f_dbg_report); + t_dev_info(dev, " f_u2_blank_chg : %d\n", opt->f_u2_blank_chg); + t_dev_info(dev, " t_boot_mode : %d\n", opt->t_boot_mode); + t_dev_info(dev, " t_sts_mask : %d\n", opt->t_sts_mask); + t_dev_info(dev, " t_chk_mode : %d\n", opt->t_chk_mode); + t_dev_info(dev, " t_sw_rst : %d\n", opt->t_sw_rst); + t_dev_info(dev, " t_clock : %d\n", opt->t_clock); + t_dev_info(dev, " t_chk_mipi : %d\n", opt->t_chk_mipi); + t_dev_info(dev, " t_chk_frame : %d\n", opt->t_chk_frame); + t_dev_info(dev, " t_chk_tci_debug : %d\n", opt->t_chk_tci_debug); + t_dev_info(dev, " t_chk_sys_error : %d\n", opt->t_chk_sys_error); + t_dev_info(dev, " t_chk_sys_fault : %d\n", opt->t_chk_sys_fault); + t_dev_info(dev, " t_chk_fault : %d\n", opt->t_chk_fault); + + t_dev_info(dev, " drv_reset_low : %d ms\n", chip->drv_reset_low); + t_dev_info(dev, " drv_delay : %d ms\n", chip->drv_delay); + t_dev_info(dev, " drv_opt_delay : %d ms\n", chip->drv_opt_delay); + + return 0; +} + +static void __siw_hal_do_remove(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + +#if defined(__SIW_SUPPORT_PM_QOS) + pm_qos_remove_request(&chip->pm_qos_req); +#endif + + siw_hal_power_free(dev); + siw_hal_free_gpios(dev); + + siw_hal_free_works(chip); + siw_hal_free_locks(chip); + + touch_set_dev_data(ts, NULL); + + touch_kfree(dev, chip); +} + +static int siw_hal_probe(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + struct siw_touch_chip *chip = NULL; + int ret = 0; + + chip = touch_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) { + t_dev_err(dev, "failed to allocate %s data\n", + touch_chip_name(ts)); + return -ENOMEM; + } + + chip->dev = dev; + chip->reg = siw_ops_reg(ts); + chip->ts = ts; + + touch_set_dev_data(ts, chip); + + siw_hal_chipset_option(chip); + + siw_hal_init_locks(chip); + siw_hal_init_works(chip); + + siw_hal_init_gpios(dev); + siw_hal_power_init(dev); + + if (siw_touch_get_boot_mode() == SIW_TOUCH_CHARGER_MODE) { + if (touch_mode_allowed(ts, LCD_MODE_U3_PARTIAL)) { + /* U3P driving and maintain 100ms before Deep sleep */ + ret = siw_hal_tc_driving(dev, LCD_MODE_U3_PARTIAL); + if (ret < 0) { + __siw_hal_do_remove(dev); + siwmon_submit_ops_step_chip_wh_name(dev, + "%s probe failed(charger mode)", + touch_chip_name(ts), 0); + return ret; + } + touch_msleep(80); + } + + /* Deep Sleep */ + siw_hal_deep_sleep(dev); + + siwmon_submit_ops_step_chip_wh_name(dev, + "%s probe done(charger mode)", + touch_chip_name(ts), 0); + return 0; + } + + siw_hal_get_tci_info(dev); + siw_hal_get_swipe_info(dev); + +#if defined(__SIW_SUPPORT_PM_QOS) + pm_qos_add_request(&chip->pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); +#endif + + ret = siw_hal_chipset_check(dev); + if (ret < 0) { + __siw_hal_do_remove(dev); + goto out; + } + + chip->lcd_mode = LCD_MODE_U3; + chip->tci_debug_type = 1; + + t_dev_dbg_base(dev, "%s probe done\n", + touch_chip_name(ts)); + + siwmon_submit_ops_step_chip_wh_name(dev, "%s probe done", + touch_chip_name(ts), 0); + + return 0; + +out: + t_dev_dbg_base(dev, "%s probe failed\n", + touch_chip_name(ts)); + + siwmon_submit_ops_step_chip_wh_name(dev, "%s probe failed", + touch_chip_name(ts), 0); + + return ret; +} + +static int siw_hal_remove(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + __siw_hal_do_remove(dev); + + t_dev_dbg_base(dev, "%s remove done\n", + touch_chip_name(ts)); + + return 0; +} + +#if defined(__SIW_CONFIG_SYSTEM_PM) +static int siw_hal_do_suspend(struct device *dev) +{ +// struct siw_ts *ts = to_touch_core(dev); + + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + + siw_hal_power(dev, POWER_OFF); + + return 0; +} + +static int siw_hal_do_resume(struct device *dev) +{ +#ifndef SOMC_TOUCH_BRINGUP + struct siw_ts *ts = to_touch_core(dev); +#endif + + siw_hal_power(dev, POWER_ON); +#ifndef SOMC_TOUCH_BRINGUP + siw_hal_trigger_gpio_reset(dev); //Double check for reset + + touch_msleep(ts->caps.hw_reset_delay); +#endif + return 0; +} +#else /* __SIW_CONFIG_SYSTEM_PM */ +static int siw_hal_do_suspend(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int mfst_mode = 0; + int ret = 0; + + if (siw_touch_get_boot_mode() == SIW_TOUCH_CHARGER_MODE) + return -EPERM; + + mfst_mode = siw_touch_boot_mode_check(dev); + if ((mfst_mode >= MINIOS_MFTS_FOLDER) && !ts->role.mfts_lpwg) { + t_dev_info(dev, "touch_suspend - MFTS\n"); + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + siw_hal_power(dev, POWER_OFF); + return -EPERM; + } + + if ((chip->lcd_mode == LCD_MODE_U2) && + siw_hal_watch_is_disp_waton(dev) && + siw_hal_watch_is_rtc_run(dev)) { + siw_hal_watch_get_curr_time(dev, NULL, NULL); + } + + if (atomic_read(&chip->init) == IC_INIT_DONE) + siw_hal_lpwg_mode(dev); + else /* need init */ + ret = 1; + + return ret; +} + +static int siw_hal_do_resume(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int mfst_mode = 0; + int ret = 0; + + mfst_mode = siw_touch_boot_mode_check(dev); + if ((mfst_mode >= MINIOS_MFTS_FOLDER) && !ts->role.mfts_lpwg) { + siw_hal_power(dev, POWER_ON); + touch_msleep(ts->caps.hw_reset_delay); + ret = siw_hal_ic_info(dev); + if (ret < 0) { + t_dev_err(dev, "ic info err, %d\n", ret); + } + if (siw_hal_upgrade(dev) == 0) { + siw_hal_power(dev, POWER_OFF); + siw_hal_power(dev, POWER_ON); + touch_msleep(ts->caps.hw_reset_delay); + } + } + + if (siw_touch_get_boot_mode() == SIW_TOUCH_CHARGER_MODE) { + if (touch_mode_allowed(ts, LCD_MODE_U3_PARTIAL)) { + /* U3P driving and maintain 100ms at Resume */ + ret = siw_hal_tc_driving(dev, LCD_MODE_U3_PARTIAL); + if (ret < 0) { + return ret; + } + touch_msleep(80); + } + + siw_hal_deep_sleep(dev); + return -EPERM; + } + + return 0; +} +#endif /* __SIW_CONFIG_SYSTEM_PM */ + +static int siw_hal_suspend(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + int ret = 0; + + if (atomic_read(&ts->state.core) != CORE_NORMAL) { + return -EPERM; + } + + ret = siw_hal_do_suspend(dev); + if (ret < 0) { + return ret; + } + + t_dev_dbg_pm(dev, "%s suspend done\n", + touch_chip_name(ts)); + + return ret; +} + +static int siw_hal_resume(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + int ret = 0; + + if (atomic_read(&ts->state.core) != CORE_NORMAL) { + return -EPERM; + } + + ret = siw_hal_do_resume(dev); + if (ret < 0) { + return ret; + } + + t_dev_dbg_pm(dev, "%s resume done\n", + touch_chip_name(ts)); + + return ret; +} + +static const struct siw_hal_reg siw_touch_default_reg = { + .spr_chip_test = SPR_CHIP_TEST, + .spr_chip_id = SPR_CHIP_ID, + .spr_rst_ctl = SPR_RST_CTL, + .spr_boot_ctl = SPR_BOOT_CTL, + .spr_sram_ctl = SPR_SRAM_CTL, + .spr_boot_status = SPR_BOOT_STS, + .spr_subdisp_status = SPR_SUBDISP_STS, + .spr_code_offset = SPR_CODE_OFFSET, + .tc_ic_status = TC_IC_STATUS, + .tc_status = TC_STS, + .tc_version = TC_VERSION, + .tc_product_id1 = TC_PRODUCT_ID1, + .tc_product_id2 = TC_PRODUCT_ID2, + .tc_version_ext = TC_VERSION_EXT, + .info_fpc_type = INFO_FPC_TYPE, + .info_wfr_type = INFO_WFR_TYPE, + .info_chip_version = INFO_CHIP_VERSION, + .info_cg_type = INFO_CG_TYPE, + .info_lot_num = INFO_LOT_NUM, + .info_serial_num = INFO_SERIAL_NUM, + .info_date = INFO_DATE, + .info_time = INFO_TIME, + .cmd_abt_loc_x_start_read = CMD_ABT_LOC_X_START_READ, + .cmd_abt_loc_x_end_read = CMD_ABT_LOC_X_END_READ, + .cmd_abt_loc_y_start_read = CMD_ABT_LOC_Y_START_READ, + .cmd_abt_loc_y_end_read = CMD_ABT_LOC_Y_END_READ, + .code_access_addr = CODE_ACCESS_ADDR, + .data_i2cbase_addr = DATA_I2CBASE_ADDR, + .prd_tcm_base_addr = PRD_TCM_BASE_ADDR, + .tc_device_ctl = TC_DEVICE_CTL, + .tc_interrupt_ctl = TC_INTERRUPT_CTL, + .tc_interrupt_status = TC_INTERRUPT_STS, + .tc_drive_ctl = TC_DRIVE_CTL, + .tci_enable_w = TCI_ENABLE_W, + .tap_count_w = TAP_COUNT_W, + .min_intertap_w = MIN_INTERTAP_W, + .max_intertap_w = MAX_INTERTAP_W, + .touch_slop_w = TOUCH_SLOP_W, + .tap_distance_w = TAP_DISTANCE_W, + .int_delay_w = INT_DELAY_W, + .act_area_x1_w = ACT_AREA_X1_W, + .act_area_y1_w = ACT_AREA_Y1_W, + .act_area_x2_w = ACT_AREA_X2_W, + .act_area_y2_w = ACT_AREA_Y2_W, + .tci_debug_fail_ctrl = TCI_DEBUG_FAIL_CTRL, + .tci_debug_fail_buffer = TCI_DEBUG_FAIL_BUFFER, + .tci_debug_fail_status = TCI_DEBUG_FAIL_STATUS, + .swipe_enable_w = SWIPE_ENABLE_W, + .swipe_dist_w = SWIPE_DIST_W, + .swipe_ratio_thr_w = SWIPE_RATIO_THR_W, + .swipe_ratio_period_w = SWIPE_RATIO_PERIOD_W, + .swipe_ratio_dist_w = SWIPE_RATIO_DIST_W, + .swipe_time_min_w = SWIPE_TIME_MIN_W, + .swipe_time_max_w = SWIPE_TIME_MAX_W, + .swipe_act_area_x1_w = SWIPE_ACT_AREA_X1_W, + .swipe_act_area_y1_w = SWIPE_ACT_AREA_Y1_W, + .swipe_act_area_x2_w = SWIPE_ACT_AREA_X2_W, + .swipe_act_area_y2_w = SWIPE_ACT_AREA_Y2_W, + .swipe_fail_debug_r = SWIPE_FAIL_DEBUG_R, + .swipe_debug_r = SWIPE_DEBUG_R, + .cmd_raw_data_report_mode_read = CMD_RAW_DATA_REPORT_MODE_READ, + .cmd_raw_data_compress_write = CMD_RAW_DATA_COMPRESS_WRITE, + .cmd_raw_data_report_mode_write = CMD_RAW_DATA_REPORT_MODE_WRITE, + .spr_charger_status = SPR_CHARGER_STS, + .ime_state = IME_STATE, + .max_delta = MAX_DELTA, + .touch_max_w = TOUCH_MAX_W, + .touch_max_r = TOUCH_MAX_R, + .call_state = CALL_STATE, + .tc_tsp_test_ctl = TC_TSP_TEST_CTL, + .tc_tsp_test_status = TC_TSP_TEST_STS, + .tc_tsp_test_pf_result = TC_TSP_TEST_PF_RESULT, + .tc_tsp_test_off_info = TC_TSP_TEST_OFF_INFO, + .tc_flash_dn_status = TC_FLASH_DN_STS, + .tc_confdn_base_addr = TC_CONFDN_BASE_ADDR, + .tc_flash_dn_ctl = TC_FLASH_DN_CTL, + .raw_data_ctl_read = RAW_DATA_CTL_READ, + .raw_data_ctl_write = RAW_DATA_CTL_WRITE, + .serial_data_offset = SERIAL_DATA_OFFSET, + /* __SIW_SUPPORT_WATCH */ + .ext_watch_font_offset = EXT_WATCH_FONT_OFFSET, + .ext_watch_font_addr = EXT_WATCH_FONT_ADDR, + .ext_watch_font_dn_addr_info = EXT_WATCH_FONT_DN_ADDR_INFO, + .ext_watch_font_crc = EXT_WATCH_FONT_CRC, + .ext_watch_dcs_ctrl = EXT_WATCH_DCS_CTRL, + .ext_watch_mem_ctrl = EXT_WATCH_MEM_CTRL, + .ext_watch_ctrl = EXT_WATCH_CTRL, + .ext_watch_area_x = EXT_WATCH_AREA_X, + .ext_watch_area_y = EXT_WATCH_AREA_Y, + .ext_watch_blink_area = EXT_WATCH_BLINK_AREA, + .ext_watch_lut = EXT_WATCH_LUT, + .ext_watch_display_on = EXT_WATCH_DISPLAY_ON, + .ext_watch_display_status = EXT_WATCH_DISPLAY_STATUS, + .ext_watch_rtc_sct = EXT_WATCH_RTC_SCT, + .ext_watch_rtc_sctcnt = EXT_WATCH_RTC_SCTCNT, + .ext_watch_rtc_capture = EXT_WATCH_RTC_CAPTURE, + .ext_watch_rtc_ctst = EXT_WATCH_RTC_CTST, + .ext_watch_rtc_ecnt = EXT_WATCH_RTC_ECNT, + .ext_watch_hour_disp = EXT_WATCH_HOUR_DISP, + .ext_watch_blink_prd = EXT_WATCH_BLINK_PRD, + .ext_watch_rtc_run = EXT_WATCH_RTC_RUN, + .ext_watch_position = EXT_WATCH_POSITION, + .ext_watch_position_r = EXT_WATCH_POSITION_R, + .ext_watch_state = EXT_WATCH_STATE, + .sys_dispmode_status = SYS_DISPMODE_STATUS, + /* __SIW_SUPPORT_PRD */ + .prd_serial_tcm_offset = PRD_SERIAL_TCM_OFFSET, + .prd_tc_mem_sel = PRD_TC_MEM_SEL, + .prd_tc_test_mode_ctl = PRD_TC_TEST_MODE_CTL, + .prd_m1_m2_raw_offset = PRD_M1_M2_RAW_OFFSET, + .prd_tune_result_offset = PRD_TUNE_RESULT_OFFSET, + .prd_open3_short_offset = PRD_OPEN3_SHORT_OFFSET, + .prd_ic_ait_start_reg = PRD_IC_AIT_START_REG, + .prd_ic_ait_data_readystatus= PRD_IC_AIT_DATA_READYSTATUS, + /* */ + .glove_en = GLOVE_EN, + .grab_en = GRAB_EN, +}; + +enum { + HAL_MON_INTERVAL_DEFAULT = 5, +}; + +static const struct siw_touch_operations siw_touch_default_ops = { + /* Register Map */ + .reg = (void *)&siw_touch_default_reg, + /* Functions */ + .early_probe = siw_hal_early_probe, + .probe = siw_hal_probe, + .remove = siw_hal_remove, + .suspend = siw_hal_suspend, + .resume = siw_hal_resume, + .init = siw_hal_init, + .reset = siw_hal_reset_ctrl, + .ic_info = siw_hal_ic_info, + .tc_con = siw_hal_tc_con, + .tc_driving = siw_hal_tc_driving, + .chk_status = siw_hal_check_status, + .irq_handler = siw_hal_irq_handler, + .irq_abs = siw_hal_irq_abs, + .irq_lpwg = siw_hal_irq_lpwg, + .power = siw_hal_power, + .upgrade = siw_hal_upgrade, + .lpwg = siw_hal_lpwg, + .asc = siw_hal_asc, + .notify = siw_hal_notify, + .set = siw_hal_set, + .get = siw_hal_get, + /* */ + .sysfs = siw_hal_sysfs, + /* */ + .mon_handler = siw_hal_mon_handler, + .mon_interval = HAL_MON_INTERVAL_DEFAULT, + /* */ + .abt_sysfs = siw_hal_abt_sysfs, + .prd_sysfs = siw_hal_prd_sysfs, + .watch_sysfs = siw_hal_watch_sysfs, +}; + +struct siw_touch_operations *siw_hal_get_default_ops(int opt) +{ + return (struct siw_touch_operations *)&siw_touch_default_ops; +} + + diff --git a/drivers/input/touchscreen/siw/siw_touch_hal.h b/drivers/input/touchscreen/siw/siw_touch_hal.h new file mode 100644 index 000000000000..04cb187fb812 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_hal.h @@ -0,0 +1,631 @@ +/* + * SiW touch hal driver + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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_HAL_H +#define __SIW_TOUCH_HAL_H + +#include "siw_touch_cfg.h" + +#if defined(__SIW_SUPPORT_PM_QOS) +#include +#endif + +#include "siw_touch_hal_reg.h" + +enum { + NON_FAULT_INT = -1, + NON_FAULT_U32 = ~0, +}; + +/* report packet - type 1 */ +struct siw_hal_touch_data_type_1 { + u32 track_id:5; + u32 tool_type:3; + u32 angle:8; + u32 event:2; + u32 x:14; + + u32 y:14; + u32 pressure:8; + u32 reserve1:10; + + u32 reserve2:4; + u32 width_major:14; + u32 width_minor:14; +} __packed; + +/* report packet */ +struct siw_hal_touch_data { + u8 tool_type:4; + u8 event:4; + u8 track_id; + u16 x; + u16 y; + u8 pressure; + u8 angle; + u16 width_major; + u16 width_minor; +} __packed; + +struct siw_hal_touch_info { + u32 ic_status; + u32 device_status; + // + u32 wakeup_type:8; + u32 touch_cnt:5; + u32 button_cnt:3; + u32 palm_bit:16; + // + struct siw_hal_touch_data data[MAX_FINGER]; +} __packed; + +#define PALM_ID 15 + +enum { + TC_DRIVE_CTL_START = (0x1<<0), + TC_DRIVE_CTL_STOP = (0x1<<1), + TC_DRIVE_CTL_DISP_U0 = (0x0<<7), + TC_DRIVE_CTL_DISP_U2 = (0x2<<7), + TC_DRIVE_CTL_DISP_U3 = (0x3<<7), + /* */ + TC_DRIVE_CTL_PARTIAL = (0x1<<9), + TC_DRIVE_CTL_QCOVER = (0x1<<10), + /* */ + TC_DRIVE_CTL_MODE_VB = (0x0<<2), + TC_DRIVE_CTL_MODE_6LHB = (0x1<<2), + TC_DRIVE_CTL_MODE_MIK = (0x1<<3), +}; + +#define CMD_DIS 0xAA +#define CMD_ENA 0xAB +#define CMD_CLK_ON 0x83 +#define CMD_CLK_OFF 0x82 +#define CMD_OSC_ON 0x81 +#define CMD_OSC_OFF 0x80 +#define CMD_RESET_LOW 0x84 +#define CMD_RESET_HIGH 0x85 + + +#define CONNECT_NONE (0x00) +#define CONNECT_USB (0x01) +#define CONNECT_DC (0x02) +#define CONNECT_OTG (0x03) +#define CONNECT_WIRELESS (0x04) + +enum { + SW_RESET = 0, + HW_RESET_ASYNC, + HW_RESET_SYNC, + // + HW_RESET_COND = 0x5A, +}; + +enum { + TOUCHSTS_IDLE = 0, + TOUCHSTS_DOWN, + TOUCHSTS_MOVE, + TOUCHSTS_UP, +}; + +enum { + LCD_MODE_U0 = 0, + LCD_MODE_U2_UNBLANK, + LCD_MODE_U2, + LCD_MODE_U3, + LCD_MODE_U3_PARTIAL, + LCD_MODE_U3_QUICKCOVER, + LCD_MODE_STOP, + LCD_MODE_MAX, +}; + +#define SIW_LCD_DRIVING_MODE_STR(_mode) \ + [LCD_MODE_##_mode] = #_mode + +static const char *__siw_lcd_driving_mode_strs[] = { + SIW_LCD_DRIVING_MODE_STR(U0), + SIW_LCD_DRIVING_MODE_STR(U2_UNBLANK), + SIW_LCD_DRIVING_MODE_STR(U2), + SIW_LCD_DRIVING_MODE_STR(U3), + SIW_LCD_DRIVING_MODE_STR(U3_PARTIAL), + SIW_LCD_DRIVING_MODE_STR(U3_QUICKCOVER), + SIW_LCD_DRIVING_MODE_STR(STOP), +}; + +static inline const char *siw_lcd_driving_mode_str(int mode) +{ + return (mode < LCD_MODE_MAX) ? + __siw_lcd_driving_mode_strs[mode] : "(invalid)"; +} + +/* + * for pdata->mode_allowed + */ +enum { + LCD_MODE_BIT_U0 = BIT(LCD_MODE_U0), + LCD_MODE_BIT_U2_UNBLANK = BIT(LCD_MODE_U2_UNBLANK), + LCD_MODE_BIT_U2 = BIT(LCD_MODE_U2), + LCD_MODE_BIT_U3 = BIT(LCD_MODE_U3), + LCD_MODE_BIT_U3_PARTIAL = BIT(LCD_MODE_U3_PARTIAL), + LCD_MODE_BIT_U3_QUICKCOVER = BIT(LCD_MODE_U3_QUICKCOVER), + LCD_MODE_BIT_STOP = BIT(LCD_MODE_STOP), + LCD_MODE_BIT_MAX = BIT(LCD_MODE_MAX), +}; + + +enum { + SWIPE_R = 0, + SWIPE_L, +}; + +enum { + SWIPE_RIGHT_BIT = 1, + SWIPE_LEFT_BIT = (1<<16), +}; + +/* swipe */ +enum { + SWIPE_ENABLE_CTRL = 0, + SWIPE_DISABLE_CTRL, + SWIPE_DIST_CTRL, + SWIPE_RATIO_THR_CTRL, + SWIPE_RATIO_PERIOD_CTRL, + SWIPE_RATIO_DIST_CTRL, + SWIPE_TIME_MIN_CTRL, + SWIPE_TIME_MAX_CTRL, + SWIPE_AREA_CTRL, +}; + +enum { + IC_INIT_NEED = 0, + IC_INIT_DONE, +}; + +enum { + IC_BOOT_DONE = 0, + IC_BOOT_FAIL, +}; + +enum { + ATTN_ESD_EN = (1U<<0), + ATTN_WDOG_EN = (1U<<1), + ATTN_ABNORMAL_SPI_EN = (1U<<2), + /* */ + ABNORMAL_IC_DETECTION = (ATTN_ESD_EN | ATTN_WDOG_EN), +}; + +#define MAX_RW_SIZE_POW (10) +#define MAX_RW_SIZE __SIZE_POW(MAX_RW_SIZE_POW) //1K +#define FLASH_CONF_SIZE_POWER (10) +#define FLASH_CONF_SIZE (1 * __SIZE_POW(FLASH_CONF_SIZE_POWER)) // 1K + +#define FLASH_KEY_CODE_CMD 0xDFC1 +#define FLASH_KEY_CONF_CMD 0xE87B +#define FLASH_BOOTCHK_VALUE 0x0A0A0000 +#define FLASH_CODE_DNCHK_VALUE 0x42 +#define FLASH_CONF_DNCHK_VALUE 0x84 + + +enum { + WAFER_TYPE_MASK = (0x07), +}; + +struct siw_hal_tc_version_bin { + u8 major:4; + u8 build:4; + u8 minor; + u16 rsvd:12; + u16 ext:4; +}; + +struct siw_hal_tc_version { + u8 minor; + u8 major:4; + u8 build:4; + u8 chip; + u8 protocol:4; + u8 ext:4; +}; + +struct siw_hal_tc_version_ext_date { + u8 rr; + u8 dd; + u8 mm; + u8 yy; +}; + +struct siw_hal_fw_info { + u32 chip_id_raw; + u8 chip_id[8]; + /* */ + union { + struct siw_hal_tc_version version; + u32 version_raw; + } v; + u32 version_ext; + /* */ + u8 product_id[8+4]; + u8 image_version[4]; + u8 image_product_id[8+4]; + u8 revision; + u32 fpc; + u32 wfr; + u32 cg; + u32 lot; + u32 sn; + u32 date; + u32 time; + u32 conf_index; + /* __SIW_FW_TYPE_1 */ + u32 conf_idx_addr; + u32 conf_dn_addr; + u32 boot_code_addr; +}; + +static inline void siw_hal_fw_set_chip_id(struct siw_hal_fw_info *fw, u32 chip_id) +{ + fw->chip_id_raw = chip_id; + memset(fw->chip_id, 0, sizeof(fw->chip_id)); + fw->chip_id[0] = (chip_id>>24) & 0xFF; + fw->chip_id[1] = (chip_id>>16) & 0xFF; + fw->chip_id[2] = (chip_id>>8) & 0xFF; + fw->chip_id[3] = chip_id & 0xFF; +} + +enum { + VERSION_EXT_DATE = 1, +}; + +static inline int siw_hal_fw_chk_version_ext(u32 version_ext, u32 ext_flag) +{ + switch (ext_flag) { + case VERSION_EXT_DATE: + { + struct siw_hal_tc_version_ext_date ext_date; + + memcpy(&ext_date, &version_ext, sizeof(ext_date)); + + if (ext_date.yy && + ext_date.mm && + ext_date.dd) { + + if (ext_date.yy < 0x16) { + break; + } + + if (ext_date.mm > 0x12) { + break; + } + + if (ext_date.dd > 0x31) { + break; + } + + return 0; + } + } + break; + } + + return -EINVAL; +} + +static inline void siw_hal_fw_set_version(struct siw_hal_fw_info *fw, + u32 version, u32 version_ext) +{ + fw->v.version_raw = version; + fw->version_ext = (fw->v.version.ext) ? version_ext : 0; +} + +static inline void siw_hal_fw_set_revision(struct siw_hal_fw_info *fw, u32 revision) +{ + fw->revision = revision & 0xFF; +} + +static inline void siw_hal_fw_set_prod_id(struct siw_hal_fw_info *fw, u8 *prod, u32 size) +{ + int len = min((int)sizeof(fw->product_id), (int)size); + memset(fw->product_id, 0, sizeof(fw->product_id)); + memcpy(fw->product_id, prod, len); +} + + +struct siw_hal_asc_info { + u16 normal_s; + u16 acute_s; + u16 obtuse_s; +}; + +struct siw_hal_swipe_info { + u8 distance; + u8 ratio_thres; + u8 ratio_distance; + u8 ratio_period; + u16 min_time; + u16 max_time; + struct active_area area; +}; + +struct siw_hal_swipe_ctrl { + u32 mode; + struct siw_hal_swipe_info info[2]; /* down is 0, up 1 - LG4894 use up */ +}; + +enum { + CHIP_REPORT_NONE = 0, + CHIP_REPORT_TYPE_0, + CHIP_REPORT_TYPE_1, +}; + +enum { + CHIP_STATUS_NONE = 0, + CHIP_STATUS_TYPE_0, + CHIP_STATUS_TYPE_1, + CHIP_STATUS_TYPE_2, +}; + +enum { + STS_ID_NONE = 0, + STS_ID_VALID_DEV_CTL, + STS_ID_VALID_CODE_CRC, + STS_ID_VALID_CFG_CRC, + STS_ID_VALID_FONT_CRC, + STS_ID_ERROR_ABNORMAL, + STS_ID_ERROR_SYSTEM, + STS_ID_ERROR_MISMTACH, + STS_ID_VALID_IRQ_PIN, + STS_ID_VALID_IRQ_EN, + STS_ID_ERROR_MEM, + STS_ID_VALID_TC_DRV, + STS_ID_ERROR_DISP, +}; + +enum { + STS_POS_VALID_DEV_CTL = 5, + STS_POS_VALID_CODE_CRC = 6, + STS_POS_VALID_CFG_CRC = 7, + STS_POS_VALID_FONT_CRC = 8, + STS_POS_ERROR_ABNORMAL = 9, + STS_POS_ERROR_SYSTEM = 10, + STS_POS_ERROR_MISMTACH = 13, + STS_POS_VALID_IRQ_PIN = 15, + STS_POS_VALID_IRQ_EN = 20, + STS_POS_ERROR_MEM = 21, + STS_POS_VALID_TC_DRV = 22, + STS_POS_ERROR_DISP = 31, + /* */ + STS_POS_VALID_CODE_CRC_TYPE_0 = 22, +}; + +struct siw_hal_status_mask_bit { + u32 valid_dev_ctl; + u32 valid_code_crc; + u32 valid_cfg_crc; + u32 error_abnormal; + u32 error_system; + u32 error_mismtach; + u32 valid_irq_pin; + u32 valid_irq_en; + u32 error_mem; + u32 valid_tv_drv; + u32 error_disp; +}; + +struct siw_hal_status_filter { + int id; + u32 width; + u32 pos; + u32 flag; +#define _STS_FILTER_FLAG_TYPE_ERROR (1<<0) +#define _STS_FILTER_FLAG_ESD_SEND (1<<16) +#define _STS_FILTER_FLAG_CHK_FAULT (1<<17) + const char *str; +}; + +#define _STS_FILTER(_id, _width, _pos, _flag, _str) \ + { .id = _id, .width = _width, .pos = _pos, .flag = _flag, .str = _str, } + +enum { + STS_FILTER_FLAG_TYPE_ERROR = _STS_FILTER_FLAG_TYPE_ERROR, + STS_FILTER_FLAG_ESD_SEND = _STS_FILTER_FLAG_ESD_SEND, + STS_FILTER_FLAG_CHK_FAULT = _STS_FILTER_FLAG_CHK_FAULT, +}; + +#define REG_LOG_MAX 8 +#define REG_DIR_NONE 0 +#define REG_DIR_RD 1 +#define REG_DIR_WR 2 +#define REG_DIR_ERR (1<<8) +#define REG_DIR_MASK (REG_DIR_ERR-1) + +struct siw_hal_reg_log { + int dir; + u32 addr; + u32 data; +}; + +struct siw_touch_chip_opt { + u32 f_info_more:1; + u32 f_ver_ext:1; + u32 f_attn_opt:1; + u32 f_glove_en:1; + u32 f_grab_en:1; + u32 f_dbg_report:1; + u32 f_u2_blank_chg:1; + u32 f_rsvd00:1; + u32 f_rsvd01:8; + u32 f_rsvd02:8; + u32 f_rsvd03:8; + /* */ + u32 t_boot_mode:4; + u32 t_sts_mask:4; + u32 t_chk_mode:4; + u32 t_sw_rst:4; + u32 t_clock:4; + u32 t_chk_mipi:4; + u32 t_chk_frame:4; + u32 t_chk_tci_debug:4; + /* */ + u32 t_chk_sys_error:4; + u32 t_chk_sys_fault:4; + u32 t_chk_fault:4; + u32 rsvd21:4; + u32 rsvd22:8; + u32 rsvd23:8; +}; + +enum { + HAL_DBG_GRP_0 = 0, + HAL_DBG_GRP_MAX, +}; + +enum { + HAL_DBG_DLY_TC_DRIVING_0 = 0, + HAL_DBG_DLY_TC_DRIVING_1, + HAL_DBG_DLY_FW_0, + HAL_DBG_DLY_FW_1, + HAL_DBG_DLY_FW_2, + HAL_DBG_DLY_HW_RST_0, + HAL_DBG_DLY_HW_RST_1, + HAL_DBG_DLY_HW_RST_2, + HAL_DBG_DLY_SW_RST_0, + HAL_DBG_DLY_SW_RST_1, + HAL_DBG_DLY_NOTIFY, + HAL_DBG_DLY_LPWG, + HAL_DBG_DLY_MAX, +}; + +struct siw_hal_debug { + /* group 0 : delay */ + u32 delay[HAL_DBG_DLY_MAX]; + /* group 1 : rsvd */ + /* group 2 : rsvd */ + /* group 3 : rsvd */ +}; + +struct siw_touch_chip { + void *ts; //struct siw_ts + struct siw_touch_chip_opt opt; + struct siw_hal_reg *reg; + struct device *dev; + struct kobject kobj; + struct siw_hal_touch_info info; + struct siw_hal_fw_info fw; + struct siw_hal_asc_info asc; + struct siw_hal_swipe_ctrl swipe; + /* */ + int report_type; + int status_type; + u32 status_mask; + u32 status_mask_normal; + u32 status_mask_logging; + u32 status_mask_reset; + u32 status_mask_ic_abnormal; + u32 status_mask_ic_error; + u32 status_mask_ic_valid; + u32 status_mask_ic_disp_err; + struct siw_hal_status_mask_bit status_mask_bit; + struct siw_hal_status_filter *status_filter; + /* */ + int drv_reset_low; + int drv_delay; + int drv_opt_delay; + u8 prev_lcd_mode; + u8 lcd_mode; + u8 driving_mode; + u8 u3fake; + void *watch; + struct mutex bus_lock; + struct delayed_work font_download_work; + struct delayed_work fb_notify_work; + u32 charger; + u32 earjack; + u8 tci_debug_type; + u8 swipe_debug_type; + atomic_t block_watch_cfg; + atomic_t init; + atomic_t boot; + atomic_t esd_noti_sent; + int boot_fail_cnt; +#if defined(__SIW_SUPPORT_PM_QOS) + struct pm_qos_request pm_qos_req; +#endif + struct siw_hal_reg_log reg_log[REG_LOG_MAX]; + struct siw_hal_debug dbg; + int fw_abs_path; +}; + +static inline int hal_dbg_delay(struct siw_touch_chip *chip, int index) +{ + return (index < HAL_DBG_DLY_MAX) ? chip->dbg.delay[index] : 0; +} + +enum { + BOOT_FAIL_RECOVERY_MAX = 3, /* to avoid infinite repetition */ +}; + +enum { + CHIP_INIT_RETRY_PROBE = 2, + CHIP_INIT_RETRY_MAX = 5, +}; + +#define TCI_MAX_NUM 2 +#define SWIPE_MAX_NUM 2 +#define TCI_DEBUG_MAX_NUM 16 +#define SWIPE_DEBUG_MAX_NUM 8 +#define DISTANCE_INTER_TAP __SIZE_POW(1) /* 2 */ +#define DISTANCE_TOUCHSLOP __SIZE_POW(2) /* 4 */ +#define TIMEOUT_INTER_TAP_LONG __SIZE_POW(3) /* 8 */ +#define MULTI_FINGER __SIZE_POW(4) /* 16 */ +#define DELAY_TIME __SIZE_POW(5) /* 32 */ +#define TIMEOUT_INTER_TAP_SHORT __SIZE_POW(6) /* 64 */ +#define PALM_STATE __SIZE_POW(7) /* 128 */ +#define TAP_TIMEOVER __SIZE_POW(8) /* 256 */ + +#define TCI_DEBUG_ALL (0 | \ + DISTANCE_INTER_TAP | \ + DISTANCE_TOUCHSLOP | \ + TIMEOUT_INTER_TAP_LONG | \ + MULTI_FINGER | \ + DELAY_TIME | \ + TIMEOUT_INTER_TAP_SHORT | \ + PALM_STATE | \ + TAP_TIMEOVER | \ + 0) + + +static inline struct siw_touch_chip *to_touch_chip(struct device *dev) +{ + return (struct siw_touch_chip *)touch_get_dev_data(to_touch_core(dev)); +} + +static inline struct siw_touch_chip *to_touch_chip_from_kobj(struct kobject *kobj) +{ + return (struct siw_touch_chip *)container_of(kobj, + struct siw_touch_chip, kobj); +} + +extern int siw_hal_read_value(struct device *dev, u32 addr, u32 *value); +extern int siw_hal_write_value(struct device *dev, u32 addr, u32 value); +extern int siw_hal_reg_read(struct device *dev, u32 addr, void *data, int size); +extern int siw_hal_reg_write(struct device *dev, u32 addr, void *data, int size); +extern void siw_hal_xfer_init(struct device *dev, void *xfer_data); +extern int siw_hal_xfer_msg(struct device *dev, struct touch_xfer_msg *xfer); +extern void siw_hal_xfer_add_rx(void *xfer_data, u32 reg, void *buf, u32 size); +extern void siw_hal_xfer_add_tx(void *xfer_data, u32 reg, void *buf, u32 size); + +extern int siw_hal_ic_test_unit(struct device *dev, u32 data); + +extern struct siw_touch_operations *siw_hal_get_default_ops(int opt); + +#endif /* __SIW_TOUCH_HAL_H */ + + diff --git a/drivers/input/touchscreen/siw/siw_touch_hal_abt.c b/drivers/input/touchscreen/siw/siw_touch_hal_abt.c new file mode 100644 index 000000000000..bfa16ca47e4f --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_hal_abt.c @@ -0,0 +1,2368 @@ +/* + * siw_touch_hal_abt.c - SiW touch hal driver for ABT + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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 "siw_touch_cfg.h" + +#if defined(__SIW_SUPPORT_ABT) //See siw_touch_cfg.h + +#include +#include +#include +#include +#include +#include +#include + +/* */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siw_touch.h" +#include "siw_touch_hal.h" +#include "siw_touch_bus.h" +#include "siw_touch_event.h" +#include "siw_touch_gpio.h" +#include "siw_touch_irq.h" +#include "siw_touch_sys.h" + +/* + * [ABT] + * LG Touch Solution has ABT function to monitor real time operation of touch IC + * and SiW touch driver supports network communication via LAN(or WiFi) for ABT. + * + * [Usage] + * (In Windows) + * 1. Run LG Touch Solution + * Setting > General > Connection Mode > WiFi [not WiFi(ABT)] + * Connect > {IP} : Select the IP of your PC + * (In Target System) + * 2. Turn on debug tool + * $ echo 1 > /sys/devices/virtual/input/{input name}/debug_tool + * {Input Name} : default is "siw_touch_input"(SIW_TOUCH_INPUT) + * and can be changed via chip_idrv_name + * 3. Setup network connection + * $ echo 2 {IP} > /sys/devices/virtual/input/{input name}/raw_report + * {IP} : the IP of your PC + * + */ + +#define DEFAULT_IP "127.0.0.1" +#define NONE_IP "0.0.0.0" + +#define MAX_REPORT_SLOT 16 +#define P_CONTOUR_POINT_MAX 8 +#define DEF_RNDCPY_EVERY_NTH_FRAME (4) +#define PACKET_SIZE 128 + +enum { + DEFAULT_PORT = 8095, + TS_TCP_PORT = 8097, + SEND_PORT = 8090, + OMK_BUF = 1000, +}; + +enum E_DATA_TYPE { + DATA_TYPE_RAW = 0, + DATA_TYPE_BASELINE, + /* */ + DATA_TYPE_RN_ORG = 10, + DATA_TYPE_SEG1 = 20, + DATA_TYPE_SEG2, + DATA_TYPE_MAX +}; + +enum DEBUG_TYPE { + DEBUG_DATA = 0, + DEBUG_MODE, + DEBUG_DATA_RW_MODE, + DEBUG_DATA_CAPTURE_MODE, + DEBUG_DATA_CMD_MODE, +}; + +enum RW_TYPE { + READ_TYPE = 55, + WRITE_TYPE = 66, +}; + +enum DEBUG_REPORT_CMD { + DEBUG_REPORT_POINT = 0x100, + DEBUG_REPORT_OCD, +}; + +enum ABT_CONNECT_TOOL { + ABT_CONN_NOTHING = 0, + ABT_CONN_STUDIO, + ABT_CONN_TOUCH, + ABT_CONN_MAX +}; + +enum { + ABT_RUNNING_OFF = 0, + ABT_RUNNING_ON, + ABT_RUNNING_EXIT, +}; + +enum { + SIW_ABT_COMM_SEND_DATA_SZ = (4<<10), +}; + +/* UDP */ +#pragma pack(push, 1) +struct siw_abt_send_data { + u8 type; + u8 mode; + u8 flag; + u8 touchCnt; + u32 timestamp; + u32 frame_num; + u8 data[SIW_ABT_COMM_SEND_DATA_SZ]; +}; + +struct siw_abt_dbg_report_hdr { + u8 key_frame; + u8 type; + u16 data_size; +}; + +/* TCP */ +enum { + TCP_REG_READ = 0x80, + TCP_REG_WRITE, + TCP_FRAME_START, + TCP_REPORT_START, + TCP_SYNC_START, + TCP_SYNCDEBUG_START, + TCP_CAPTURE_STOP, + TCP_CONNECT_CMD, + TCP_DISCONNECT_CMD +}; + +enum { + COMM_RES_SUCCESS = 0, + COMM_RES_WR_FAILED = 0x8001, +}; + +struct siw_abt_packet_hdr { + u8 cmd; + u16 addr; + u16 size; +}; + +/* + * This shall be less than bus(i2c, spi) buffer size + * See 'siw_touch_bus_alloc_buffer' + */ +enum { + SIW_ABT_COMM_PACKET_FRAME_MAX = (32<<10), +}; + +struct siw_abt_comm_packet { + struct siw_abt_packet_hdr hdr; + union { + u32 value; + u8 frame[SIW_ABT_COMM_PACKET_FRAME_MAX]; + } data; +}; + +enum { + ABT_COMM_NAME_SZ = 128, + ABT_SEND_IP_SIZE = 16, +}; + +typedef int (*abt_sock_listener_t)(void *data, uint8_t *buf, uint32_t len); + +struct siw_hal_abt_comm { +// struct device *dev; + + char name[ABT_COMM_NAME_SZ]; + + struct task_struct *thread; + + int family; + int type; + int protocol; + + /*ABT Studio socket */ + struct socket *sock; + struct sockaddr_in addr; + + /* Touch Solution socket */ + struct socket *ts_sock; + struct sockaddr_in ts_addr; + + struct socket *sock_send; + struct sockaddr_in addr_send; + + struct socket *curr_sock; + struct sockaddr_in *curr_addr; + + abt_sock_listener_t sock_listener; + + struct siw_abt_send_data *data_send; + + struct siw_abt_comm_packet *recv_packet; + struct siw_abt_comm_packet *send_packet; + + int send_connected; + char send_ip[ABT_SEND_IP_SIZE+4]; + + atomic_t running; +}; + +struct siw_hal_abt_log_file_head { + /* 4 byte => 4B */ + /* 2B : resolution x */ + unsigned short resolution_x; + /* 2B : resoultion y */ + unsigned short resolution_y; + /* 4 byte => 8B */ + /* 1B : real node count x */ + unsigned char node_cnt_x; + /* 1B : real node count y */ + unsigned char node_cnt_y; + /* 1B : additional node count */ + unsigned char additional_node_cnt; + unsigned char dummy1; + /* 4 byte => 12B */ + /* 2B : RN MIN value */ + unsigned short rn_min; + /* 2B : RN MAX value */ + unsigned short rn_max; + /* 4 byte => 16B */ + /* 1B : RAW Size (1B or 2B) */ + unsigned char raw_data_size; + /* 1B : RN Size (1B or 2B) */ + unsigned char rn_data_size; + /* 1B : frame buf type data type : E_DATA_TYPE number */ + unsigned char frame_data_type; + /* 1B : frame buf size data : unit size (1B or 2B) */ + unsigned char frame_data_size; + /* 4 byte => 20B */ + /* 4B : x_node start/end x location(screen resolution) */ + unsigned short loc_x[2]; + /* 4 byte => 24B */ + /* 4B : y_node start/end y location(screen resolution) */ + unsigned short loc_y[2]; + /* 104 byte => 128B */ + /* total 128B */ + unsigned char dummy[104]; +} __packed; + +#pragma pack(pop) + +struct siw_hal_abt_touch_info { + u8 wakeUpType; + u8 touchCnt:5; + u8 buttonCnt:3; + u16 palmBit; +} __packed; + +struct siw_hal_abt_touch_data { + u8 toolType:4; + u8 event:4; + s8 track_id; + u16 x; + u16 y; + union { + u8 pressure; + u8 contourPcnt; //contour_point count + } byte; + u8 angle; + u16 width_major; + u16 width_minor; +} __packed; + +struct siw_hal_abt_tc_reg_cpy_to_report { + u32 tc_reg_copy[5]; +}; + +struct siw_hal_abt_on_chip_dbg { + u32 rnd_addr; + u32 rnd_piece_no; +}; + +struct siw_hal_abt_report_p { + struct siw_hal_abt_touch_info touch_info; + struct siw_hal_abt_touch_data touch_data[MAX_REPORT_SLOT]; + u16 contour_p[P_CONTOUR_POINT_MAX]; //contour_point + struct siw_hal_abt_tc_reg_cpy_to_report tc_reg; + struct siw_hal_abt_on_chip_dbg ocd; + u8 dummy[16]; +}; + +struct siw_hal_abt_data { + struct device *dev; + struct mutex abt_comm_lock; + struct mutex abt_socket_lock; + + struct siw_hal_abt_comm abt_comm; + int abt_socket_mutex_flag; + int abt_socket_report_mode; + + u32 prev_rnd_piece_no; + u32 dbg_offset; + u32 dbg_offset_base; + u32 ocd_pieces_cnt; + int ocd_piece_size; + + u16 frame_num; + int abt_report_mode; + u8 abt_report_point; + u8 abt_report_ocd; + int abt_report_mode_onoff; + int abt_conn_tool; + + int set_get_data_func; + + int client_connected; + int client_connect_trying; + + u32 connect_error_count; +}; + +enum { + ABT_SOCK_SEND_TIME_OUT = 10, + ABT_SOCK_RECV_TIME_OUT = 10, +}; + +static u32 t_abt_dbg_mask = 0; +static u32 t_abt_sock_sndtimeo = ABT_SOCK_SEND_TIME_OUT; +static u32 t_abt_sock_rcvtimeo = ABT_SOCK_RECV_TIME_OUT; + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/s_abt_dbg_mask + * (2) insmod {Siw Touch Module Name}.ko s_abt_dbg_mask= + */ +module_param_named(s_abt_dbg_mask, t_abt_dbg_mask, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/s_abt_sock_sndtimeo + * (2) insmod {Siw Touch Module Name}.ko s_abt_sock_sndtimeo= + */ +module_param_named(s_abt_sock_sndtimeo, t_abt_sock_sndtimeo, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/s_abt_sock_rcvtimeo + * (2) insmod {Siw Touch Module Name}.ko s_abt_sock_rcvtimeo= + */ +module_param_named(s_abt_sock_rcvtimeo, t_abt_sock_rcvtimeo, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +static u32 t_abt_port_default = DEFAULT_PORT; +static u32 t_abt_port_tcp = TS_TCP_PORT; +static u32 t_abt_port_send = SEND_PORT; +static u32 t_abt_buf_omk = OMK_BUF; + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/s_abt_port_default + * (2) insmod {Siw Touch Module Name}.ko s_abt_port_default= + */ +module_param_named(s_abt_port_default, t_abt_port_default, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/s_abt_port_tcp + * (2) insmod {Siw Touch Module Name}.ko s_abt_port_tcp= + */ +module_param_named(s_abt_port_tcp, t_abt_port_tcp, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/s_abt_port_send + * (2) insmod {Siw Touch Module Name}.ko s_abt_port_send= + */ +module_param_named(s_abt_port_send, t_abt_port_send, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/s_abt_buf_omk + * (2) insmod {Siw Touch Module Name}.ko s_abt_buf_omk= + */ +module_param_named(s_abt_buf_omk, t_abt_buf_omk, uint, S_IRUGO|S_IWUSR|S_IWGRP); + + +static const char *abt_conn_name_str[] = { + [ABT_CONN_NOTHING] = "CONN_NOTHING", + [ABT_CONN_STUDIO] = "CONN_STUDIO", + [ABT_CONN_TOUCH] = "CONN_TOUCH", +}; + +static inline const char *abt_conn_name(int idx) +{ + return (idx < ABT_CONN_MAX) ? abt_conn_name_str[idx] : "(invalid)"; +} + +#define SIW_ABT_TAG "abt" + +#define t_abt_info(_abt, fmt, args...) \ + __t_dev_info(_abt->dev, SIW_ABT_TAG "[%d(%s)]: " fmt, \ + _abt->abt_conn_tool, abt_conn_name(_abt->abt_conn_tool), \ + ##args) + +#define t_abt_err(_abt, fmt, args...) \ + __t_dev_err(_abt->dev, SIW_ABT_TAG "[%d(%s)] : " fmt, \ + _abt->abt_conn_tool, abt_conn_name(_abt->abt_conn_tool), \ + ##args) + +#define t_abt_warn(_abt, fmt, args...) \ + __t_dev_warn(_abt->dev, SIW_ABT_TAG "[%d(%s)] : " fmt, \ + _abt->abt_conn_tool, abt_conn_name(_abt->abt_conn_tool), \ + ##args) + +#define t_abt_dbg(condition, _abt, fmt, args...) \ + do { \ + if (unlikely(t_abt_dbg_mask & (condition))) \ + t_abt_info(_abt, fmt, ##args); \ + } while (0) + +#define t_abt_dbg_base(_dev, fmt, args...) \ + t_abt_dbg(DBG_BASE, _dev, fmt, ##args) + +#define t_abt_dbg_trace(_dev, fmt, args...) \ + t_abt_dbg(DBG_TRACE, _dev, fmt, ##args) + + +#define siw_abt_sysfs_err_invalid_param(_abt) \ + t_abt_err(_abt, "Invalid param\n"); + + +/* + * Do not modify these string arbitrarily, + * which are related to simon operation. + */ +#define ABT_MON_SOCK_RSTR "@ABTR" +#define ABT_MON_SOCK_SSTR "@ABTS" + +static void __used siwmon_submit_ops_abt_sock( + struct siw_hal_abt_data *abt, + void *buf, + int len, + int ret, + int recv) +{ + size_t data[2] = { (size_t)buf, len }; + siwmon_submit_ops(abt->dev, + (recv) ? ABT_MON_SOCK_RSTR : ABT_MON_SOCK_SSTR, + (void *)data, + 2, + ret); +} + +#define siwmon_submit_ops_abt_sock_recv(_abt, _buf, _len, _ret) \ + siwmon_submit_ops_abt_sock(_abt, _buf, _len, _ret, 1); + +#define siwmon_submit_ops_abt_sock_send(_abt, _buf, _len, _ret) \ + siwmon_submit_ops_abt_sock(_abt, _buf, _len, _ret, 0); + + +#define ABT_IP_FORMAT "%d.%d.%d.%d" +#define ABT_IP_PRT_PARAM(_addr) \ + ((_addr)>>24)&0XFF, ((_addr)>>16)&0XFF, ((_addr)>>8)&0XFF, (_addr)&0XFF + +static int __used abt_sock_msg_chk_size(struct siw_hal_abt_data *abt, + int size, int boundary, char *name) +{ + if (size >= boundary) { + return 0; + } + + /* + * EAGAIN is not real error + * becase it happens when the server doesn't send any packet + */ + if (size == -EAGAIN) { + return 0; + } + + /* + * 0 means normal disconnection + */ + if (size != 0) + t_abt_err(abt, "%s failed, %d\n", name, size); + + return -1; +} + +static int abt_sock_recvmsg(struct siw_hal_abt_data *abt, + struct socket *sock, + struct sockaddr_in *addr_in, + unsigned char *buf, + int len, + int num) +{ + struct msghdr msg; + struct kvec iov = { + .iov_base = buf, + .iov_len = len + }; + int ret = 0; + + memset(&msg, 0, sizeof(struct msghdr)); + + msg.msg_name = addr_in; + msg.msg_namelen = sizeof(struct sockaddr_in); + + ret = kernel_recvmsg(sock, &msg, &iov, num, len, 0); + siwmon_submit_ops_abt_sock_recv(abt, buf, len, ret); + (void)abt_sock_msg_chk_size(abt, ret, 0, "kernel_recvmsg"); + + return ret; +} + +static int abt_sock_sendmsg(struct siw_hal_abt_data *abt, + struct socket *sock, + struct sockaddr_in *addr_in, + unsigned char *buf, + int len, + int num) +{ + struct msghdr msg; + struct kvec iov = { + .iov_base = buf, + .iov_len = len + }; + int ret; + + memset(&msg, 0, sizeof(struct msghdr)); + + msg.msg_name = addr_in; + msg.msg_namelen = sizeof(struct sockaddr_in); + + ret = kernel_sendmsg(sock, &msg, &iov, 1, len); + siwmon_submit_ops_abt_sock_send(abt, buf, len, ret); + (void)abt_sock_msg_chk_size(abt, ret, 0, "kernel_sendmsg"); + + return ret; +} + +#define t_abt_log_sock_create(_abt, _name, _ret) \ + { \ + if (_ret < 0) { \ + t_abt_err(_abt, "failed to create a %s socket, %d\n", _name, _ret); \ + } else { \ + t_abt_dbg_base(_abt, "%s socket created\n", _name); \ + } \ + } while (0) + +static int siw_sock_create_kern(int family, int type, int protocol, struct socket **res) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)) + return sock_create_kern(&init_net, family, type, protocol, res); +#else + return sock_create_kern(family, type, protocol, res); +#endif +} + +static int __used abt_sock_create_data(struct siw_hal_abt_data *abt, + struct socket **res) +{ + int ret = siw_sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, res); + t_abt_log_sock_create(abt, "datagram", ret); + return ret; +} + +static int __used abt_sock_create_stream(struct siw_hal_abt_data *abt, + struct socket **res) +{ + int ret = siw_sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, res); + t_abt_log_sock_create(abt, "stream", ret); + return ret; +} + +static char *__abt_sock_connect_err_str(int ret) +{ + char *str; + + switch (ret) { + case -EPFNOSUPPORT: + str = "protocol family not supported"; + break; + case -EAFNOSUPPORT: + str = "address family not supported"; + break; + case -EADDRINUSE: + str = "address already in use"; + break; + case -EADDRNOTAVAIL: + str = "can't assign requested address"; + break; + case -ENETDOWN: + str = "network is down"; + break; + case -ENETUNREACH: + str = "network is unreachable"; + break; + case -ENETRESET: + str = "network dropped connection because of reset"; + break; + case -ECONNABORTED: + str = "sw caused connection abort"; + break; + case -ECONNRESET: + str = "connection reset by peer"; + break; + default: + str = " "; + break; + } + + return str; +} + +static inline int __abt_sock_ret_is_err(int ret) +{ + return (ret < 0); +} + +static int __used abt_sock_listen(struct siw_hal_abt_data *abt, + struct socket *sock, + struct sockaddr_in *addr_in, + int backlog) +{ + int ret = kernel_listen(sock, backlog); + if (__abt_sock_ret_is_err(ret)) { + char *str = __abt_sock_connect_err_str(ret); + + t_abt_err(abt, "kernel_listen[" ABT_IP_FORMAT ":%d, %d](%d) failed, %d(%s)\n", + ABT_IP_PRT_PARAM(__be32_to_cpu(addr_in->sin_addr.s_addr)), + __be16_to_cpu(addr_in->sin_port), + addr_in->sin_family, + backlog, + ret, str); + } else { + t_abt_dbg_base(abt, "kernel_listen[" ABT_IP_FORMAT ":%d, %d](%d) done\n", + ABT_IP_PRT_PARAM(__be32_to_cpu(addr_in->sin_addr.s_addr)), + __be16_to_cpu(addr_in->sin_port), + addr_in->sin_family, + backlog); + } + return ret; +} + +static int __used abt_sock_bind(struct siw_hal_abt_data *abt, + struct socket *sock, + struct sockaddr_in *addr_in, + int addrlen) +{ + int ret = kernel_bind(sock, (struct sockaddr *)addr_in, addrlen); + if (__abt_sock_ret_is_err(ret)) { + char *str = __abt_sock_connect_err_str(ret); + + t_abt_err(abt, "kernel_bind[" ABT_IP_FORMAT ":%d, %d] failed, %d(%s)\n", + ABT_IP_PRT_PARAM(__be32_to_cpu(addr_in->sin_addr.s_addr)), + __be16_to_cpu(addr_in->sin_port), + addr_in->sin_family, + ret, str); + } else { + t_abt_dbg_base(abt, "kernel_bind[" ABT_IP_FORMAT ":%d, %d] done\n", + ABT_IP_PRT_PARAM(__be32_to_cpu(addr_in->sin_addr.s_addr)), + __be16_to_cpu(addr_in->sin_port), + addr_in->sin_family); + } + return ret; +} + +static int __used abt_sock_connect(struct siw_hal_abt_data *abt, + struct socket *sock, + struct sockaddr_in *addr_in, + int addrlen, + int flags) +{ + int ret = kernel_connect(sock, (struct sockaddr *)addr_in, addrlen, flags); + if (__abt_sock_ret_is_err(ret)) { + char *str = __abt_sock_connect_err_str(ret); + + t_abt_err(abt, "kernel_connect[" ABT_IP_FORMAT ":%d, %d] failed, %d(%s)\n", + ABT_IP_PRT_PARAM(__be32_to_cpu(addr_in->sin_addr.s_addr)), + __be16_to_cpu(addr_in->sin_port), + addr_in->sin_family, + ret, str); + } else { + t_abt_dbg_base(abt, "kernel_connect[" ABT_IP_FORMAT ":%d, %d] done\n", + ABT_IP_PRT_PARAM(__be32_to_cpu(addr_in->sin_addr.s_addr)), + __be16_to_cpu(addr_in->sin_port), + addr_in->sin_family); + } + return ret; +} + +static inline int __abt_sock_ret_is_err_sd(int ret) +{ + if (ret == -ENOTCONN) + return 0; + + return (ret < 0); +} + +static int __used abt_sock_shutdown(struct siw_hal_abt_data *abt, + struct socket *sock, + struct sockaddr_in *addr_in) +{ + int ret = kernel_sock_shutdown(sock, SHUT_RDWR); + if (__abt_sock_ret_is_err_sd(ret)) { + t_abt_err(abt, "kernel_sock_shutdown[" ABT_IP_FORMAT ":%d, %d] failed, %d\n", + ABT_IP_PRT_PARAM(__be32_to_cpu(addr_in->sin_addr.s_addr)), + __be16_to_cpu(addr_in->sin_port), + addr_in->sin_family, + ret); + } else { + t_abt_dbg_base(abt, "kernel_sock_shutdown[" ABT_IP_FORMAT ":%d, %d] done\n", + ABT_IP_PRT_PARAM(__be32_to_cpu(addr_in->sin_addr.s_addr)), + __be16_to_cpu(addr_in->sin_port), + addr_in->sin_family); + } + return ret; +} + +static void __used abt_sock_set_timeout(struct siw_hal_abt_data *abt, + struct socket *sock, + long new_sndtimeo, + long new_rcvtimeo, + char *name) +{ + long old_sndtimeo, old_rcvtimeo; + + spin_lock_bh(&sock->sk->sk_lock.slock); + + if (new_sndtimeo) { + old_sndtimeo = sock->sk->sk_sndtimeo/HZ; + sock->sk->sk_sndtimeo = new_sndtimeo * HZ; + t_abt_dbg_base(abt, + "time_out(%s) changed : send %ld secs <- %ld secs\n", + name, new_sndtimeo, old_sndtimeo); + } + + if (new_rcvtimeo) { + old_rcvtimeo = sock->sk->sk_rcvtimeo/HZ; + sock->sk->sk_rcvtimeo = new_rcvtimeo * HZ; + t_abt_dbg_base(abt, + "time_out(%s) changed : recv %ld secs <- %ld secs\n", + name, new_rcvtimeo, old_rcvtimeo); + } +// t_abt_info(abt, "rcvbuf : %d, sndbuf : %d\n", sock->sk->sk_rcvbuf, sock->sk->sk_sndbuf); + + spin_unlock_bh(&sock->sk->sk_lock.slock); +} + +#if 0 +static int __used __abt_sock_getsockopt(struct siw_hal_abt_data *abt, + struct socket *sock, int level, int optname, + char *optval, int *optlen) +{ + int ret = kernel_getsockopt(sock, level, optname, optval, optlen); + if (ret < 0) { + t_abt_err(abt, "kernel_getsockopt failed, %d\n", ret); + } else { + t_abt_dbg_base(abt, "kernel_getsockopt done\n"); + } + return ret; +} + +static int __used __abt_sock_setsockopt(struct siw_hal_abt_data *abt, + struct socket *sock, int level, int optname, + char *optval, int optlen) +{ + int ret = kernel_setsockopt(sock, level, optname, optval, optlen); + if (ret < 0) { + t_abt_err(abt, "kernel_setsockopt failed, %d\n", ret); + } else { + t_abt_dbg_base(abt, "kernel_setsockopt done\n"); + } + return ret; +} +#endif + +static int __used abt_conn_is_invalid(struct siw_hal_abt_data *abt) +{ + int inval = !!(!abt->abt_conn_tool || (abt->abt_conn_tool >= ABT_CONN_MAX)); + if (inval) + t_abt_warn(abt, "Invalid Tool Select, %d\n", abt->abt_conn_tool); + + return inval; +} + +static int __used abt_is_set_func(struct siw_hal_abt_data *abt) +{ + return abt->set_get_data_func; +} + +static void __used abt_set_data_func(struct siw_hal_abt_data *abt, u8 mode) +{ + abt->set_get_data_func = !!(mode); + t_abt_dbg_base(abt, "set_get_data_func = %d\n", mode); +} + +static int abt_read_memory(struct siw_hal_abt_data *abt, + u32 raddr, u32 waddr, + u32 wdata, int wsize, + u8 *rdata, int rsize) +{ + struct device *dev = abt->dev; + u16 curr_read = 0; + u32 rest_read = rsize; + int ret = 0; + +#if 0 + if (!raddr || !waddr) { + t_abt_err(abt, "NULL addr : raddr 0x%X, waddr 0x%X\n", + raddr, waddr); + return -EINVAL; + } +#endif + + curr_read = (rest_read > MAX_RW_SIZE) ? MAX_RW_SIZE : rest_read; + + while (curr_read > 0) { + ret = siw_hal_reg_write(dev, waddr, (void *)&wdata, wsize); + if (ret < 0) { + return ret; + } + ret = siw_hal_reg_read(dev, raddr, (void *)rdata, curr_read); + if (ret < 0) { + return ret; + } + curr_read = ret; + + if (rest_read > curr_read) { + rest_read -= curr_read; + rdata += curr_read; + } else { + rest_read = 0; + } + wdata += curr_read>>2; + + curr_read = (rest_read > MAX_RW_SIZE) ? MAX_RW_SIZE : rest_read; + } + + return rsize; +} + +static int abt_force_set_report_mode(struct siw_hal_abt_data *abt, + u32 mode) +{ + struct device *dev = abt->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 rdata = 0; + u32 wdata = mode; + int ret = 0; + + /* send debug mode*/ + ret = siw_hal_write_value(dev, + reg->cmd_raw_data_report_mode_write, + wdata); + if (ret < 0) { + t_abt_err(abt, "debug report buffer write error\n"); + goto out; + } + abt->abt_report_mode = mode; + t_abt_info(abt, "mode(%d) set done\n", mode); + + /* receive debug report buffer*/ + if (mode >= 0) { + ret = siw_hal_read_value(dev, + reg->cmd_raw_data_report_mode_read, + &rdata); + t_abt_info(abt, "rdata(0x%08X)\n", rdata); + if (ret < 0 || rdata <= 0) { + t_abt_err(abt, "debug report buffer pointer error\n"); + goto out; + } + t_abt_info(abt, "debug report buffer pointer, 0x%x\n", rdata); + } + + +out: + if (ret) { + int t_ret = siw_hal_write_value(dev, + reg->cmd_raw_data_report_mode_write, + 0); + if (t_ret < 0) + t_dev_warn(dev, "%s\n", __func__); + abt->abt_report_mode = 0; + abt->abt_report_mode_onoff = 0; + } + + return ret; +} + +static int abt_set_report_mode(struct siw_hal_abt_data *abt, u32 mode) +{ +// struct device *dev = abt->dev; + int ret; + +// t_abt_info(abt, "[ABT] mode:%d\n", mode); + + if (abt->abt_report_mode == mode) { + #if 0 + t_abt_warn(abt, + "mode(%d) is already set\n", + mode); + #endif + return 0; + } + + if (mode < 0) { + t_abt_err(abt, + "mode(%d) is invalid\n", mode); + return -EINVAL; + } + + ret = abt_force_set_report_mode(abt, mode); + + return ret; +} + +static int abt_ksocket_do_send(struct siw_hal_abt_data *abt, + struct socket *sock, + struct sockaddr_in *addr, + unsigned char *buf, int len) +{ + int ret = 0; + + if (!sock) { + t_abt_err(abt, "socket send err : NULL sock\n"); + return 0; + } + + ret = abt_sock_sendmsg(abt, sock, addr, buf, len, 1); + + return ret; +} + +static int abt_ksocket_send(struct siw_hal_abt_data *abt, + struct socket *sock, + struct sockaddr_in *addr, + unsigned char *buf, int len) +{ + if (abt_conn_is_invalid(abt)) { + return 0; + } + + return abt_ksocket_do_send(abt, sock, addr, buf, len); +} + +static void __used abt_ksocket_send_disconn(struct siw_hal_abt_data *abt, + struct socket *sock, struct sockaddr_in *addr_in) +{ + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + struct siw_abt_comm_packet *send_pkt = abt_comm->send_packet; + + t_abt_dbg_base(abt, "send TCP_DISCONNECT_CMD(%d)\n", TCP_DISCONNECT_CMD); + + /* send disconnect command to server */ + send_pkt->hdr.cmd = TCP_DISCONNECT_CMD; + send_pkt->hdr.size = 4; + abt_ksocket_send(abt, + sock, + addr_in, + (u8 *)send_pkt, + sizeof(struct siw_abt_packet_hdr) + + send_pkt->hdr.size); +} + +static int __used abt_ksocket_send_exit(struct siw_hal_abt_data *abt) +{ +// struct device *dev = abt->dev; + struct socket *sock; + struct sockaddr_in send_addr_in; + uint8_t send_buf[1] = { 1 }; + int ret = 0; + + ret = abt_sock_create_data(abt, &sock); + if (ret < 0) { + goto out; + } + + memset(&send_addr_in, 0, sizeof(struct sockaddr)); + send_addr_in.sin_family = PF_INET; + send_addr_in.sin_addr.s_addr = in_aton(DEFAULT_IP); + send_addr_in.sin_port = htons(t_abt_port_default); + + ret = abt_sock_connect(abt, sock, + &send_addr_in, + sizeof(struct sockaddr), + !O_NONBLOCK); + if (ret < 0) { + goto out_sock; + } + + t_abt_dbg_base(abt, "connect send socket (%s, %d)\n", + DEFAULT_IP, t_abt_port_default); + + ret = abt_sock_sendmsg(abt, sock, &send_addr_in, + send_buf, sizeof(send_buf), 1); + +out_sock: + sock_release(sock); + +out: + return ret; +} + +static void __used abt_ksocket_free_send_socket(struct siw_hal_abt_data *abt) +{ + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + + abt_comm->send_connected = 0; + if (abt_comm->sock_send != NULL) { + sock_release(abt_comm->sock_send); + abt_comm->sock_send = NULL; + t_abt_info(abt, "send socket released\n"); + } +} + +static int32_t __used abt_ksocket_init_send_socket(struct siw_hal_abt_data *abt) +{ +// struct device *dev = abt->dev; + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + struct socket *sock; + struct sockaddr_in *addr_in = &abt_comm->addr_send; + char *ip = (char *)abt_comm->send_ip; + int ret = 0; + + if (abt_comm->sock_send != NULL) { + return 0; + } + + ret = abt_sock_create_data(abt, &sock); + if (ret < 0) { + goto out; + } + abt_comm->sock_send = sock; + + abt_sock_set_timeout(abt, sock, + t_abt_sock_sndtimeo, t_abt_sock_rcvtimeo, + "send_socket"); + + memset(addr_in, 0, sizeof(struct sockaddr)); + addr_in->sin_family = PF_INET; + addr_in->sin_addr.s_addr = in_aton(ip); + addr_in->sin_port = htons(t_abt_port_send); + + ret = abt_sock_connect(abt, sock, + addr_in, + sizeof(struct sockaddr), + !O_NONBLOCK); + if (ret < 0) { + goto out; + } + + abt_comm->send_connected = 1; + t_abt_info(abt, "connect send socket (%s,%d)\n", ip, t_abt_port_send); + + return ret; + +out: + abt_ksocket_free_send_socket(abt); + + return ret; +} + +static void abt_ksocket_exit_disconn(struct siw_hal_abt_data *abt) +{ + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + struct socket *sock; + struct sockaddr_in *addr_in; +// int ret = 0; + + mutex_lock(&abt->abt_comm_lock); + + if (atomic_read(&abt_comm->running) == ABT_RUNNING_OFF) { + goto out; + } + + atomic_set(&abt_comm->running, ABT_RUNNING_EXIT); + + sock = (abt->abt_conn_tool != ABT_CONN_TOUCH) ? + abt_comm->sock : abt_comm->ts_sock; + addr_in = (abt->abt_conn_tool != ABT_CONN_TOUCH) ? + &abt_comm->addr : &abt_comm->ts_addr; + + if (!sock) { + goto out; + } + + switch (abt->abt_conn_tool) { + case ABT_CONN_TOUCH: + if (abt->client_connected) { + #if 0 + abt_ksocket_send_disconn(abt, sock, addr_in); + #else + abt_sock_shutdown(abt, sock, addr_in); + #endif + } else if (abt->client_connect_trying) { + abt_sock_shutdown(abt, sock, addr_in); + touch_msleep(100); + } + break; + + case ABT_CONN_STUDIO: + /* Reserved */ + break; + + default: + break; + } + +out: + mutex_unlock(&abt->abt_comm_lock); +} + +static void abt_ksocket_exit_kill(struct siw_hal_abt_data *abt) +{ + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + int ret = 0; + + mutex_lock(&abt->abt_comm_lock); + if (abt_comm->thread != NULL) { + ret = kthread_stop(abt_comm->thread); + if (ret < 0) { + t_abt_err(abt, + "unknown error %d while trying to " \ + "terminate kernel thread\n", + ret); + } else { + u32 running = 0; + do { + running = atomic_read(&abt_comm->running); + if (running == ABT_RUNNING_OFF) { + break; + } + t_abt_dbg_base(abt, + "waiting for killing thread. " \ + "running = %d\n", + running); + touch_msleep(10); + } while (1); + t_abt_dbg_base(abt, "succesfully killed kernel thread!\n"); + } + abt_comm->thread = NULL; + } + + if (abt_comm->sock != NULL) { + sock_release(abt_comm->sock); + abt_comm->sock = NULL; + t_abt_dbg_base(abt, "sock released\n"); + } + + if (abt_comm->ts_sock != NULL) { + sock_release(abt_comm->ts_sock); + abt_comm->ts_sock = NULL; + t_abt_dbg_base(abt, "ts_sock released\n"); + } + + abt_ksocket_free_send_socket(abt); + + abt_comm->curr_sock = NULL; + abt_comm->curr_addr = NULL; + + abt->abt_socket_mutex_flag = 0; + abt->abt_socket_report_mode = 0; + + abt->abt_conn_tool = ABT_CONN_NOTHING; + mutex_unlock(&abt->abt_comm_lock); +} + +static void abt_ksocket_exit(struct siw_hal_abt_data *abt) +{ + t_abt_dbg_base(abt, "start killing thread[%d]\n", abt->abt_conn_tool); + + abt_ksocket_exit_disconn(abt); + + t_abt_dbg_base(abt, "waiting for killing thread\n"); + + abt_ksocket_exit_kill(abt); + + t_abt_dbg_base(abt, "socket exit\n"); + + siw_touch_mon_resume(abt->dev); +} + + +static int32_t abt_ksocket_raw_data_send( + struct siw_hal_abt_data *abt, uint8_t *buf, uint32_t len) +{ +// struct device *dev = abt->dev; + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + int ret = 0; + + if (abt_comm->sock_send == NULL) { + abt_ksocket_init_send_socket(abt); + } + + if (!abt_comm->send_connected) { + abt->connect_error_count++; + if (abt->connect_error_count > 10) { + t_abt_err(abt, "connection error - socket release\n"); + abt_force_set_report_mode(abt, 0); + abt_ksocket_exit(abt); + } + return ret; + } + + ret = abt_ksocket_do_send(abt, + abt_comm->sock_send, + &abt_comm->addr_send, + buf, len); + + return ret; +} + +static int abt_ksocket_recv_err(struct siw_hal_abt_data *abt, + struct siw_abt_comm_packet *recv_pkt, + uint32_t len) +{ + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + struct siw_abt_comm_packet *send_pkt = abt_comm->send_packet; + const char *msg = NULL; + int ret = 0; + + if (len < sizeof(struct siw_abt_packet_hdr)) { + msg = "abnormal"; + ret = -EPROTONOSUPPORT; + } else if (recv_pkt->hdr.size > sizeof(recv_pkt->data.frame)) { + msg = "overflow"; + ret = -EOVERFLOW; + } else if (len < (recv_pkt->hdr.size + sizeof(struct siw_abt_packet_hdr))) { + msg = "slicing"; + ret = -EPROTONOSUPPORT; + } + + if (ret < 0) { + t_abt_err(abt, "%s: len %d, size %d, cmd %02Xh, addr %04Xh\n", + msg, + len, + recv_pkt->hdr.size, + recv_pkt->hdr.cmd, recv_pkt->hdr.addr); + + send_pkt->data.value = COMM_RES_WR_FAILED; + } + + return ret; +} + +static int abt_ksocket_recv_from_pctool( + void *data, uint8_t *buf, uint32_t len) +{ + struct siw_hal_abt_data *abt = data; + struct device *dev = abt->dev; + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + struct siw_abt_comm_packet *send_pkt = abt_comm->send_packet; + struct siw_abt_comm_packet *recv_pkt = NULL; + int ret = 0; + + recv_pkt = (struct siw_abt_comm_packet *)buf; + abt_comm->recv_packet = recv_pkt; + + send_pkt->hdr.cmd = recv_pkt->hdr.cmd; + send_pkt->hdr.addr = recv_pkt->hdr.addr; + send_pkt->hdr.size = sizeof(send_pkt->data.value); + + ret = abt_ksocket_recv_err(abt, recv_pkt, len); + if (ret < 0) { + goto send_resp; + } + + t_abt_dbg_trace(abt, ">> cmd %02Xh, addr %04Xh, size %d\n", + recv_pkt->hdr.cmd, recv_pkt->hdr.addr, recv_pkt->hdr.size); + + if (recv_pkt->hdr.cmd == TCP_REG_READ) { + ret = siw_hal_reg_read(dev, + recv_pkt->hdr.addr, + (void *)send_pkt->data.frame, + recv_pkt->data.value); + if (ret < 0) { + t_abt_err(abt, + "TCP REG READ error : %d\n", + ret); + // goto out; + } + + send_pkt->hdr.size = recv_pkt->data.value; + goto send_resp; + } + + if (recv_pkt->hdr.cmd == TCP_REG_WRITE) { + u8 *rcv_frame = recv_pkt->data.frame; + + if (recv_pkt->hdr.size > SIW_ABT_COMM_PACKET_FRAME_MAX) { + t_abt_err(abt, "write size overflow : 0x%X > 0x%X\n", + recv_pkt->hdr.size, + SIW_ABT_COMM_PACKET_FRAME_MAX); + return -EINVAL; + } + + ret = siw_hal_reg_write(dev, + recv_pkt->hdr.addr, + (void *)rcv_frame, + recv_pkt->hdr.size); + if (ret < 0) { + t_abt_err(abt, + "TCP REG WRITE spi_read error : %d\n", + ret); + send_pkt->data.value = COMM_RES_WR_FAILED; + } else { + send_pkt->data.value = COMM_RES_SUCCESS; + } + + goto send_resp; + } + + send_pkt->data.value = COMM_RES_WR_FAILED; + +send_resp: + abt_ksocket_send(abt, + abt_comm->ts_sock, &abt_comm->ts_addr, + (u8 *)send_pkt, + sizeof(struct siw_abt_packet_hdr) + send_pkt->hdr.size); + + return ret; +} + +static int abt_ksocket_do_recv(struct siw_hal_abt_data *abt, + unsigned char *buf, int len) +{ +// struct device *dev = abt->dev; + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + struct socket *sock = NULL; + struct sockaddr_in *addr = NULL; + int size = 0; + + WARN_ON(abt_comm->sock_listener == NULL); + + sock = abt_comm->curr_sock; + addr = abt_comm->curr_addr; + if (!sock) { + t_abt_err(abt, "socket recv err : NULL sock\n"); + return 0; + } + + size = abt_sock_recvmsg(abt, sock, addr, buf, len, 1); + if (size <= 0) { + goto out; + } + + if (abt_comm->sock_listener) { + abt_comm->sock_listener((void *)abt, buf, size); + } + +out: + return size; +} + +static int abt_ksocket_recv(struct siw_hal_abt_data *abt, + unsigned char *buf, int len) +{ + if (abt_conn_is_invalid(abt)) { + return 0; + } + + return abt_ksocket_do_recv(abt, buf, len); +} + + +/* + * Ref. from 'include/uapi/asm-generic/errno-base.h' + * #define EAGAIN 11 // Try again + * #define EMLINK 31 // Too many links + * #define EPIPE 32 // Broken pipe + * + * Ref. from 'include/uapi/asm-generic/errno.h' + * #define ESTRPIPE 86 // Streams pipe error + * #define EUSERS 87 // Too many users + * #define ENOTSOCK 88 // Socket operation on non-socket + * #define EDESTADDRREQ 89 // Destination address required + * #define EMSGSIZE 90 // Message too long + * #define EPROTOTYPE 91 // Protocol wrong type for socket + * #define ENOPROTOOPT 92 // Protocol not available + * #define EPROTONOSUPPORT 93 // Protocol not supported + * #define ESOCKTNOSUPPORT 94 // Socket type not supported + * #define EOPNOTSUPP 95 // Operation not supported on transport endpoint + * #define EPFNOSUPPORT 96 // Protocol family not supported + * #define EAFNOSUPPORT 97 // Address family not supported by protocol + * #define EADDRINUSE 98 // Address already in use + * #define EADDRNOTAVAIL 99 // Cannot assign requested address + * #define ENETDOWN 100 // Network is down + * #define ENETUNREACH 101 // Network is unreachable + * #define ENETRESET 102 // Network dropped connection because of reset + * #define ECONNABORTED 103 // Software caused connection abort + * #define ECONNRESET 104 // Connection reset by peer + * #define ENOBUFS 105 // No buffer space available + * #define EISCONN 106 // Transport endpoint is already connected + * #define ENOTCONN 107 // Transport endpoint is not connected + * #define ESHUTDOWN 108 // Cannot send after transport endpoint shutdown + * #define ETIMEDOUT 110 + * #define ECONNREFUSED 111 // Connection refused + * #define EHOSTDOWN 112 // Host is down + * #define EHOSTUNREACH 113 // No route to host + * #define EALREADY 114 // Operation already in progress + * #define EINPROGRESS 115 // Operation now in progress + */ + +enum { + ABT_SOCK_USE_NONE = 0, + ABT_SOCK_USE_CONN = (1<<0), + ABT_SOCK_USE_LISTEN = (1<<1), + ABT_SOCK_USE_BIND = (1<<2), +}; + +enum { + ABT_SOCK_PCTOOL_OPT = ABT_SOCK_USE_CONN, + ABT_SOCK_STUDIO_OPT = ABT_SOCK_USE_BIND, +}; + +static int abt_ksocket_thread_init(struct siw_hal_abt_data *abt, + int tool, int opt) +{ + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + struct sockaddr_in *addr_in = NULL; + struct socket *sock = NULL; + __be32 s_addr = 0; + u32 s_port = 0; + int ret = 0; + + t_abt_dbg_base(abt, "sock init[%d, %d] start\n", tool, opt); + + /* create a socket */ + switch (tool) { + case ABT_CONN_STUDIO: + /* Reserved */ + ret = -ESRCH; + goto out; + break; + case ABT_CONN_TOUCH: + ret = abt_sock_create_stream(abt, &sock); + break; + default: + ret = -EINVAL; + break; + } + if (ret < 0) { + goto out; + } + + switch (tool) { + case ABT_CONN_STUDIO: + /* Reserved */ + goto out; + break; + case ABT_CONN_TOUCH: + addr_in = &abt_comm->ts_addr; + s_addr = in_aton(abt_comm->send_ip); + s_port = htons(t_abt_port_tcp); + + abt_comm->ts_sock = sock; + abt_comm->curr_addr = &abt_comm->ts_addr; + + break; + } + abt_comm->curr_sock = sock; + + addr_in = abt_comm->curr_addr; + + memset(addr_in, 0, sizeof(struct sockaddr_in)); + addr_in->sin_family = PF_INET; + addr_in->sin_addr.s_addr = s_addr; + addr_in->sin_port = s_port; + + t_abt_dbg_base(abt, "sock init[%d, %d] addr config : " ABT_IP_FORMAT ":%d, %d\n", + tool, opt, + ABT_IP_PRT_PARAM(__be32_to_cpu(s_addr)), + __be16_to_cpu(s_port), + addr_in->sin_family); + + abt_sock_set_timeout(abt, sock, + t_abt_sock_sndtimeo, t_abt_sock_rcvtimeo, + "thread"); + + if (opt & ABT_SOCK_USE_BIND) { + ret = abt_sock_bind(abt, sock, + addr_in, + sizeof(struct sockaddr)); + if (ret < 0) { + goto out; + } + } + + if (opt & ABT_SOCK_USE_LISTEN) { + ret = abt_sock_listen(abt, sock, + addr_in, 5); + if (ret < 0) { + goto out; + } + } + + if (opt & ABT_SOCK_USE_CONN) { + abt->client_connect_trying = 1; + ret = abt_sock_connect(abt, sock, + addr_in, + sizeof(struct sockaddr), !O_NONBLOCK); + abt->client_connect_trying = 0; + if (ret < 0) { + goto out; + } + } + + t_abt_dbg_base(abt, "sock init[%d, %d] done : " ABT_IP_FORMAT ":%d, %d\n", + tool, opt, + ABT_IP_PRT_PARAM(__be32_to_cpu(s_addr)), + __be16_to_cpu(s_port), + addr_in->sin_family + ); + + return 0; + +out: + t_abt_err(abt, "sock init[%d, %d] failed, %d\n", + tool, opt, ret); + return ret; +} + +static void abt_ksocket_thread_body(struct siw_hal_abt_data *abt, + unsigned char *buf, int bufsize, + int boundary, int delay) +{ + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + int size; + int ret; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + + memset(buf, 0, bufsize); + + if (atomic_read(&abt_comm->running) != ABT_RUNNING_ON) { + t_abt_dbg_base(abt, "running off\n"); + break; + } + + // mutex_lock(&abt->abt_comm_lock); + size = abt_ksocket_recv(abt, buf, bufsize); + // mutex_unlock(&abt->abt_comm_lock); + + ret = abt_sock_msg_chk_size(abt, size, boundary, + "abt_ksocket_recv"); + if (ret < 0) { + break; + } + + if (kthread_should_stop()) { + t_abt_dbg_base(abt, "kthread_should_stop\n"); + break; + } + + touch_msleep(delay); + } +} + +static void abt_ksocket_thread_exit(struct siw_hal_abt_data *abt, + struct socket **res, + struct sockaddr_in *addr) +{ + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + struct socket *sock = *res; + int tool = abt->abt_conn_tool; + + mutex_lock(&abt->abt_comm_lock); + + if (*res != NULL) { + if (atomic_read(&abt_comm->running) != ABT_RUNNING_EXIT) { + abt_sock_shutdown(abt, sock, addr); + } + sock_release(sock); + *res = NULL; + } + + abt_comm->thread = NULL; + atomic_set(&abt_comm->running, ABT_RUNNING_OFF); + + abt->abt_conn_tool = ABT_CONN_NOTHING; + + t_abt_dbg_base(abt, "thread terminated[%d]\n", tool); + + mutex_unlock(&abt->abt_comm_lock); +} + +static int abt_ksocket_thread_for_pctool(void *data) +{ + struct siw_hal_abt_data *abt = data; + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; +// struct socket *sock = NULL; + unsigned char *buf = NULL; + int bufsize = 0; + int tool = abt->abt_conn_tool; + int ret = 0; + + /* kernel thread initialization */ + atomic_set(&abt_comm->running, ABT_RUNNING_ON); +// abt_comm->dev = dev; + + ret = abt_ksocket_thread_init(abt, tool, + ABT_SOCK_PCTOOL_OPT); + if (ret) { + atomic_set(&abt_comm->running, ABT_RUNNING_EXIT); + goto out; + } + + abt->abt_report_point = 1; + abt->abt_report_ocd = 1; + + abt->client_connected = 1; + t_abt_info(abt, + "TCP connected with TS (ip %s, port %d)\n", + abt_comm->send_ip, t_abt_port_tcp); + + buf = kzalloc(sizeof(struct siw_abt_comm_packet), GFP_KERNEL); + bufsize = sizeof(struct siw_abt_comm_packet); + + abt_ksocket_thread_body(abt, buf, bufsize, 1, 0); + + kfree(buf); + +out: + __set_current_state(TASK_RUNNING); + + abt_ksocket_thread_exit(abt, + &abt_comm->ts_sock, + &abt_comm->ts_addr); + + abt->client_connected = 0; + + abt->abt_report_point = 0; + abt->abt_report_ocd = 0; + + t_abt_info(abt, "%s terminated\n", abt_conn_name(tool)); + + return ret; +} + + +#define abt_kthread_run(_abt, _func) \ + ({ \ + struct task_struct *_thread; \ + t_abt_dbg_base(_abt, "Run kthread for %s\n", #_func); \ + _thread = kthread_run(_func, _abt, "abt-%s", dev_name(_abt->dev)); \ + _thread; \ + }) + +static int abt_ksocket_init(struct siw_hal_abt_data *abt, + char *ip, int tool, + abt_sock_listener_t listener) +{ +// struct device *dev = abt->dev; + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + struct task_struct *thread = NULL; + + abt->abt_conn_tool = tool; + + if (abt_conn_is_invalid(abt)) { + return -EINVAL; + } + + t_abt_info(abt, "Tool Start\n"); + + abt->abt_socket_report_mode = 1; + + memcpy(abt_comm->send_ip, ip, ABT_SEND_IP_SIZE); + + switch(abt->abt_conn_tool) { + case ABT_CONN_STUDIO: + /* Reserved */ + break; + default: /* ABT_CONN_TOUCH */ + thread = abt_kthread_run(abt, + abt_ksocket_thread_for_pctool); + break; + } + if (IS_ERR(thread)) { + t_abt_err(abt, "unable to start kernel thread\n"); + return -ENOMEM; + } + + abt_comm->thread = thread; + abt_comm->sock_listener = listener; + + siw_touch_mon_pause(abt->dev); + + return 0; +} + +enum { + SHOW_ABT_TOOL_MODE_SIZE = 4, + SHOW_ABT_TOOL_IP_SIZE = 16, + SHOW_ABT_TOOL_RET_SIZE = (SHOW_ABT_TOOL_MODE_SIZE + SHOW_ABT_TOOL_IP_SIZE), +}; + +/* + * binary + */ +static ssize_t abt_show_tool_b(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_abt_data *abt = (struct siw_hal_abt_data *)ts->abt; + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + int size = 0; + + buf[0] = abt->abt_report_mode_onoff; + size = SHOW_ABT_TOOL_MODE_SIZE; + + if (atomic_read(&abt_comm->running) == ABT_RUNNING_OFF) { + memcpy((u8 *)&buf[1], NONE_IP, strlen(NONE_IP)); + size += strlen(NONE_IP); + goto out; + } + + memcpy((u8 *)&buf[1], (u8 *)abt_comm->send_ip, + SHOW_ABT_TOOL_IP_SIZE); + size += SHOW_ABT_TOOL_IP_SIZE; + +out: + t_abt_info(abt, "read raw report mode - mode:%d ip:%s\n", + buf[0], (char *)&buf[1]); + + return (ssize_t)size; +} + +/* + * text (echo) + */ +static ssize_t abt_show_tool_t(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_abt_data *abt = (struct siw_hal_abt_data *)ts->abt; + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + int mode = abt->abt_report_mode_onoff; + int size = 0; + + if (atomic_read(&abt_comm->running) == ABT_RUNNING_OFF) { + size += siw_snprintf(buf, size, + "mode:%d, ip:%s\n", + mode, NONE_IP); + goto out; + } + + size += siw_snprintf(buf, size, + "mode:%d, ip:%s\n", + mode, abt_comm->send_ip); + +out: + t_abt_info(abt, "read raw report mode - mode:%d ip:%s\n", + mode, abt_comm->send_ip); + + return (ssize_t)size; +} + + +#define STORE_ABT_TOOL_MODE_MAX '0' + +enum { + STORE_ABT_MODE_STUDIO = 1, + STORE_ABT_MODE_TOUCH, + STORE_ABT_MODE_MAX, +}; + +static int abt_tool_do_start(struct siw_hal_abt_data *abt, + char *ip, int tool, + abt_sock_listener_t listener) +{ + int ret = 0; + + abt->abt_report_point = 0; + abt->abt_report_ocd = 0; + + ret = abt_ksocket_init(abt, ip, tool, listener); + if (ret) { + t_abt_err(abt, "ksocket init[%d] failed, %d\n", + abt->abt_conn_tool, ret); + return ret; + } + abt_set_data_func(abt, 1); + return 0; +} + +static int abt_tool_start(struct siw_hal_abt_data *abt, + char *ip, int tool, + abt_sock_listener_t listener) +{ + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + + if (abt_comm->thread != NULL) { + if (!memcmp((u8 *)abt_comm->send_ip, (u8 *)ip, ABT_SEND_IP_SIZE)) { + return 0; + } + + t_abt_info(abt, "IP changed : ksocket exit, then restart\n"); + + abt_ksocket_exit(abt); + } + + return abt_tool_do_start(abt, ip, tool, listener); +} + +static int abt_store_tool_touch(struct siw_hal_abt_data *abt, char *ip) +{ + int ret = 0; + + ret = abt_tool_start(abt, ip, + ABT_CONN_TOUCH, abt_ksocket_recv_from_pctool); + + return ret; +} + +static int abt_store_tool_exit_chk(struct siw_hal_abt_data *abt) +{ + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + + if (atomic_read(&abt_comm->running) != ABT_RUNNING_OFF) + return 1; + + if (abt_comm->thread != NULL) + return 1; + + if (abt_comm->sock != NULL) + return 1; + + if (abt_comm->ts_sock != NULL) + return 1; + + if (abt_comm->sock_send != NULL) + return 1; + + if (abt_comm->send_connected) + return 1; + + if (abt_comm->curr_sock != NULL) + return 1; + + return 0; +} + +static int abt_store_tool_exit(struct siw_hal_abt_data *abt, char *ip) +{ +// struct device *dev = abt->dev; + + mutex_lock(&abt->abt_comm_lock); + if (!abt_store_tool_exit_chk(abt)) { + mutex_unlock(&abt->abt_comm_lock); + return 0; + } + mutex_unlock(&abt->abt_comm_lock); + + abt_ksocket_exit(abt); + abt_set_data_func(abt, 0); + + mutex_lock(&abt->abt_comm_lock); + abt_set_report_mode(abt, 0); + mutex_unlock(&abt->abt_comm_lock); + return 0; +} + +static ssize_t __abt_store_tool(struct device *dev, + const char *buf, size_t count, int opt) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_abt_data *abt = (struct siw_hal_abt_data *)ts->abt; + int mode = 0; + char __ip[ABT_SEND_IP_SIZE+1] = { '0', }; + char *ip = NULL; + bool setFlag = false; + int ret; + + if (atomic_read(&ts->state.debug_tool) != DEBUG_TOOL_ENABLE){ + t_abt_err(abt, "tool disabled\n"); + return count; + } + + if (opt) { + ip = __ip; + if (sscanf(buf, "%d %s", &mode, ip) <= 0) { + siw_abt_sysfs_err_invalid_param(abt); + return count; + } + } else { + mode = buf[0]; + if (mode >= '0') { + mode -= '0'; + } + ip = (char *)&buf[1]; + } + + if (mode && mode < STORE_ABT_MODE_MAX) { + if (!ip[0] || (ip[0] == '0')) { + t_abt_err(abt, "Invalid IP\n"); + return count; + } + } + + t_abt_info(abt, + "set raw report mode - mode:%d, IP:%s\n", + mode, ip); + + switch (mode) { + case STORE_ABT_MODE_STUDIO: + t_abt_info(abt, "Not supperted\n"); + break; + + case STORE_ABT_MODE_TOUCH: + ret = abt_store_tool_touch(abt, ip); + break; + + default: + ret = abt_store_tool_exit(abt, ip); + break; + } + + if (setFlag) { + mutex_lock(&abt->abt_comm_lock); + abt_set_report_mode(abt, mode); + mutex_unlock(&abt->abt_comm_lock); + } + + if (!opt) { + if (touch_test_abt_quirks(ts, ABT_QUIRK_RAW_RETURN_MODE_VAL)) { + return (ssize_t)mode; + } + } + + return count; +} + +/* + * binary + */ +static ssize_t abt_store_tool_b(struct device *dev, + const char *buf, size_t count) +{ + return __abt_store_tool(dev, buf, count, 0); +} + +/* + * text (echo) + */ +static ssize_t abt_store_tool_t(struct device *dev, + const char *buf, size_t count) +{ + return __abt_store_tool(dev, buf, count, 1); +} + +static void __used siw_hal_abt_report_mode(struct device *dev, u8 *all_data) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_abt_data *abt = (struct siw_hal_abt_data *)ts->abt; + struct siw_hal_abt_comm *abt_comm = &abt->abt_comm; + struct siw_abt_send_data *packet_ptr = NULL; + struct siw_abt_dbg_report_hdr *d_header = NULL; + struct siw_hal_touch_info *t_info = NULL; + struct timeval t_stamp; + u32 rst_offset_val; + u32 dbg_offset; + u8 *d_data_ptr; + int d_header_size; + int i; + int ret = 0; + + packet_ptr = abt_comm->data_send; + d_header = (struct siw_abt_dbg_report_hdr *)(abt_comm->data_send->data); + d_header_size = sizeof(struct siw_abt_dbg_report_hdr); + t_info = (struct siw_hal_touch_info *)all_data; + rst_offset_val = 1; + dbg_offset = abt->dbg_offset_base; + d_data_ptr = (u8 *)d_header + d_header_size; + + if (abt->abt_report_mode) { + ret = abt_read_memory(abt, + reg->data_i2cbase_addr, + reg->serial_data_offset, + dbg_offset, + (int)sizeof(u32), + (u8 *)d_header, + d_header_size); + if (ret < 0) { + t_abt_err(abt, + "Report reg addr read failed(%d, %d), %d\n", + dbg_offset, + (int)sizeof(u32), + ret); + } + + dbg_offset += (d_header_size>>2); + if (d_header->type == abt->abt_report_mode) { + for (i = 0; i < d_header->data_size>>MAX_RW_SIZE_POW; i++) { + ret = abt_read_memory(abt, + reg->data_i2cbase_addr, + reg->serial_data_offset, + dbg_offset, + (int)sizeof(u32), + d_data_ptr, + sizeof(u8)<>2; + } + + if (d_header->data_size % MAX_RW_SIZE != 0) { + ret = abt_read_memory(abt, + reg->data_i2cbase_addr, + reg->serial_data_offset, + dbg_offset, + (int)sizeof(u32), + d_data_ptr, + sizeof(u8)*(d_header->data_size%MAX_RW_SIZE)); + if (ret < 0) { + t_abt_err(abt, + "Report reg addr read failed(%d, %d), %d\n", + dbg_offset, + (int)sizeof(u32), + ret); + } + + d_data_ptr += (sizeof(u8)*(d_header->data_size + % MAX_RW_SIZE)); + dbg_offset += (sizeof(u8)*(d_header->data_size + % MAX_RW_SIZE)>>2); + } + } else { + t_abt_err(abt, "debug data load error : type %d, size %d\n", + d_header->type, d_header->data_size); + } + ret = siw_hal_reg_write(dev, + reg->tc_interrupt_status, + (void *)&rst_offset_val, sizeof(rst_offset_val)); + } else + packet_ptr->touchCnt = 0; + + /* ABS0 */ + if (t_info->wakeup_type == 0) { + if (abt->abt_report_ocd) { + memcpy(d_data_ptr, &all_data[36<<2], sizeof(u8) * 112); + d_data_ptr += (sizeof(u8) * 112); + } + + if (t_info->data[0].track_id != 15) { + if (abt->abt_report_point) { + packet_ptr->touchCnt = t_info->touch_cnt; + memcpy(d_data_ptr, &t_info->data[0], + sizeof(struct siw_hal_touch_data) * t_info->touch_cnt); + d_data_ptr += sizeof(struct siw_hal_touch_data) + * t_info->touch_cnt; + } else + packet_ptr->touchCnt = 0; + } + } + + + if (((u8 *)d_data_ptr) - ((u8 *)packet_ptr) > 0) { + do_gettimeofday(&t_stamp); + + abt->frame_num++; + packet_ptr->type = DEBUG_DATA; + packet_ptr->mode = abt->abt_report_mode; + packet_ptr->frame_num = abt->frame_num; + packet_ptr->timestamp = + t_stamp.tv_sec * 1000000 + t_stamp.tv_usec; + + packet_ptr->flag = 0; + if (abt->abt_report_point) + packet_ptr->flag |= 0x1; + if (abt->abt_report_ocd) + packet_ptr->flag |= (0x1)<<1; + + abt_ksocket_raw_data_send(abt, (u8 *)packet_ptr, + (u8 *)d_data_ptr - (u8 *)packet_ptr); + } +} + +static int siw_hal_abt_irq_handler(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_abt_data *abt = (struct siw_hal_abt_data *)ts->abt; + u8 all_data[264]; + int report_mode = abt_is_set_func(abt); + int ret = 0; + + if (atomic_read(&chip->init) == IC_INIT_NEED) { + t_dev_warn(dev, "Not Ready, Need IC init\n"); + return 0; + } + +#if defined(__SIW_SUPPORT_PM_QOS) + pm_qos_update_request(&chip->pm_qos_req, 10); +#endif + ret = siw_hal_reg_read(dev, + reg->tc_ic_status, + (void *)all_data, sizeof(all_data)); +#if defined(__SIW_SUPPORT_PM_QOS) + pm_qos_update_request(&chip->pm_qos_req, PM_QOS_DEFAULT_VALUE); +#endif + if (ret < 0) { + goto out; + } + + memcpy(&chip->info, all_data, sizeof(chip->info)); + + ret = siw_ops_chk_status(ts); + if (ret < 0) { + goto out; + } + + if (report_mode) { + if (chip->info.wakeup_type == ABS_MODE) + ret = siw_ops_irq_abs(ts); + else + ret = siw_ops_irq_lpwg(ts); + + siw_hal_abt_report_mode(dev, all_data); + goto out; + } + + if (chip->info.wakeup_type == ABS_MODE) { + ret = siw_ops_irq_abs(ts); + goto out; + } + + ret = siw_ops_irq_lpwg(ts); + +out: + return ret; +} + +void siw_hal_switch_to_abt_irq_handler(struct siw_ts *ts) +{ + siw_ops_set_irq_handler(ts, (void *)siw_hal_abt_irq_handler); + t_dev_info(ts->dev, "set new irq handler for ABT\n"); +} + +#if defined(__SIW_ATTR_PERMISSION_ALL) +#define __TOUCH_ABT_PERM (S_IRUGO | S_IWUGO) +#else +#define __TOUCH_ABT_PERM (S_IRUGO | S_IWUSR | S_IWGRP) +#endif + +#define SIW_TOUCH_HAL_ABT_ATTR(_name, _show, _store) \ + __TOUCH_ATTR(_name, __TOUCH_ABT_PERM, _show, _store) + +#define _SIW_TOUCH_HAL_ABT_T(_name) \ + touch_attr_##_name + +static SIW_TOUCH_HAL_ABT_ATTR(raw_report, abt_show_tool_b, abt_store_tool_b); +static SIW_TOUCH_HAL_ABT_ATTR(raw_report_t, abt_show_tool_t, abt_store_tool_t); + +static struct attribute *siw_hal_abt_attribute_list[] = { + &_SIW_TOUCH_HAL_ABT_T(raw_report).attr, + &_SIW_TOUCH_HAL_ABT_T(raw_report_t).attr, + NULL, +}; + +static const struct attribute_group __used siw_hal_abt_attribute_group = { + .attrs = siw_hal_abt_attribute_list, +}; + +static struct siw_hal_abt_data *siw_hal_abt_alloc(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_abt_data *abt = NULL; + struct siw_abt_send_data *data_send = NULL; + struct siw_abt_comm_packet *send_packet = NULL; + + abt = touch_kzalloc(dev, sizeof(*abt), GFP_KERNEL); + if (!abt) { + t_dev_err(dev, + "failed to allocate memory for abt\n"); + goto out; + } + + data_send = touch_kzalloc(dev, sizeof(struct siw_abt_send_data), GFP_KERNEL); + if (!data_send) { + t_dev_err(dev, + "failed to allocate data_send\n"); + goto out_data_send; + } + abt->abt_comm.data_send = data_send; + + send_packet = touch_kzalloc(dev, sizeof(struct siw_abt_comm_packet), GFP_KERNEL); + if (!send_packet) { + t_dev_err(dev, + "failed to allocate send_packet\n"); + goto out_send_packet; + } + abt->abt_comm.send_packet = send_packet; + + t_dev_dbg_base(dev, "create abt (0x%zX)\n", (size_t)sizeof(*abt)); + + abt->dev = ts->dev; + + mutex_init(&abt->abt_comm_lock); + mutex_init(&abt->abt_socket_lock); + abt->abt_socket_mutex_flag = 1; + + abt->prev_rnd_piece_no = DEF_RNDCPY_EVERY_NTH_FRAME; + + abt->abt_conn_tool = ABT_CONN_NOTHING; + + abt->set_get_data_func = 0; + + switch (touch_chip_type(ts)) { + case CHIP_LG4894: + abt->dbg_offset_base = 0x2A98; + break; + case CHIP_LG4895: + abt->dbg_offset_base = 0x2E10; + break; + default: + abt->dbg_offset_base = 0x3800; + break; + } + abt->dbg_offset_base >>= 2; + abt->dbg_offset = abt->dbg_offset_base; + + ts->abt = abt; + + return abt; + +out_send_packet: + touch_kfree(dev, send_packet); + +out_data_send: + touch_kfree(dev, abt); + +out: + return NULL; +} + +static void siw_hal_abt_free(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_abt_data *abt = (struct siw_hal_abt_data *)ts->abt; + + if (abt) { + t_dev_dbg_base(dev, "free abt\n"); + + abt_store_tool_exit(abt, NULL); + + mutex_destroy(&abt->abt_comm_lock); + mutex_destroy(&abt->abt_socket_lock); + ts->abt = NULL; + + touch_kfree(dev, abt->abt_comm.send_packet); + touch_kfree(dev, abt->abt_comm.data_send); + touch_kfree(dev, abt); + } +} + +int siw_hal_abt_init(struct device *dev) +{ + /* Reserved */ + + return 0; +} + +static int siw_hal_abt_create_sysfs(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct device *idev = &ts->input->dev; + struct kobject *kobj = &ts->kobj; + struct siw_hal_abt_data *abt; + int ret = 0; + + if (kobj->parent != idev->kobj.parent) { + t_dev_err(dev, "Invalid kobject\n"); + return -EINVAL; + } + + abt = siw_hal_abt_alloc(dev); + if (!abt) { + ret = -ENOMEM; + goto out; + } + + ret = sysfs_create_group(kobj, &siw_hal_abt_attribute_group); + if (ret < 0) { + t_dev_err(dev, "%s abt sysfs register failed, %d\n", + touch_chip_name(ts), ret); + goto out_sysfs; + } + + t_dev_dbg_base(dev, "%s abt sysfs registered\n", + touch_chip_name(ts)); + + return 0; + +out_sysfs: + siw_hal_abt_free(dev); + +out: + return ret; +} + +static void siw_hal_abt_remove_sysfs(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct device *idev = &ts->input->dev; + struct kobject *kobj = &ts->kobj; + + if (kobj->parent != idev->kobj.parent) { + t_dev_err(dev, "Invalid kobject\n"); + return; + } + + if (ts->abt == NULL) { + return; + } + + sysfs_remove_group(kobj, &siw_hal_abt_attribute_group); + + siw_hal_abt_free(dev); + + t_dev_dbg_base(dev, "abt sysfs unregistered\n"); +} + +int siw_hal_abt_sysfs(struct device *dev, int on_off) +{ + if (on_off == DRIVER_INIT) { + return siw_hal_abt_create_sysfs(dev); + } + + siw_hal_abt_remove_sysfs(dev); + return 0; + +} + +__siw_setup_u32("siw_abt_port_default=", abt_setup_port_default, t_abt_port_default); +__siw_setup_u32("siw_abt_port_tcp=", abt_setup_port_tcp, t_abt_port_tcp); +__siw_setup_u32("siw_abt_port_send=", abt_setup_port_send, t_abt_port_send); + +#endif /* __SIW_SUPPORT_ABT */ + + diff --git a/drivers/input/touchscreen/siw/siw_touch_hal_prd.c b/drivers/input/touchscreen/siw/siw_touch_hal_prd.c new file mode 100644 index 000000000000..ceee33c0e4fe --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_hal_prd.c @@ -0,0 +1,6750 @@ +/* + * siw_touch_hal_prd.c - SiW touch hal driver for PRD + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Sungyeal Park + * Hyunho Kim + * + * 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 "siw_touch_cfg.h" + +#if defined(__SIW_SUPPORT_PRD) //See siw_touch_cfg.h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siw_touch.h" +#include "siw_touch_hal.h" +#include "siw_touch_bus.h" +#include "siw_touch_event.h" +#include "siw_touch_gpio.h" +#include "siw_touch_irq.h" +#include "siw_touch_sys.h" + +/* + * [PRD] + * Touch IC Production Test + * This supports a method to verify the quality of Touch IC using input/output file test. + * + * 1. Input setup file(spec file) + * 2. Self testing + * 3. Output result file(self test result) + * 4. Verify the result + * + */ + +#define __SIW_SUPPORT_PRD_SET_SD + +#if defined(__SIW_SUPPORT_PRD_SET_SD) +//#define __SIW_SUPPORT_PRD_SET_SD_ONLY +#endif + +enum { + PRD_LINE_NUM = (1<<10), +// PRD_PATH_SIZE = (1<<6), //64 +// PRD_BURST_SIZE = (1<<9), //512 + /* */ + MAX_LOG_FILE_COUNT = (4), + MAX_LOG_FILE_SIZE = (10 * (1<<20)), /* 10M byte */ + /* */ + MAX_TEST_CNT = 2, +}; + +enum { + PRD_RAWDATA_SZ_POW = 1, + PRD_RAWDATA_SIZE = (1<> 24) +#define PRD_OFFSET_QUIRK_GET_OFFSET(_addr) ((_addr) & 0x00FFFFFF) + +#define __PRD_PARAM_DIMENSION(_row, _col, _col_add, _ch, _m1_col, _m1_cnt, _m2_cnt) \ + .row = (_row), .col = (_col), .col_add = (_col_add), .ch = (_ch), \ + .m1_col = (_m1_col), .m1_cnt = (_m1_cnt), .m2_cnt = (_m2_cnt) + +#define __PRD_2ND_SCR(_bound_i, _bound_j) \ + .second_scr = { _bound_i, _bound_j } + +static const char *prd_param_name_lg4894_k[] = { + "L0W53K6P", NULL +}; + +static const char *prd_param_name_lg4894_t[] = { + "T0W53CV3", "TJW53CV3", NULL +}; + +static const char *prd_param_name_lg4894_cv[] = { + "L0W53CV3", "L0W50CV1", "LJW53CV3", NULL +}; + +static const char *prd_param_name_lg4894_lv[] = { + "L0W53LV5", "L0W50LV3", NULL +}; + +static const char *prd_param_name_lg4894_sf[] = { + "L0W53SF3", NULL +}; + +static const char *prd_param_name_lg4895_k[] = { + "LPW49K5", NULL +}; + +static const char *prd_param_name_lg4946_g[] = { + "L0L53P1", "L0W53P1", NULL +}; + +static const char *prd_param_name_sw49501_type_1[] = { + "AURORA58", NULL +}; + +static const char *prd_param_name_sw49106_type_1[] = { + "CSOTDEMO", NULL +}; + +enum { + SD_FLAG_LG4894 = (0 | \ + U3_M2_RAWDATA_TEST_FLAG | \ + OPEN_SHORT_NODE_TEST_FLAG | \ + U3_JITTER_TEST_FLAG | \ + U3_BLU_JITTER_TEST_FLAG | \ + OPEN_SHORT_RESULT_DATA_FLAG | \ + 0), + LPWG_SD_FLAG_LG4894 = (0 | \ + U0_M2_RAWDATA_TEST_FLAG | \ + U0_M1_RAWDATA_TEST_FLAG | \ + U0_JITTER_TEST_FLAG | \ + 0), +}; + +enum { + SD_FLAG_LG4895 = (0 | \ + U3_M2_RAWDATA_TEST_FLAG | \ + OPEN_SHORT_NODE_TEST_FLAG | \ + U3_BLU_JITTER_TEST_FLAG | \ + OPEN_SHORT_RESULT_DATA_FLAG | \ + 0), + LPWG_SD_FLAG_LG4895 = (0 | \ + U0_M2_RAWDATA_TEST_FLAG | \ + U0_M1_RAWDATA_TEST_FLAG | \ + 0), +}; + +enum { + SD_FLAG_LG4946 = (0 | \ + U3_M2_RAWDATA_TEST_FLAG | \ + OPEN_SHORT_NODE_TEST_FLAG | \ + OPEN_SHORT_RESULT_RAWDATA_FLAG | \ + 0), + LPWG_SD_FLAG_LG4946 = (0 | \ + U0_M2_RAWDATA_TEST_FLAG | \ + U0_M1_RAWDATA_TEST_FLAG | \ + 0), +}; + +enum { + __SD_FLAG_SW49XXX = (0 | \ + U3_M2_RAWDATA_TEST_FLAG | \ + OPEN_SHORT_NODE_TEST_FLAG | \ + U3_JITTER_TEST_FLAG | \ + OPEN_SHORT_RESULT_RAWDATA_FLAG | \ + 0), + + __LPWG_SD_FLAG_SW49XXX = (0 | \ + U0_M2_RAWDATA_TEST_FLAG | \ + U0_M1_RAWDATA_TEST_FLAG | \ + 0), + + SD_FLAG_SW49105 = __SD_FLAG_SW49XXX | OPEN_SHORT_RESULT_DATA_FLAG, + LPWG_SD_FLAG_SW49105 = __LPWG_SD_FLAG_SW49XXX| U0_JITTER_TEST_FLAG, + + SD_FLAG_SW49106 = __SD_FLAG_SW49XXX | OPEN_SHORT_RESULT_DATA_FLAG, + LPWG_SD_FLAG_SW49106 = __LPWG_SD_FLAG_SW49XXX | U0_JITTER_TEST_FLAG, + + SD_FLAG_SW49406 = __SD_FLAG_SW49XXX, + LPWG_SD_FLAG_SW49406 = __LPWG_SD_FLAG_SW49XXX | U0_JITTER_TEST_FLAG, + + SD_FLAG_SW49407 = __SD_FLAG_SW49XXX, + LPWG_SD_FLAG_SW49407 = __LPWG_SD_FLAG_SW49XXX | U0_M1_JITTER_TEST_FLAG, + + SD_FLAG_SW49408 = __SD_FLAG_SW49XXX, + LPWG_SD_FLAG_SW49408 = __LPWG_SD_FLAG_SW49XXX | U0_JITTER_TEST_FLAG, + + SD_FLAG_SW49501 = __SD_FLAG_SW49XXX, +}; + +enum { + SD_FLAG_SW1828 = (0 | \ + U3_M2_RAWDATA_TEST_FLAG | \ + OPEN_SHORT_NODE_TEST_FLAG | \ + U3_JITTER_TEST_FLAG | \ + 0), +}; + +static const struct siw_hal_prd_param prd_params[] = { + /* + * LG4894 group + */ + { .chip_type = CHIP_LG4894, + .name = prd_param_name_lg4894_k, + .cmd_type = PRD_CMD_TYPE_2, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xA8C), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xC0F), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_ODD, 0xCD2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xD95), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0xE83), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0xBCF), + 0, + }, + __PRD_PARAM_DIMENSION(26, 15, 1, 32, PRD_M1_COL_SIZE, 2, 2), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = 0, + .sd_test_flag = SD_FLAG_LG4894, + .lpwg_sd_test_flag = LPWG_SD_FLAG_LG4894, + }, + { .chip_type = CHIP_LG4894, + .name = prd_param_name_lg4894_lv, + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xA8C), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xC0F), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_ODD, 0xCD2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xD95), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0xE83), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0xBCF), + 0, + }, + __PRD_PARAM_DIMENSION(26, 15, 1, 32, PRD_M1_COL_SIZE, 2, 2), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = 0, + .sd_test_flag = SD_FLAG_LG4894, + .lpwg_sd_test_flag = LPWG_SD_FLAG_LG4894, + }, + { .chip_type = CHIP_LG4894, + .name = prd_param_name_lg4894_sf, + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xA02), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xB22), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xC42), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0xD96), + 0, + }, + __PRD_PARAM_DIMENSION(32, 18, 0, 32, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = (PRD_SYS_EN_DEBUG_BUF|PRD_SYS_EN_APP_DEBUG_BUF), + .sd_test_flag = SD_FLAG_LG4894, + .lpwg_sd_test_flag = LPWG_SD_FLAG_LG4894, + }, + { .chip_type = CHIP_LG4894, + .name = prd_param_name_lg4894_cv, + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xC0F), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xCD2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xD95), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0xE83), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0xA8C), + 0, + }, + __PRD_PARAM_DIMENSION(26, 15, 1, 32, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = 0, + .sd_test_flag = (SD_FLAG_LG4894 & ~U3_BLU_JITTER_TEST_FLAG), + .lpwg_sd_test_flag = LPWG_SD_FLAG_LG4894, + }, + { .chip_type = CHIP_LG4894, + .name = prd_param_name_lg4894_t, + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xA02), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xB22), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xC42), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0xD96), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0x9CE), + 0, + }, + __PRD_PARAM_DIMENSION(32, 18, 0, 32, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = 0, + .sd_test_flag = (SD_FLAG_LG4894 & ~U3_BLU_JITTER_TEST_FLAG), + .lpwg_sd_test_flag = LPWG_SD_FLAG_LG4894, + }, + { .chip_type = CHIP_LG4894, + .name = NULL, //NULL meas 'Last & Default' + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xA02), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xB22), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xC42), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0xD96), + 0, + }, + __PRD_PARAM_DIMENSION(32, 18, 0, 32, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = (PRD_SYS_EN_DEBUG_BUF|PRD_SYS_EN_APP_DEBUG_BUF), + .sd_test_flag = SD_FLAG_LG4894, + .lpwg_sd_test_flag = LPWG_SD_FLAG_LG4894, + }, + /* + * LG4895 group + */ + { .chip_type = CHIP_LG4895, + .name = prd_param_name_lg4895_k, + .cmd_type = PRD_CMD_TYPE_2, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xD1C), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xE4E), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xF80), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0x10E8), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_F_DELTA, 0x7FD), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0xADA), + 0, + }, + __PRD_PARAM_DIMENSION(34, 18, 0, 32+2, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(1, 4), + .sysfs_off_flag = 0, + .sd_test_flag = SD_FLAG_LG4895, + .lpwg_sd_test_flag = LPWG_SD_FLAG_LG4895, + }, + { .chip_type = CHIP_LG4895, + .name = NULL, //NULL meas 'Last & Default' + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xD1C), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xE4E), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xF80), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0x10E8), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_F_DELTA, 0x7FD), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0xADA), + 0, + }, + __PRD_PARAM_DIMENSION(34, 18, 0, 32+2, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(1, 4), + .sysfs_off_flag = 0, + .sd_test_flag = SD_FLAG_LG4895, + .lpwg_sd_test_flag = LPWG_SD_FLAG_LG4895, + }, + /* + * LG4946 group + */ + { .chip_type = CHIP_LG4946, + .name = prd_param_name_lg4946_g, + .cmd_type = PRD_CMD_TYPE_2, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xB82), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xCA2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xDC2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0xF16), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_F_DELTA, 0x7FD), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0xADA), + 0, + }, + __PRD_PARAM_DIMENSION(32, 18, 0, 32, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = 0, + .sd_test_flag = SD_FLAG_LG4946, + .lpwg_sd_test_flag = LPWG_SD_FLAG_LG4946, + }, + { .chip_type = CHIP_LG4946, + .name = NULL, //NULL meas 'Last & Default' + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xB82), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xCA2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xDC2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0xF16), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0xADA), + 0, + }, + __PRD_PARAM_DIMENSION(32, 18, 0, 32, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = 0, + .sd_test_flag = SD_FLAG_LG4946, + .lpwg_sd_test_flag = LPWG_SD_FLAG_LG4946, + }, + /* + * SW49105 group (Not fixed) + */ + { .chip_type = CHIP_SW49105, + .name = NULL, //NULL meas 'Last & Default' + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xB85), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xCA5), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xDC5), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0xF19), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0xB0F), + 0, + }, + __PRD_PARAM_DIMENSION(30, 18, 0, 32, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = 0, + .sd_test_flag = SD_FLAG_SW49105, + .lpwg_sd_test_flag = LPWG_SD_FLAG_SW49105, + }, + /* + * SW49106 group (Not fixed) + */ + { .chip_type = CHIP_SW49106, + .name = prd_param_name_sw49106_type_1, + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xD82), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xEA2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xFC2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0x1116), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0xC3F), + 0, + }, + __PRD_PARAM_DIMENSION(30, 18, 0, 32, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = 0, + .sd_test_flag = SD_FLAG_SW49106, + .lpwg_sd_test_flag = LPWG_SD_FLAG_SW49106, + }, + { .chip_type = CHIP_SW49106, + .name = NULL, //NULL meas 'Last & Default' + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xF88), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0x104C), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0x1110), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0x1200), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0xC3F), + 0, + }, + __PRD_PARAM_DIMENSION(28, 14, 0, 32, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = 0, + .sd_test_flag = SD_FLAG_SW49106, + .lpwg_sd_test_flag = LPWG_SD_FLAG_SW49106, + }, + /* + * SW49406 group (Not fixed) + */ + { .chip_type = CHIP_SW49406, + .name = NULL, //NULL meas 'Last & Default' + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xB82), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xCA2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xDC2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0xF16), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0xADA), + 0, + }, + __PRD_PARAM_DIMENSION(32, 18, 0, 32, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = 0, + .sd_test_flag = SD_FLAG_SW49406, + .lpwg_sd_test_flag = LPWG_SD_FLAG_SW49406, + }, + /* + * SW49407 group + */ + { .chip_type = CHIP_SW49407, + .name = NULL, //NULL meas 'Last & Default' + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xF1C), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0x104E), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0x1180), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0x12E8), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0xCCF), + 0, + }, + __PRD_PARAM_DIMENSION(32+2, 18, 0, 32+2, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(1, 4), + .sysfs_off_flag = 0, + .sd_test_flag = SD_FLAG_SW49407, + .lpwg_sd_test_flag = LPWG_SD_FLAG_SW49407, + }, + /* + * SW49408 group (Not fixed) + */ + { .chip_type = CHIP_SW49408, + .name = NULL, //NULL meas 'Last & Default' + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xB85), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xCA5), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xDC5), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0xF19), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0xB0F), + 0, + }, + __PRD_PARAM_DIMENSION(30, 18, 0, 32, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = 0, + .sd_test_flag = SD_FLAG_SW49408, + .lpwg_sd_test_flag = LPWG_SD_FLAG_SW49408, + }, + /* + * SW49501 group + */ + { .chip_type = CHIP_SW49501, + .name = prd_param_name_sw49501_type_1, + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0x1982), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0x1AA2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0x1BC2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0x1D16), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0x14C8), + 0, + }, + __PRD_PARAM_DIMENSION(32, 18, 0, 32, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = PRD_SYS_EN_LPWG_SD, + .sd_test_flag = SD_FLAG_SW49501 | \ + U3_M1_RAWDATA_TEST_FLAG | \ + U3_M1_JITTER_TEST_FLAG | \ + SHORT_FULL_TEST_FLAG | \ + IRQ_TEST_FLAG | \ + OPEN_SHORT_RESULT_DATA_FLAG | \ + OPEN_SHORT_RESULT_ALWAYS_FLAG, + .lpwg_sd_test_flag = 0, + }, + { .chip_type = CHIP_SW49501, + .name = NULL, //NULL meas 'Last & Default' + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0x1982), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0x1AA2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0x1BC2), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0x1D16), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DEBUG, 0x14C8), + 0, + }, + __PRD_PARAM_DIMENSION(32, 18, 0, 32, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = PRD_SYS_EN_LPWG_SD, + .sd_test_flag = SD_FLAG_SW49501, + .lpwg_sd_test_flag = 0, + }, + /* + * SW1828 group + */ + { .chip_type = CHIP_SW1828, + .name = NULL, //NULL meas 'Last & Default' + .cmd_type = PRD_CMD_TYPE_1, + .addr = { + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_RAW, 0xACF), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_BASELINE_EVEN, 0xC0F), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_DELTA, 0xD4F), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_LABEL, 0xEC5), + PRD_OFFSET_QUIRK_SET(IMG_OFFSET_IDX_F_DELTA, 0x7FD), + 0, + }, + __PRD_PARAM_DIMENSION(20, 32, 0, 48, PRD_M1_COL_SIZE, 1, 1), + __PRD_2ND_SCR(0, 0), + .sysfs_off_flag = (PRD_SYS_EN_DEBUG_BUF|PRD_SYS_EN_APP_DEBUG_BUF|PRD_SYS_EN_LPWG_SD), + .sd_test_flag = SD_FLAG_SW1828, + .lpwg_sd_test_flag = 0, + }, + /* + * End + */ + { 0, }, +}; + +#define __RESULT_FLAG_CAL(_result_all, result, flag) \ + ((_result_all) | ((result) << (flag))) + +#define RAW_OFFSET_EVEN(_addr) (((_addr) >> 16) & 0xFFFF) +#define RAW_OFFSET_ODD(_addr) ((_addr) & 0xFFFF) + +#define siw_prd_buf_snprintf(_buf, _size, _fmt, _args...) \ + __siw_snprintf(_buf, PRD_BUF_SIZE, _size, _fmt, ##_args) + +#define siw_prd_log_buf_snprintf(_buf, _size, _fmt, _args...) \ + __siw_snprintf(_buf, PRD_LOG_BUF_SIZE, _size, _fmt, ##_args) + + +enum { + CMD_TEST_EXIT = 0, + CMD_TEST_ENTER, +}; + +enum { + CMD_RAWDATA_PRD = 1, + CMD_RAWDATA_TCM, + CMD_RAWDATA_AIT, + CMD_AIT_BASEDATA, + CMD_FILTERED_DELTADATA, + CMD_DELTADATA, + CMD_LABELDATA, + CMD_BLU_JITTER, + CMD_DEBUG_BUF, + CMD_MAX, +}; + +#define __PRD_GET_DATA_CMD_SET(_idx) [_idx] = #_idx + +static const char *prd_get_data_cmd_name[] = { + [0] = "(none)", + __PRD_GET_DATA_CMD_SET(CMD_RAWDATA_PRD), + __PRD_GET_DATA_CMD_SET(CMD_RAWDATA_TCM), + __PRD_GET_DATA_CMD_SET(CMD_RAWDATA_AIT), + __PRD_GET_DATA_CMD_SET(CMD_AIT_BASEDATA), + __PRD_GET_DATA_CMD_SET(CMD_FILTERED_DELTADATA), + __PRD_GET_DATA_CMD_SET(CMD_DELTADATA), + __PRD_GET_DATA_CMD_SET(CMD_LABELDATA), + __PRD_GET_DATA_CMD_SET(CMD_BLU_JITTER), + __PRD_GET_DATA_CMD_SET(CMD_DEBUG_BUF), +}; + +static const char *prd_cmp_tool_str[][2] = { + [U3_M2_RAWDATA_TEST] = { + [0] = "U3_M2_Lower", + [1] = "U3_M2_Upper", + }, + [U3_M1_RAWDATA_TEST] = { + [0] = "U3_M1_Lower", + [1] = "U3_M1_Upper", + }, + [U0_M2_RAWDATA_TEST] = { + [0] = "U0_M2_Lower", + [1] = "U0_M2_Upper", + }, + [U0_M1_RAWDATA_TEST] = { + [0] = "U0_M1_Lower", + [1] = "U0_M1_Upper", + }, + [U3_BLU_JITTER_TEST] = { + [0] = "U3_Blu_Jitter_Lower", + [1] = "U3_Blu_Jitter_Upper", + }, + [U3_JITTER_TEST] = { + [0] = "U3_Jitter_Lower", + [1] = "U3_Jitter_Upper", + }, + [U3_M1_JITTER_TEST] = { + [0] = "U3_M1_Jitter_Lower", + [1] = "U3_M1_Jitter_Upper", + }, + [U0_JITTER_TEST] = { + [0] = "U0_Jitter_Lower", + [1] = "U0_Jitter_Upper", + }, + [U0_M1_JITTER_TEST] = { + [0] = "U0_M1_Jitter_Lower", + [1] = "U0_M1_Jitter_Upper", + }, +}; + +enum{ + M1_EVEN_DATA = 0, + M1_ODD_DATA, + M2_EVEN_DATA, + M2_ODD_DATA , + LABEL_DATA, + DEBUG_DATA, + }; + +/* PRD RAWDATA test RESULT Save On/Off CMD */ +enum { + RESULT_OFF = 0, + RESULT_ON, +}; + +/* TCM Memory Access Select */ +//tc_mem_sel(0x0457) RAW : 0 , BASE1 : 1 , BASE2 : 2 , BASE3: 3 +enum { + TCM_MEM_RAW = 0, + TCM_MEM_BASE1, + TCM_MEM_BASE2, + TCM_MEM_BASE3, +}; + +/* AIT Algorithm Engine HandShake CMD */ +enum { + IT_IMAGE_NONE = 0, + IT_IMAGE_RAW, + IT_IMAGE_BASELINE, + IT_IMAGE_DELTA, + IT_IMAGE_LABEL, + IT_IMAGE_FILTERED_DELTA, + IT_IMAGE_RESERVED, + IT_IMAGE_DEBUG, + IT_DONT_USE_CMD = 0xEE, + IT_WAIT = 0xFF, +}; + +/* AIT Algorithm Engine HandShake Status */ +enum { + RS_READY = 0xA0, + RS_NONE = 0x05, + RS_LOG = 0x77, + RS_IMAGE = 0xAA +}; + +enum { + PRD_TIME_STR_SZ = 64, + PRD_TMP_FILE_NAME_SZ = PATH_MAX, +}; + +enum { + PRD_TUNE_DISPLAY_TYPE_1 = 1, + PRD_TUNE_DISPLAY_TYPE_2, +}; + +static char __prd_in_file[PRD_TMP_FILE_NAME_SZ] = \ + "/sdcard/siw/prd_in.txt"; +static char __prd_in_file_m[PRD_TMP_FILE_NAME_SZ] = \ + "/sdcard/siw/prd_in_mfts.txt"; +static char __prd_out_file[PRD_TMP_FILE_NAME_SZ] = \ + "/sdcard/siw/prd_out.txt"; +static char __prd_out_file_mo_aat[PRD_TMP_FILE_NAME_SZ] = \ + "/sdcard/siw/prd_out_mfts_aat.txt"; +static char __prd_out_file_mo_mfo[PRD_TMP_FILE_NAME_SZ] = \ + "/sdcard/siw/prd_out_mfts_mfo.txt"; +static char __prd_out_file_mo_mfl[PRD_TMP_FILE_NAME_SZ] = \ + "/sdcard/siw/prd_out_mfts_mfl.txt"; +static char __prd_out_file_mo_mcv[PRD_TMP_FILE_NAME_SZ] = \ + "/sdcard/siw/prd_out_mfts_mcv.txt"; + +#if defined(MODULE) +/* use eg. prd_if=arc1 to change name */ +module_param_string(prd_if, __prd_in_file, sizeof(__prd_in_file), 0); + +/* use eg. prd_if_m=arc1 to change name */ +module_param_string(prd_if_m, __prd_in_file_m, sizeof(__prd_in_file_m), 0); + +/* use eg. prd_of=arc1 to change name */ +module_param_string(prd_of, __prd_out_file, sizeof(__prd_out_file), 0); + +/* use eg. prd_of_mo_aat=arc1 to change name */ +module_param_string(prd_of_mo_aat, __prd_out_file_mo_aat, sizeof(__prd_out_file_mo_aat), 0); + +/* use eg. prd_of_mo_mfo=arc1 to change name */ +module_param_string(prd_of_mo_mfo, __prd_out_file_mo_mfo, sizeof(__prd_out_file_mo_mfo), 0); + +/* use eg. prd_of_mo_mfl=arc1 to change name */ +module_param_string(prd_of_mo_mfl, __prd_out_file_mo_mfl, sizeof(__prd_out_file_mo_mfl), 0); + +/* use eg. prd_of_mo_mcv=arc1 to change name */ +module_param_string(prd_of_mo_mcv, __prd_out_file_mo_mcv, sizeof(__prd_out_file_mo_mcv), 0); +#endif + + +static u32 t_prd_dbg_mask = 0; + +/* usage + * (1) echo > /sys/module/{Siw Touch Module Name}/parameters/s_prd_dbg_mask + * (2) insmod {Siw Touch Module Name}.ko s_prd_dbg_mask= + */ +module_param_named(s_prd_dbg_mask, t_prd_dbg_mask, int, S_IRUGO|S_IWUSR|S_IWGRP); + + +enum { + PRD_SHOW_FLAG_DISABLE_PRT_RAW = (1<<0), +}; + +#define SIW_PRD_TAG "prd: " + +#define t_prd_info(_prd, fmt, args...) \ + __t_dev_info(_prd->dev, SIW_PRD_TAG fmt, ##args) + +#define t_prd_err(_prd, fmt, args...) \ + __t_dev_err(_prd->dev, SIW_PRD_TAG fmt, ##args) + +#define t_prd_warn(_prd, fmt, args...) \ + __t_dev_warn(_prd->dev, SIW_PRD_TAG fmt, ##args) + +#define t_prd_dbg(condition, _prd, fmt, args...) \ + do { \ + if (unlikely(t_prd_dbg_mask & (condition))) \ + t_prd_info(_prd, fmt, ##args); \ + } while (0) + +#define t_prd_dbg_base(_prd, fmt, args...) \ + t_prd_dbg(DBG_BASE, _prd, fmt, ##args) + +#define t_prd_dbg_trace(_prd, fmt, args...) \ + t_prd_dbg(DBG_TRACE, _prd, fmt, ##args) + +#define t_prd_info_flag(_prd, _flag, fmt, args...) \ + do { \ + if (!(_flag & PRD_SHOW_FLAG_DISABLE_PRT_RAW)){ \ + t_prd_info(_prd, fmt, ##args); \ + } \ + } while(0) + +#define t_prd_dbg_base_flag(_prd, _flag, fmt, args...) \ + do { \ + if (!(_flag & PRD_SHOW_FLAG_DISABLE_PRT_RAW)){ \ + t_prd_dbg_base(_prd, fmt, ##args); \ + } \ + } while(0) + +#define siw_prd_sysfs_err_invalid_param(_prd) \ + t_prd_err(_prd, "Invalid param\n"); + + +static int prd_drv_exception_check(struct siw_hal_prd_data *prd) +{ + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + + if (atomic_read(&chip->init) != IC_INIT_DONE) { + t_dev_warn(dev, "Not Ready, Need IC init (prd)\n"); + return 1; + } + + return 0; +} + +static int prd_ic_exception_check(struct siw_hal_prd_data *prd, char *buf) +{ + struct device *dev = prd->dev; + int boot_mode = 0; + + boot_mode = siw_touch_boot_mode_check(dev); + + return boot_mode; +} + +static int prd_chip_reset(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int ret; + +// mutex_lock(&ts->lock); + ret = siw_ops_reset(ts, HW_RESET_SYNC); +// mutex_unlock(&ts->lock); + + return ret; +} + +static int prd_chip_info(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + return siw_ops_ic_info(ts); +} + +static int prd_chip_driving(struct device *dev, int mode) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + return siw_ops_tc_driving(ts, mode); +} + +static ssize_t prd_kernel_write(struct file *file, + const char *buf, size_t count, + loff_t *pos) +{ + mm_segment_t old_fs; + ssize_t res; + + old_fs = get_fs(); + set_fs(get_ds()); + /* The cast to a user pointer is valid due to the set_fs() */ + res = vfs_write(file, (__force const char __user *)buf, count, pos); + set_fs(old_fs); + + return res; +} + +static int __prd_boot_mode_is_err(struct device *dev, int boot_mode) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + + if ((boot_mode > MINIOS_MFTS_CURVED) || (boot_mode < NORMAL_BOOT)) { + t_prd_err(prd, "boot mode(%d) not support mode\n", boot_mode); + return -EINVAL; + } + return 0; +} + +static int __used prd_chk_file_mode(struct siw_hal_prd_data *prd, + struct file *filp, + char *fname) +{ + struct dentry *fentry = filp->f_path.dentry; + + if (filp->f_flags & (O_RDWR|O_WRONLY)) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)) + if (!(filp->f_mode & FMODE_CAN_WRITE)) { + t_prd_err(prd, "file not writeable : %s\n", fname); + return -EINVAL; + } +#endif + } + + if (filp->f_flags & (O_RDWR|O_RDONLY)) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)) + if (!(filp->f_mode & FMODE_CAN_READ)) { + t_prd_err(prd, "file not readable : %s\n", fname); + return -EINVAL; + } +#endif + + if (S_ISDIR(fentry->d_inode->i_mode)) { + t_prd_err(prd, "%s is folder\n", fname); + return -EOPNOTSUPP; + } + + if (S_ISLNK(fentry->d_inode->i_mode)) { + t_prd_err(prd, "%s is link\n", fname); + return -EOPNOTSUPP; + } + } + return 0; +} + +static struct file __used *prd_vfs_file_open(struct siw_hal_prd_data *prd, + char *fname, int flags, umode_t mode) +{ + struct file *filp; + + filp = filp_open((const char *)fname, flags, mode); + if (IS_ERR(filp)) { + int ret = (int)PTR_ERR(filp); + t_prd_err(prd, "file[%s, 0x%X, 0x%X] open failed, %d\n", + fname, flags, mode, ret); + return NULL; + } + + return filp; +} + +static int __used prd_vfs_file_chk(struct siw_hal_prd_data *prd, + char *fname, + int flags, + umode_t mode, + loff_t *size) +{ + struct file *filp; + int ret = 0; + + filp = prd_vfs_file_open(prd, fname, flags, mode); + if (filp == NULL) { + ret = -ENOENT; + goto out; + } + + if (size) { + *size = vfs_llseek(filp, 0, SEEK_END); + } + + ret = prd_chk_file_mode(prd, filp, fname); + if (ret < 0) { + goto out_close; + } + +out_close: + filp_close(filp, 0); + +out: + return ret; +} + +static void __used prd_vfs_file_close(struct file *filp, fl_owner_t id) +{ + filp_close(filp, id); +} + + +/* + * [Safety and Compatibility] + * Using call_usermodehelper is recommended + * because direct access to file system in kernel space is not good way. + */ +#define __PRD_USE_USERMODE_HELPER + +static inline char *prd_find_name_pos(char *pathname) +{ + char *pos, *tpos; + + pos = strrchr(pathname, '/'); + tpos = (pos) ? pos + 1 : pathname; + + return tpos; +} + +#if defined(__PRD_USE_USERMODE_HELPER) +#define t_prd_err_uh(_prd, _ret, fmt, args...) \ + t_prd_err(_prd, "user mode helper" fmt " failed, %d\n", \ + ##args, _ret) + +#define t_prd_dbg_uh(_prd, fmt, args...) \ + t_prd_dbg_trace(_prd, "user mode helper" fmt " done\n", ##args) + +static int prd_vfs_uh_remove(struct siw_hal_prd_data *prd, + char *pathname) +{ + char *argv[4] = { "/bin/rm", NULL, NULL, NULL }; + char *envp[1] = { NULL }; + int ret = 0; + + argv[1] = (char *)pathname; + +// envp[0] = "HOME=/"; +// envp[1] = "PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin"; + + ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); + if (ret < 0) { + t_prd_err_uh(prd, ret, "[%s %s]", argv[0], argv[1]); + return ret; + } + + t_prd_dbg_uh(prd, "[%s %s]", argv[0], argv[1]); + + return ret; +} + +static int prd_vfs_uh_rename(struct siw_hal_prd_data *prd, + char *pathname, + char *pathname_new) +{ + char *argv[4] = { "/bin/mv", NULL, NULL, NULL }; + char *envp[1] = { NULL }; + int ret = 0; + + argv[1] = (char *)pathname; + argv[2] = (char *)pathname_new; + +// envp[0] = "HOME=/"; +// envp[1] = "PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin"; + + ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); + if (ret < 0) { + t_prd_err_uh(prd, ret, "[%s %s %s]", argv[0], argv[1], argv[2]); + return ret; + } + + t_prd_dbg_uh(prd, "[%s %s %s]", argv[0], argv[1], argv[2]); + + return ret; +} + +static int prd_vfs_remove(struct siw_hal_prd_data *prd, + char *fname) +{ + struct file *filp = NULL; + char *tmp = NULL; + char *pathname = NULL; + int ret = 0; + + tmp = (char *)kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!tmp) { + return -ENOMEM; + } + + /* + * Step 1. Get fentry data + */ + filp = prd_vfs_file_open(prd, fname, O_RDONLY, 0); + if (filp == NULL) { + ret = -ENOENT; + goto out; + } + + pathname = d_path(&filp->f_path, tmp, PAGE_SIZE); + if (IS_ERR(pathname)) { + ret = -ENOMEM; + goto out; + } + + prd_vfs_file_close(filp, 0); + + if (ret < 0) { + goto out; + } + + /* + * Step 2. Remove target file + */ + ret = prd_vfs_uh_remove(prd, pathname); + +out: + kfree(tmp); + return ret; +} + +static int prd_vfs_rename(struct siw_hal_prd_data *prd, + char *fname, + char *fname_new) +{ + struct file *filp = NULL; + char *tmp = NULL; + char *tmp_new = NULL; + char *pathname = NULL; + char *pathname_new = NULL; + char *wpos, *rpos; + int ret = 0; + + tmp = (char *)kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!tmp) { + return -ENOMEM; + } + + tmp_new = (char *)kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!tmp_new) { + kfree(tmp); + return -ENOMEM; + } + + /* + * Step 1. Get fentry data + */ + filp = prd_vfs_file_open(prd, fname, O_RDONLY, 0); + if (filp == NULL) { + ret = -ENOENT; + goto out; + } + + pathname = d_path(&filp->f_path, tmp, PAGE_SIZE); + if (IS_ERR(pathname)) { + ret = -ENOMEM; + goto out; + } + + prd_vfs_file_close(filp, 0); + + if (ret < 0) { + goto out; + } + + /* + * Step 2. pathname_new + */ + pathname_new = tmp_new; + strncpy(pathname_new, pathname, strlen(pathname)); + + wpos = prd_find_name_pos(pathname_new); + rpos = prd_find_name_pos(fname_new); + + memcpy(wpos, rpos, strlen(rpos)); + + /* + * Step 3. Change name + */ + ret = prd_vfs_uh_rename(prd, pathname, pathname_new); + +out: + kfree(tmp_new); + kfree(tmp); + return ret; +} +#else /* __PRD_USE_USERMODE_HELPER */ +static int prd_vfs_do_remove(struct siw_hal_prd_data *prd, + struct dentry *fentry, + char *fname) +{ + struct dentry *dir = NULL; + int ret = 0; + + dir = dget_parent(fentry); + + mutex_lock(&dir->d_inode->i_mutex); + + spin_lock(&fentry->d_lock); + + t_prd_dbg_trace(prd, "unhashed 0x%X, d_inode %p\n", + d_unhashed(fentry), fentry->d_inode); + + if (d_unhashed(fentry) && fentry->d_inode) { + spin_unlock(&fentry->d_lock); + goto out; + } + + dget_dlock(fentry); + __d_drop(fentry); + spin_unlock(&fentry->d_lock); + + vfs_unlink(dir->d_inode, fentry); + + t_prd_dbg_trace(prd, "vfs_unlink done\n"); + +out: + mutex_unlock(&dir->d_inode->i_mutex); + return ret; +} + +static int prd_vfs_do_rename(struct siw_hal_prd_data *prd, + struct dentry *fentry, + char *fname_new) +{ + struct dentry *dir = NULL; + struct inode *d_inode = NULL; + struct dentry *fentry_new = NULL; + char *base_name = NULL; + int ret = 0; + + dir = dget_parent(fentry); + d_inode = dir->d_inode; + + lock_rename(dir, dir); + + base_name = prd_find_name_pos(fname_new); + t_prd_dbg_trace(prd, "base name : %s\n", base_name); + + fentry_new = lookup_one_len(base_name, dir, strlen(base_name)); + if (IS_ERR(fentry_new)) { + ret = PTR_ERR(fentry_new); + t_prd_err(prd, "lookup_one_len for fentry_new failed, %d\n", ret); + goto out; + } + + ret = vfs_rename(d_inode, fentry, d_inode, fentry_new); + +out: + unlock_rename(dir, dir); + + return ret; +} + +static int prd_vfs_remove(struct siw_hal_prd_data *prd, + char *fname) +{ + struct file *filp = NULL; + struct dentry *fentry = NULL; + int ret = 0; + + /* + * Step 1. Get fentry data + */ + filp = prd_vfs_file_open(prd, fname, O_RDONLY, 0); + if (filp == NULL) { + return -ENOENT; + } + + fentry = filp->f_path.dentry; + + prd_vfs_file_close(filp, 0); + + /* + * Step 2. Remove target file + */ + ret = prd_vfs_do_remove(prd, fentry, fname); + + return ret; +} + +static int prd_vfs_rename(struct siw_hal_prd_data *prd, + char *fname, + char *fname_new) +{ + struct file *filp = NULL; + struct dentry *fentry = NULL; + int ret = 0; + + /* + * Step 1. Get fentry data + */ + filp = prd_vfs_file_open(prd, fname, O_RDONLY, 0); + if (filp == NULL) { + return -ENOENT; + } + + fentry = filp->f_path.dentry; + + prd_vfs_file_close(filp, 0); + + /* + * Step 2. Change name + */ + ret = prd_vfs_do_rename(prd, fentry, fname_new); + + return ret; +} + +#endif /* __PRD_USE_USERMODE_HELPER */ + + +#define t_prd_dbg_file_modify(_prd, fmt, args...) \ + t_prd_dbg_trace(_prd, "try to " fmt "\n", ##args) + +#define t_prd_dbg_file_remove(_prd, fmt, args...) \ + t_prd_dbg_file_modify(_prd, "remove " fmt, ##args) + +#define t_prd_dbg_file_rename(_prd, fmt, args...) \ + t_prd_dbg_file_modify(_prd, "rename " fmt, ##args) + +static int __used prd_vfs_file_remove(struct siw_hal_prd_data *prd, + char *fname) +{ + int ret = 0; + + t_prd_dbg_file_remove(prd, "%s", fname); + + ret = prd_vfs_remove(prd, fname); + if (ret < 0) { + if (ret != -ENOENT) { + t_prd_err(prd, "failed to remove file : %s, %d\n", fname, ret); + } + goto out; + } + t_prd_info(prd, "file removed : %s\n", fname); + +out: + return 0; +} + +static int __used prd_vfs_file_rename(struct siw_hal_prd_data *prd, + char *fname, char *fname_new) +{ + int ret = 0; + + t_prd_dbg_file_rename(prd, "%s -> %s", fname, fname_new); + + ret = prd_vfs_rename(prd, fname, fname_new); + if (ret < 0) { + if (ret != -ENOENT) { + t_prd_err(prd, "failed to rename file : %s -> %s, %d\n", + fname, fname_new, ret); + } + goto out; + } + t_prd_info(prd, "file renamed : %s -> %s\n", + fname, fname_new); + +out: + return 0; +} + +static int prd_check_type(struct siw_hal_prd_data *prd, + int type, int index) +{ + int ret = 0; + + switch (type) { + case U3_M2_RAWDATA_TEST: + case U0_M2_RAWDATA_TEST: + case U3_BLU_JITTER_TEST: + case U3_JITTER_TEST: + case U0_JITTER_TEST: + break; + + case U3_M1_RAWDATA_TEST: + case U0_M1_RAWDATA_TEST: + case U3_M1_JITTER_TEST: + case U0_M1_JITTER_TEST: + ret = 1; + break; + + default: + t_prd_err(prd, "[%d] unsupported test mode, %d\n", + index, type); + ret = -EINVAL; + } + + return ret; +} + +static int prd_log_file_size_check(struct siw_hal_prd_data *prd) +{ + char *prd_out_fname[] = { + [NORMAL_BOOT] = __prd_out_file, + [MINIOS_AAT] = __prd_out_file_mo_aat, + [MINIOS_MFTS_FOLDER] = __prd_out_file_mo_mfo, + [MINIOS_MFTS_FLAT] = __prd_out_file_mo_mfl, + [MINIOS_MFTS_CURVED] = __prd_out_file_mo_mcv, + }; + struct device *dev = prd->dev; + char *fname = NULL; + loff_t file_size = 0; + int i = 0; + char *buf1 = NULL; + char *buf2 = NULL; + int boot_mode = 0; + int ret = 0; + + boot_mode = siw_touch_boot_mode_check(dev); + + if (__prd_boot_mode_is_err(dev, boot_mode)) { + return -EINVAL; + } + + buf1 = touch_getname(); + if (buf1 == NULL) { + t_prd_err(prd, "failed to allocate name buffer 1\n"); + return -ENOMEM; + } + + buf2 = touch_getname(); + if (buf2 == NULL) { + t_prd_err(prd, "failed to allocate name buffer 2\n"); + touch_putname(buf1); + return -ENOMEM; + } + + fname = (char *)prd_out_fname[boot_mode]; + + ret = prd_vfs_file_chk(prd, fname, O_RDONLY, 0666, &file_size); + if (ret < 0) { + goto out; + } + + if (file_size <= MAX_LOG_FILE_SIZE) { + goto out; + } + + t_prd_info(prd, "file size : %s, %lld > MAX_LOG_FILE_SIZE(%d)\n", + fname, file_size, MAX_LOG_FILE_SIZE); + + for (i = MAX_LOG_FILE_COUNT - 1; i >= 0; i--) { + if (i == 0) { + snprintf(buf1, PATH_MAX, "%s", fname); + } else { + snprintf(buf1, PATH_MAX, "%s.%d", fname, i); + } + + if (i == (MAX_LOG_FILE_COUNT - 1)) { + ret = prd_vfs_file_remove(prd, buf1); + if (ret < 0) { + goto out; + } + t_prd_info(prd, "file removed : %s\n", buf1); + } else { + snprintf(buf2, PATH_MAX, "%s.%d", fname, (i + 1)); + + ret = prd_vfs_file_rename(prd, buf1, buf2); + if (ret < 0) { + goto out; + } + } + } + +out: + touch_putname(buf2); + touch_putname(buf1); + + return ret; +} + +static int prd_do_write_file(struct siw_hal_prd_data *prd, + char *fname, + char *data, + int write_time) +{ +// struct device *dev = prd->dev; + struct file *filp = NULL; + loff_t offset = 0; + char time_string[PRD_TIME_STR_SZ] = {0, }; + int twlen = 0; + int nwlen = 0; + int ret = 0; + + filp = prd_vfs_file_open(prd, fname, + O_WRONLY|O_CREAT|O_APPEND, 0666); + if (filp == NULL) { + ret = -ENOENT; + goto out; + } + + ret = prd_chk_file_mode(prd, filp, fname); + if (ret < 0) { + goto out_close; + } + + if (write_time == TIME_INFO_WRITE) { + struct timespec my_time; + struct tm my_date; + + my_time = current_kernel_time(); + time_to_tm(my_time.tv_sec, + sys_tz.tz_minuteswest * 60 * (-1), + &my_date); + snprintf(time_string, 64, + "\n[%02d-%02d %02d:%02d:%02d.%03lu]\n", + my_date.tm_mon + 1, + my_date.tm_mday, my_date.tm_hour, + my_date.tm_min, my_date.tm_sec, + (unsigned long) my_time.tv_nsec / 1000000); + ret = prd_kernel_write(filp, (const char *)time_string, + strlen(time_string), &offset); + if (ret < 0) { + t_prd_err(prd, "failed to write file : %s(%d), %d \n", + fname, (int)offset, ret); + goto out_close; + } + twlen += strlen(time_string); + nwlen += ret; + } + ret = prd_kernel_write(filp, (const char *)data, + strlen(data), &offset); + if (ret < 0) { + t_prd_err(prd, "failed to write file : %s(%d), %d \n", + fname, (int)offset, ret); + goto out_close; + } + twlen += strlen(data); + nwlen += ret; + +out_close: + filp_close(filp, 0); + if (ret >= 0) { + t_prd_info(prd, "file write done : %s(%d, %d)\n", + fname, twlen, nwlen); + } + +out: + return ret; +} + +static int prd_write_file(struct siw_hal_prd_data *prd, char *data, int write_time) +{ + char *prd_out_fname[] = { + [NORMAL_BOOT] = __prd_out_file, + [MINIOS_AAT] = __prd_out_file_mo_aat, + [MINIOS_MFTS_FOLDER] = __prd_out_file_mo_mfo, + [MINIOS_MFTS_FLAT] = __prd_out_file_mo_mfl, + [MINIOS_MFTS_CURVED] = __prd_out_file_mo_mcv, + }; + struct device *dev = prd->dev; + char *fname = NULL; + int boot_mode = 0; + int ret = 0; + + boot_mode = siw_touch_boot_mode_check(dev); + if (__prd_boot_mode_is_err(dev, boot_mode)) { + return -EINVAL; + } + + fname = (char *)prd_out_fname[boot_mode]; + + ret = prd_do_write_file(prd, fname, data, write_time); + + return ret; +} + +/* + * Conrtol LCD Backlightness + */ +static int prd_set_blu(struct device *dev) +{ + int backlightness; + +// LCD brightness ON 742ms -> LCD brightness OFF 278mms -> LCD brightness ON 278ms + + backlightness = siw_touch_sys_get_panel_bl(dev); + + touch_msleep(742); + + siw_touch_sys_set_panel_bl(dev, 0); + + touch_msleep(278); + + siw_touch_sys_set_panel_bl(dev, backlightness); + + touch_msleep(278); + + return 0; +} + +static int prd_write_test_mode(struct siw_hal_prd_data *prd, int type) +{ + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_sd_cmd *sd_cmd = &prd->sd_cmd; + u32 testmode = 0; + int retry = 40; + u32 rdata = 0x01; + u32 line_filter_option = LINE_FILTER_OPTION; + int waiting_time = 200; + int addr = reg->tc_tsp_test_ctl; + int ret = 0; + + switch (touch_chip_type(ts)) { + case CHIP_SW1828: + line_filter_option = 0; + switch (type) { + case U3_JITTER_TEST: + waiting_time = 6000; + break; + } + break; + case CHIP_SW49501: + case CHIP_SW49407: + line_filter_option = 0; + break; + case CHIP_SW49106: + if(type == U0_JITTER_TEST) { + line_filter_option = 0; + } + break; + } + + switch (type) { + case U3_M2_RAWDATA_TEST: + testmode = (U3_TEST_PRE_CMD << 8) + sd_cmd->cmd_m2_rawdata; + break; + case U3_M1_RAWDATA_TEST: + testmode = (U3_TEST_PRE_CMD << 8) + sd_cmd->cmd_m1_rawdata; + break; + case U0_M2_RAWDATA_TEST: + testmode = (U0_TEST_PRE_CMD << 8) + sd_cmd->cmd_m2_rawdata; + break; + case U0_M1_RAWDATA_TEST: + testmode = (U0_TEST_PRE_CMD << 8) + sd_cmd->cmd_m1_rawdata; + break; + /* */ + case OPEN_NODE_TEST: + testmode = (U3_TEST_PRE_CMD << 8) + sd_cmd->cmd_open_node; + waiting_time = 10; + break; + case SHORT_NODE_TEST: + testmode = (U3_TEST_PRE_CMD << 8) + sd_cmd->cmd_short_node; + waiting_time = 1000; + break; + case SHORT_FULL_TEST: + testmode = (U3_TEST_PRE_CMD << 8) + sd_cmd->cmd_short_full; + waiting_time = 1000; + break; + /* */ + case U3_BLU_JITTER_TEST: + testmode = ((U3_TEST_PRE_CMD << 8) + sd_cmd->cmd_jitter) | line_filter_option; + waiting_time = 10; + break; + case U3_JITTER_TEST: + testmode = ((U3_TEST_PRE_CMD << 8) + sd_cmd->cmd_jitter) | line_filter_option; + break; + case U3_M1_JITTER_TEST: + testmode = ((U3_TEST_PRE_CMD << 8) + sd_cmd->cmd_m1_jitter) | line_filter_option; + break; + case U0_JITTER_TEST: + testmode = ((U0_TEST_PRE_CMD << 8) + sd_cmd->cmd_jitter) | line_filter_option; + break; + case U0_M1_JITTER_TEST: + testmode = ((U0_TEST_PRE_CMD << 8) + sd_cmd->cmd_m1_jitter) | line_filter_option; + break; + default: + t_prd_err(prd, "unsupported test mode, %d\n", type); + return -EINVAL; + } + + /* TestType Set */ + ret = siw_hal_write_value(dev, addr, testmode); + if (ret < 0) { + return ret; + } + t_prd_info(prd, "write testmode[%04Xh] = %x\n", + addr, testmode); + touch_msleep(waiting_time); + + if(type == U3_BLU_JITTER_TEST) { + prd_set_blu(dev); + } + + /* Check Test Result - wait until 0 is written */ + addr = reg->tc_tsp_test_status; + do { + touch_msleep(50); + ret = siw_hal_read_value(dev, addr, &rdata); + if (ret < 0) { + return ret; + } + if (ret >= 0) { + t_prd_dbg_base(prd, "rdata[%04Xh] = 0x%x\n", + addr, rdata); + } + } while ((rdata != 0xAA) && retry--); + + if (rdata != 0xAA) { + t_prd_err(prd, "ProductionTest Type [%d] Time out, %08Xh\n", + type, rdata); + goto out; + } + + return 1; + +out: + t_prd_err(prd, "Write test mode failed\n"); + return 0; +} + +static int prd_spec_file_read_ext(struct siw_hal_prd_data *prd) +{ + char *prd_in_fname[] = { + [0] = __prd_in_file, + [1] = __prd_in_file_m, + }; + struct device *dev = prd->dev; + struct file *filp = NULL; + loff_t offset = 0; + char *fname; + int path_idx = 0; + int ret = 0; + + path_idx = !!(siw_touch_boot_mode_check(dev) >= MINIOS_MFTS_FOLDER); + fname = (char *)prd_in_fname[path_idx]; + + filp = prd_vfs_file_open(prd, fname, O_RDONLY, 0); + if (filp == NULL) { + ret = -ENOENT; + goto out; + } + + kernel_read(filp, offset, (char *)prd->line, sizeof(prd->line)); + + filp_close(filp, 0); + + t_prd_info(prd, "file detected : %s\n", fname); + +out: + return ret; +} + +static int prd_spec_file_read(struct siw_hal_prd_data *prd) +{ + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + const struct firmware *fwlimit = NULL; + const char *path[2] = { + ts->panel_spec, + ts->panel_spec_mfts + }; + char *fname; + int path_idx = 0; + int ret = 0; + + path_idx = !!(siw_touch_boot_mode_check(dev) >= MINIOS_MFTS_FOLDER); + fname = (char *)path[path_idx]; + + if (ts->panel_spec == NULL || ts->panel_spec_mfts == NULL) { + t_prd_err(prd, "panel spec file name is null\n"); + siw_prd_buf_snprintf(prd->buf_write, + 0, + "[E] panel spec file name is null\n"); + ret = -EINVAL; + goto out; + } + + ret = request_firmware(&fwlimit, fname, dev); + if (ret < 0) { + t_prd_err(prd, "request file is failed in normal mode\n"); + siw_prd_buf_snprintf(prd->buf_write, + 0, + "[E] request file is failed in normal mode\n"); + goto out; + } + + if (fwlimit->data == NULL) { + ret = -EFAULT; + t_prd_err(prd, "fwlimit->data is NULL\n"); + siw_prd_buf_snprintf(prd->buf_write, + 0, + "[E] fwlimit->data is NULL\n"); + goto out; + } + + strlcpy(prd->line, fwlimit->data, sizeof(prd->line)); + +out: + if (fwlimit) + release_firmware(fwlimit); + + return ret; +} + +static int __used prd_get_limit(struct siw_hal_prd_data *prd, + char *breakpoint, int *limit) +{ + struct device *dev = prd->dev; + int p = 0; + int q = 0; + int q_limit; + int cipher = 1; + char *found; + int boot_mode = 0; + int value = ~0; + int ret = 0; + + if (limit) + *limit = ~0; + + if (breakpoint == NULL) { + ret = -EINVAL; + goto out; + } + + boot_mode = siw_touch_boot_mode_check(dev); + if ((boot_mode > MINIOS_MFTS_CURVED) || + (boot_mode < NORMAL_BOOT)) { + ret = -EINVAL; + goto out; + } + + ret = prd_spec_file_read_ext(prd); + if (ret < 0) { + ret = prd_spec_file_read(prd); + if (ret < 0) + goto out; + } + + found = strnstr(prd->line, breakpoint, sizeof(prd->line)); + if (found == NULL) { + t_prd_err(prd, + "failed to find %s, spec file is wrong\n", + breakpoint); + ret = -EFAULT; + goto out; + } + q = found - prd->line; + + q_limit = ARRAY_SIZE(prd->line); + while (q < q_limit) { + if (prd->line[q] == ',') { + value = 0; + + cipher = 1; + for (p = 1; (prd->line[q - p] >= '0') && + (prd->line[q - p] <= '9'); p++) { + value += ((prd->line[q - p] - '0') * cipher); + cipher *= 10; + } + + if (limit) + *limit = value; + + t_prd_info(prd, "scanning spec file done, %s = %d\n", + breakpoint, value); + + break; + } + q++; + } + + if (value == ~0) { + t_prd_err(prd, "%s found, but getting num failed\n", breakpoint); + ret = -EFAULT; + goto out; + } + +out: + return ret; +} + +#define PRD_IRQ_TEST_CNT 10 + +static int prd_irq_test_handler(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + int size = 0; + + if (!prd->irq_test) { + return -ESRCH; + } + + size = 12 + (sizeof(struct siw_hal_touch_data) * touch_max_finger(ts)); + siw_hal_reg_read(dev, reg->tc_ic_status, (void *)&chip->info, size); + + t_prd_info(prd, "irq_test: irq detected %d (h/w:%Xh, f/w:%Xh)\n", + ++prd->irq_cnt, chip->info.ic_status, chip->info.device_status); + + return 0; +} + +struct prd_irq_setup { + int addr; + u32 data; +}; + +static const struct prd_irq_setup prd_irq_test_setup_sw49501[] = { + { 0x34, 0 }, + { 0xB8, 0 }, + { -1, 0 } /* end mark */ +}; + +static const struct prd_irq_setup prd_irq_test_ctrl_sw49501 = { 0x02, 0 }; + +static int prd_do_irq_test_setup(struct siw_hal_prd_data *prd, + struct prd_irq_setup *ctrl) +{ + struct device *dev = prd->dev; + int addr; + u32 data; + int ret = 0; + + while (ctrl) { + addr = ctrl->addr; + data = ctrl->data; + if (addr == -1) { + break; + } + + if (addr) { + t_prd_info(prd, "irq_test: setup wr[%04Xh] = %08Xh\n", + addr, data); + ret = siw_hal_write_value(dev, addr, data); + if (ret < 0) { + return ret; + } + } + + ctrl++; + } + + return 0; + +} + +static int prd_do_irq_test_trigger(struct siw_hal_prd_data *prd, + struct prd_irq_setup *ctrl) +{ + struct device *dev = prd->dev; + int addr; + u32 data; + int i; + int ret; + + addr = ctrl->addr; + data = ctrl->data; + + prd->irq_cnt = 0; + + for (i = 0; i < PRD_IRQ_TEST_CNT; i++) { + if (prd->irq_cnt != i) { + t_prd_warn(prd, + "irq_test: ctrl: irq cnt mismatch, trigger(%d) != detection(%d)\n", + i, prd->irq_cnt); + } + + t_prd_info(prd, "irq_test: ctrl: irq trigger %d (wr[%04Xh] = %d)\n", + i + 1, addr, data); + ret = siw_hal_write_value(dev, addr, data); + if (ret < 0) { + return ret; + } + + touch_msleep(20); + } + + return 0; +} + +static int prd_do_irq_test(struct siw_hal_prd_data *prd, char *buf) +{ + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct prd_irq_setup *irq_test_setup = NULL; + struct prd_irq_setup *irq_test_ctrl = NULL; + int irq_cnt; + int ret = 0; + + switch (touch_chip_type(ts)) { + case CHIP_SW49501: + irq_test_setup = (struct prd_irq_setup *)prd_irq_test_setup_sw49501; + irq_test_ctrl = (struct prd_irq_setup *)&prd_irq_test_ctrl_sw49501; + break; + default: + t_prd_info(prd, "IRQ Test not supported, skip\n"); + return 0; + } + + ret = prd_do_irq_test_setup(prd, irq_test_setup); + if (ret < 0) { + return ret; + } + + /* temporary setup */ + atomic_set(&chip->init, IC_INIT_NEED); + + ts->ops->irq_dbg_handler = prd_irq_test_handler; + + /* temporary enable */ + siw_touch_irq_control(dev, INTERRUPT_ENABLE); + + prd->irq_test = 1; + + ret = prd_do_irq_test_trigger(prd, irq_test_ctrl); + + prd->irq_test = 0; + + /* restore */ + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + + ts->ops->irq_dbg_handler = NULL; + + /* restore */ + atomic_set(&chip->init, IC_INIT_DONE); + + if (ret < 0) { + siw_snprintf(buf, 0, "IRQ : Fail(trigger io failed)\n"); + goto out; + } + + irq_cnt = prd->irq_cnt; + + ret = (irq_cnt == PRD_IRQ_TEST_CNT) ? 0 : 1; + + if (ret) { + siw_snprintf(buf, 0, "IRQ : Fail, trigger(%d) != detection(%d)\n", + PRD_IRQ_TEST_CNT, irq_cnt); + } else { + siw_snprintf(buf, 0, "IRQ : Pass\n"); + } + +out: + t_prd_info(prd, "%s", buf); + + return ret; +} + +static int __used prd_irq_test(struct siw_hal_prd_data *prd, int result_on) +{ + char test_type[32] = {0, }; + int ret; + + t_prd_info(prd, "========IRQ_TEST========\n"); + + memset(prd->buf_write, 0, PRD_BUF_SIZE); + + ret = prd_do_irq_test(prd, prd->buf_write); + + if (result_on == RESULT_ON) { + snprintf(test_type, sizeof(test_type), "\n\n%s\n", "[IRQ_TEST]"); + /* Test Type Write */ + prd_write_file(prd, test_type, TIME_INFO_SKIP); + + prd_write_file(prd, prd->buf_write, TIME_INFO_SKIP); + } + + return ret; +} + +static int prd_os_result_rawdata_get(struct siw_hal_prd_data *prd, int type) +{ + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; + u16 open_data_offset = 0; + u16 short_data_offset = 0; + u32 os_result_offset; + u32 data_offset; + u32 read_size; + int16_t *buf_result_data = NULL; + char *info_str_title = NULL; + char *info_str_log = NULL; + int ret = 0; + + ret = siw_hal_read_value(dev, + reg->prd_open3_short_offset, + &os_result_offset); + if (ret < 0) { + goto out; + } + open_data_offset = os_result_offset & 0xFFFF; + short_data_offset = (os_result_offset >> 16) & 0xFFFF; + + switch (type) { + case OPEN_NODE_TEST: + info_str_title = "\n[OPEN_NODE_TEST Result Rawdata]\n"; + info_str_log = "OPEN_NODE_TEST"; + + data_offset = open_data_offset; + + buf_result_data = prd->open_buf_result_rawdata; + + read_size = ctrl->m2_row_col_buf_size * PRD_RAWDATA_SIZE; + break; + case SHORT_NODE_TEST: + info_str_title = "\n[SHORT_NODE_TEST Result Rawdata]\n"; + info_str_log = "SHORT_NODE_TEST"; + + data_offset = short_data_offset; + + buf_result_data = prd->short_buf_result_rawdata; + + read_size = ctrl->m2_row_col_buf_size * PRD_RAWDATA_SIZE; + break; + case SHORT_FULL_TEST: + info_str_title = "\n[SHORT_FULL_TEST Result Rawdata]\n"; + info_str_log = "SHORT_FULL_TEST"; + + data_offset = short_data_offset; + + buf_result_data = prd->short_buf_result_rawdata; + + read_size = ctrl->m2_row_col_buf_size * PRD_RAWDATA_SIZE; + break; + } + + if (info_str_log == NULL) { + t_prd_err(prd, "[result_rawdata_get] unknown type, %d\n", type); + goto out; + } + + prd_write_file(prd, info_str_title, TIME_INFO_SKIP); + + t_prd_info(prd, "%s Rawdata Offset = %xh\n", info_str_log, data_offset); + t_prd_info(prd, "%s", &info_str_title[1]); + + //offset write + ret = siw_hal_write_value(dev, reg->serial_data_offset, + data_offset); + if (ret < 0) { + goto out; + } + + //read open raw data + ret = siw_hal_reg_read(dev, reg->data_i2cbase_addr, + buf_result_data, + read_size); + if (ret < 0) { + goto out; + } + +out: + return ret; +} + +static int prd_os_result_data_get(struct siw_hal_prd_data *prd, int type) +{ + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; + u16 open_short_data_offset = 0; + u32 os_result_offset; + u32 read_size; + int16_t *buf_result_data = NULL; + char *info_str_title = NULL; + char *info_str_log = NULL; + int ret = 0; + + ret = siw_hal_read_value(dev, + reg->prd_tune_result_offset, + &os_result_offset); + if (ret < 0) { + goto out; + } + open_short_data_offset = os_result_offset & 0xFFFF; + + switch (type) { + case OPEN_NODE_TEST: + info_str_title = "\n[OPEN_NODE_TEST Result Data]\n"; + info_str_log = "OPEN_NODE_TEST"; + + buf_result_data = prd->open_buf_result_data; + + read_size = (prd->open_result_type) ? \ + ctrl->m2_row_col_buf_size : ctrl->m1_row_col_size; + read_size *= PRD_RAWDATA_SIZE; + break; + case SHORT_NODE_TEST: + info_str_title = "\n[SHORT_NODE_TEST Result Data]\n"; + info_str_log = "SHORT_NODE_TEST"; + + buf_result_data = prd->short_buf_result_data; + + read_size = ctrl->m1_row_col_size * PRD_RAWDATA_SIZE; + break; + case SHORT_FULL_TEST: + info_str_title = "\n[SHORT_FULL_TEST Result Data]\n"; + info_str_log = "SHORT_FULL_TEST"; + + buf_result_data = prd->short_buf_result_data; + + read_size = ctrl->m2_row_col_buf_size * PRD_RAWDATA_SIZE; + break; + } + + if (info_str_log == NULL) { + t_prd_err(prd, "[result_data_get] unknown type, %d\n", type); + goto out; + } + + prd_write_file(prd, info_str_title, TIME_INFO_SKIP); + + t_prd_info(prd, "%s Data Offset = %xh\n", info_str_log, open_short_data_offset); + t_prd_info(prd, "%s", &info_str_title[1]); + + //offset write + ret = siw_hal_write_value(dev, reg->serial_data_offset, + open_short_data_offset); + if (ret < 0) { + goto out; + } + + //read data + ret = siw_hal_reg_read(dev, reg->data_i2cbase_addr, + buf_result_data, + read_size); + if (ret < 0) { + goto out; + } + +out: + return ret; +} + +enum { + PRD_OS_RESULT_TMP_BUF_SZ = 64, +}; + +static int prd_print_os_data(struct siw_hal_prd_data *prd, int type) +{ + struct siw_hal_prd_param *param = &prd->param; + int i = 0, j = 0; + int size = 0; + int log_size = 0; + u16 *os_buf = NULL; + int col_size = 0; + char *log_buf = prd->log_buf; + + memset(prd->buf_write, 0, PRD_BUF_SIZE); + + switch (type) { + case OPEN_NODE_TEST: + col_size = (prd->open_result_type) ? \ + param->col + param->col_add : param->m1_col; + os_buf = prd->open_buf_result_data; + break; + case SHORT_NODE_TEST: + col_size = param->m1_col; + os_buf = prd->short_buf_result_data; + break; + case SHORT_FULL_TEST: + col_size = param->col + param->col_add; + os_buf = prd->short_buf_result_data; + break; + } + + size = siw_prd_buf_snprintf(prd->buf_write, size, "\n : "); + log_size = siw_prd_log_buf_snprintf(log_buf, log_size, " : "); + + for (i = 0; i < col_size; i++) { + size += siw_prd_buf_snprintf(prd->buf_write, + size, "[%2d] ", i); + log_size += siw_prd_log_buf_snprintf(log_buf, + log_size, "[%2d] ", i); + } + t_prd_info(prd, "%s\n", log_buf); + + for (i = 0; i < param->row; i++) { + log_size = 0; + + size += siw_prd_buf_snprintf(prd->buf_write, + size, "\n[%2d] ", i); + log_size += siw_prd_log_buf_snprintf(log_buf, + log_size, "[%2d] ", i); + for (j = 0; j < col_size; j++) { + //if (j == param->col) + // continue; + size += siw_prd_buf_snprintf(prd->buf_write, + size, "%4X ", os_buf[i * col_size + j]); + log_size += siw_prd_log_buf_snprintf(log_buf, + log_size, "%4X ", os_buf[i * col_size + j]); + } + t_prd_info(prd, "%s\n", log_buf); + } + size += siw_prd_buf_snprintf(prd->buf_write, size, "\n"); + prd_write_file(prd, prd->buf_write, TIME_INFO_SKIP); + memset(prd->buf_write, 0, size); + + return size; +} + +static int prd_print_os_rawdata(struct siw_hal_prd_data *prd, u8 type) +{ + struct siw_hal_prd_param *param = &prd->param; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + int i = 0, j = 0; + int size = 0; + int log_size = 0; + u16 *os_buf = NULL; + int os_buf_size = ctrl->m2_row_col_buf_size; + int col_size = 0; + char *log_buf = prd->log_buf; + u16 short_temp_buf[os_buf_size]; + + memset(prd->buf_write, 0, PRD_BUF_SIZE); + + switch (type) { + case OPEN_NODE_TEST: + col_size = param->col + param->col_add; + os_buf = prd->open_buf_result_rawdata; + break; + case SHORT_NODE_TEST: + col_size = 4; + os_buf = prd->short_buf_result_rawdata; + break; + case SHORT_FULL_TEST: + col_size = param->col + param->col_add; + os_buf = prd->short_buf_result_rawdata; + break; + } + + size = siw_prd_buf_snprintf(prd->buf_write, size, "\n : "); + log_size = siw_prd_log_buf_snprintf(log_buf, log_size, " : "); + + for (i = 0; i < col_size; i++) { + size += siw_prd_buf_snprintf(prd->buf_write, + size, "[%2d] ", i); + log_size += siw_prd_log_buf_snprintf(log_buf, + log_size, "[%2d] ", i); + } + t_prd_info(prd, "%s\n", log_buf); + + /* + * short data replace algorithm + */ + if(type == SHORT_NODE_TEST) { + memcpy(short_temp_buf, os_buf, sizeof(short_temp_buf)); + memset(os_buf, 0, os_buf_size); + + for (i = 0; i < col_size; i++) { + for (j = 0; j < param->row; j++) { + os_buf[j * col_size + i] = short_temp_buf[i*param->row + j]; + } + } + } + + for (i = 0; i < param->row; i++) { + log_size = 0; + + size += siw_prd_buf_snprintf(prd->buf_write, + size, "\n[%2d] ", i); + log_size += siw_prd_log_buf_snprintf(log_buf, + log_size, "[%2d] ", i); + for (j = 0; j < col_size; j++) { + if (j == param->col) + continue; + size += siw_prd_buf_snprintf(prd->buf_write, + size, "%4d ", os_buf[i * col_size + j]); + log_size += siw_prd_log_buf_snprintf(log_buf, + log_size, "%4d ", os_buf[i * col_size + j]); + } + t_prd_info(prd, "%s\n", log_buf); + } + + size += siw_prd_buf_snprintf(prd->buf_write, size, "\n"); + prd_write_file(prd, prd->buf_write, TIME_INFO_SKIP); + memset(prd->buf_write, 0, size); + + return size; +} + +static int prd_os_xline_result_read(struct siw_hal_prd_data *prd, int type) +{ + struct siw_hal_prd_param *param = &prd->param; + int ret = 0; + + + if(param->sd_test_flag & OPEN_SHORT_RESULT_DATA_FLAG) { + //open short fail reason "result data" + ret = prd_os_result_data_get(prd, type); + prd_print_os_data(prd, type); + } + + if(param->sd_test_flag & OPEN_SHORT_RESULT_RAWDATA_FLAG) { + //open short fail reason "result raw data" + ret = prd_os_result_rawdata_get(prd, type); + prd_print_os_rawdata(prd, type); + } + return ret; +} + +#define PRD_SD_ERROR_RET_FLAG 0x07 + +static int prd_do_open_short_test(struct siw_hal_prd_data *prd) +{ + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; + u32 openshort_all_result = 0; + u32 result; + int type = 0; + struct prd_os_con { + int type; + char *str; + int cmd; + int enable; + } os_con[3] = { + [0] = { + .type = OPEN_NODE_TEST, + .str = "open", + .cmd = prd->sd_cmd.cmd_open_node, + .enable = 1, + }, + [1] = { + .type = SHORT_NODE_TEST, + .str = "short", + .cmd = prd->sd_cmd.cmd_short_node, + .enable = 1, + }, + [2] = { + .type = SHORT_FULL_TEST, + .str = "short max", + .cmd = prd->sd_cmd.cmd_short_full, + .enable = prd->param.sd_test_flag & SHORT_FULL_TEST_FLAG, + }, + }; + int i; + int ret = 0; + + /* Test Type Write */ + ret = prd_write_file(prd, "\n\n[OPEN_SHORT_ALL_TEST]\n", TIME_INFO_SKIP); + if (ret < 0) { + return ret; + } + + t_prd_dbg_base(prd, "result resister:%d \n", reg->tc_tsp_test_pf_result); + + for (i = 0; i < ARRAY_SIZE(os_con); i++) { + type = os_con[i].type; + + if (!type) { + break; + } + + if (!os_con[i].cmd || !os_con[i].enable) { + continue; + } + + ret = prd_write_test_mode(prd, type); + if (ret < 0) { + return ret; + } + if (!ret) { + t_prd_err(prd, "write test mode failed\n"); + return PRD_SD_ERROR_RET_FLAG; + } + + ret = siw_hal_read_value(dev, + reg->tc_tsp_test_pf_result, + &result); + if (ret < 0) { + return ret; + } + t_prd_info(prd, "%s result = %d\n", os_con[i].str, result); + + if (result | (prd->dbg_mask & PRD_DBG_OPEN_SHORT_DATA)) { + ret = prd_os_xline_result_read(prd, type); + if (ret < 0) { + return ret; + } + } + + openshort_all_result |= (result<log_buf; + int i; + int log_size = 0; + + t_prd_info(prd, "-------- %s(%d %d) --------\n", + name, row_size, col_size); + + size += siw_prd_buf_snprintf(buf, size, + "-------- %s(%d %d) --------\n", + name, row_size, col_size); + + /* print a frame data */ + size += siw_prd_buf_snprintf(buf, size, " : "); + log_size += siw_prd_log_buf_snprintf(log_buf, + log_size, " : "); + + for (i = 0; i < col_size; i++) { + size += siw_prd_buf_snprintf(buf, size, "[%2d] ", i); + log_size += siw_prd_log_buf_snprintf(log_buf, + log_size, "[%2d] ", i); + } + t_prd_info(prd, "%s\n", log_buf); + + return size; +} + +static int prd_print_post(struct siw_hal_prd_data *prd, char *buf, + int size, int min, int max) +{ + size += siw_prd_buf_snprintf(buf, size, "\n"); + + size += siw_prd_buf_snprintf(buf, size, + "\nRawdata min : %d , max : %d\n", + min, max); + t_prd_info(prd, "Rawdata min : %d , max : %d\n", + min, max); + + return size; +} + +enum { + PRD_PRT_TYPE_U8 = 0, + PRD_PRT_TYPE_S16, + PRD_PRT_TYPE_MAX, +}; + +static int prd_print_xxx(struct siw_hal_prd_data *prd, char *buf, + int size, void *rawdata_buf, + int row_size, int col_size, + const char *name, int opt, int type) +{ + struct siw_hal_prd_param *param = &prd->param; + char *log_buf = prd->log_buf; + u8 *rawdata_u8 = rawdata_buf; + int16_t *rawdata_s16 = rawdata_buf; + int curr_raw; + int i, j; + int col_i = 0; + int col_add = (opt) ? param->col_add : 0; + int log_size = 0; + int min = 9999; + int max = 0; + + if (type >= PRD_PRT_TYPE_MAX) { + t_prd_err(prd, "invalid print type, %d\n", type); + return -EINVAL; + } + + if (rawdata_buf == NULL) { + t_prd_err(prd, "print failed: NULL buf\n"); + return -EFAULT; + } + + size = prd_print_pre(prd, buf, size, row_size, col_size, name); + + col_i = 0; + for (i = 0; i < row_size; i++) { + size += siw_prd_buf_snprintf(buf, size, "\n[%2d] ", i); + + log_size = 0; + memset(log_buf, 0, sizeof(prd->log_buf)); + log_size += siw_prd_log_buf_snprintf(log_buf, + log_size, "[%2d] ", i); + + if (type == PRD_PRT_TYPE_S16) { + rawdata_s16 = &((int16_t *)rawdata_buf)[col_i]; + } else { + rawdata_u8 = &((u8 *)rawdata_buf)[col_i]; + } + for (j = 0; j < col_size; j++) { + if (type == PRD_PRT_TYPE_S16) { + curr_raw = *rawdata_s16++; + } else { + curr_raw = *rawdata_u8++; + } + + size += siw_prd_buf_snprintf(buf, size, + "%4d ", curr_raw); + + log_size += siw_prd_buf_snprintf(log_buf, + log_size, + "%4d ", curr_raw); + + if (curr_raw && (curr_raw < min)) { + min = curr_raw; + } + if (curr_raw > max) { + max = curr_raw; + } + } + t_prd_info(prd, "%s\n", log_buf); + + col_i += (col_size + col_add); + } + + size = prd_print_post(prd, buf, size, min, max); + + return size; +} + +static int prd_print_u8(struct siw_hal_prd_data *prd, char *buf, + int size, u8 *rawdata_buf_u8, + int row_size, int col_size, + const char *name, int opt) +{ + return prd_print_xxx(prd, buf, size, (void *)rawdata_buf_u8, + row_size, col_size, name, opt, PRD_PRT_TYPE_U8); +} + +static int prd_print_s16(struct siw_hal_prd_data *prd, char *buf, + int size, int16_t *rawdata_buf_u16, + int row_size, int col_size, + const char *name, int opt) +{ + return prd_print_xxx(prd, buf, size, (void *)rawdata_buf_u16, + row_size, col_size, name, opt, PRD_PRT_TYPE_S16); +} + +static int prd_print_rawdata(struct siw_hal_prd_data *prd, + char *buf, int type, int size, int opt) +{ + struct siw_hal_prd_param *param = &prd->param; + int16_t *rawdata_buf_16 = NULL; + u8 *rawdata_buf_u8 = NULL; + const char *name = NULL; + int row_size = param->row; + int col_size = param->col; + + switch (type) { + case M1_EVEN_DATA: + col_size = param->m1_col; + rawdata_buf_16 = prd->m1_buf_even_rawdata; + name = "EVEN Data"; + break; + case M1_ODD_DATA: + col_size = param->m1_col; + rawdata_buf_16 = prd->m1_buf_odd_rawdata; + name = "ODD Data"; + break; + case M2_EVEN_DATA: + rawdata_buf_16 = prd->m2_buf_even_rawdata; + name = "EVEN Data"; + break; + case M2_ODD_DATA: + rawdata_buf_16 = prd->m2_buf_odd_rawdata; + name = "ODD Data"; + break; + case LABEL_DATA: + rawdata_buf_u8 = prd->buf_label; + name = "LABEL Data"; + break; + case DEBUG_DATA: + rawdata_buf_16 = prd->buf_debug; + name = "DEBUG Data"; + break; + default: + t_prd_warn(prd, "prd_print_rawdata: invalud type, %d\n", type); + break; + } + + if (rawdata_buf_16) { + size = prd_print_s16(prd, buf, size, rawdata_buf_16, + row_size, col_size, name, opt); + } else if (rawdata_buf_u8) { + size = prd_print_u8(prd, buf, size, rawdata_buf_u8, + row_size, col_size, name, opt); + } + + return size; +} + +#define STR_SPEC_BY_PARAM "(p)" + +/* +* return "result Pass:0 , Fail:1" +*/ +static int prd_compare_tool(struct siw_hal_prd_data *prd, + int test_cnt, int16_t **buf, + int row, int col, int type, int opt) +{ + struct siw_hal_prd_param *param = &prd->param; +// struct device *dev = prd->dev; +// struct siw_ts *ts = to_touch_core(dev); + struct siw_touch_second_screen *second_screen = NULL; + int16_t *raw_buf; + int16_t *raw_curr; + int i, j ,k; + int col_i; + int col_add = (opt) ? param->col_add : 0; + int size = 0; + int curr_raw; + int curr_lower, curr_upper; + int raw_err = 0; + int result = 0; + int spec_by_param = 0; + int cnt = 0; + + spec_by_param = !!(opt & (1<<16)); + opt &= 0xFFFF; + + col_add = (opt) ? param->col_add : 0; + + if (!test_cnt) { + t_prd_err(prd, "zero test count\n"); + result = 1; + size += siw_prd_buf_snprintf(prd->buf_write, + size, + "zero test count\n"); + goto out; + } + + curr_lower = prd->image_lower; + curr_upper = prd->image_upper; + + t_prd_info(prd, "lower %d, upper %d %s\n", + curr_lower, curr_upper, + (spec_by_param) ? STR_SPEC_BY_PARAM : ""); + size += siw_prd_buf_snprintf(prd->buf_write, + size, + "lower %d, upper %d %s\n", + curr_lower, curr_upper, + (spec_by_param) ? STR_SPEC_BY_PARAM : ""); + + switch (type) { + case U3_M1_RAWDATA_TEST: + case U0_M1_RAWDATA_TEST: + case U3_M1_JITTER_TEST: + case U0_M1_JITTER_TEST: + break; + + default: + second_screen = ¶m->second_scr; + break; + } + + for (k = 0; k < test_cnt; k++) { + t_prd_info(prd, + "-------- Compare[%d/%d] --------\n", + k, test_cnt); + size += siw_prd_buf_snprintf(prd->buf_write, + size, + "-------- Compare[%d/%d] --------\n", + k, test_cnt); + raw_buf = buf[k]; + if (raw_buf == NULL) { + t_prd_err(prd, "compare failed: NULL buf\n"); + result = 1; + size += siw_prd_buf_snprintf(prd->buf_write, + size, + "compare failed: NULL buf\n"); + goto out; + } + col_i = 0; + + for (i = 0; i < row; i++) { + raw_curr = &raw_buf[col_i]; + + for (j = 0; j < col; j++) { + curr_raw = *raw_curr++; + + if ((curr_raw >= curr_lower) && + (curr_raw <= curr_upper)) { + t_prd_dbg_trace(prd, + "cnt %d, %d(lower) <= %d <= %d(upper) \n", + cnt, curr_lower, curr_raw, curr_upper); + cnt++; + continue; + } + + raw_err = 1; + if ((second_screen != NULL) && + (i <= second_screen->bound_i) && + (j <= second_screen->bound_j)) { + raw_err = !!(curr_raw)<<1; /* 2 or 0 */ + } + + if (raw_err) { + result = 1; + t_prd_info(prd, + "F [%d][%d] = %d(%d)\n", + i, j, curr_raw, raw_err); + size += siw_prd_buf_snprintf(prd->buf_write, + size, + "F [%d][%d] = %d(%d)\n", + i, j, curr_raw, raw_err); + } else { + #if 0 + t_prd_info(prd, + "G [%d][%d] = %d\n", + i, j, curr_raw); + #endif + } + } + col_i += (col + col_add); + } + + if (!result) { + t_prd_info(prd, + "(none)\n"); + size += siw_prd_buf_snprintf(prd->buf_write, + size, + "(none)\n"); + } + } + +// t_prd_info(prd, "type %d, result %d\n", type, result); + +out: + return result; +} + +/* + * Rawdata compare result + * Pass : reurn 0 + * Fail : return 1 + */ +static int prd_compare_rawdata(struct siw_hal_prd_data *prd, int type) +{ + struct siw_hal_prd_param *param = &prd->param; +#if defined(__SIW_SUPPORT_PRD_SET_SD) + struct siw_hal_prd_sd_param *sd_param = &prd->sd_param; +#endif + int spec_by_param = 0; +// struct device *dev = prd->dev; + /* spec reading */ + char lower_str[64] = {0, }; + char upper_str[64] = {0, }; + int16_t *rawdata_buf[MAX_TEST_CNT] = { + [0] = prd->m2_buf_even_rawdata, + [1] = prd->m2_buf_odd_rawdata, + }; + int row_size = param->row; + int col_size = param->col; + int lower = 0, upper = 0; + int invalid = 0; + int test_cnt = 0; + int sel_m1 = 0; + int opt = 1; + int ret = 0; + + if ((type >= UX_INVALID) && !prd_cmp_tool_str[type]) { + t_prd_err(prd, "invalid type, %d\n", type); + return -EINVAL; + } + + snprintf(lower_str, sizeof(lower_str), prd_cmp_tool_str[type][0]); + snprintf(upper_str, sizeof(upper_str), prd_cmp_tool_str[type][1]); + + sel_m1 = prd_check_type(prd, type, 2); + if (sel_m1 < 0) { + return sel_m1; + } + + if (sel_m1) { + row_size = param->row; + col_size = param->m1_col; + rawdata_buf[0] = prd->m1_buf_even_rawdata; + rawdata_buf[1] = prd->m1_buf_odd_rawdata; + test_cnt = param->m1_cnt; + opt = 0; + } else { + test_cnt = param->m2_cnt; + } + +#if defined(__SIW_SUPPORT_PRD_SET_SD) + switch (type) { + case U3_M2_RAWDATA_TEST: + lower = sd_param->u3_m2[0]; + upper = sd_param->u3_m2[1]; + break; + case U3_M1_RAWDATA_TEST: + lower = sd_param->u3_m1[0]; + upper = sd_param->u3_m1[1]; + break; + case U0_M2_RAWDATA_TEST: + lower = sd_param->u0_m2[0]; + upper = sd_param->u0_m2[1]; + break; + case U0_M1_RAWDATA_TEST: + lower = sd_param->u0_m1[0]; + upper = sd_param->u0_m1[1]; + break; + case U3_BLU_JITTER_TEST: + lower = sd_param->u3_blu_jitter[0]; + upper = sd_param->u3_blu_jitter[1]; + break; + case U3_JITTER_TEST: + lower = sd_param->u3_jitter[0]; + upper = sd_param->u3_jitter[1]; + break; + case U3_M1_JITTER_TEST: + lower = sd_param->u3_m1_jitter[0]; + upper = sd_param->u3_m1_jitter[1]; + break; + case U0_JITTER_TEST: + lower = sd_param->u0_jitter[0]; + upper = sd_param->u0_jitter[1]; + break; + case U0_M1_JITTER_TEST: + lower = sd_param->u0_m1_jitter[0]; + upper = sd_param->u0_m1_jitter[1]; + break; + } + if (lower || upper) { + spec_by_param = (1<<16); + } +#endif + + if (spec_by_param) { + t_prd_info(prd, "type %d, skip reading spec file\n", type); + goto skip_file; + } + +#if !defined(__SIW_SUPPORT_PRD_SET_SD_ONLY) + ret = prd_get_limit(prd, + lower_str, &lower); + if (ret < 0) { + return ret; + } + + ret = prd_get_limit(prd, + upper_str, &upper); + if (ret < 0) { + return ret; + } +#endif + +skip_file: + invalid |= !!(lower < 0); + invalid |= !!(upper < 0); + invalid |= !!(lower >= upper); + + if (invalid) { + t_prd_err(prd, "wrong condition: type %d, lower %d, upper %d %s\n", + type, lower, upper, + (spec_by_param) ? STR_SPEC_BY_PARAM : ""); + + siw_prd_buf_snprintf(prd->buf_write, 0, + "wrong condition: type %d, lower %d, upper %d %s\n", + type, lower, upper, + (spec_by_param) ? STR_SPEC_BY_PARAM : ""); + + return -EINVAL; + } + + prd->image_lower = lower; + prd->image_upper = upper; + + ret = prd_compare_tool(prd, test_cnt, + rawdata_buf, row_size, col_size, type, + opt | spec_by_param); + + return ret; +} + +/* + * SIW TOUCH IC F/W Stop HandShake + */ +static int prd_stop_firmware(struct siw_hal_prd_data *prd, u32 wdata, int flag) +{ + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; + u32 read_val; + u32 check_data = 0; + int try_cnt = 0; + int ret = 0; + + /* + * STOP F/W to check + */ + ret = siw_hal_write_value(dev, reg->prd_ic_ait_start_reg, wdata); + if (ret < 0) { + goto out; + } + ret = siw_hal_read_value(dev, reg->prd_ic_ait_start_reg, &check_data); + if (ret < 0) { + goto out; + } +#if 0 + ret = siw_hal_read_value(dev, reg->prd_ic_ait_start_reg, &check_data); + if (ret < 0) { + goto out; + } +#endif + t_prd_info_flag(prd, flag, "check_data : %x\n", check_data); + + try_cnt = 1000; + do { + --try_cnt; + if (try_cnt == 0) { + t_prd_err(prd, "[ERR] stop FW: timeout, %08Xh\n", read_val); + ret = -ETIMEDOUT; + goto out; + } + siw_hal_read_value(dev, reg->prd_ic_ait_data_readystatus, &read_val); + t_prd_dbg_base_flag(prd, flag, "Read RS_IMAGE = [%x] , OK RS_IMAGE = [%x]\n", + read_val, (u32)RS_IMAGE); + touch_msleep(2); + } while (read_val != (u32)RS_IMAGE); + +out: + return ret; +} + +static int prd_start_firmware(struct siw_hal_prd_data *prd) +{ + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; + u32 const cmd = IT_IMAGE_NONE; + u32 check_data = 0; + u32 read_val = 0; + int ret = 0; + + /* Release F/W to operate */ + ret = siw_hal_write_value(dev, reg->prd_ic_ait_start_reg, cmd); + if (ret < 0) { + goto out; + } + ret = siw_hal_read_value(dev, reg->prd_ic_ait_start_reg, &check_data); + if (ret < 0) { + goto out; + } +#if 0 + ret = siw_hal_read_value(dev, reg->prd_ic_ait_start_reg, &check_data); + if (ret < 0) { + goto out; + } +#endif + t_prd_dbg_base(prd, "check_data : %x\n", check_data); + + //for test + ret = siw_hal_read_value(dev, reg->prd_ic_ait_data_readystatus, &read_val); + if (ret < 0) { + goto out; + } + t_prd_dbg_base(prd, "Read RS_IMAGE = [%x]\n", read_val); + +out: + return ret; +} + +static int prd_read_rawdata(struct siw_hal_prd_data *prd, int type) +{ + struct siw_hal_prd_param *param = &prd->param; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + int __m1_frame_size = ctrl->m1_frame_size; + int __m2_frame_size = ctrl->m2_frame_size; + u32 raw_offset_info = 0; + u32 raw_data_offset[MAX_TEST_CNT] = {0, }; + int16_t *buf_rawdata[MAX_TEST_CNT] = { + [0] = prd->m2_buf_even_rawdata, + [1] = prd->m2_buf_odd_rawdata, + }; + int16_t *tmp_buf = prd->m1_buf_tmp; + int16_t *raw_buf; + int addr = reg->prd_m1_m2_raw_offset; + int sel_m1 = 0; + int i, j = 0; + int ret = 0; + + if (__m1_frame_size & 0x3) + __m1_frame_size = (((__m1_frame_size >> 2) + 1) << 2); + if (__m2_frame_size & 0x3) + __m2_frame_size = (((__m2_frame_size >> 2) + 1) << 2); + + ret = siw_hal_read_value(dev, + addr, + &raw_offset_info); + if (ret < 0) { + goto out; + } + + switch (touch_chip_type(ts)) { + case CHIP_SW1828: + raw_data_offset[0] = RAW_OFFSET_ODD(raw_offset_info); + raw_data_offset[1] = RAW_OFFSET_EVEN(raw_offset_info); + break; + default: + raw_data_offset[0] = RAW_OFFSET_EVEN(raw_offset_info); + raw_data_offset[1] = RAW_OFFSET_ODD(raw_offset_info); + break; + } + t_prd_info(prd, "test[%04Xh] type %d, even offset %04Xh, odd offset %04Xh\n", + addr, type, raw_data_offset[0], raw_data_offset[1]); + + sel_m1 = prd_check_type(prd, type, 0); + if (sel_m1 < 0) { + ret = sel_m1; + goto out; + } + + if (sel_m1) { + buf_rawdata[0] = prd->m1_buf_even_rawdata; + buf_rawdata[1] = prd->m1_buf_odd_rawdata; + + for (i = 0; i < param->m1_cnt; i++) { + raw_buf = buf_rawdata[i]; + + if (raw_buf == NULL) { + t_prd_err(prd, "reading rawdata(%d) failed: NULL buf\n", type); + ret = -EFAULT; + goto out; + } + + /* raw data offset write */ + ret = siw_hal_write_value(dev, + reg->serial_data_offset, + raw_data_offset[i]); + if (ret < 0) { + goto out; + } + + /* raw data read */ + memset(raw_buf, 0, __m1_frame_size); + memset(tmp_buf, 0, __m1_frame_size); + + ret = siw_hal_reg_read(dev, + reg->data_i2cbase_addr, + (void *)tmp_buf, __m1_frame_size); + if (ret < 0) { + goto out; + } + + for (j = 0; j < param->row; j++) { + raw_buf[j<<1] = tmp_buf[j]; + raw_buf[(j<<1)+1] = tmp_buf[param->row+j]; + } + + } + } else { + for (i = 0; i < param->m2_cnt; i++) { + /* raw data offset write */ + ret = siw_hal_write_value(dev, + reg->serial_data_offset, + raw_data_offset[i]); + if (ret < 0) { + goto out; + } + + if (buf_rawdata[i] == NULL) { + t_prd_err(prd, "reading rawdata(%d) failed: NULL buf\n", type); + ret = -EFAULT; + goto out; + } + + /* raw data read */ + memset(buf_rawdata[i], 0, __m2_frame_size); + + ret = siw_hal_reg_read(dev, + reg->data_i2cbase_addr, + (void *)buf_rawdata[i], __m2_frame_size); + if (ret < 0) { + goto out; + } + } + } + +out: + return ret; +} + +static void prd_tune_display(struct siw_hal_prd_data *prd, char *tc_tune_code, + int offset, int type, int result_on) +{ + struct siw_hal_prd_tune *tune = &prd->tune; +// struct device *dev = prd->dev; + char *log_buf = prd->log_buf; + char *temp; + int code_size = tune->code_size; + int size = 0; + int i = 0; + + temp = kzalloc(code_size, GFP_KERNEL); + if (temp == NULL) { + t_prd_err(prd, "tune_display failed: NULL buf\n"); + return; + } + + switch (type) { + case PRD_TUNE_DISPLAY_TYPE_1: + size = snprintf(log_buf, code_size, + "GOFT tune_code_read : "); + if ((tc_tune_code[offset] >> 4) == 1) { + temp[offset] = tc_tune_code[offset] - (0x1 << 4); + size += snprintf(log_buf + size, + code_size - size, + " %d ", temp[offset]); + } else { + size += snprintf(log_buf + size, + code_size - size, + "-%d ", tc_tune_code[offset]); + } + t_prd_info(prd, "%s\n", log_buf); + size += snprintf(log_buf + size, code_size - size, "\n"); + if (result_on == RESULT_ON) { + prd_write_file(prd, log_buf, TIME_INFO_SKIP); + } + break; + case PRD_TUNE_DISPLAY_TYPE_2: + size = snprintf(log_buf, code_size, + "LOFT tune_code_read : "); + for (i = 0; i < tune->ch; i++) { + if ((tc_tune_code[offset+i]) >> 5 == 1) { + temp[offset+i] = + tc_tune_code[offset+i] - (0x1 << 5); + size += snprintf(log_buf + size, + code_size - size, + " %d ", temp[offset+i]); + } else { + size += snprintf(log_buf + size, + code_size - size, + "-%d ", + tc_tune_code[offset+i]); + } + } + t_prd_info(prd, "%s\n", log_buf); + size += snprintf(log_buf + size, code_size - size, "\n"); + if (result_on == RESULT_ON) { + prd_write_file(prd, log_buf, TIME_INFO_SKIP); + } + break; + default: + t_prd_err(prd, "unknown tune type\n"); + break; + } + + kfree(temp); +} + +/* +* tune code result check +*/ +static int prd_do_read_tune_code(struct siw_hal_prd_data *prd, u8 *buf, int type, int result_on) +{ + struct siw_hal_prd_tune *tune = &prd->tune; + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; + u32 tune_code_offset; + u32 offset; + int ret = 0; + + ret = siw_hal_read_value(dev, + reg->prd_tune_result_offset, + &tune_code_offset); + if (ret < 0) { + goto out; + } + offset = (tune_code_offset >> 16) & 0xFFFF; + + t_prd_info(prd, "tune_code_offset = %Xh", offset); +// t_prd_dbg_base(prd, "tune_code_offset = %Xh", offset); + + ret = siw_hal_write_value(dev, + reg->serial_data_offset, + offset); + if (ret < 0) { + goto out; + } + ret = siw_hal_reg_read(dev, + reg->data_i2cbase_addr, + (void *)buf, tune->code_size); + if (ret < 0) { + goto out; + } + + if (result_on == RESULT_ON) { + prd_write_file(prd, "\n[Read Tune Code]\n", TIME_INFO_SKIP); + } + + switch (type) { + case U3_M1_RAWDATA_TEST: + case U0_M1_RAWDATA_TEST: + prd_tune_display(prd, buf, + tune->code_l_goft_offset, + PRD_TUNE_DISPLAY_TYPE_1, + result_on); + prd_tune_display(prd, buf, + tune->code_r_goft_offset, + PRD_TUNE_DISPLAY_TYPE_1, + result_on); + prd_tune_display(prd, buf, + tune->code_l_m1_oft_offset, + PRD_TUNE_DISPLAY_TYPE_2, + result_on); + prd_tune_display(prd, buf, + tune->code_r_m1_oft_offset, + PRD_TUNE_DISPLAY_TYPE_2, + result_on); + break; + case U3_M2_RAWDATA_TEST: + case U0_M2_RAWDATA_TEST: + prd_tune_display(prd, buf, + tune->code_l_goft_offset + 1, + PRD_TUNE_DISPLAY_TYPE_1, + result_on); + prd_tune_display(prd, buf, + tune->code_r_goft_offset + 1, + PRD_TUNE_DISPLAY_TYPE_1, + result_on); + prd_tune_display(prd, buf, + tune->code_l_g1_oft_offset, + PRD_TUNE_DISPLAY_TYPE_2, + result_on); + prd_tune_display(prd, buf, + tune->code_l_g2_oft_offset, + PRD_TUNE_DISPLAY_TYPE_2, + result_on); + prd_tune_display(prd, buf, + tune->code_l_g3_oft_offset, + PRD_TUNE_DISPLAY_TYPE_2, + result_on); + prd_tune_display(prd, buf, + tune->code_r_g1_oft_offset, + PRD_TUNE_DISPLAY_TYPE_2, + result_on); + prd_tune_display(prd, buf, + tune->code_r_g2_oft_offset, + PRD_TUNE_DISPLAY_TYPE_2, + result_on); + prd_tune_display(prd, buf, + tune->code_r_g3_oft_offset, + PRD_TUNE_DISPLAY_TYPE_2, + result_on); + break; + } + if (result_on == RESULT_ON) { + prd_write_file(prd, "\n", TIME_INFO_SKIP); + } + +out: + return ret; +} + +static int prd_read_tune_code(struct siw_hal_prd_data *prd, int type, int result_on) +{ + struct siw_hal_prd_tune *tune = &prd->tune; + u8 *buf; + int ret; + + buf = kzalloc(tune->code_size, GFP_KERNEL); + if (buf == NULL) { + t_prd_err(prd, "reading tune code failed: NULL buf\n"); + return -ENOMEM; + } + + ret = prd_do_read_tune_code(prd, buf, type, result_on); + + kfree(buf); + + return ret; +} + +/* + * print Frame Data & Compare rawdata & save result + * return result Pass:0 Fail:1 + */ +static int prd_conrtol_rawdata_result(struct siw_hal_prd_data *prd, int type, int result_on) +{ + struct siw_hal_prd_param *param = &prd->param; + int print_type[2] = {0, }; + int i,test_cnt; + int sel_m1 = 0; + int opt = 1; + int size = 0; + int result = 0; + + sel_m1 = prd_check_type(prd, type, 1); + if (sel_m1 < 0) { + return 1; + } + + if (sel_m1) { + print_type[0] = M1_EVEN_DATA; + print_type[1] = M1_ODD_DATA; + test_cnt = param->m1_cnt; + opt = 0; + } else { + print_type[0] = M2_EVEN_DATA; + print_type[1] = M2_ODD_DATA; + test_cnt = param->m2_cnt; + } + +#if 1 + /* print Raw Data */ + for (i = 0; i < test_cnt; i++) { + size = prd_print_rawdata(prd, prd->buf_write, print_type[i], size, opt); + } + if (size) + size += siw_prd_buf_snprintf(prd->buf_write, size, "\n\n"); + + if (result_on == RESULT_ON) { + /* Frame Data write to Result File */ + prd_write_file(prd, prd->buf_write, TIME_INFO_SKIP); + + memset(prd->buf_write, 0, PRD_BUF_SIZE); + + /* rawdata compare result(pass : 0 fail : 1) */ + result = prd_compare_rawdata(prd, type); + prd_write_file(prd, prd->buf_write, TIME_INFO_SKIP); + } +#else + /* print Raw Data */ + for (i = 0; i < test_cnt; i++) { + size = prd_print_rawdata(prd, prd->buf_write, print_type[i], size, opt); + + if (size) + size += siw_prd_buf_snprintf(prd->buf_write, size, "\n\n"); + + if (result_on == RESULT_ON) { + /* Frame Data write to Result File */ + prd_write_file(prd, prd->buf_write, TIME_INFO_SKIP); + + memset(prd->buf_write, 0, PRD_BUF_SIZE); + } + } + + if (result_on == RESULT_ON) { + /* rawdata compare result(pass : 0 fail : 1) */ + result = prd_compare_rawdata(prd, type); + prd_write_file(prd, prd->buf_write, TIME_INFO_SKIP); + } +#endif + + return result; +} + +/* + * Index "result_on" -> Save Result File ON: 1 , OFF: 0 + * return - Pass: 0 , Fail: 1 + */ +static int prd_do_rawdata_test(struct siw_hal_prd_data *prd, + int type, int result_on) +{ +// struct device *dev = prd->dev; + char *info_str = NULL; + char *sprt_str = NULL; + char test_type[32] = {0, }; + int ret = 0; + + switch (type) { + case U3_M2_RAWDATA_TEST: + info_str = "========U3_M2_RAWDATA_TEST========"; + if (result_on) { + sprt_str = "[U3_M2_RAWDATA_TEST]"; + } + break; + case U3_M1_RAWDATA_TEST: + info_str = "========U3_M1_RAWDATA_TEST========"; + if (result_on) { + sprt_str = "[U3_M1_RAWDATA_TEST]"; + } + break; + case U3_BLU_JITTER_TEST: + info_str = "========U3_BLU_JITTER_TEST========"; + if (result_on) { + sprt_str= "[U3_BLU_JITTER_TEST]"; + } + break; + case U3_JITTER_TEST: + info_str = "========U3_JITTER_TEST========"; + if (result_on) { + sprt_str= "[U3_JITTER_TEST]"; + } + break; + case U3_M1_JITTER_TEST: + info_str = "========U3_M1_JITTER_TEST========"; + if (result_on) { + sprt_str= "[U3_M1_JITTER_TEST]"; + } + break; + case U0_JITTER_TEST: + info_str = "========U0_JITTER_TEST========"; + if (result_on) { + sprt_str= "[U0_JITTER_TEST]"; + } + break; + case U0_M1_JITTER_TEST: + info_str = "========U0_M1_JITTER_TEST========"; + if (result_on) { + sprt_str= "[U0_M1_JITTER_TEST]"; + } + break; + case U0_M2_RAWDATA_TEST: + info_str = "========U0_M2_RAWDATA_TEST========"; + if (result_on) { + sprt_str = "[U0_M2_RAWDATA_TEST]"; + } + break; + case U0_M1_RAWDATA_TEST: + info_str = "========U0_M1_RAWDATA_TEST========"; + if (result_on) { + sprt_str = "[U0_M1_RAWDATA_TEST]"; + } + break; + default: + t_prd_err(prd, "Test Type not defined, %d\n", type); + return 1; + } + + if (info_str) { + t_prd_info(prd, "%s\n", info_str); + } + if (sprt_str) { + snprintf(test_type, sizeof(test_type), "\n\n%s\n", sprt_str); + /* Test Type Write */ + prd_write_file(prd, test_type, TIME_INFO_SKIP); + } + + /* Test Start & Finish Check */ + ret = prd_write_test_mode(prd, type); + if (ret < 0) { + return ret; + } + if (!ret) { + return 1; + } + + /* Read Frame Data */ + prd_read_rawdata(prd, type); + + /* Compare Spec & Print & Save result*/ + ret = prd_conrtol_rawdata_result(prd, type, result_on); + + /* tune code result check */ + switch (type) { + case U3_M2_RAWDATA_TEST: + case U3_M1_RAWDATA_TEST: + case U0_M2_RAWDATA_TEST: + case U0_M1_RAWDATA_TEST: + prd_read_tune_code(prd, type, result_on); + break; + default: + break; + } + + return ret; +} + +static int prd_rawdata_test(struct siw_hal_prd_data *prd, + int type, int result_on) +{ + int ret; + + ret = prd_do_rawdata_test(prd, type, result_on); + if (ret < 0) { + t_prd_err(prd, "prd_rawdata_test(%d) failed, %d\n", + type, ret); + } + + return ret; +} + +static void prd_firmware_version_log(struct siw_hal_prd_data *prd) +{ + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_fw_info *fw = &chip->fw; + char *log_buf = prd->log_buf; + int boot_mode = 0; + int ret = 0; + + memset(log_buf, 0, sizeof(prd->log_buf)); + + boot_mode = siw_touch_boot_mode_check(dev); + if (boot_mode >= MINIOS_MFTS_FOLDER) + ret = prd_chip_info(dev); + + ret = snprintf(log_buf, PRD_LOG_BUF_SIZE, + "======== Firmware Info ========\n"); + if (fw->version_ext) { + ret += snprintf(log_buf + ret, PRD_LOG_BUF_SIZE - ret, + "version : %08X\n", + fw->version_ext); + } else { + ret += snprintf(log_buf + ret, PRD_LOG_BUF_SIZE - ret, + "version : v%d.%02d\n", + fw->v.version.major, + fw->v.version.minor); + } + ret += snprintf(log_buf + ret, PRD_LOG_BUF_SIZE - ret, + "revision : %d\n", + fw->revision); + ret += snprintf(log_buf + ret, PRD_LOG_BUF_SIZE - ret, + "product id : %s\n", + fw->product_id); + + prd_write_file(prd, log_buf, TIME_INFO_SKIP); +} + +static void prd_ic_run_info_print(struct siw_hal_prd_data *prd) +{ + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; + char *log_buf = prd->log_buf; + int ret = 0; + u32 rdata[4] = {0}; + + memset(log_buf, 0, sizeof(prd->log_buf)); + + ret = siw_hal_reg_read(dev, + reg->info_lot_num, + (void *)&rdata, sizeof(rdata)); + + ret = snprintf(log_buf, PRD_LOG_BUF_SIZE, + "\n===== Production Info =====\n"); + ret += snprintf(log_buf + ret, PRD_LOG_BUF_SIZE - ret, + "lot : %d\n", rdata[0]); + ret += snprintf(log_buf + ret, PRD_LOG_BUF_SIZE - ret, + "serial : 0x%X\n", rdata[1]); + ret += snprintf(log_buf + ret, PRD_LOG_BUF_SIZE - ret, + "date : 0x%X 0x%X\n", + rdata[2], rdata[3]); + ret += snprintf(log_buf + ret, PRD_LOG_BUF_SIZE - ret, + "date : %04d.%02d.%02d %02d:%02d:%02d Site%d\n", + rdata[2] & 0xFFFF, (rdata[2] >> 16 & 0xFF), + (rdata[2] >> 24 & 0xFF), rdata[3] & 0xFF, + (rdata[3] >> 8 & 0xFF), + (rdata[3] >> 16 & 0xFF), + (rdata[3] >> 24 & 0xFF)); + + prd_write_file(prd, log_buf, TIME_INFO_SKIP); +} + +static int prd_write_test_control(struct siw_hal_prd_data *prd, u32 mode) +{ + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; + u32 test_mode_enter_cmt = mode; + u32 test_mode_enter_check = -1; + unsigned int delay_ms = 30; + int addr = reg->prd_tc_test_mode_ctl; + int ret = 0; + + ret = siw_hal_write_value(dev, + addr, + test_mode_enter_cmt); + if (ret < 0) { + goto out; + } + t_prd_info(prd, "wr prd_tc_test_mode_ctl[%04Xh] = %d\n", + addr, test_mode_enter_cmt); + + touch_msleep(delay_ms); + + ret = siw_hal_read_value(dev, + addr, + &test_mode_enter_check); + if (ret < 0) { + goto out; + } + t_prd_info(prd, "rd prd_tc_test_mode_ctl[%04Xh] = %d\n", + addr, test_mode_enter_check); + +out: + return ret; +} + +#define siw_snprintf_sd_result(_buf, _size, _item, _ret) \ + siw_snprintf(_buf, _size, "%s : %s\n", _item, (_ret) ? "Fail" : "Pass") + +#define __PRD_LOG_VIA_SHELL + +static int prd_show_do_sd(struct siw_hal_prd_data *prd, char *buf) +{ + struct device *dev = prd->dev; + struct siw_hal_prd_param *param = &prd->param; + int u3_rawdata_ret = 0; + int u3_m1_rawdata_ret = 0; + int openshort_ret = 0; + int blu_jitter_ret = 0; + int u3_jitter_ret = 0; + int u3_m1_jitter_ret = 0; + int irq_ret = 0; + int size = 0; + int ret; + + ret = prd_write_test_control(prd, CMD_TEST_ENTER); + if (ret < 0) { + goto out; + } + + ret = prd_chip_driving(dev, LCD_MODE_STOP); + if (ret < 0) { + goto out; + } + + /* + * IRQ_TEST + * ret - pass : 0, fail : 1 + */ + if (param->sd_test_flag & IRQ_TEST_FLAG) { + irq_ret = prd_irq_test(prd, RESULT_ON); + if (irq_ret < 0) { + goto out; + } + } + + /* + * U3_M2_RAWDATA_TEST + * rawdata - pass : 0, fail : 1 + * rawdata tunecode - pass : 0, fail : 2 + */ + if(param->sd_test_flag & U3_M2_RAWDATA_TEST_FLAG) { + u3_rawdata_ret = prd_rawdata_test(prd, U3_M2_RAWDATA_TEST, RESULT_ON); + if (u3_rawdata_ret < 0) { + goto out; + } + } + + /* + * U3_M1_RAWDATA_TEST + * rawdata - pass : 0, fail : 1 + * rawdata tunecode - pass : 0, fail : 2 + */ + if(param->sd_test_flag & U3_M1_RAWDATA_TEST_FLAG) { + u3_m1_rawdata_ret = prd_rawdata_test(prd, U3_M1_RAWDATA_TEST, RESULT_ON); + if (u3_m1_rawdata_ret < 0) { + goto out; + } + } + + /* + U3_JITTER_TEST + Jitter - pass : 0, fail : 1 + This will be enabled later. + */ + if(param->sd_test_flag & U3_JITTER_TEST_FLAG) { + u3_jitter_ret = prd_rawdata_test(prd, U3_JITTER_TEST, RESULT_ON); + if (u3_jitter_ret < 0) { + goto out; + } + } + + /* + U3_M1_JITTER_TEST + Jitter - pass : 0, fail : 1 + This will be enabled later. + */ + if(param->sd_test_flag & U3_M1_JITTER_TEST_FLAG) { + u3_m1_jitter_ret = prd_rawdata_test(prd, U3_M1_JITTER_TEST, RESULT_ON); + if (u3_m1_jitter_ret < 0) { + goto out; + } + } + + /* + * U3_BLU_JITTER_TEST + * BLU Jitter - pass : 0, fail : 1 + * : if you want to BLU_JITTER_TEST You should be able to control LCD backlightness + * BLU_JITTER_TEST Start CMD Write -> + * LCD brightness ON 742ms -> LCD brightness OFF 278mms -> LCD brightness ON 278ms -> + * Read RawData & Compare with BLU Jitter Spec + */ + if(param->sd_test_flag & U3_BLU_JITTER_TEST_FLAG) { + blu_jitter_ret = prd_rawdata_test(prd, U3_BLU_JITTER_TEST, RESULT_ON); + if (blu_jitter_ret < 0) { + goto out; + } + } + + /* + * OPEN_SHORT_ALL_TEST + * open - pass : 0, fail : 1 + * short - pass : 0, fail : 2 + */ + if(param->sd_test_flag & OPEN_SHORT_NODE_TEST_FLAG) { + openshort_ret = prd_open_short_test(prd); + if (openshort_ret < 0) { + goto out; + } + } + + size += siw_snprintf(buf, size, + "\n========RESULT=======\n"); + + if (param->sd_test_flag & IRQ_TEST_FLAG) { + size += siw_snprintf_sd_result(buf, size, + "IRQ", irq_ret); + } + + if(param->sd_test_flag & U3_M2_RAWDATA_TEST_FLAG) { + size += siw_snprintf_sd_result(buf, size, + "U3 Raw Data", u3_rawdata_ret); + } + + if(param->sd_test_flag & U3_M1_RAWDATA_TEST_FLAG) { + size += siw_snprintf_sd_result(buf, size, + "U3 M1 Raw Data", u3_m1_rawdata_ret); + } + + if(param->sd_test_flag & U3_JITTER_TEST_FLAG) { + size += siw_snprintf_sd_result(buf, size, + "U3 Jitter", u3_jitter_ret); + } + + if(param->sd_test_flag & U3_M1_JITTER_TEST_FLAG) { + size += siw_snprintf_sd_result(buf, size, + "U3 M1 Jitter", u3_m1_jitter_ret); + } + + if(param->sd_test_flag & U3_BLU_JITTER_TEST_FLAG) { + size += siw_snprintf_sd_result(buf, size, + "Blu Jitter", blu_jitter_ret); + } + + if(param->sd_test_flag & OPEN_SHORT_NODE_TEST_FLAG) { + if (!openshort_ret) { + size += siw_snprintf(buf, size, + "Channel Status : Pass\n"); + } else { + int open_failed = (openshort_ret & 0x1); + int short_failed = ((openshort_ret>>1) & 0x3); + size += siw_snprintf(buf, size, + "Channel Status : Fail (open:%s/short:%s)\n", + (open_failed) ? "F" : "P", + (short_failed) ? "F" : "P"); + } + } + + prd_write_file(prd, buf, TIME_INFO_SKIP); + t_prd_info(prd, "%s \n", buf); + prd_write_test_control(prd, CMD_TEST_EXIT); + +out: + return size; +} + +static ssize_t prd_show_sd(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + int size = 0; + int ret = 0; + + ret = prd_drv_exception_check(prd); + if (ret) { + size += siw_snprintf(buf, size, + "drv exception(%d) detected, test canceled\n", ret); + t_prd_err(prd, "%s", buf); + goto out; + } + + /* LCD mode check */ + if (chip->lcd_mode != LCD_MODE_U3) { + size += siw_snprintf(buf, size, + "LCD mode is not U3. Test Result : Fail\n"); + goto out; + } + + /* ic rev check - MINIOS mode, MFTS mode check */ + ret = prd_ic_exception_check(prd, buf); + if (ret > 0) { + t_prd_err(prd, "ic exception(%d) detected, test canceled\n", ret); + size += siw_snprintf(buf, size, + "ic exception(%d) detected, test canceled\n", ret); + goto out; + } + + siw_touch_mon_pause(dev); + + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + + /* file create , time log */ + prd_write_file(prd, "\nShow_sd Test Start", TIME_INFO_SKIP); + prd_write_file(prd, "\n", TIME_INFO_WRITE); + + t_prd_info(prd, "show_sd test begins\n"); + + prd_firmware_version_log(prd); + prd_ic_run_info_print(prd); + + mutex_lock(&ts->lock); + size = prd_show_do_sd(prd, buf); + mutex_unlock(&ts->lock); + + prd_write_file(prd, "Show_sd Test End\n", TIME_INFO_WRITE); + prd_log_file_size_check(prd); + + t_prd_info(prd, "show_sd test terminated\n\n"); + + prd_chip_reset(dev); + + siw_touch_mon_resume(dev); + +out: + return (ssize_t)size; +} + +static int prd_show_prd_get_data_raw_core(struct device *dev, + u8 +*buf, int size, + u32 cmd, u32 offset, int flag) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + int ret = 0; + + if (!offset) { + t_prd_err(prd, "raw core(cmd %d) failed: zero offset\n", cmd); + ret = -EFAULT; + goto out; + } + + if (!size) { + t_prd_err(prd, "raw core(cmd %d) failed: zero size\n", cmd); + ret = -EFAULT; + goto out; + } + + if (buf == NULL) { + t_prd_err(prd, "raw core(cmd %d) failed: NULL buf\n", cmd); + ret = -EFAULT; + goto out; + } + + if (cmd != IT_DONT_USE_CMD) { + ret = prd_stop_firmware(prd, cmd, flag); + if (ret < 0) { + goto out; + } + } + + ret = siw_hal_write_value(dev, + reg->serial_data_offset, + offset); + if (ret < 0) { + goto out; + } + + memset(buf, 0, size); + + ret = siw_hal_reg_read(dev, reg->data_i2cbase_addr, + (void *)buf, + size); + if (ret < 0) { + goto out; + } + +out: + return ret; +} + +static int prd_show_prd_get_data_raw_prd(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + int ret = 0; + + t_prd_info(prd, "======== CMD_RAWDATA_PRD ========\n"); + + mutex_lock(&ts->lock); + + ret = prd_write_test_control(prd, CMD_TEST_ENTER); + if (ret < 0) { + goto out; + } + + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + + ret = prd_chip_driving(dev, LCD_MODE_STOP); + if (ret < 0) { + goto out; + } + + if (chip->lcd_mode == LCD_MODE_U3) { + ret = prd_rawdata_test(prd, U3_M2_RAWDATA_TEST, RESULT_OFF); + } else if (chip->lcd_mode == LCD_MODE_U0) { + ret = prd_rawdata_test(prd, U0_M2_RAWDATA_TEST, RESULT_OFF); + } else { + t_prd_err(prd, "LCD mode is not U3 or U0!! current mode = %d\n", + chip->lcd_mode); + ret = -EINVAL; + goto out; + } + + siw_touch_irq_control(dev, INTERRUPT_ENABLE); + + if (ret < 0) { + goto out; + } + + ret = prd_write_test_control(prd, CMD_TEST_EXIT); + if (ret < 0) { + goto out; + } + +out: + mutex_unlock(&ts->lock); + + prd_chip_reset(dev); + + return ret; +} + +static int prd_show_prd_get_data_raw_tcm(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + int __m2_frame_size = ctrl->m2_frame_size; + void *buf = prd->m2_buf_even_rawdata; + int buf_size = prd->ctrl.m2_row_col_buf_size; + int log_size = 0; + int ret = 0; + + /* LCD mode check */ +#if 0 + if (chip->lcd_mode != LCD_MODE_U3) { + t_prd_info(prd, "LCD mode is not U3. Test Result : Fail\n"); + goto out; + } +#endif + +#if defined(__PRD_LOG_VIA_SHELL) + t_prd_info(prd, "======== rawdata(tcm) ========\n"); + log_size += siw_prd_buf_snprintf(prd->buf_write, log_size, + "======== rawdata(tcm) ========\n"); +#else + t_prd_info(prd, "======== CMD_RAWDATA_TCM ========\n"); +#endif + + if (buf == NULL) { + t_prd_err(prd, "getting raw tcm failed: NULL buf\n"); + return -EFAULT; + } + + /* TCM Offset write 0 */ + ret = siw_hal_write_value(dev, reg->prd_serial_tcm_offset, 0); + if (ret < 0) { + goto out; + } + + /* + * TCM Memory Access Select + * tc_mem_sel(0x0457) "RAW" or "BASE1" or "BASE2" or "BASE3" + */ + ret = siw_hal_write_value(dev, reg->prd_tc_mem_sel, TCM_MEM_RAW); + if (ret < 0){ + goto out; + } + + /* Read Rawdata */ + memset(buf, 0, buf_size); + ret = siw_hal_reg_read(dev, + reg->prd_tcm_base_addr, + buf, + __m2_frame_size); + if (ret < 0){ + goto out; + } + + /* Print RawData Buffer */ + prd_print_rawdata(prd, prd->buf_write, M2_EVEN_DATA, log_size, 0); + +out: + return ret; +} + +static int prd_show_prd_get_data_do_raw_ait(struct device *dev, u8 +*buf, int size, int flag) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + u8 *pbuf = (buf) ? buf : (u8 *)prd->m2_buf_even_rawdata; + + return prd_show_prd_get_data_raw_core(dev, pbuf, size, + prd->img_cmd.raw, prd->img_offset.raw, flag); +} + +static int prd_show_prd_get_data_raw_ait(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + int size = (ctrl->m2_row_col_size<buf_write, log_size, + "======== rawdata ========\n"); +#else + t_prd_info(prd, "======== CMD_RAWDATA_AIT ========\n"); +#endif + + ret = prd_show_prd_get_data_do_raw_ait(dev, + (u8 *)prd->m2_buf_even_rawdata, + size, + 0); + if (ret < 0) { + goto out; + } + + prd_print_rawdata(prd, prd->buf_write, M2_EVEN_DATA, log_size, 0); + + ret = prd_start_firmware(prd); + if (ret < 0) { + goto out; + } + +out: + return ret; +} + +static int prd_show_prd_get_data_do_ait_basedata(struct device *dev, + u8 +*buf, int size, int step, int flag) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + u32 ait_cmd[MAX_TEST_CNT] = {prd->img_cmd.baseline_even, \ + prd->img_cmd.baseline_odd}; + u32 ait_offset[MAX_TEST_CNT] = {prd->img_offset.baseline_even, \ + prd->img_offset.baseline_odd}; + int16_t *buf_rawdata[MAX_TEST_CNT] = { \ + [0] = prd->m2_buf_even_rawdata, \ + [1] = prd->m2_buf_odd_rawdata, \ + }; + u8 *pbuf = (buf) ? buf : (u8 *)buf_rawdata[step]; + + return prd_show_prd_get_data_raw_core(dev, pbuf, size, + ait_cmd[step], ait_offset[step], flag); +} + +static int prd_show_prd_get_data_ait_basedata(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_param *param = &prd->param; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + u32 buf_type[MAX_TEST_CNT] = {M2_EVEN_DATA, M2_ODD_DATA}; + int size = (ctrl->m2_row_col_size<buf_write, log_size, + "======== basedata ========\n"); +#else + t_prd_info(prd, "======== CMD_AIT_BASEDATA ========\n"); +#endif + + for (i = 0; i < param->m2_cnt; i++) { + ret = prd_show_prd_get_data_do_ait_basedata(dev, + NULL, + size, + i, + 0); + if (ret < 0) { + goto out; + } + + log_size = prd_print_rawdata(prd, prd->buf_write, buf_type[i], log_size, 0); + #if !defined(__PRD_LOG_VIA_SHELL) + log_size = 0; + #endif + + ret = prd_start_firmware(prd); + if (ret < 0) { + goto out; + } + } + +out: + return ret; +} + +static int prd_show_prd_get_data_do_filtered_deltadata(struct device *dev, u8 *buf, int size, int flag) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_param *param = &prd->param; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + int16_t *pbuf = (buf) ? (int16_t *)buf : prd->m2_buf_even_rawdata; + int size_rd = (ctrl->delta_size<col; + int row, col; + int i; + int ret = 0; + + ret = prd_show_prd_get_data_raw_core(dev, (u8 *)prd->buf_delta, size_rd, + prd->img_cmd.f_delta, prd->img_offset.f_delta, flag); + if (ret < 0) { + goto out; + } + + memset(pbuf, 0, size); + + for (i = 0; i < ctrl->m2_row_col_size; i++){ + row = i / param_col; + col = i % param_col; + pbuf[i] = prd->buf_delta[(row + 1)*(param_col + 2) + (col + 1)]; + } + +out: + return ret; +} + +static int prd_show_prd_get_data_filtered_deltadata(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + int size = (ctrl->m2_row_col_size<buf_write, log_size, + "======== filtered deltadata ========\n"); +#else + t_prd_info(prd, "======== CMD_FILTERED_DELTADATA ========\n"); +#endif + + ret = prd_show_prd_get_data_do_filtered_deltadata(dev, + (u8 *)prd->m2_buf_even_rawdata, + size, + 0); + if (ret < 0) { + goto out; + } + + prd_print_rawdata(prd, prd->buf_write, M2_EVEN_DATA, log_size, 0); + + ret = prd_start_firmware(prd); + if (ret < 0) { + goto out; + } + +out: + return ret; +}; + +static int prd_show_prd_get_data_do_deltadata(struct device *dev, u8 *buf, int size, int flag) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_param *param = &prd->param; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + int16_t *pbuf = (buf) ? (int16_t *)buf : prd->m2_buf_even_rawdata; + int size_rd = (ctrl->delta_size<col; + int row, col; + int i; + int ret = 0; + + ret = prd_show_prd_get_data_raw_core(dev, (u8 *)prd->buf_delta, size_rd, + prd->img_cmd.delta, prd->img_offset.delta, flag); + if (ret < 0) { + goto out; + } + + memset(pbuf, 0, size); + + for (i = 0; i < ctrl->m2_row_col_size; i++){ + row = i / param_col; + col = i % param_col; + pbuf[i] = prd->buf_delta[(row + 1)*(param_col + 2) + (col + 1)]; + } + +out: + return ret; +} + +static int prd_show_prd_get_data_deltadata(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + int size = (ctrl->m2_row_col_size<buf_write, log_size, + "======== deltadata ========\n"); +#else + t_prd_info(prd, "======== CMD_DELTADATA ========\n"); +#endif + + ret = prd_show_prd_get_data_do_deltadata(dev, + (u8 *)prd->m2_buf_even_rawdata, + size, + 0); + if (ret < 0) { + goto out; + } + + prd_print_rawdata(prd, prd->buf_write, M2_EVEN_DATA, log_size, 0); + + ret = prd_start_firmware(prd); + if (ret < 0) { + goto out; + } + +out: + return ret; + +} + +static int prd_show_prd_get_data_do_labeldata(struct device *dev, u8 *buf, int size, int flag) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_param *param = &prd->param; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + u8 *pbuf = (buf) ? buf : prd->buf_label; + int size_rd = ctrl->label_tmp_size; + int param_col = param->col; + int row, col; + int i; + int ret = 0; + + ret = prd_show_prd_get_data_raw_core(dev, (u8 *)prd->buf_label_tmp, size_rd, + prd->img_cmd.label, prd->img_offset.label, flag); + if (ret < 0) { + goto out; + } + + memset(pbuf, 0, size); + + for (i = 0; i < ctrl->m2_row_col_size; i++){ + row = i / param_col; + col = i % param_col; + pbuf[i] = prd->buf_label_tmp[(row + 1)*(param_col + 2) + (col + 1)]; + } + +out: + return ret; + +} + +static int prd_show_prd_get_data_labeldata(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + int size = ctrl->m2_row_col_size; + int log_size = 0; + int ret = 0; + +#if defined(__PRD_LOG_VIA_SHELL) + t_prd_info(prd, "======== labeldata ========\n"); + log_size += siw_prd_buf_snprintf(prd->buf_write, log_size, + "======== labeldata ========\n"); +#else + t_prd_info(prd, "======== CMD_LABELDATA ========\n"); +#endif + + ret = prd_show_prd_get_data_do_labeldata(dev, + (u8 *)prd->buf_label, + size, + 0); + if (ret < 0) { + goto out; + } + + prd_print_rawdata(prd, prd->buf_write, LABEL_DATA, log_size, 0); + + ret = prd_start_firmware(prd); + if (ret < 0) { + goto out; + } + +out: + return ret; +} + +static int prd_show_prd_get_data_blu_jitter(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + int ret = 0; + + t_prd_info(prd, "======== CMD_BLU_JITTER ========\n"); + + mutex_lock(&ts->lock); + + ret = prd_write_test_control(prd, CMD_TEST_ENTER); + if (ret < 0) { + goto out; + } + + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + + ret = prd_chip_driving(dev, LCD_MODE_STOP); + if (ret < 0) { + goto out; + } + + ret = prd_rawdata_test(prd, U3_BLU_JITTER_TEST, RESULT_OFF); + + siw_touch_irq_control(dev, INTERRUPT_ENABLE); + + if (ret < 0) { + goto out; + } + + ret = prd_write_test_control(prd, CMD_TEST_EXIT); + if (ret < 0) { + goto out; + } + +out: + mutex_unlock(&ts->lock); + + prd_chip_reset(dev); + + return ret; +} + +static int prd_show_prd_get_data_do_debug_buf(struct device *dev, u8 *buf, int size, int flag) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + u8 *pbuf = (buf) ? buf : (u8 *)prd->buf_debug; + + return prd_show_prd_get_data_raw_core(dev, pbuf, size, + IT_DONT_USE_CMD, prd->img_offset.debug, flag); +} + +static int prd_show_prd_get_data_debug_buf(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + int size = (ctrl->debug_buf_size<buf_write, log_size, + "======== debugdata ========\n"); +#else + t_prd_info(prd, "======== CMD_DEBUGDATA ========\n"); +#endif + + ret = prd_show_prd_get_data_do_debug_buf(dev, + (u8 *)prd->buf_debug, + size, + 0); + if (ret < 0) { + goto out; + } + + prd_print_rawdata(prd, prd->buf_write, DEBUG_DATA, log_size, 0); + +out: + return ret; +} + + +static ssize_t prd_show_prd_get_data(struct device *dev, int type) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + int ret = 0; + + switch (type) { + case CMD_RAWDATA_PRD: + ret = prd_show_prd_get_data_raw_prd(dev); + break; + case CMD_RAWDATA_TCM: + ret = prd_show_prd_get_data_raw_tcm(dev); + break; + case CMD_RAWDATA_AIT: + ret = prd_show_prd_get_data_raw_ait(dev); + break; + case CMD_AIT_BASEDATA: + ret = prd_show_prd_get_data_ait_basedata(dev); + break; + case CMD_FILTERED_DELTADATA: + ret = prd_show_prd_get_data_filtered_deltadata(dev); + break; + case CMD_DELTADATA: + ret = prd_show_prd_get_data_deltadata(dev); + break; + case CMD_LABELDATA: + ret = prd_show_prd_get_data_labeldata(dev); + break; + case CMD_BLU_JITTER: + ret = prd_show_prd_get_data_blu_jitter(dev); + break; + case CMD_DEBUG_BUF: + ret = prd_show_prd_get_data_debug_buf(dev); + break; + default: + t_prd_err(prd, "invalid get_data request CMD"); + ret = -EINVAL; + break; + }; + + return ret; +} + +static ssize_t prd_show_get_data_common(struct device *dev, char *buf, int type) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + int size = 0; + int ret = 0; + + ret = prd_drv_exception_check(prd); + if (ret) { + size += siw_snprintf(buf, size, + "drv exception(%d) detected, test canceled\n", ret); + t_prd_err(prd, "%s", buf); + return (ssize_t)size; + } + + siw_touch_mon_pause(dev); + ret = prd_show_prd_get_data(dev, type); + siw_touch_mon_resume(dev); + if (ret < 0){ + t_prd_err(prd, "prd_show_prd_get_data(%d) failed, %d\n", + type, ret); + } + + /* + * to prepare the response for APP I/F (not fixed) + */ +#if defined(__PRD_LOG_VIA_SHELL) + switch (type) { + case CMD_BLU_JITTER: + break; + default: + size += siw_snprintf(buf, size, "%s\n", prd->buf_write); + break; + } +#endif + size += siw_snprintf(buf, size, "Get Data[%s] result:\n", + prd_get_data_cmd_name[type]); + size += siw_snprintf(buf, size, "%s\n", + (ret < 0) ? "Fail" : "Pass"); + return (ssize_t)size; +} + +static ssize_t prd_show_delta(struct device *dev, char *buf) +{ + return (ssize_t)prd_show_get_data_common(dev, buf, CMD_DELTADATA); +} + +static ssize_t prd_show_label(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + int size = 0; + + /* LCD mode check */ + if (chip->lcd_mode != LCD_MODE_U3) { + size += siw_snprintf(buf, size, + "Current LCD mode(%d) is not U3, halted\n", + chip->lcd_mode); + return (ssize_t)size; + } + return (ssize_t)prd_show_get_data_common(dev, buf, CMD_LABELDATA); +} + +static ssize_t prd_show_rawdata_prd(struct device *dev, char *buf) +{ + return (ssize_t)prd_show_get_data_common(dev, buf, CMD_RAWDATA_PRD); +} + +static ssize_t prd_show_rawdata_tcm(struct device *dev, char *buf) +{ + return (ssize_t)prd_show_get_data_common(dev, buf, CMD_RAWDATA_TCM); +} + +static ssize_t prd_show_rawdata_ait(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + int size = 0; + + /* LCD mode check */ + if (chip->lcd_mode != LCD_MODE_U3) { + size += siw_snprintf(buf, size, + "Current LCD mode(%d) is not U3, halted\n", + chip->lcd_mode); + return (ssize_t)size; + } + return (ssize_t)prd_show_get_data_common(dev, buf, CMD_RAWDATA_AIT); +} + +static ssize_t prd_show_debug_buf(struct device *dev, char *buf) +{ + return (ssize_t)prd_show_get_data_common(dev, buf, CMD_DEBUG_BUF); +} + +static ssize_t prd_show_basedata(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + int size = 0; + + /* LCD mode check */ + if (chip->lcd_mode != LCD_MODE_U3) { + size += siw_snprintf(buf, size, + "Current LCD mode(%d) is not U3, halted\n", + chip->lcd_mode); + return size; + } + + return (ssize_t)prd_show_get_data_common(dev, buf, CMD_AIT_BASEDATA); +} + +static int prd_show_do_lpwg_sd(struct siw_hal_prd_data *prd, char *buf) +{ + struct device *dev = prd->dev; + struct siw_hal_prd_param *param = &prd->param; + int m1_rawdata_ret = 0; + int m2_rawdata_ret = 0; + int u0_jitter_ret = 0; + int u0_m1_jitter_ret = 0; + u32 sd_u0_test = 0; + int size = 0; + int ret = 0; + + ret = prd_write_test_control(prd, CMD_TEST_ENTER); + if (ret < 0) { + return ret; + } + + ret = prd_chip_driving(dev, LCD_MODE_STOP); + if (ret < 0) { + goto out; + } + + /* + * U0_M2_RAWDATA_TEST & U0_M1_RAWDATA_TEST + * rawdata - pass : 0, fail : 1 + * rawdata tunecode - pass : 0, fail : 2 + */ + if(param->lpwg_sd_test_flag & U0_M2_RAWDATA_TEST_FLAG) { + m2_rawdata_ret = prd_rawdata_test(prd, U0_M2_RAWDATA_TEST, RESULT_ON); + if (m2_rawdata_ret < 0) { + goto out; + } + } + + if(param->lpwg_sd_test_flag & U0_M1_RAWDATA_TEST_FLAG) { + m1_rawdata_ret = prd_rawdata_test(prd, U0_M1_RAWDATA_TEST, RESULT_ON); + if (m1_rawdata_ret < 0) { + goto out; + } + } + + /* + * U0_JITTER_TEST + * Jitter - pass : 0, fail : 1 + * This will be enabled later. + */ + if(param->lpwg_sd_test_flag & U0_JITTER_TEST_FLAG) { + u0_jitter_ret = prd_rawdata_test(prd, U0_JITTER_TEST, RESULT_ON); + if (u0_jitter_ret < 0) { + goto out; + } + } + + /* + * U0_M1_JITTER_TEST + * Jitter - pass : 0, fail : 1 + * This will be enabled later. + */ + if(param->lpwg_sd_test_flag & U0_M1_JITTER_TEST_FLAG) { + u0_m1_jitter_ret = prd_rawdata_test(prd, U0_M1_JITTER_TEST, RESULT_ON); + if (u0_m1_jitter_ret < 0) { + goto out; + } + } + + size = siw_snprintf(buf, size, "========RESULT=======\n"); + + sd_u0_test = param->lpwg_sd_test_flag & \ + (U0_M2_RAWDATA_TEST_FLAG | U0_M1_RAWDATA_TEST_FLAG); + + if (sd_u0_test) { + if (!m1_rawdata_ret && !m2_rawdata_ret) { + size += siw_snprintf(buf, size, + "LPWG RawData : Pass\n"); + } else { + size += siw_snprintf(buf, size, + "LPWG RawData : Fail "); + switch (sd_u0_test) { + case (U0_M2_RAWDATA_TEST_FLAG | U0_M1_RAWDATA_TEST_FLAG): + size += siw_snprintf(buf, size, + "(m2 %d, m1 %d)\n", m2_rawdata_ret, m1_rawdata_ret); + break; + case U0_M2_RAWDATA_TEST_FLAG: + size += siw_snprintf(buf, size, + "(m2 %d)\n", m2_rawdata_ret); + break; + case U0_M1_RAWDATA_TEST_FLAG: + size += siw_snprintf(buf, size, + "(m1 %d)\n", m1_rawdata_ret); + break; + } + } + } + + if(param->lpwg_sd_test_flag & U0_JITTER_TEST_FLAG) { + size += siw_snprintf_sd_result(buf, size, + "U0 Jitter", u0_jitter_ret); + } + + if(param->lpwg_sd_test_flag & U0_M1_JITTER_TEST_FLAG) { + size += siw_snprintf_sd_result(buf, size, + "U0 M1 Jitter", u0_m1_jitter_ret); + } + + prd_write_file(prd, buf, TIME_INFO_SKIP); + +out: + return size; +} + +static ssize_t prd_show_lpwg_sd(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + int size = 0; + int ret = 0; + + ret = prd_drv_exception_check(prd); + if (ret) { + size += siw_snprintf(buf, size, + "drv exception(%d) detected, test canceled\n", ret); + t_prd_err(prd, "%s", buf); + goto out; + } + + /* LCD mode check */ + if (chip->lcd_mode != LCD_MODE_U0) { + size = siw_snprintf(buf, size, + "LCD mode is not U0. Test Result : Fail\n"); + goto out; + } + + siw_touch_mon_pause(dev); + + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + + /* file create , time log */ + prd_write_file(prd, "\nShow_lpwg_sd Test Start", TIME_INFO_SKIP); + prd_write_file(prd, "\n", TIME_INFO_WRITE); + + t_prd_info(prd, "show_lpwg_sd test begins\n"); + + prd_firmware_version_log(prd); + prd_ic_run_info_print(prd); + + mutex_lock(&ts->lock); + size = prd_show_do_lpwg_sd(prd, buf); + mutex_unlock(&ts->lock); + + prd_write_file(prd, "Show_lpwg_sd Test End\n", TIME_INFO_WRITE); + prd_log_file_size_check(prd); + + t_prd_info(prd, "show_lpwg_sd test terminated\n"); + + prd_chip_reset(dev); + + siw_touch_mon_resume(dev); + +out: + return (ssize_t)size; +} + + +#define __PRD_FILE_RW_TEST + +#if defined(__PRD_FILE_RW_TEST) + +enum { + PRD_FILE_TEST_OPTION_0 = 0, + PRD_FILE_TEST_OPTION_1, + PRD_FILE_TEST_OPTION_2, + PRD_FILE_TEST_OPTION_MAX, +}; + +static void __prd_show_file_test_usage(struct siw_hal_prd_data *prd) +{ + t_prd_info(prd, "[Usage]\n"); + t_prd_info(prd, " Check : echo 0 {fname} > file_test\n"); + t_prd_info(prd, " Write : echo 1 {fname} > file_test\n"); + t_prd_info(prd, " : Remove old file and create new one\n"); + t_prd_info(prd, " Write : echo 2 {fname} > file_test\n"); + t_prd_info(prd, " : Rename old file and create new one\n"); +} + +static ssize_t prd_show_file_test(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + + __prd_show_file_test_usage(prd); + + return 0; +} + +static ssize_t prd_store_file_test(struct device *dev, + const char *buf, size_t count) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + char *fname = NULL; + int value = 0; + int ret = 0; + + fname = touch_getname(); + if (fname == NULL) { + t_prd_err(prd, "failed to allocate fname\n"); + return -ENOMEM; + } + + if (sscanf(buf, "%d %s", &value, fname) <= 0) { + siw_prd_sysfs_err_invalid_param(prd); + __prd_show_file_test_usage(prd); + goto out; + } + if (value >= PRD_FILE_TEST_OPTION_MAX) { + t_prd_err(prd, "invalude option, %d\n", value); + __prd_show_file_test_usage(prd); + goto out; + } + if (fname == NULL) { + t_prd_err(prd, "target file name is NULL\n"); + __prd_show_file_test_usage(prd); + goto out; + } + + ret = prd_vfs_file_chk(prd, fname, O_RDONLY, 0666, NULL); + if (ret < 0) { + /* */ + } + + /* Check mode */ + if (value == PRD_FILE_TEST_OPTION_0) { + goto out; + } + + if (!ret) { + if (value == PRD_FILE_TEST_OPTION_2) { /* Rename */ + char *buf1 = NULL; + char *buf2 = NULL; + int i; + + buf1 = touch_getname(); + if (buf1 == NULL) { + t_prd_err(prd, "failed to allocate name buf1\n"); + ret = -ENOMEM; + goto out; + } + + buf2 = touch_getname(); + if (buf2 == NULL) { + t_prd_err(prd, "failed to allocate name buf2\n"); + ret = -ENOMEM; + touch_putname(buf1); + goto out; + } + + for (i = MAX_LOG_FILE_COUNT - 1; i >= 0; i--) { + if (i == 0) { + snprintf(buf1, PATH_MAX, "%s", fname); + } else { + snprintf(buf1, PATH_MAX, "%s.%d", fname, i); + } + + if (i == (MAX_LOG_FILE_COUNT - 1)) { + ret = prd_vfs_file_remove(prd, buf1); + if (ret < 0) { + break; + } + } else { + snprintf(buf2, PATH_MAX, "%s.%d", fname, (i + 1)); + + ret = prd_vfs_file_rename(prd, buf1, buf2); + if (ret < 0) { + break; + } + } + } + + touch_putname(buf2); + touch_putname(buf1); + + if (ret < 0) { + goto out; + } + } else { /* Remove */ + ret = prd_vfs_file_remove(prd, fname); + if (ret < 0) { + goto out; + } + } + } + + ret = prd_do_write_file(prd, fname, "[File Write Test]\n", TIME_INFO_WRITE); + if (ret < 0) { + t_prd_err(prd, "File Write Test failed\n"); + } + +out: + touch_putname(fname); + + return (ssize_t)count; +} +#else /* __PRD_FILE_RW_TEST */ +static ssize_t prd_show_file_test(struct device *dev, char *buf) +{ + t_dev_info(dev, "Nop ...\n"); + return 0; +} + +static ssize_t prd_store_file_test(struct device *dev, + const char *buf, size_t count) +{ + t_dev_info(dev, "Nop ...\n"); + return (ssize_t)count; +} +#endif /* __PRD_FILE_RW_TEST */ + +enum { + REPORT_END_RS_NG = 0x05, + REPORT_END_RS_OK = 0xAA, +}; + +enum { + REPORT_OFF = 0, + REPORT_RAW, + REPORT_BASE, + REPORT_DELTA, + REPORT_LABEL, + REPORT_DEBUG_BUF, + REPORT_MAX, +}; + +static const char *prd_app_mode_str[] = { + [REPORT_OFF] = "OFF", + [REPORT_RAW] = "RAW", + [REPORT_BASE] = "BASE", + [REPORT_LABEL] = "LABEL", + [REPORT_DELTA] = "DELTA", + [REPORT_DEBUG_BUF] = "DEBUG_BUF", +}; + +static ssize_t prd_show_app_op_end(struct device *dev, char *buf, int prev_mode) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + int ret = 0; + + buf[0] = REPORT_END_RS_OK; + if (prev_mode != REPORT_OFF) { + prd->prd_app_mode = REPORT_OFF; + ret = prd_start_firmware(prd); + if (ret < 0) { + t_prd_err(prd, "prd_start_firmware failed, %d\n", ret); + buf[0] = REPORT_END_RS_NG; + } + } + + return 1; +} + +static ssize_t prd_show_app_operator(struct device *dev, char *buf, int mode) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + u8 *pbuf = (u8 *)prd->m2_buf_even_rawdata; + int size = (ctrl->m2_row_col_size<prd_app_mode; +// int ret = 0; + + if (mode < REPORT_MAX) { + t_prd_dbg_base(prd, "show app mode : %s(%d), 0x%X\n", + prd_app_mode_str[mode], mode, flag); + } + + if (mode == REPORT_OFF) { + size = prd_show_app_op_end(dev, buf, prev_mode); + goto out; + } + + if (mode < REPORT_MAX) { + prd->prd_app_mode = mode; + } + + switch (mode) { + case REPORT_RAW: + prd_show_prd_get_data_do_raw_ait(dev, pbuf, size, flag); + break; + case REPORT_BASE: + prd_show_prd_get_data_do_ait_basedata(dev, pbuf, size, 0, flag); + break; + case REPORT_DELTA: + prd_show_prd_get_data_do_deltadata(dev, pbuf, size, flag); + break; + case REPORT_LABEL: + size = ctrl->m2_row_col_size; + pbuf = (u8 *)prd->buf_label, + prd_show_prd_get_data_do_labeldata(dev, pbuf, size, flag); + break; + case REPORT_DEBUG_BUF: + size = ctrl->debug_buf_size; + pbuf = (u8 *)prd->buf_debug, + prd_show_prd_get_data_do_debug_buf(dev, pbuf, size, flag); + break; + default: + t_prd_err(prd, "unknown mode, %d\n", mode); + if (prev_mode != REPORT_OFF) { + prd_show_app_op_end(dev, buf, prev_mode); + siw_touch_mon_resume(dev); + } + size = 0; + break; + } + + if (size) { + memcpy(buf, pbuf, size); + } + +out: + if (touch_test_prd_quirks(ts, PRD_QUIRK_RAW_RETURN_MODE_VAL)) { + return (ssize_t)mode; + } + + return (ssize_t)size; +} + +static ssize_t prd_show_app_raw(struct device *dev, char *buf) +{ + return prd_show_app_operator(dev, buf, REPORT_RAW); +} + +static ssize_t prd_show_app_base(struct device *dev, char *buf) +{ + return prd_show_app_operator(dev, buf, REPORT_BASE); +} + +static ssize_t prd_show_app_label(struct device *dev, char *buf) +{ + return prd_show_app_operator(dev, buf, REPORT_LABEL); +} + +static ssize_t prd_show_app_delta(struct device *dev, char *buf) +{ + return prd_show_app_operator(dev, buf, REPORT_DELTA); +} + +static ssize_t prd_show_app_debug_buf(struct device *dev, char *buf) +{ + return prd_show_app_operator(dev, buf, REPORT_DEBUG_BUF); +} + +static ssize_t prd_show_app_end(struct device *dev, char *buf) +{ + return prd_show_app_operator(dev, buf, REPORT_OFF); +} + +static ssize_t prd_show_app_info(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_param *param = &prd->param; + int temp = prd->sysfs_flag; + + memset(buf, 0, PRD_APP_INFO_SIZE); + + buf[0] = (temp & 0xff); + buf[1] = ((temp >> 8) & 0xff); + buf[2] = ((temp >> 16) & 0xff); + buf[3] = ((temp >> 24) & 0xff); + + buf[8] = param->row; + buf[9] = param->col; + buf[10] = param->col_add; + buf[11] = param->ch; + buf[12] = param->m1_col; + buf[13] = param->cmd_type; + buf[14] = param->second_scr.bound_i; + buf[15] = param->second_scr.bound_j; + + t_prd_info(prd, + "prd info: f %08Xh, r %d, c %d, ca %d, ch %d, m1 %d, cmd %d, bi %d, bj %d\n", + temp, param->row, param->col, param->col_add, param->ch, param->m1_col, + param->cmd_type, param->second_scr.bound_i, param->second_scr.bound_j); + + if (prd->mon_flag) { + siw_touch_mon_resume(dev); + } else { + siw_touch_mon_pause(dev); + } + prd->mon_flag = !prd->mon_flag; + + return PRD_APP_INFO_SIZE; +} + +static ssize_t prd_show_dbg_mask(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + int size = 0; + + size += siw_snprintf(buf, size, + "prd->dbg_mask %08Xh\n", + prd->dbg_mask); + size += siw_snprintf(buf, size, + "t_prd_dbg_mask %08Xh\n", + t_prd_dbg_mask); + + size += siw_snprintf(buf, size, + "\nUsage:\n"); + size += siw_snprintf(buf, size, + " prd->dbg_mask : echo 0 {mask_value} > prd_dbg_mask\n"); + size += siw_snprintf(buf, size, + " t_prd_dbg_mask : echo 9 {mask_value} > prd_dbg_mask\n"); + + return (ssize_t)size; +} + +static void prd_store_dbg_mask_usage(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + + t_prd_info(prd, "Usage:\n"); + t_prd_info(prd, " prd->dbg_mask : echo 0 {mask_value(hex)} > prd_dbg_mask\n"); + t_prd_info(prd, " t_prd_dbg_mask : echo 9 {mask_value(hex)} > prd_dbg_mask\n"); +} + +static ssize_t prd_store_dbg_mask(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + int type = 0; + u32 old_value, new_value = 0; + + if (sscanf(buf, "%d %X", &type, &new_value) <= 0) { + t_prd_err(prd, "Invalid param\n"); + prd_store_dbg_mask_usage(dev); + return count; + } + + switch (type) { + case 0 : + old_value = prd->dbg_mask; + prd->dbg_mask = new_value; + t_prd_info(prd, "prd->dbg_mask changed : %08Xh -> %08xh\n", + old_value, new_value); + break; + case 9 : + old_value = t_prd_dbg_mask; + t_prd_dbg_mask = new_value; + t_dev_info(dev, "t_prd_dbg_mask changed : %08Xh -> %08xh\n", + old_value, new_value); + break; + default : + prd_store_dbg_mask_usage(dev); + break; + } + + return count; +} + +#if defined(__SIW_SUPPORT_PRD_SET_SD) +#define siw_prd_set_sd_sprintf(_buf, _title) \ + sprintf(_buf, "%-20s(%d) -", #_title, _title) + +#define t_prd_info_set_sd(_prd, _title) \ + t_prd_info(_prd, " %-20s - %d\n", #_title, _title) + +static int prd_show_set_sd_item(char *buf, int size, char *title, int *value) +{ + int lower = value[0]; + int upper = value[1]; + int disabled = 0; + int invalid = 0; + + disabled = !!(!lower && !upper); + + invalid |= !!(lower < 0); + invalid |= !!(upper < 0); + invalid |= !!(lower >= upper); + + size = siw_snprintf(buf, size, + " %s lower %d, upper %d %s\n", + title, lower, upper, + (disabled) ? "(disabled)" : + (invalid) ? "(invalid)" : "(enabled)"); + + return size; +} + +static ssize_t prd_show_set_sd(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_param *param = &prd->param; + struct siw_hal_prd_sd_param *sd_param = &prd->sd_param; + char title[32] = {0, }; + int valid = 0; + int disabled = 0; + int invalid = 0; + int type; + int lower, upper; + int size = 0; + + if (!param->sd_test_flag) { + size += siw_snprintf(buf, size, "[sd - disabled]\n"); + goto skip_show_set_sd; + } + + size += siw_snprintf(buf, size, "[sd]\n"); + + if(param->sd_test_flag & U3_M2_RAWDATA_TEST_FLAG) { + siw_prd_set_sd_sprintf(title, U3_M2_RAWDATA_TEST); + size += prd_show_set_sd_item(buf, size, title, sd_param->u3_m2); + } + if(param->sd_test_flag & U3_M1_RAWDATA_TEST_FLAG) { + siw_prd_set_sd_sprintf(title, U3_M1_RAWDATA_TEST); + size += prd_show_set_sd_item(buf, size, title, sd_param->u3_m1); + } + if(param->sd_test_flag & U3_BLU_JITTER_TEST_FLAG) { + siw_prd_set_sd_sprintf(title, U3_BLU_JITTER_TEST); + size += prd_show_set_sd_item(buf, size, title, sd_param->u3_blu_jitter); + } + if(param->sd_test_flag & U3_JITTER_TEST_FLAG) { + siw_prd_set_sd_sprintf(title, U3_JITTER_TEST); + size += prd_show_set_sd_item(buf, size, title, sd_param->u3_jitter); + } + if(param->sd_test_flag & U3_M1_JITTER_TEST_FLAG) { + siw_prd_set_sd_sprintf(title, U3_M1_JITTER_TEST); + size += prd_show_set_sd_item(buf, size, title, sd_param->u3_m1_jitter); + } +skip_show_set_sd: + + if (!param->lpwg_sd_test_flag) { + size += siw_snprintf(buf, size, "[lpwg_sd - disabled]\n"); + goto skip_show_set_lpwg_sd; + } + + size += siw_snprintf(buf, size, "[lpwg_sd]\n"); + + if(param->lpwg_sd_test_flag & U0_M2_RAWDATA_TEST_FLAG) { + siw_prd_set_sd_sprintf(title, U0_M2_RAWDATA_TEST); + size += prd_show_set_sd_item(buf, size, title, sd_param->u0_m2); + } + if(param->lpwg_sd_test_flag & U0_M1_RAWDATA_TEST_FLAG) { + siw_prd_set_sd_sprintf(title, U0_M1_RAWDATA_TEST); + size += prd_show_set_sd_item(buf, size, title, sd_param->u0_m1); + } + if(param->lpwg_sd_test_flag & U0_JITTER_TEST_FLAG) { + siw_prd_set_sd_sprintf(title, U0_JITTER_TEST); + size += prd_show_set_sd_item(buf, size, title, sd_param->u0_jitter); + } + if(param->lpwg_sd_test_flag & U0_M1_JITTER_TEST_FLAG) { + siw_prd_set_sd_sprintf(title, U0_M1_JITTER_TEST); + size += prd_show_set_sd_item(buf, size, title, sd_param->u0_m1_jitter); + } +skip_show_set_lpwg_sd: + + if (!param->sd_test_flag && !param->lpwg_sd_test_flag) { + return (ssize_t)size; + } + + type = sd_param->last_type; + lower = sd_param->last_val[0]; + upper = sd_param->last_val[1]; + if ((type >=0) && (type < UX_INVALID)) { + if (param->sd_test_flag & (1<lpwg_sd_test_flag & (1<= upper); + + size += siw_snprintf(buf, size, + "\nLast:\n"); + size += siw_snprintf(buf, size, + " type %d, lower %d, upper %d %s\n", + type,lower, upper, + (disabled) ? "(disabled)" : + (invalid) ? "(invalid)" : "(enabled)"); + + size += siw_snprintf(buf, size, + "\nUsage:\n"); + size += siw_snprintf(buf, size, + " echo {type} {lower} {upper} > set_sd\n\n"); + + return (ssize_t)size; +} + +static void prd_store_set_sd_usage(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_param *param = &prd->param; + + if (!param->sd_test_flag && !param->lpwg_sd_test_flag) { + return; + } + + t_prd_info(prd, "Usage:\n"); + t_prd_info(prd, " echo {type} {lower} {upper} > set_sd\n"); + + if (!param->sd_test_flag) { + goto skip_store_set_sd_usage; + } + + t_prd_info(prd, " [type for sd]\n"); + if(param->sd_test_flag & U3_M2_RAWDATA_TEST_FLAG) { + t_prd_info_set_sd(prd, U3_M2_RAWDATA_TEST); + } + if(param->sd_test_flag & U3_M1_RAWDATA_TEST_FLAG) { + t_prd_info_set_sd(prd, U3_M1_RAWDATA_TEST); + } + if(param->sd_test_flag & U3_BLU_JITTER_TEST_FLAG) { + t_prd_info_set_sd(prd, U3_BLU_JITTER_TEST); + } + if(param->sd_test_flag & U3_JITTER_TEST_FLAG) { + t_prd_info_set_sd(prd, U3_JITTER_TEST); + } + if(param->sd_test_flag & U3_M1_JITTER_TEST_FLAG) { + t_prd_info_set_sd(prd, U3_M1_JITTER_TEST); + } +skip_store_set_sd_usage: + + if (!param->lpwg_sd_test_flag) { + return; + } + + t_prd_info(prd, " [type for lpwd_sd]\n"); + if(param->lpwg_sd_test_flag & U0_M2_RAWDATA_TEST_FLAG) { + t_prd_info_set_sd(prd, U0_M2_RAWDATA_TEST); + } + if(param->lpwg_sd_test_flag & U0_M1_RAWDATA_TEST_FLAG) { + t_prd_info_set_sd(prd, U0_M1_RAWDATA_TEST); + } + if(param->lpwg_sd_test_flag & U0_JITTER_TEST_FLAG) { + t_prd_info_set_sd(prd, U0_JITTER_TEST); + } + if(param->lpwg_sd_test_flag & U0_M1_JITTER_TEST_FLAG) { + t_prd_info_set_sd(prd, U0_M1_JITTER_TEST); + } +} + +static ssize_t prd_store_set_sd(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_param *param = &prd->param; + struct siw_hal_prd_sd_param *sd_param = &prd->sd_param; + int type = 0; + int valid = 0; + int lower = 0, upper = 0; + int set = 0; + + if (sscanf(buf, "%d %d %d", &type, &lower, &upper) <= 0) { + t_prd_err(prd, "Invalid param\n"); + prd_store_set_sd_usage(dev); + return count; + } + + if ((type >=0) && (type < UX_INVALID)) { + if (param->sd_test_flag & (1<lpwg_sd_test_flag & (1<last_type = type; + sd_param->last_val[0] = lower; + sd_param->last_val[1] = upper; + + if (!valid) { + prd_store_set_sd_usage(dev); + return count; + } + + if (!param->sd_test_flag) { + goto skip_set_sd; + } + if(!(param->sd_test_flag & (1<u3_m2[0] = lower; + sd_param->u3_m2[1] = upper; + set |= (1<u3_m1[0] = lower; + sd_param->u3_m1[1] = upper; + set |= (1<u3_blu_jitter[0] = lower; + sd_param->u3_blu_jitter[1] = upper; + set |= (1<u3_jitter[0] = lower; + sd_param->u3_jitter[1] = upper; + set |= (1<u3_m1_jitter[0] = lower; + sd_param->u3_m1_jitter[1] = upper; + set |= (1<lpwg_sd_test_flag) { + goto skip_set_lpwg_sd; + } + if(!(param->lpwg_sd_test_flag & (1<u0_m2[0] = lower; + sd_param->u0_m2[1] = upper; + set |= (1<u0_m1[0] = lower; + sd_param->u0_m1[1] = upper; + set |= (1<u0_jitter[0] = lower; + sd_param->u0_jitter[1] = upper; + set |= (1<u0_m1_jitter[0] = lower; + sd_param->u0_m1_jitter[1] = upper; + set |= (1<ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct kobject *kobj = &ts->kobj; + struct siw_hal_prd_attribute *prd_attr; + struct attribute **attr_actual = siw_hal_prd_attribute_list; + int sysfs_flag = prd->sysfs_flag; + int attr_size = PRD_SYS_ATTR_SIZE; + int i = 0; + int j = 0; + int added = 0; + int ret = 0; + + prd_attr = (struct siw_hal_prd_attribute *)siw_hal_prd_attribute_list_all; + + memset(siw_hal_prd_attribute_list, 0, sizeof(siw_hal_prd_attribute_list)); + + for(i = 0; i < attr_size; i++){ + added = 0; + if ((prd_attr->flag == -1) || + (prd_attr->flag & sysfs_flag)){ + attr_actual[j] = prd_attr->attr; + j++; + added = 1; + } + + if (prd_attr->attr == NULL) { + break; + } + + t_dev_dbg_base(dev, SIW_PRD_TAG "sysfs %02d(%20s) %s\n", + i, prd_attr->attr->name, + (added) ? "added" : "not supported"); + + prd_attr++; + } + + ret = sysfs_create_group(kobj, &siw_hal_prd_attribute_group); + if (ret >= 0) { + prd->sysfs_done = 1; + } + + return ret; +} + +static void siw_hal_prd_remove_group(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct kobject *kobj = &ts->kobj; + + if (prd->sysfs_done) { + prd->sysfs_done = 0; + sysfs_remove_group(kobj, &siw_hal_prd_attribute_group); + } +} + +static void siw_hal_prd_free_buffer(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + + t_prd_dbg_base(prd, "[param free buffer]\n"); + + if (prd->buf_src != NULL) { + t_prd_info(prd, "buffer released: %p(%d)\n", + prd->buf_src, prd->buf_size); + kfree(prd->buf_src); + + prd->buf_src = NULL; + prd->buf_size = 0; + + prd->m2_buf_even_rawdata = NULL; + prd->m2_buf_odd_rawdata = NULL; + + prd->m1_buf_even_rawdata = NULL; + prd->m1_buf_odd_rawdata = NULL; + prd->m1_buf_tmp = NULL; + + prd->open_buf_result_rawdata = NULL; + prd->short_buf_result_rawdata = NULL; + prd->open_buf_result_data = NULL; + prd->short_buf_result_data = NULL; + + prd->buf_delta = NULL; + prd->buf_debug = NULL; + + prd->buf_label_tmp = NULL; + prd->buf_label = NULL; + } +} + +static int siw_hal_prd_alloc_buffer(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + u8 *buf; + size_t buf_addr; + int total_size; + + t_prd_dbg_base(prd, "[param alloc buffer]\n"); + + total_size = (ctrl->m2_row_col_buf_size * 6); + total_size += (ctrl->m1_row_col_size * 3); + total_size += ctrl->delta_size; + total_size += ctrl->debug_buf_size; + total_size += ctrl->label_tmp_size; + total_size += ctrl->m2_row_col_size; + + switch (touch_chip_type(ts)) { + case CHIP_LG4894: + case CHIP_LG4895: + case CHIP_LG4946: + case CHIP_LG4951: + case CHIP_SW1828: + case CHIP_SW49105: + case CHIP_SW49106: + prd->open_result_type = 0; + break; + default: + prd->open_result_type = 1; + break; + } + + buf = kzalloc(total_size + 128, GFP_KERNEL); + if (buf == NULL) { + t_prd_err(prd, "falied to allocate buffer(%d)\n", total_size); + return -ENOMEM; + } + + t_prd_info(prd, "buffer allocted: %p(%d)\n", buf, total_size); + + prd->buf_src = buf; + prd->buf_size = total_size; + +#if 1 + buf_addr = (size_t)buf; + if (buf_addr & 0x03) { + buf_addr += 0x03; + buf_addr &= ~0x03; + buf = (u8 *)buf_addr; + t_prd_info(prd, "buffer align tuned: %p\n", buf); + } +#endif + + prd->m2_buf_even_rawdata = (int16_t *)buf; + buf += ctrl->m2_row_col_buf_size; + prd->m2_buf_odd_rawdata = (int16_t *)buf; + buf += ctrl->m2_row_col_buf_size; + + prd->m1_buf_even_rawdata = (int16_t *)buf; + buf += ctrl->m1_row_col_size; + prd->m1_buf_odd_rawdata = (int16_t *)buf; + buf += ctrl->m1_row_col_size; + prd->m1_buf_tmp = (int16_t *)buf; + buf += ctrl->m1_row_col_size; + + prd->open_buf_result_rawdata = (int16_t *)buf; + buf += ctrl->m2_row_col_buf_size; + prd->short_buf_result_rawdata = (int16_t *)buf; + buf += ctrl->m2_row_col_buf_size; + prd->open_buf_result_data = (int16_t *)buf; + buf += ctrl->m2_row_col_buf_size; + prd->short_buf_result_data = (int16_t *)buf; + buf += ctrl->m2_row_col_buf_size; + + prd->buf_delta = (int16_t *)buf; + buf += ctrl->delta_size; + + prd->buf_debug = (int16_t *)buf; + buf += ctrl->debug_buf_size; + + prd->buf_label_tmp = (u8 *)buf; + buf += ctrl->label_tmp_size; + + prd->buf_label = (u8 *)buf; +// buf += ctrl->m2_row_col_size; + + return 0; +} + +static void siw_hal_prd_parse_ctrl(struct device *dev, struct siw_hal_prd_param *param) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_ctrl *ctrl = &prd->ctrl; + u32 row_size = param->row; + u32 col_size = param->col; + + t_prd_dbg_base(prd, "[param parse ctrl]\n"); + + ctrl->m2_row_col_size = (row_size * col_size); + ctrl->m2_row_col_buf_size = (row_size * (col_size + param->col_add)); + ctrl->m1_row_col_size = (row_size * param->m1_col); + ctrl->m2_frame_size = (ctrl->m2_row_col_buf_size<m1_frame_size = (ctrl->m1_row_col_size<delta_size = ((row_size + 2) * (col_size + 2)); + ctrl->label_tmp_size = ((row_size + 2) * (col_size + 2)); + ctrl->debug_buf_size = PRD_DEBUG_BUF_SIZE; + + t_prd_dbg_base(prd, "ctrl: m2_row_col %d, m2_row_col_buf %d, m1_row_col %d\n", + ctrl->m2_row_col_size, ctrl->m2_row_col_buf_size, ctrl->m1_row_col_size); + t_prd_dbg_base(prd, "ctrl: m2_frame %d, m1_frame %d\n", + ctrl->m2_frame_size, ctrl->m1_frame_size); + t_prd_dbg_base(prd, "ctrl: delta %d, label_tmp %d\n", + ctrl->delta_size, ctrl->label_tmp_size); +} + +static void siw_hal_prd_parse_tune(struct device *dev, struct siw_hal_prd_param *param) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_tune *tune = &prd->tune; + u32 ch = param->ch; + + t_prd_dbg_base(prd, "[param parse tune]\n"); + + tune->ch = ch; + tune->code_size = ((ch<<3)+4); + + tune->code_l_goft_offset = 0; + tune->code_l_m1_oft_offset = (1<<1); + tune->code_l_g1_oft_offset = (tune->code_l_m1_oft_offset + ch); + tune->code_l_g2_oft_offset = (tune->code_l_g1_oft_offset + ch); + tune->code_l_g3_oft_offset = (tune->code_l_g2_oft_offset + ch); + + tune->code_r_goft_offset = (tune->code_l_g3_oft_offset + ch); + tune->code_r_m1_oft_offset = (tune->code_r_goft_offset + 2); + tune->code_r_g1_oft_offset = (tune->code_r_m1_oft_offset + ch); + tune->code_r_g2_oft_offset = (tune->code_r_g1_oft_offset + ch); + tune->code_r_g3_oft_offset = (tune->code_r_g2_oft_offset + ch); + + t_prd_dbg_base(prd, "tune: ch %d, code %d\n", + ch, tune->code_size); + t_prd_dbg_base(prd, "tune: l_goft %Xh, l_m1_oft %Xh\n", + tune->code_l_goft_offset, + tune->code_l_m1_oft_offset); + t_prd_dbg_base(prd, "tune: l_g1_oft %Xh, l_g2_oft %Xh, l_g3_oft %Xh\n", + tune->code_l_g1_oft_offset, + tune->code_l_g2_oft_offset, + tune->code_l_g3_oft_offset); + t_prd_dbg_base(prd, "tune: r_goft %Xh, r_m1_oft %Xh\n", + tune->code_r_goft_offset, + tune->code_r_m1_oft_offset); + t_prd_dbg_base(prd, "tune: r_g1_oft %Xh, r_g2_oft %Xh, r_g3_oft %Xh\n", + tune->code_r_g1_oft_offset, + tune->code_r_g2_oft_offset, + tune->code_r_g3_oft_offset); +} + +static void siw_hal_prd_set_offset(struct device *dev, struct siw_hal_prd_param *param) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + u32 idx, addr, old_addr, flag; + int i; + + t_prd_dbg_base(prd, "[param parse set offset]\n"); + + flag = 0; + for (i = 0; i < IMG_OFFSET_IDX_MAX ; i++) { + if (!param->addr[i]) { + continue; + } + + idx = PRD_OFFSET_QUIRK_GET_IDX(param->addr[i]); + addr = PRD_OFFSET_QUIRK_GET_OFFSET(param->addr[i]); + + if (flag & (1<img_offset.raw; + prd->img_offset.raw = addr; + break; + case IMG_OFFSET_IDX_BASELINE_EVEN: + old_addr = prd->img_offset.baseline_even; + prd->img_offset.baseline_even = addr; + break; + case IMG_OFFSET_IDX_BASELINE_ODD: + old_addr = prd->img_offset.baseline_odd; + prd->img_offset.baseline_odd = addr; + break; + case IMG_OFFSET_IDX_DELTA: + old_addr = prd->img_offset.delta; + prd->img_offset.delta = addr; + break; + case IMG_OFFSET_IDX_LABEL: + old_addr = prd->img_offset.label; + prd->img_offset.label = addr; + break; + case IMG_OFFSET_IDX_F_DELTA: + old_addr = prd->img_offset.f_delta; + prd->img_offset.f_delta = addr; + break; + case IMG_OFFSET_IDX_DEBUG: + old_addr = prd->img_offset.debug; + prd->img_offset.debug = addr; + break; + default: + old_addr = 0; + addr = 0; + t_prd_info(prd, "unknown idx: %Xh (%d)\n", addr, idx); + break; + } + + if (addr) { + t_prd_info(prd, "debug offset: %Xh(%d)\n", addr, idx); + } + } +} + +static void siw_hal_prd_set_cmd(struct device *dev, int cmd_type) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_img_cmd *img_cmd = &prd->img_cmd; + char *type_name[] = { + [PRD_CMD_TYPE_1] = "PRD_CMD_TYPE_1", + [PRD_CMD_TYPE_2] = "PRD_CMD_TYPE_2", + }; + + t_prd_dbg_base(prd, "[param parse set cmd]\n"); + + t_prd_info(prd, "cmd type: %s\n", type_name[cmd_type]); + + if (cmd_type == PRD_CMD_TYPE_2) { + img_cmd->raw = IT_IMAGE_RAW; + img_cmd->baseline_even = IT_IMAGE_BASELINE; + img_cmd->baseline_odd = IT_IMAGE_BASELINE + 1; + img_cmd->delta = IT_IMAGE_DELTA + 1; + img_cmd->label = IT_IMAGE_LABEL + 1; + img_cmd->f_delta = IT_IMAGE_FILTERED_DELTA + 1; + img_cmd->debug = IT_IMAGE_DEBUG + 1; + return; + } + + img_cmd->raw = IT_IMAGE_RAW; + img_cmd->baseline_even = IT_IMAGE_BASELINE; + img_cmd->baseline_odd = IT_IMAGE_BASELINE; + img_cmd->delta = IT_IMAGE_DELTA; + img_cmd->label = IT_IMAGE_LABEL; + img_cmd->f_delta = IT_IMAGE_FILTERED_DELTA; + img_cmd->debug = IT_IMAGE_DEBUG; +} + +static void siw_hal_prd_set_sd_cmd(struct siw_hal_prd_data *prd) +{ + struct device *dev = prd->dev; + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_sd_cmd *sd_cmd = &prd->sd_cmd; + int chip_type = touch_chip_type(ts); + + sd_cmd->cmd_open_node = OPEN_NODE_TEST_POST_CMD; + sd_cmd->cmd_short_node = SHORT_NODE_TEST_POST_CMD; + sd_cmd->cmd_m2_rawdata = M2_RAWDATA_TEST_POST_CMD; + sd_cmd->cmd_m1_rawdata = M1_RAWDATA_TEST_POST_CMD; + sd_cmd->cmd_jitter = JITTER_TEST_POST_CMD; + sd_cmd->cmd_m1_jitter = 0; + sd_cmd->cmd_short_full = 0; + + switch (chip_type) { + case CHIP_SW49407: + sd_cmd->cmd_open_node = 1; + sd_cmd->cmd_short_node = 2; + sd_cmd->cmd_m2_rawdata = 5; + sd_cmd->cmd_m1_rawdata = 3; + sd_cmd->cmd_jitter = 6; + sd_cmd->cmd_m1_jitter = 4; + break; + case CHIP_SW49408: + case CHIP_SW49409: + case CHIP_SW49501: + sd_cmd->cmd_open_node = 1; + sd_cmd->cmd_short_node = 2; + sd_cmd->cmd_m2_rawdata = 5; + sd_cmd->cmd_m1_rawdata = 3; + sd_cmd->cmd_jitter = 10; + sd_cmd->cmd_m1_jitter = 4; + sd_cmd->cmd_short_full = 12; + break; + case CHIP_LG4946: + sd_cmd->cmd_jitter = 10; + break; + } + + t_prd_info(prd, + "cmd_open_node : %d\n", + sd_cmd->cmd_open_node); + t_prd_info(prd, + "cmd_short_node : %d\n", + sd_cmd->cmd_short_node); + t_prd_info(prd, + "cmd_m2_rawdata : %d\n", + sd_cmd->cmd_m2_rawdata); + t_prd_info(prd, + "cmd_m1_rawdata : %d\n", + sd_cmd->cmd_m1_rawdata); + t_prd_info(prd, + "cmd_jitter : %d\n", + sd_cmd->cmd_jitter); + if (sd_cmd->cmd_m1_jitter) { + t_prd_info(prd, + "cmd_m1_jitter : %d\n", + sd_cmd->cmd_m1_jitter); + } + if (sd_cmd->cmd_short_full) { + t_prd_info(prd, + "cmd_short_full : %d\n", + sd_cmd->cmd_short_full); + } +} + +static void siw_hal_prd_parse_work(struct device *dev, struct siw_hal_prd_param *param) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + + t_prd_dbg_base(prd, "[param parse work]\n"); + + siw_hal_prd_set_offset(dev, param); + + siw_hal_prd_set_cmd(dev, param->cmd_type); + + siw_hal_prd_set_sd_cmd(prd); + + prd->sysfs_flag = PRD_SYS_ATTR_EN_FLAG & ~param->sysfs_off_flag; //Disable quirk bits + + t_prd_info(prd, "sysfs flag: %Xh (%Xh)\n", prd->sysfs_flag, param->sysfs_off_flag); +} + +static void siw_hal_prd_show_param(struct device *dev, struct siw_hal_prd_param *param) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + int i; + + t_prd_dbg_base(prd, "[current param]\n"); + + t_prd_dbg_base(prd, "param: chip_type %Xh, cmd_type %d\n", + param->chip_type, + param->cmd_type); + if (param->name != NULL) { + char *name; + int name_idx = 0; + while (1) { + name = (char *)param->name[name_idx]; + if (name == NULL) { + break; + } + t_prd_dbg_base(prd, + "param: name[%d] %s\n", + name_idx, name); + name_idx++; + } + } + + for (i = 0; i < IMG_OFFSET_IDX_MAX; i++) { + if (!param->addr[i]) + continue; + + t_prd_dbg_base(prd, "param: addr[%d] %Xh\n", + i, param->addr[i]); + } + + t_prd_info(prd, "param: row %d, col %d\n", + param->row, param->col); + t_prd_dbg_base(prd, "param: col_add %d, ch %d\n", + param->col_add, param->ch); + t_prd_dbg_base(prd, "param: m1_col %d, m1_cnt %d, m2_cnt %d\n", + param->m1_col, param->m1_cnt, param->m2_cnt); + + t_prd_dbg_base(prd, "param: sysfs_off_flag %Xh\n", + param->sysfs_off_flag); +} + +static int siw_hal_prd_parse_param(struct device *dev, struct siw_hal_prd_param *param) +{ + siw_hal_prd_show_param(dev, param); + + siw_hal_prd_parse_ctrl(dev, param); + + siw_hal_prd_parse_tune(dev, param); + + siw_hal_prd_parse_work(dev, param); + + return siw_hal_prd_alloc_buffer(dev); +} + +static void siw_hal_prd_param_quirks(struct device *dev, + struct siw_hal_prd_param *param) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_fw_info *fw = &chip->fw; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + int data = 0; + + switch (touch_chip_type(ts)) { + case CHIP_LG4894: + data = (param->name == prd_param_name_lg4894_k) | + (param->name == prd_param_name_lg4894_lv) | + (param->name == prd_param_name_lg4894_sf); + + reg->prd_m1_m2_raw_offset = (data) ? PRD_M1_M2_RAW_OFFSET : 0x286; + + if (PRD_M1_M2_RAW_OFFSET != reg->prd_m1_m2_raw_offset) { + t_prd_info(prd, "%s[%s] reg quirks: %04Xh -> %04Xh\n", + touch_chip_name(ts), fw->product_id, + PRD_M1_M2_RAW_OFFSET, reg->prd_m1_m2_raw_offset); + } + break; + default: + break; + } +} + +static int siw_hal_prd_init_param(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_fw_info *fw = &chip->fw; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + struct siw_hal_prd_param *param = (struct siw_hal_prd_param *)prd_params; + struct siw_hal_prd_param __param = {0, }; + int len, found; + int idx; + int ret; + + idx = 0; + while (1) { + if (!param->chip_type) { + break; + } + + if (param->chip_type == touch_chip_type(ts)) { + found = !!(param->name == NULL); + if (!found) { + char *name; + int name_idx = 0; + while (1) { + name = (char *)param->name[name_idx]; + if (name == NULL) { + break; + } + len = strlen(name); + found = !strncmp(fw->product_id, name, len); + if (found) { + break; + } + name_idx++; + } + } + + if (found) { + /* to disable prd control for a specific type */ + if (!param->row || !param->col) { + t_prd_info(prd, "%s[%s] disabled\n", + touch_chip_name(ts), fw->product_id); + return -EFAULT; + } + + t_prd_dbg_base(prd, "%s[%s] param %d selected\n", + touch_chip_name(ts), fw->product_id, idx); + + memcpy(&__param, param, sizeof(__param)); + param = &__param; + + if (!touch_mode_allowed(ts, LCD_MODE_U0)) { + param->lpwg_sd_test_flag = 0; + } + + if (!param->sd_test_flag) { + param->sysfs_off_flag |= PRD_SYS_EN_SD; + } + if (!param->lpwg_sd_test_flag) { + param->sysfs_off_flag |= PRD_SYS_EN_LPWG_SD; + } + + siw_hal_prd_param_quirks(dev, param); + + t_prd_info(prd, "sd_test_flag %Xh, lpwg_sd_test_flag %Xh\n", + param->sd_test_flag, param->lpwg_sd_test_flag); + + if (param->sd_test_flag & OPEN_SHORT_RESULT_ALWAYS_FLAG) { + prd->dbg_mask |= PRD_DBG_OPEN_SHORT_DATA; + } + + ret = siw_hal_prd_parse_param(dev, param); + if (ret < 0) { + t_prd_err(prd, "%s[%s] param parsing failed, %d\n", + touch_chip_name(ts), fw->product_id, ret); + } else { + memcpy(&prd->param, param, sizeof(*param)); + } + return ret; + } + } + + param++; + idx++; + } + + t_prd_info(prd, "%s[%s] param not found\n", + touch_chip_name(ts), fw->product_id); + + return -EFAULT; +} + +static void siw_hal_prd_free_param(struct device *dev) +{ + siw_hal_prd_free_buffer(dev); +} + +static struct siw_hal_prd_data *siw_hal_prd_alloc(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd; + + prd = touch_kzalloc(dev, sizeof(*prd), GFP_KERNEL); + if (!prd) { + t_dev_err(dev, + "failed to allocate memory for prd\n"); + goto out; + } + + t_dev_dbg_base(dev, "create prd (0x%X)\n", (int)sizeof(*prd)); + + prd->dev = ts->dev; + + ts->prd = prd; + +#if defined(__SIW_SUPPORT_PRD_SET_SD) + prd->sd_param.last_type = UX_INVALID; +#endif + + return prd; + +out: + return NULL; +} + +static void siw_hal_prd_free(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_prd_data *prd = (struct siw_hal_prd_data *)ts->prd; + + if (prd) { + t_dev_dbg_base(dev, "free prd\n"); + + ts->prd = NULL; + touch_kfree(dev, prd); + } +} + +static int siw_hal_prd_create_sysfs(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct device *idev = &ts->input->dev; + struct kobject *kobj = &ts->kobj; + struct siw_hal_prd_data *prd; + int ret = 0; + + if (kobj->parent != idev->kobj.parent) { + t_dev_err(dev, "Invalid kobject\n"); + return -EINVAL; + } + + prd = siw_hal_prd_alloc(dev); + if (!prd) { + ret = -ENOMEM; + goto out; + } + + ret = siw_hal_prd_init_param(dev); + if (ret < 0) { + /* Just skip sysfs generation */ + goto out_skip; + } + + ret = siw_hal_prd_create_group(dev); + if (ret < 0) { + t_dev_err(dev, "%s prd sysfs register failed, %d\n", + touch_chip_name(ts), ret); + goto out_sysfs; + } + + t_dev_dbg_base(dev, "%s prd sysfs registered\n", + touch_chip_name(ts)); + +out_skip: + return 0; + +out_sysfs: + siw_hal_prd_free_param(dev); + + siw_hal_prd_free(dev); + +out: + return ret; +} + +static void siw_hal_prd_remove_sysfs(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct device *idev = &ts->input->dev; + struct kobject *kobj = &ts->kobj; + + if (kobj->parent != idev->kobj.parent) { + t_dev_err(dev, "Invalid kobject\n"); + return; + } + + if (ts->prd == NULL) { + return; + } + + siw_hal_prd_remove_group(dev); + + siw_hal_prd_free_param(dev); + + siw_hal_prd_free(dev); + + t_dev_dbg_base(dev, "%s prd sysfs unregistered\n", + touch_chip_name(ts)); +} + +int siw_hal_prd_sysfs(struct device *dev, int on_off) +{ + if (on_off == DRIVER_INIT) { + return siw_hal_prd_create_sysfs(dev); + } + + siw_hal_prd_remove_sysfs(dev); + return 0; +} + +int siw_hal_set_prd_file(struct device *dev, const char *path, int idx) +{ + char *prd_file_group[] = { + [0] = __prd_in_file, + [1] = __prd_in_file_m, + [2] = __prd_out_file, + [3] = __prd_out_file_mo_aat, + [4] = __prd_out_file_mo_mfo, + [5] = __prd_out_file_mo_mfl, + [6] = __prd_out_file_mo_mcv, + }; + + if (!path) { + t_dev_err(dev, "NULL prd path (%d)\n", idx); + return -EINVAL; + } + + if (idx >= ARRAY_SIZE(prd_file_group)) { + t_dev_err(dev, "wrong prd file index, %d\n", idx); + return -EINVAL; + } + + t_dev_info(dev, "prd file setup: %s -> %s\n", + prd_file_group[idx], path); + memcpy(prd_file_group[idx], path, strlen(path)); + + return 0; +} + +__siw_setup_str("siw_prd_if=", prd_setup_if, __prd_in_file); +__siw_setup_str("siw_prd_if_m=", prd_setup_if_m, __prd_in_file_m); + +__siw_setup_str("siw_prd_of_=", prd_setup_of, __prd_out_file); +__siw_setup_str("siw_prd_of_mo_aat=", prd_setup_of_aat, __prd_out_file_mo_aat); +__siw_setup_str("siw_prd_of_mo_mfo=", prd_setup_of_mfo, __prd_out_file_mo_mfo); +__siw_setup_str("siw_prd_of_mo_mfl=", prd_setup_of_mfl, __prd_out_file_mo_mfl); +__siw_setup_str("siw_prd_of_mo_mcv=", prd_setup_of_mcv, __prd_out_file_mo_mcv); + +#endif /* __SIW_SUPPORT_PRD */ + + diff --git a/drivers/input/touchscreen/siw/siw_touch_hal_reg.h b/drivers/input/touchscreen/siw/siw_touch_hal_reg.h new file mode 100644 index 000000000000..ea8fca1a74d6 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_hal_reg.h @@ -0,0 +1,310 @@ +/* + * SiW touch hal driver - reg. map + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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_HAL_REG_H +#define __SIW_TOUCH_HAL_REG_H + +#include "siw_touch_cfg.h" + +#define SPR_CHIP_TEST (0x041) + +#define SPR_CHIP_ID (0x000) +#define SPR_RST_CTL (0x006) +#define SPR_BOOT_CTL (0x00F) +#define SPR_SRAM_CTL (0x010) +#define SPR_BOOT_STS (0x011) +#define SPR_SUBDISP_STS (0x021) + +#define SPR_CODE_OFFSET (0x078) + +#define TC_IC_STATUS (0x200) /* siw_hal_touch_info base addr*/ +#define TC_STS (0x201) +#define TC_VERSION (0x242) +#define TC_PRODUCT_ID1 (0x244) +#define TC_PRODUCT_ID2 (0x245) +#define TC_VERSION_EXT (0x269) /* YYMMDDXX format (BCD)*/ + +#define INFO_FPC_TYPE (0x278) +#define INFO_WFR_TYPE (0x27B) +#define INFO_CHIP_VERSION (0x27C) +#define INFO_CG_TYPE (0x27D) +#define INFO_LOT_NUM (0x27E) +#define INFO_SERIAL_NUM (0x27F) +#define INFO_DATE (0x280) +#define INFO_TIME (0x281) + +/* for abt monitor app. header */ +#define CMD_ABT_LOC_X_START_READ (0x2A6) +#define CMD_ABT_LOC_X_END_READ (0x2A7) +#define CMD_ABT_LOC_Y_START_READ (0x2A8) +#define CMD_ABT_LOC_Y_END_READ (0x2A9) + +#define CODE_ACCESS_ADDR (0x300) +#define DATA_I2CBASE_ADDR (0x301) +#define PRD_TCM_BASE_ADDR (0x303) + +#define TC_DEVICE_CTL (0xC00) +#define TC_INTERRUPT_CTL (0xC01) +#define TC_INTERRUPT_STS (0xC02) +#define TC_DRIVE_CTL (0xC03) + +#define TCI_FAIL_DEBUG_R (0x28D) //Not used +#define TCI_FAIL_BIT_R (0x28E) //Not used +#define TCI_DEBUG_R (0x2AE) //Not used +#define TCI_ENABLE_W (0xC20) +#define TCI_FAIL_DEBUG_W (0xC2C) //Not used +#define TCI_FAIL_BIT_W (0xC2D) //Not used + +#define TAP_COUNT_W (0xC21) +#define MIN_INTERTAP_W (0xC22) +#define MAX_INTERTAP_W (0xC23) +#define TOUCH_SLOP_W (0xC24) +#define TAP_DISTANCE_W (0xC25) +#define INT_DELAY_W (0xC26) +#define ACT_AREA_X1_W (0xC27) +#define ACT_AREA_Y1_W (0xC28) +#define ACT_AREA_X2_W (0xC29) +#define ACT_AREA_Y2_W (0xC2A) + +#define TCI_DEBUG_FAIL_CTRL (0xC2C) +#define TCI_DEBUG_FAIL_BUFFER (0xC2D) +#define TCI_DEBUG_FAIL_STATUS (0xC70) + +#define SWIPE_ENABLE_W (0xC30) +#define SWIPE_DIST_W (0xC31) +#define SWIPE_RATIO_THR_W (0xC32) +#define SWIPE_RATIO_DIST_W (0xC33) +#define SWIPE_RATIO_PERIOD_W (0xC34) +#define SWIPE_TIME_MIN_W (0xC35) +#define SWIPE_TIME_MAX_W (0xC36) +#define SWIPE_ACT_AREA_X1_W (0xC37) +#define SWIPE_ACT_AREA_Y1_W (0xC38) +#define SWIPE_ACT_AREA_X2_W (0xC39) +#define SWIPE_ACT_AREA_Y2_W (0xC3A) +#define SWIPE_FAIL_DEBUG_W (0xC3D) //Not used +#define SWIPE_FAIL_DEBUG_R (0x29D) +#define SWIPE_DEBUG_R (0x2B6) + +/* debug data report mode setting */ +#define CMD_RAW_DATA_REPORT_MODE_READ (0x2A4) +#define CMD_RAW_DATA_COMPRESS_WRITE (0xC47) +#define CMD_RAW_DATA_REPORT_MODE_WRITE (0xC49) + +/* charger status */ +#define SPR_CHARGER_STS (0xC50) + +#define IME_STATE (0xC51) +#define MAX_DELTA (0x2AD) +#define TOUCH_MAX_R (0x2A1) +#define TOUCH_MAX_W (0xC53) +#define CALL_STATE (0xC54) + +/* production test */ +#define TC_TSP_TEST_CTL (0xC04) +#define TC_TSP_TEST_STS (0x265) +#define TC_TSP_TEST_PF_RESULT (0x266) +#define TC_TSP_TEST_OFF_INFO (0x2FB) + +/* Firmware control */ +#define TC_FLASH_DN_STS (0x247) +#define TC_CONFDN_BASE_ADDR (0x2F9) +#define TC_FLASH_DN_CTL (0xC05) + +/* */ +#define RAW_DATA_CTL_READ (0x2A4) +#define RAW_DATA_CTL_WRITE (0xC49) +#define SERIAL_DATA_OFFSET (0x07B) + + +/* __SIW_SUPPORT_WATCH */ +#define EXT_WATCH_FONT_OFFSET (0x07F) +#define EXT_WATCH_FONT_ADDR (0x307) +#define EXT_WATCH_FONT_DN_ADDR_INFO (0x2F7) +#define EXT_WATCH_FONT_CRC (0xC18) +#define EXT_WATCH_DCS_CTRL (0xC19) +#define EXT_WATCH_MEM_CTRL (0xC1A) + +#define EXT_WATCH_CTRL (0x2D2) +#define EXT_WATCH_AREA_X (0x2D3) +#define EXT_WATCH_AREA_Y (0x2D4) +#define EXT_WATCH_BLINK_AREA (0x2D5) +#define EXT_WATCH_LUT (0x2D6) + +#define EXT_WATCH_DISPLAY_ON (0xC1B) /* Watch display off :0, on :1*/ +#define EXT_WATCH_DISPLAY_STATUS (0x039) + +#define EXT_WATCH_RTC_SCT (0x081) /* Synchronous current time */ +/* Synchronous current time for milesec*/ +#define EXT_WATCH_RTC_SCTCNT (0x082) +/* Target time for occurring date change int */ +#define EXT_WATCH_RTC_CAPTURE (0x084) /* Current time capture*/ +#define EXT_WATCH_RTC_CTST (0x087) /* Current time */ +#define EXT_WATCH_RTC_ECNT (0x088) /* end count */ +#define EXT_WATCH_HOUR_DISP (0xC14) /* 0 :zerodisp, 1:h24en, 2:dispmode */ +#define EXT_WATCH_BLINK_PRD (0xC15) /* Stop :0x00, 1s :0x01, 1.5s :0x10 */ +#define EXT_WATCH_RTC_RUN (0xC10) /* Watch RTC Stop :0x10, Start 0x01 */ + +#define EXT_WATCH_POSITION (0xC11) /*Write only*/ +#define EXT_WATCH_POSITION_R (0x271) /*Read only*/ +#define EXT_WATCH_STATE (0x270) /*Watch state, Read only*/ + +#define SYS_DISPMODE_STATUS (0x021) /* DIC status */ +/* __SIW_SUPPORT_WATCH */ + + +/* __SIW_SUPPORT_PRD */ +#define PRD_SERIAL_TCM_OFFSET (0x07C) +#define PRD_TC_MEM_SEL (0x457) +#define PRD_TC_TEST_MODE_CTL (0xC6E) +#define PRD_M1_M2_RAW_OFFSET (0x287) +#define PRD_TUNE_RESULT_OFFSET (0x289) //See 'chip_reg_quirks' in touch_lg4895.c +#define PRD_OPEN3_SHORT_OFFSET (0x288) +#define PRD_IC_AIT_START_REG (0xC6C) //AIT Algorithm Engine HandShake Reg +#define PRD_IC_AIT_DATA_READYSTATUS (0xC64) + +#define GLOVE_EN (0xF22) +#define GRAB_EN (0xF24) + +/* __SIW_SUPPORT_PRD */ + +struct siw_hal_reg { + u32 spr_chip_test; + u32 spr_chip_id; + u32 spr_rst_ctl; + u32 spr_boot_ctl; + u32 spr_sram_ctl; + u32 spr_boot_status; + u32 spr_subdisp_status; + u32 spr_code_offset; + u32 tc_ic_status; + u32 tc_status; + u32 tc_version; + u32 tc_product_id1; + u32 tc_product_id2; + u32 tc_version_ext; + u32 info_fpc_type; + u32 info_wfr_type; + u32 info_chip_version; + u32 info_cg_type; + u32 info_lot_num; + u32 info_serial_num; + u32 info_date; + u32 info_time; + u32 cmd_abt_loc_x_start_read; + u32 cmd_abt_loc_x_end_read; + u32 cmd_abt_loc_y_start_read; + u32 cmd_abt_loc_y_end_read; + u32 code_access_addr; + u32 data_i2cbase_addr; + u32 prd_tcm_base_addr; + u32 tc_device_ctl; + u32 tc_interrupt_ctl; + u32 tc_interrupt_status; + u32 tc_drive_ctl; + u32 tci_enable_w; + u32 tap_count_w; + u32 min_intertap_w; + u32 max_intertap_w; + u32 touch_slop_w; + u32 tap_distance_w; + u32 int_delay_w; + u32 act_area_x1_w; + u32 act_area_y1_w; + u32 act_area_x2_w; + u32 act_area_y2_w; + u32 tci_debug_fail_ctrl; + u32 tci_debug_fail_buffer; + u32 tci_debug_fail_status; + u32 swipe_enable_w; + u32 swipe_dist_w; + u32 swipe_ratio_thr_w; + u32 swipe_ratio_period_w; + u32 swipe_ratio_dist_w; + u32 swipe_time_min_w; + u32 swipe_time_max_w; + u32 swipe_act_area_x1_w; + u32 swipe_act_area_y1_w; + u32 swipe_act_area_x2_w; + u32 swipe_act_area_y2_w; + u32 swipe_fail_debug_r; + u32 swipe_debug_r; + u32 cmd_raw_data_report_mode_read; + u32 cmd_raw_data_compress_write; + u32 cmd_raw_data_report_mode_write; + u32 spr_charger_status; + u32 ime_state; + u32 max_delta; + u32 touch_max_w; + u32 touch_max_r; + u32 call_state; + u32 tc_tsp_test_ctl; + u32 tc_tsp_test_status; + u32 tc_tsp_test_pf_result; + u32 tc_tsp_test_off_info; + u32 tc_flash_dn_status; + u32 tc_confdn_base_addr; + u32 tc_flash_dn_ctl; + u32 raw_data_ctl_read; + u32 raw_data_ctl_write; + u32 serial_data_offset; + /* __SIW_SUPPORT_WATCH */ + u32 ext_watch_font_offset; + u32 ext_watch_font_addr; + u32 ext_watch_font_dn_addr_info; + u32 ext_watch_font_crc; + u32 ext_watch_dcs_ctrl; + u32 ext_watch_mem_ctrl; + u32 ext_watch_ctrl; + u32 ext_watch_area_x; + u32 ext_watch_area_y; + u32 ext_watch_blink_area; + u32 ext_watch_lut; + u32 ext_watch_display_on; + u32 ext_watch_display_status; + u32 ext_watch_rtc_sct; + u32 ext_watch_rtc_sctcnt; + u32 ext_watch_rtc_capture; + u32 ext_watch_rtc_ctst; + u32 ext_watch_rtc_ecnt; + u32 ext_watch_hour_disp; + u32 ext_watch_blink_prd; + u32 ext_watch_rtc_run; + u32 ext_watch_position; + u32 ext_watch_position_r; + u32 ext_watch_state; + u32 sys_dispmode_status; + /* __SIW_SUPPORT_PRD */ + u32 prd_serial_tcm_offset; + u32 prd_tc_mem_sel; + u32 prd_tc_test_mode_ctl; + u32 prd_m1_m2_raw_offset; + u32 prd_tune_result_offset; + u32 prd_open3_short_offset; + u32 prd_ic_ait_start_reg; + u32 prd_ic_ait_data_readystatus; + /* */ + u32 glove_en; + u32 grab_en; +}; + +#define cmd_abt_ocd_on_write cmd_raw_data_report_mode_write +#define cmd_abt_ocd_on_read cmd_raw_data_report_mode_read + +/* Reg. exchange */ +struct siw_hal_reg_quirk { + u32 old_addr; + u32 new_addr; +}; + +#endif /* __SIW_TOUCH_HAL_REG_H */ + + diff --git a/drivers/input/touchscreen/siw/siw_touch_hal_sysfs.c b/drivers/input/touchscreen/siw/siw_touch_hal_sysfs.c new file mode 100644 index 000000000000..966fc72aab4d --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_hal_sysfs.c @@ -0,0 +1,1103 @@ +/* + * siw_touch_hal_sysfs.c - SiW touch hal driver + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siw_touch.h" +#include "siw_touch_hal.h" +#include "siw_touch_bus.h" +#include "siw_touch_event.h" +#include "siw_touch_gpio.h" +#include "siw_touch_irq.h" +#include "siw_touch_sys.h" + + +//#define __SIW_USE_BUS_TEST + + +#define siw_hal_sysfs_err_invalid_param(_dev) \ + t_dev_err(_dev, "Invalid param\n"); + +#define _reg_snprintf(_buf, _size, _reg, _element) \ + siw_snprintf(_buf, _size, "# 0x%04X [%s]\n", _reg->_element, #_element) + +static int __show_reg_list(struct device *dev, char *buf, int size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + + size += siw_snprintf(buf, size, "# Reg. Map List\n"); + + size += _reg_snprintf(buf, size, reg, spr_chip_id); + size += _reg_snprintf(buf, size, reg, spr_rst_ctl); + size += _reg_snprintf(buf, size, reg, spr_boot_ctl); + size += _reg_snprintf(buf, size, reg, spr_sram_ctl); + size += _reg_snprintf(buf, size, reg, spr_boot_status); + size += _reg_snprintf(buf, size, reg, spr_subdisp_status); + size += _reg_snprintf(buf, size, reg, spr_code_offset); + size += _reg_snprintf(buf, size, reg, tc_ic_status); + size += _reg_snprintf(buf, size, reg, tc_status); + size += _reg_snprintf(buf, size, reg, tc_version); + size += _reg_snprintf(buf, size, reg, tc_product_id1); + size += _reg_snprintf(buf, size, reg, tc_product_id2); + size += _reg_snprintf(buf, size, reg, tc_version_ext); + size += _reg_snprintf(buf, size, reg, info_chip_version); + size += _reg_snprintf(buf, size, reg, info_lot_num); + size += _reg_snprintf(buf, size, reg, info_serial_num); + size += _reg_snprintf(buf, size, reg, info_date); + size += _reg_snprintf(buf, size, reg, info_time); + size += _reg_snprintf(buf, size, reg, cmd_abt_loc_x_start_read); + size += _reg_snprintf(buf, size, reg, cmd_abt_loc_x_end_read); + size += _reg_snprintf(buf, size, reg, cmd_abt_loc_y_start_read); + size += _reg_snprintf(buf, size, reg, cmd_abt_loc_y_end_read); + size += _reg_snprintf(buf, size, reg, code_access_addr); + size += _reg_snprintf(buf, size, reg, data_i2cbase_addr); + size += _reg_snprintf(buf, size, reg, prd_tcm_base_addr); + size += _reg_snprintf(buf, size, reg, tc_device_ctl); + size += _reg_snprintf(buf, size, reg, tc_interrupt_ctl); + size += _reg_snprintf(buf, size, reg, tc_interrupt_status); + size += _reg_snprintf(buf, size, reg, tc_drive_ctl); +#if defined(__SIW_CONFIG_KNOCK) + size += _reg_snprintf(buf, size, reg, tci_enable_w); + size += _reg_snprintf(buf, size, reg, tap_count_w); + size += _reg_snprintf(buf, size, reg, min_intertap_w); + size += _reg_snprintf(buf, size, reg, max_intertap_w); + size += _reg_snprintf(buf, size, reg, touch_slop_w); + size += _reg_snprintf(buf, size, reg, tap_distance_w); + size += _reg_snprintf(buf, size, reg, int_delay_w); + size += _reg_snprintf(buf, size, reg, act_area_x1_w); + size += _reg_snprintf(buf, size, reg, act_area_y1_w); + size += _reg_snprintf(buf, size, reg, act_area_x2_w); + size += _reg_snprintf(buf, size, reg, act_area_y2_w); + size += _reg_snprintf(buf, size, reg, tci_debug_fail_ctrl); + size += _reg_snprintf(buf, size, reg, tci_debug_fail_buffer); + size += _reg_snprintf(buf, size, reg, tci_debug_fail_status); +#endif +#if defined(__SIW_CONFIG_SWIPE) + size += _reg_snprintf(buf, size, reg, swipe_enable_w); + size += _reg_snprintf(buf, size, reg, swipe_dist_w); + size += _reg_snprintf(buf, size, reg, swipe_ratio_thr_w); + size += _reg_snprintf(buf, size, reg, swipe_ratio_period_w); + size += _reg_snprintf(buf, size, reg, swipe_ratio_dist_w); + size += _reg_snprintf(buf, size, reg, swipe_time_min_w); + size += _reg_snprintf(buf, size, reg, swipe_time_max_w); + size += _reg_snprintf(buf, size, reg, swipe_act_area_x1_w); + size += _reg_snprintf(buf, size, reg, swipe_act_area_y1_w); + size += _reg_snprintf(buf, size, reg, swipe_act_area_x2_w); + size += _reg_snprintf(buf, size, reg, swipe_act_area_y2_w); + size += _reg_snprintf(buf, size, reg, swipe_fail_debug_r); + size += _reg_snprintf(buf, size, reg, swipe_debug_r); +#endif + size += _reg_snprintf(buf, size, reg, cmd_raw_data_report_mode_read); + size += _reg_snprintf(buf, size, reg, cmd_raw_data_compress_write); + size += _reg_snprintf(buf, size, reg, cmd_raw_data_report_mode_write); + size += _reg_snprintf(buf, size, reg, spr_charger_status); + size += _reg_snprintf(buf, size, reg, ime_state); +#if defined(__SIW_SUPPORT_ASC) + size += _reg_snprintf(buf, size, reg, max_delta); + size += _reg_snprintf(buf, size, reg, touch_max_w); + size += _reg_snprintf(buf, size, reg, touch_max_r); + size += _reg_snprintf(buf, size, reg, call_state); +#endif + size += _reg_snprintf(buf, size, reg, tc_tsp_test_ctl); + size += _reg_snprintf(buf, size, reg, tc_tsp_test_status); + size += _reg_snprintf(buf, size, reg, tc_tsp_test_pf_result); + size += _reg_snprintf(buf, size, reg, tc_tsp_test_off_info); + size += _reg_snprintf(buf, size, reg, tc_flash_dn_status); + size += _reg_snprintf(buf, size, reg, tc_confdn_base_addr); + size += _reg_snprintf(buf, size, reg, tc_flash_dn_ctl); + size += _reg_snprintf(buf, size, reg, raw_data_ctl_read); + size += _reg_snprintf(buf, size, reg, raw_data_ctl_write); + size += _reg_snprintf(buf, size, reg, serial_data_offset); + +#if defined(__SIW_SUPPORT_WATCH) + if (!touch_test_quirks(chip->ts, CHIP_QUIRK_NOT_SUPPORT_WATCH)) { + size += _reg_snprintf(buf, size, reg, ext_watch_font_offset); + size += _reg_snprintf(buf, size, reg, ext_watch_font_addr); + size += _reg_snprintf(buf, size, reg, ext_watch_font_dn_addr_info); + size += _reg_snprintf(buf, size, reg, ext_watch_font_crc); + size += _reg_snprintf(buf, size, reg, ext_watch_dcs_ctrl); + size += _reg_snprintf(buf, size, reg, ext_watch_mem_ctrl); + size += _reg_snprintf(buf, size, reg, ext_watch_ctrl); + size += _reg_snprintf(buf, size, reg, ext_watch_area_x); + size += _reg_snprintf(buf, size, reg, ext_watch_area_y); + size += _reg_snprintf(buf, size, reg, ext_watch_blink_area); + + switch (touch_chip_type(chip->ts)) { + case CHIP_LG4895: + /* SKIP : reg->ext_watch_lut not available */ + break; + default: + size += _reg_snprintf(buf, size, reg, ext_watch_lut); + break; + } + + size += _reg_snprintf(buf, size, reg, ext_watch_display_on); + size += _reg_snprintf(buf, size, reg, ext_watch_display_status); + size += _reg_snprintf(buf, size, reg, ext_watch_rtc_sct); + size += _reg_snprintf(buf, size, reg, ext_watch_rtc_sctcnt); + size += _reg_snprintf(buf, size, reg, ext_watch_rtc_capture); + size += _reg_snprintf(buf, size, reg, ext_watch_rtc_ctst); + size += _reg_snprintf(buf, size, reg, ext_watch_rtc_ecnt); + size += _reg_snprintf(buf, size, reg, ext_watch_hour_disp); + size += _reg_snprintf(buf, size, reg, ext_watch_blink_prd); + size += _reg_snprintf(buf, size, reg, ext_watch_rtc_run); + size += _reg_snprintf(buf, size, reg, ext_watch_position); + size += _reg_snprintf(buf, size, reg, ext_watch_position_r); + size += _reg_snprintf(buf, size, reg, ext_watch_state); + size += _reg_snprintf(buf, size, reg, sys_dispmode_status); + } +#endif /* __SIW_SUPPORT_WATCH */ + + /* __SIW_SUPPORT_PRD */ + size += _reg_snprintf(buf, size, reg, prd_serial_tcm_offset); + size += _reg_snprintf(buf, size, reg, prd_tc_mem_sel); + size += _reg_snprintf(buf, size, reg, prd_tc_test_mode_ctl); + size += _reg_snprintf(buf, size, reg, prd_m1_m2_raw_offset); + size += _reg_snprintf(buf, size, reg, prd_tune_result_offset); + size += _reg_snprintf(buf, size, reg, prd_open3_short_offset); + size += _reg_snprintf(buf, size, reg, prd_ic_ait_start_reg); + size += _reg_snprintf(buf, size, reg, prd_ic_ait_data_readystatus); + + return size; +} + +static ssize_t _show_reg_list(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_fw_info *fw = &chip->fw; + u32 bootmode = 0; + u32 boot_chk_offset = 0; + int size = 0; + int ret = 0; + + ret = siw_hal_read_value(dev, + reg->spr_boot_status, + &bootmode); + if (ret < 0) { + return (ssize_t)ret; + } + + size += __show_reg_list(dev, buf, size); + + size += siw_snprintf(buf, size, "\n"); + + if (fw->version_ext) { + size += siw_snprintf(buf, size, + ">> version : %08X, chip : %u, protocol : %u\n", + fw->version_ext, + fw->v.version.chip, + fw->v.version.protocol); + } else { + size += siw_snprintf(buf, size, + ">> version : v%u.%02u, chip : %u, protocol : %u\n", + fw->v.version.major, + fw->v.version.minor, + fw->v.version.chip, + fw->v.version.protocol); + } + + size += siw_snprintf(buf, size, + ">> revision : %d\n", + fw->revision); + + size += siw_snprintf(buf, size, + ">> product id : %s\n", + fw->product_id); + + switch (chip->opt.t_boot_mode) { + case 2: + /* fall through */ + case 1: + boot_chk_offset = 0; + break; + default: + boot_chk_offset = 1; + break; + } + + size += siw_snprintf(buf, size, + ">> flash boot : %s(%s), crc %s (0x%08X)\n", + (bootmode >> (boot_chk_offset) & 0x1) ? "BUSY" : "idle", + (bootmode >> (boot_chk_offset + 1) & 0x1) ? "done" : "booting", + (bootmode >> (boot_chk_offset + 2) & 0x1) ? "ERROR" : "ok", + bootmode); + + size += siw_snprintf(buf, size, + ">> status : type %d[%08Xh, %08Xh, %08Xh]\n", + chip->status_type, + chip->status_mask_normal, + chip->status_mask_logging, + chip->status_mask_reset); + + size += siw_snprintf(buf, size, "\n"); + + return (ssize_t)size; +} + +#define REG_BURST_MAX 512 +#define REG_BURST_LOG_BUF_SZ (1<<10) +#define REG_BURST_COL_PWR 4 + +static int __show_reg_ctrl_log_history(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg_log *reg_log = chip->reg_log; + char *dir_name; + int reg_burst; + int reg_err; + int i; + int size = 0; + + size += siw_snprintf(buf, size, "[Test History]\n"); + for (i = 0; i < REG_LOG_MAX; i++) { + if (reg_log->dir) { + dir_name = !!((reg_log->dir & REG_DIR_MASK) == REG_DIR_WR) ? + "wr" : "rd"; + reg_burst = !!(reg_log->dir & (REG_DIR_ERR<<1)); + reg_err = !!(reg_log->dir & REG_DIR_ERR); + } else { + dir_name = "__"; + reg_burst = 0; + reg_err = 0; + } + + if (reg_burst) { + size += siw_snprintf(buf, size, " %s: reg[0x%04X] = (burst) %s\n", + dir_name, reg_log->addr, + (reg_err) ? "(err)" : ""); + } else { + size += siw_snprintf(buf, size, " %s: reg[0x%04X] = 0x%08X %s\n", + dir_name, reg_log->addr, reg_log->data, + (reg_err) ? "(err)" : ""); + } + + reg_log++; + } + + return size; +} + +static ssize_t _show_reg_ctrl(struct device *dev, char *buf) +{ +// struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + int size = 0; + + size += __show_reg_ctrl_log_history(dev, buf); + + size += siw_snprintf(buf, size, "\n[Usage]\n"); + size += siw_snprintf(buf, size, " echo wr 0x1234 {value} > reg_ctrl\n"); + size += siw_snprintf(buf, size, " echo rd 0x1234 > reg_ctrl\n"); + size += siw_snprintf(buf, size, " (burst access)\n"); + size += siw_snprintf(buf, size, " echo rd 0x1234 0x111 > reg_ctrl, 0x111 is size(max 0x%X)\n", + REG_BURST_MAX); + + return (ssize_t)size; +} + +static void __store_reg_ctrl_log_add(struct device *dev, + struct siw_hal_reg_log *new_log) + +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg_log *reg_log = chip->reg_log; + + memmove(®_log[1], reg_log, sizeof(*reg_log) * (REG_LOG_MAX - 1)); + memcpy(reg_log, new_log, sizeof(*reg_log)); +} + +static inline void __store_reg_ctrl_rd_burst_log(struct device *dev, + u8 *row_buf, u8 *log_buf, int log_buf_sz, int row, int col) +{ + int log_size = 0; + int i; + + if (!col) + return; + + for (i = 0; i < col ; i++) { + log_size += snprintf(log_buf + log_size, log_buf_sz - log_size, + "%02X ", *row_buf++); + } + t_dev_info(dev, "rd: [%3Xh] %s\n", row, log_buf); +} + +static int __store_reg_ctrl_rd_burst(struct device *dev, u32 addr, int size) +{ + u8 *rd_buf, *row_buf; + u8 *log_buf; + int log_buf_sz = REG_BURST_LOG_BUF_SZ; + int col_power = REG_BURST_COL_PWR; + int col_width = (1<= 0) { + t_dev_info(dev, + "wr: reg[0x%04X] = 0x%08X\n", + reg_addr, data); + } else { + reg_log.dir |= REG_DIR_ERR; + } + __store_reg_ctrl_log_add(dev, ®_log); + goto out; + } else if (!wr) { + reg_log.dir = REG_DIR_RD; + reg_log.addr = reg_addr; + if (value <= 4) { + ret = siw_hal_read_value(dev, + reg_addr, + &data); + reg_log.data = data; + if (ret >= 0) { + t_dev_info(dev, + "rd: reg[0x%04X] = 0x%08X\n", + reg_addr, data); + } + } else { + reg_log.dir |= (REG_DIR_ERR<<1); + ret = __store_reg_ctrl_rd_burst(dev, reg_addr, value); + } + if (ret < 0) { + reg_log.dir |= REG_DIR_ERR; + } + __store_reg_ctrl_log_add(dev, ®_log); + goto out; + } + + t_dev_info(dev, "[Usage]\n"); + t_dev_info(dev, " echo wr 0x1234 {value} > reg_ctrl\n"); + t_dev_info(dev, " echo rd 0x1234 > reg_ctrl\n"); + t_dev_info(dev, " (burst access)\n"); + t_dev_info(dev, " echo rd 0x1234 0x111 > reg_ctrl, 0x111 is size(max 0x%X)\n", + REG_BURST_MAX); + +out: + return count; +} + +#if defined(__SIW_CONFIG_KNOCK) +static ssize_t _show_tci_debug(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 rdata = 0; + int size = 0; + int ret = 0; + + if (!chip->opt.t_chk_tci_debug) { + t_dev_info(dev, "This chipset doesn't support tci debugging\n"); + goto out; + } + + ret = siw_hal_read_value(dev, + reg->tci_debug_fail_ctrl, + &rdata); + if (ret < 0) { + t_dev_err(dev, "failed to get tci_debug_fail_ctrl, %d\n", ret); + goto out; + } + + size += siw_snprintf(buf, size, + "TCI Debug Status = DRV %s, IC %s\n", + (chip->tci_debug_type) ? "Enabled" : "Disabled", + (rdata & 0x01) ? "Enabled" : "Disabled"); + +out: + return size; +} + +static ssize_t _store_tci_debug(struct device *dev, + const char *buf, size_t count) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + int value = 0; + + if (!chip->opt.t_chk_tci_debug) { + t_dev_info(dev, "This chipset doesn't support tci debugging\n"); + return count; + } + + if (sscanf(buf, "%d", &value) <= 0) { + siw_hal_sysfs_err_invalid_param(dev); + return count; + } + + chip->tci_debug_type = (u8)!!value; + t_dev_info(dev, "TCI-Debug %s\n", (value) ? "Enabled" : "Disabled"); + + return count; +} + +static ssize_t _show_lcd_mode(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + int size = 0; + + size += siw_snprintf(buf, size, "current driving mode is %s\n", + siw_lcd_driving_mode_str(chip->lcd_mode)); + + return size; +} + +static ssize_t _store_lcd_mode(struct device *dev, + const char *buf, size_t count) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int value = 0; + + if (sscanf(buf, "%d", &value) <= 0) { + siw_hal_sysfs_err_invalid_param(dev); + return count; + } + + t_dev_info(dev, "try to change driving mode: %s -> %s\n", + siw_lcd_driving_mode_str(chip->lcd_mode), + siw_lcd_driving_mode_str(value)); + siw_ops_notify(ts, LCD_EVENT_LCD_MODE, &value); + + return count; +} + +#endif /* __SIW_CONFIG_KNOCK */ + +#if defined(__SIW_CONFIG_SWIPE) +static const char *siw_hal_debug_type_str[] = { + "Disable Type", + "Buffer Type", + "Always Report Type" +}; + +static ssize_t _show_swipe_debug(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 rdata = -1; + int size = 0; + int ret = 0; + + ret = siw_hal_read_value(dev, + reg->swipe_fail_debug_r, + &rdata); + if (ret < 0) { + t_dev_info(dev, "Fail to Read SWIPE Debug reason type\n"); + return (ssize_t)ret; + } + + size += siw_snprintf(buf, size, + "Read SWIPE Debug reason type[IC] = %s\n", + siw_hal_debug_type_str[rdata]); + size += siw_snprintf(buf, size, + "Read SWIPE Debug reason type[Driver] = %s\n", + siw_hal_debug_type_str[chip->swipe_debug_type]); + + return (ssize_t)size; +} + +static ssize_t _store_swipe_debug(struct device *dev, + const char *buf, size_t count) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + int value = 0; + + if (sscanf(buf, "%d", &value) <= 0) { + siw_hal_sysfs_err_invalid_param(dev); + return count; + } + + if (value > 2 || value < 0) { + t_dev_err(dev, "SET SWIPE debug reason wrong, 0, 1, 2 only\n"); + return count; + } + + chip->swipe_debug_type = (u8)value; + t_dev_info(dev, "Write SWIPE Debug reason type = %s\n", + siw_hal_debug_type_str[value]); + + return count; +} +#endif /* __SIW_CONFIG_SWIPE */ + +static ssize_t _show_reset_ctrl(struct device *dev, char *buf) +{ + int size = 0; + + size += siw_snprintf(buf, size, "Reset Control Usage\n"); + size += siw_snprintf(buf, size, + " SW Reset : echo %d > hal_reset_ctrl\n", + SW_RESET); + size += siw_snprintf(buf, size, + " HW Reset(Async) : echo %d > hal_reset_ctrl\n", + HW_RESET_ASYNC); + size += siw_snprintf(buf, size, + " HW Reset(Sync) : echo %d > hal_reset_ctrl\n", + HW_RESET_SYNC); + + size += siw_snprintf(buf, size, + " HW Reset(Cond) : echo 0x%X > hal_reset_ctrl\n", + HW_RESET_COND); + + return size; +} + +static ssize_t _store_reset_xxx(struct device *dev, int type) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + if (type == HW_RESET_COND) { + siw_ops_mon_handler(ts, MON_FLAG_RST_ONLY); + goto out; + } + + siw_ops_reset(ts, type); + +out: + return 0; +} + +static ssize_t _store_reset_ctrl(struct device *dev, + const char *buf, size_t count) +{ + int value = 0; + + if (sscanf(buf, "%X", &value) <= 0) { + siw_hal_sysfs_err_invalid_param(dev); + return count; + } + + _store_reset_xxx(dev, value); + + return count; +} + +#if defined(__SIW_ATTR_RST_BY_READ) +static ssize_t _show_reset_sw(struct device *dev, char *buf) +{ + return _store_reset_xxx(dev, SW_RESET); +} + +static ssize_t _show_reset_hw(struct device *dev, char *buf) +{ + return _store_reset_xxx(dev, HW_RESET_SYNC); +} +#endif + +#if defined(__SIW_USE_BUS_TEST) +u32 t_dbg_bus_cnt = 10000; +module_param_named(dbg_bus_cnt, t_dbg_bus_cnt, uint, S_IRUGO|S_IWUSR|S_IWGRP); + +static int _show_debug_bus_test(struct device *dev, int tcnt, int *lcnt) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; + struct siw_hal_fw_info *fw = &chip->fw; + u32 chip_id; + u32 data; + int log_cnt = 10; + int i; + int last = 0; + int ret = 0; + + if (tcnt >= 100000) { + log_cnt = 10000; + } else if (tcnt >= 10000) { + log_cnt = 1000; + } else if (tcnt >= 1000) { + log_cnt = 100; + } + + for (i = 0 ; i < tcnt ; i++) { + data = i & 0xFFFF; + data |= (i & 0x01) ? 0x5A5A0000 : 0xA5A50000; + + ret = siw_hal_ic_test_unit(dev, data); + if (ret < 0) { + break; + } + + if (!i) + continue; + + last = (i == (tcnt - 1)); + + if (last || !(i % log_cnt)) { + t_dev_info(dev, "bus testing... (%d)\n", i); + } + + if (last || !(i % SIW_TOUCH_MAX_BUF_IDX)) { + ret = siw_hal_read_value(dev, + reg->spr_chip_id, + &chip_id); + if (ret < 0) { + break; + } + + if (fw->chip_id_raw != chip_id) { + ret = -EFAULT; + break; + } + } + } + + if (lcnt != NULL) { + *lcnt = i; + } + + return ret; +} + +static ssize_t _show_debug_bus(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + char *bus_str = NULL; + u32 bus_type = touch_bus_type(ts); + u32 bus_freq = 0; + int size = 0; + int tcnt = t_dbg_bus_cnt; + int lcnt = 0; + int ret = 0; + + switch (bus_type) { + case BUS_IF_I2C: + bus_str = "I2C"; + break; + case BUS_IF_SPI: + bus_str = "SPI"; + break; + default: + t_dev_err(dev, "unknown bus type, %d\n", bus_type); + goto out; + } + + t_dev_info(dev, "%s bus test(%d) begins\n", bus_str, tcnt); + + if (bus_type == BUS_IF_SPI) { + bus_freq = touch_max_freq(ts); + if ((bus_freq == ~0) || (bus_freq < 1000000)) { + t_dev_err(dev, "wrong frequency, %d\n", bus_freq); + goto out; + } + + t_dev_info(dev, "SPI bus freq: %d MHz\n", bus_freq / 1000000); + } + + siw_touch_mon_pause(dev); + +#if defined(__SIW_SUPPORT_WAKE_LOCK) + wake_lock_timeout(&ts->lpwg_wake_lock, msecs_to_jiffies(1000)); +#endif + + touch_msleep(10); + + mutex_lock(&ts->lock); + + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + + ret = _show_debug_bus_test(dev, tcnt, &lcnt); + + siw_touch_irq_control(dev, INTERRUPT_ENABLE); + + mutex_unlock(&ts->lock); + + siw_touch_mon_resume(dev); + + t_dev_info(dev, "%s bus test(%d) %s, %d\n", + bus_str, tcnt, (ret < 0) ? "failed" : "done", lcnt); + + size += siw_snprintf(buf, size, + "%s bus test(%d) %s, %d\n", + bus_str, tcnt, (ret < 0) ? "failed" : "done", lcnt); + +out: + return (ssize_t)size; +} +#endif /* __SIW_USE_BUS_TEST */ + +static const char *__debug_hal_delay_str[] = { + [HAL_DBG_DLY_TC_DRIVING_0] = "(Group - TC Driving)", + [HAL_DBG_DLY_FW_0] = "(Group - FW)", + [HAL_DBG_DLY_HW_RST_0] = "(Group - Reset)", + [HAL_DBG_DLY_NOTIFY] = "(Group - Notify)", + [HAL_DBG_DLY_LPWG] = "(Group - LPWG)", +}; + +static ssize_t _show_debug_hal(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_debug *dbg = &chip->dbg; + char *str; + int size = 0; + int i; + + for (i = 0; i < HAL_DBG_DLY_MAX; i++) { + switch (i) { + case HAL_DBG_DLY_TC_DRIVING_0: + case HAL_DBG_DLY_FW_0: + case HAL_DBG_DLY_HW_RST_0: + case HAL_DBG_DLY_NOTIFY: + case HAL_DBG_DLY_LPWG: + str = (char *)__debug_hal_delay_str[i]; + break; + default: + str = ""; + } + size += siw_snprintf(buf, size, + "debug_hal: delay[%d] = %Xh %s\n", + i, dbg->delay[i], str); + } + + return (ssize_t)size; +} + +static int _store_debug_hal_delay(struct device *dev, int sel, int val, int opt) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_debug *dbg = &chip->dbg; + + if (sel < HAL_DBG_DLY_MAX) { + u32 *delay = &dbg->delay[sel]; + + if ((*delay) != val) { + t_dev_info(dev, "debug_hal: delay[%d] changed: %Xh -> %Xh\n", + sel, (*delay), val); + (*delay) = val; + } + } + + return 0; +} + +static ssize_t _store_debug_hal(struct device *dev, + const char *buf, size_t count) +{ +// struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + int code = 0; + int sel = 0; + int val = 0; + int opt = 0; + int ret; + + if (sscanf(buf, "%X %X %X %X", &code, &sel, &val, &opt) <= 0) { + siw_hal_sysfs_err_invalid_param(dev); + return count; + } + + t_dev_info(dev, "debug_hal: code %Xh, sel %Xh, val %Xh, opt %Xh\n", + code, sel, val, opt); + + switch (code) { + case HAL_DBG_GRP_0: + ret = _store_debug_hal_delay(dev, sel, val, opt); + break; + default: + break; + } + + return count; +} + + +#define SIW_TOUCH_HAL_ATTR(_name, _show, _store) \ + TOUCH_ATTR(_name, _show, _store) + +#define _SIW_TOUCH_HAL_ATTR_T(_name) \ + touch_attr_##_name + +static SIW_TOUCH_HAL_ATTR(reg_list, _show_reg_list, NULL); +static SIW_TOUCH_HAL_ATTR(reg_ctrl, _show_reg_ctrl, _store_reg_ctrl); +#if defined(__SIW_CONFIG_KNOCK) +static SIW_TOUCH_HAL_ATTR(tci_debug, _show_tci_debug, _store_tci_debug); +static SIW_TOUCH_HAL_ATTR(lcd_mode, _show_lcd_mode, _store_lcd_mode); +#endif +#if defined(__SIW_CONFIG_SWIPE) +static SIW_TOUCH_HAL_ATTR(swipe_debug, _show_swipe_debug, _store_swipe_debug); +#endif +static SIW_TOUCH_HAL_ATTR(reset_ctrl, _show_reset_ctrl, _store_reset_ctrl); +#if defined(__SIW_ATTR_RST_BY_READ) +static SIW_TOUCH_HAL_ATTR(reset_sw, _show_reset_sw, NULL); +static SIW_TOUCH_HAL_ATTR(reset_hw, _show_reset_hw, NULL); +#endif +#if defined(__SIW_USE_BUS_TEST) +static SIW_TOUCH_HAL_ATTR(debug_bus, _show_debug_bus, NULL); +#endif +static SIW_TOUCH_HAL_ATTR(debug_hal, _show_debug_hal, _store_debug_hal); + +static struct attribute *siw_hal_attribute_list[] = { + &_SIW_TOUCH_HAL_ATTR_T(reg_list).attr, + &_SIW_TOUCH_HAL_ATTR_T(reg_ctrl).attr, +#if defined(__SIW_CONFIG_KNOCK) + &_SIW_TOUCH_HAL_ATTR_T(tci_debug).attr, + &_SIW_TOUCH_HAL_ATTR_T(lcd_mode).attr, +#endif +#if defined(__SIW_CONFIG_SWIPE) + &_SIW_TOUCH_HAL_ATTR_T(swipe_debug).attr, +#endif + &_SIW_TOUCH_HAL_ATTR_T(reset_ctrl).attr, +#if defined(__SIW_ATTR_RST_BY_READ) + &_SIW_TOUCH_HAL_ATTR_T(reset_sw).attr, + &_SIW_TOUCH_HAL_ATTR_T(reset_hw).attr, +#endif +#if defined(__SIW_USE_BUS_TEST) + &_SIW_TOUCH_HAL_ATTR_T(debug_bus).attr, +#endif + &_SIW_TOUCH_HAL_ATTR_T(debug_hal).attr, + NULL, +}; + +static const struct attribute_group siw_hal_attribute_group = { + .attrs = siw_hal_attribute_list, +}; + +static int __siw_hal_sysfs_add_abt(struct device *dev, int on_off) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int ret = 0; + + ret = siw_ops_abt_sysfs(ts, on_off); + if ((on_off == DRIVER_INIT) && (ret < 0)) { + t_dev_err(dev, "%s abt sysfs register failed\n", + touch_chip_name(ts)); + } + + return ret; +} + +static int __siw_hal_sysfs_add_prd(struct device *dev, int on_off) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int ret = 0; + + ret = siw_ops_prd_sysfs(ts, on_off); + if ((on_off == DRIVER_INIT) && (ret < 0)) { + t_dev_err(dev, "%s prd sysfs register failed\n", + touch_chip_name(ts)); + } + + return ret; +} + +static int __siw_hal_sysfs_add_watch(struct device *dev, int on_off) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int ret = 0; + + ret = siw_ops_watch_sysfs(ts, on_off); + if ((on_off == DRIVER_INIT) && (ret < 0)) { + t_dev_err(dev, "%s watch sysfs register failed\n", + touch_chip_name(ts)); + } + + return ret; +} + + +static int siw_hal_sysfs_add(struct device *dev, int on_off) +{ + int ret = 0; + + if (on_off == DRIVER_INIT) { + ret = __siw_hal_sysfs_add_abt(dev, DRIVER_INIT); + if (ret < 0) { + goto out; + } + + ret = __siw_hal_sysfs_add_prd(dev, DRIVER_INIT); + if (ret < 0) { + goto out_prd; + } + + ret = __siw_hal_sysfs_add_watch(dev, DRIVER_INIT); + if (ret < 0) { + goto out_watch; + } + return 0; + } + + __siw_hal_sysfs_add_watch(dev, DRIVER_FREE); + +out_watch: + __siw_hal_sysfs_add_prd(dev, DRIVER_FREE); + +out_prd: + __siw_hal_sysfs_add_abt(dev, DRIVER_FREE); + +out: + return ret; +} + +static int siw_hal_create_sysfs(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct kobject *kobj = &ts->kobj; + int ret = 0; + + ret = sysfs_create_group(kobj, &siw_hal_attribute_group); + if (ret < 0) { + t_dev_err(dev, "%s sysfs register failed\n", + touch_chip_name(ts)); + goto out; + } + + ret = siw_hal_sysfs_add(dev, DRIVER_INIT); + if (ret < 0) { + goto out_add; + } + + t_dev_dbg_base(dev, "%s sysfs registered\n", + touch_chip_name(ts)); + + return 0; + +out_add: + sysfs_remove_group(kobj, &siw_hal_attribute_group); + +out: + + return ret; +} + +static void siw_hal_remove_sysfs(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + + siw_hal_sysfs_add(dev, DRIVER_FREE); + + sysfs_remove_group(&ts->kobj, &siw_hal_attribute_group); + + t_dev_dbg_base(dev, "%s sysfs unregistered\n", + touch_chip_name(ts)); +} + +int siw_hal_sysfs(struct device *dev, int on_off) +{ + if (on_off == DRIVER_INIT) { + return siw_hal_create_sysfs(dev); + } + + siw_hal_remove_sysfs(dev); + return 0; +} + + diff --git a/drivers/input/touchscreen/siw/siw_touch_hal_watch.c b/drivers/input/touchscreen/siw/siw_touch_hal_watch.c new file mode 100644 index 000000000000..8bd536c1a5a0 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_hal_watch.c @@ -0,0 +1,3561 @@ +/* + * siw_touch_hal_watch.c - SiW touch hal driver for ABT + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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 "siw_touch_cfg.h" + +#if defined(__SIW_SUPPORT_WATCH) //See siw_touch_cfg.h + +#include +#include +#include +#include +#include + +#include "siw_touch.h" +#include "siw_touch_hal.h" +#include "siw_touch_bus.h" +#include "siw_touch_event.h" +#include "siw_touch_gpio.h" +#include "siw_touch_irq.h" +#include "siw_touch_sys.h" + +#define __SUPPORT_WATCH_CTRL_ACCESS + +#define __SUPPORT_WATCH_FONT_STS_CHK + +/* + * [Notice : __SUPPORT_WATCH_CTRL_ACCESS] + * This chipset has watch function in its spec, + * but its concept has been changed differently than before. + * - The role for watch control including font download is mainly managed by MIPI. + * - This watch layer supports 'config_fontdata' + * but this is just a kind of 'Plan B' for test or debugging + * when the MIPI control for font download doesn't work well. + */ +#if defined(CONFIG_TOUCHSCREEN_SIW_SW49408) +#undef __SUPPORT_WATCH_CTRL_ACCESS +#undef __SUPPORT_WATCH_FONT_STS_CHK +#endif + +#define SIW_MAX_FONT_SIZE (6<<10) + +#define SIW_FONT_MAGIC_CODE (0x3EEAFEEB) +#define SIW_FONT_MAGIC_CODE_SIZE (4) + +enum { + EXT_WATCH_CFG_DEBUG = 'd', +}; + +enum { + EXT_WATCH_LUT_NUM = 7, + EXT_WATCH_LUT_MAX = 7, +}; + +enum { + NOT_SUPPORT = 0, + SUPPORT, +}; + +enum { + UNBLOCKED = 0, + BLOCKED, +}; + +enum { + EXT_WATCH_OFF = 0, + EXT_WATCH_ON, +}; + +enum { + EXT_WATCH_RTC_START = 1, + EXT_WATCH_RTC_STOP = 2, +}; + +enum { + EXT_WATCH_SET_RTC_COUNT = 305, + EXT_WATCH_SET_RTC_ECNT = 32764, +}; + +enum { + FONT_MEM_CRC = (1<<8), +}; + +enum { + FONT_EMPTY = 0, + FONT_DOWNLOADING, + FONT_READY, +}; + +enum { + RTC_CLEAR = 0, + RTC_RUN, +}; + +#define WATCH_TYPE(_watch) ((_watch->type) & 0xFFFF) +#define WATCH_TYPE_VAR(_watch) (((_watch->type) >> 16) & 0xFF) + +#define WATCH_FLAG(_watch) (_watch->flag) + +#define FONT_TYPE(_watch) (_watch->font_type) + +struct ext_watch_font_header { + u32 magic_code; + u8 width_num; + u8 width_colon; + u8 height; + u8 font_id; + u32 size; +}; + +__packed struct ext_watch_query_font_data { + bool Font_supported; /* 0:not supported, 1:supported */ + u8 max_font_x_size; /* 1~9 number X max size. */ + u8 max_font_y_size; /* 1~9 number Y max size. */ + u8 max_cln_x_size; /* ":" X max size. (ex. 23:47) */ + u8 max_cln_y_size; /* ":" Y max size. (ex. 23:47) */ +}; + +struct ext_watch_query_font_position { /* 0:not supported, 1:supported */ + bool vertical_position_supported; + bool horizontal_position_supported; +}; + +struct ext_watch_query_font_time { /* 0:not supported, 1:supported */ + bool h24_supported; + bool ampm_supported; +}; + +__packed struct ext_watch_query_font_color { /* 0:not supported, 1:supported */ + u8 max_num; /* The number of LUT */ + bool lut_supported; + bool alpha_supported; + bool gradation_supported; +}; + +__packed struct ext_watch_query_font_effect { + bool zero_supported; /* 0:display off, 1:display on */ + u8 blink_type; /* 0:blink disable, 1:500ms, 2:1sec, 3:2sec. */ +}; + +struct ext_watch_config_font_effect_blink { + u32 blink_type; /* 0:off, 1:125ms, 2:250ms, 3:500ms, 4:1s, 5:2sec. 6:4s, 7:8s */ + u32 bstartx; /* blink startx: watstartx <= bstartx <= bendx */ + u32 bendx; /* blink end: bendx <= watendx */ +}; + +struct ext_watch_config_font_effect { + u32 len; + u32 watchon; /* 0:watch off, 1:watch on */ + u32 h24_en; /* 0:12 hour display, 1:24 hour display */ + u32 zero_disp; /* 0:display off, 1:display on */ + u32 clock_disp_type; /* 0:hour and min, 1:min and sec */ + u32 midnight_hour_zero_en; /* 0: 12:00 mode, 1: 00:00 */ + struct ext_watch_config_font_effect_blink blink; /*for blink effect */ +}; + +struct ext_watch_config_font_lut { + /* LUT */ + u32 rgb_blue; + u32 rgb_green; + u32 rgb_red; +}; + +struct ext_watch_config_font_prop { + u32 len; + u32 max_num; /* The number of LUT */ + struct ext_watch_config_font_lut lut[EXT_WATCH_LUT_NUM]; +}; + +struct ext_watch_config_font_pos { + u32 len; + u32 watstartx; + u32 watendx; + u32 watstarty; + u32 watendy; + /* relative position in watch area */ + u32 h1x_pos; /* 1 ~ 9hour position */ + u32 h10x_pos; /* 10, 20 hour position */ + u32 m1x_pos; /* 1 ~ 9min position */ + u32 m10x_pos; /* 10 ~ 50 min position */ + u32 clx_pos; /* colon position */ +}; + +struct ext_watch_config_time_sync { /* to sync with AP's current time */ + u32 len; + u32 rtc_cwhour; /* for hour */ + u32 rtc_cwmin; /* for min */ + u32 rtc_cwsec; /* for sec */ + u32 rtc_cwmilli; /* for millisecond */ +}; + +__packed struct ext_watch_bits_ctrl { /* 0x2D2 */ + u32 reserved0:1; + u32 dispmode:1; /* 0:U2, 1:AnyMode */ + u32 reserved1:5; + u32 alpha:9; +}; + +__packed struct ext_watch_bits_area { /* 0x2D3, 0x2D4 */ + u32 watstart:12; + u32 watend:12; +}; + +__packed struct ext_watch_bits_blink_area { /* 0x2D5 */ + u32 bstartx:12; + u32 bendx:12; +}; + +__packed struct ext_watch_bits_lut { /* 0x2D6 */ + u8 b; + u8 g; + u8 r; + u8 rssvd0; +}; + +__packed struct ext_watch_bits_time { + u32 hour:5; + u32 min:6; + u32 sec:6; + u32 rsvd0:15; +}; + +__packed struct ext_watch_cfg_mode { /* 36 bytes */ + struct ext_watch_bits_ctrl watch_ctrl; /* 2 bytes */ + struct ext_watch_bits_area watch_area_x; /* 3 bytes */ + struct ext_watch_bits_area watch_area_y; /* 3 bytes */ + struct ext_watch_bits_blink_area blink_area; /* 3 bytes */ + struct ext_watch_bits_lut __lut[EXT_WATCH_LUT_NUM]; /* 21 bytes */ + struct ext_watch_bits_lut *lut; +}; + +__packed struct ext_watch_cfg_time { /* 36 bytes */ + u32 disp_waton; /* 0xC10 watch display off:0, on:1*/ + struct ext_watch_bits_time rtc_sct; /* 0x081 */ + u32 rtc_sctcnt; /* 0x082 */ + u32 rtc_mit; /* 0x083 */ + u32 rtc_capture; /* 0x084 */ + struct ext_watch_bits_time rtc_ctst; /* 0x087 */ + u32 rtc_ecnt; /* 0x088 */ +}; + +enum { + POS_MAX_H10X = (1<<9), + POS_MAX_H1X = (1<<9), + POS_MAX_M10X = (1<<9), + POS_MAX_M1X = (1<<9), + POS_MAX_CLX = (1<<9), +}; + +/* relative position in watch area */ +__packed struct ext_watch_cfg_position { /* 0xC11 */ + u32 h10x_pos:9; + u32 h1x_pos:9; + u32 reserved0:14; + /* */ + u32 m10x_pos:9; + u32 m1x_pos:9; + u32 reserved1:14; + /* */ + u32 clx_pos:9; + u32 reserved2:23; + /* */ + u32 zero_disp:1; + u32 h24_en:1; + u32 clock_disp_mode:1; + u32 midnight_hour_zero_en:1; + u32 reserved3:28; + /* */ + u32 bhprd:3; + u32 reserved4:29; + /* */ + u32 num_width:8; + u32 colon_width:8; + u32 height:8; + u32 font_id:8; +}; + +static inline int __check_pos_not_set(struct ext_watch_cfg_position *position) +{ + return !(position->m1x_pos); +} + +__packed struct ext_watch_cfg_status { /* 0x270*/ + u32 step:3; + u32 en:1; + u32 en_24:1; + u32 zero_en:1; + u32 disp_mode:1; + u32 bhprd:3; + u32 cur_hour:5; + u32 cur_min:6; + u32 cur_sec:6; + u32 midnight_hour_zero_en:1; + u32 reserved0:4; +}; + +#if defined(__SUPPORT_WATCH_CTRL_ACCESS) +struct ext_watch_cfg { + u8 *font_data; + u32 magic_code; + u32 font_crc; + struct ext_watch_cfg_mode mode; + struct ext_watch_cfg_time time; + struct ext_watch_cfg_position position; +}; + +struct watch_state_info { + atomic_t font_status; + atomic_t rtc_status; + struct ext_watch_cfg_mode mode_r; + struct ext_watch_cfg_status status_r; + struct ext_watch_cfg_position position_r; +}; +#else /* __SUPPORT_WATCH_CTRL_ACCESS */ +struct ext_watch_cfg { + u8 *font_data; + u32 magic_code; + u32 font_crc; + struct ext_watch_cfg_time time; +}; + +struct watch_state_info { + atomic_t font_status; + atomic_t rtc_status; +}; +#endif /* __SUPPORT_WATCH_CTRL_ACCESS */ + +struct watch_data { + struct watch_state_info state; + struct bin_attribute fontdata_attr; + int font_written_size; + int font_max_size; + int font_type; + int font_crc_offset; + u32 font_magic_code; + struct reset_area *win_fixed; + /* for TOUCH_USE_FONT_BINARY only */ + int font_num; + int font_idx; + struct ext_watch_cfg ext_wdata; + int flag; +#define _WATCH_FLAG_SKIP_GET_MODE (1UL<<0) +#define _WATCH_FLAG_SKIP_SET_MODE (1UL<<1) +#define _WATCH_FLAG_SKIP_LUT_UPDATE (1UL<<2) +#define _WATCH_FLAG_SKIP_MEM_CTRL (1UL<<3) +#define _WATCH_FLAG_SKIP_SET_TIME (1UL<<4) +#define _WATCH_FLAG_SKIP_SET_POS (1UL<<5) +#define _WATCH_FLAG_SKIP_SET_DISP (1UL<<6) + + int type; + /* */ + u32 dcst_offset; + u32 dcst_data; +}; + +enum { + WATCH_FLAG_SKIP_GET_MODE = _WATCH_FLAG_SKIP_GET_MODE, + WATCH_FLAG_SKIP_SET_MODE = _WATCH_FLAG_SKIP_SET_MODE, + WATCH_FLAG_SKIP_LUT_UPDATE = _WATCH_FLAG_SKIP_LUT_UPDATE, + WATCH_FLAG_SKIP_MEM_CTRL = _WATCH_FLAG_SKIP_MEM_CTRL, + WATCH_FLAG_SKIP_SET_TIME = _WATCH_FLAG_SKIP_SET_TIME, + WATCH_FLAG_SKIP_SET_POS = _WATCH_FLAG_SKIP_SET_POS, + WATCH_FLAG_SKIP_SET_DISP = _WATCH_FLAG_SKIP_SET_DISP, +}; + +/* Disable all */ +#define WATCH_FLAG_SKIP_FOR_DEFAULT (0 | \ + WATCH_FLAG_SKIP_GET_MODE | \ + WATCH_FLAG_SKIP_SET_MODE | \ + WATCH_FLAG_SKIP_LUT_UPDATE | \ + WATCH_FLAG_SKIP_MEM_CTRL | \ + WATCH_FLAG_SKIP_SET_TIME | \ + WATCH_FLAG_SKIP_SET_POS | \ + WATCH_FLAG_SKIP_SET_DISP | \ + 0) + +enum { + WATCH_TYPE_0 = 0, /* LG4895 */ + WATCH_TYPE_1, /* LG4946 */ +}; + +#define SIW_WATCH_TAG "watch: " + +#define t_watch_info(_dev, fmt, args...) \ + __t_dev_info(_dev, SIW_WATCH_TAG fmt, ##args) + +#define t_watch_err(_dev, fmt, args...) \ + __t_dev_err(_dev, SIW_WATCH_TAG fmt, ##args) + +#define t_watch_warn(_dev, fmt, args...) \ + __t_dev_warn(_dev, SIW_WATCH_TAG fmt, ##args) + +#define t_watch_dbg(_dev, fmt, args...) \ + t_dev_dbg_watch(_dev, SIW_WATCH_TAG fmt, ##args) + +#define siw_watch_snprintf(_buf, _buf_max, _size, _fmt, _args...) \ + __siw_snprintf(_buf, _buf_max, _size, _fmt, ##_args) + + +#define TS_MODULE "[watch]" + +enum { + FONT_TEMP_LOG_SZ = 256, +}; + +enum { + WATCH_MAX_RW_SIZE = (SIW_TOUCH_MAX_BUF_SIZE>>1), +}; + + +// LOOK UP TABLE for CRC16 generation +// Polynomial X^16+X^15+X^2+1 +static const u16 ext_watch_crc16lut[] = { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, + 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, + 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, + 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, + 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, + 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, + 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, + 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, + 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 +}; + +#define __ret_val_blocked(_val) (-EPERM) +//#define __ret_val_blocked(_val) (_val) + +static int ext_quirk_check(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + unsigned long test_bit; + + test_bit = touch_test_quirks(ts, CHIP_QUIRK_NOT_SUPPORT_WATCH); + if (test_bit) + t_dev_dbg_base(dev, "%s watch control not supported\n", + touch_chip_name(ts)); + + return !!test_bit; +} + +#if defined(__SUPPORT_WATCH_CTRL_ACCESS) + +#if defined(CONFIG_TOUCHSCREEN_SIW_SW49407) +#define __SIW_WATCH_MODE_DCST +#endif + +enum { + DCST_RANGE_8BIT = 1, // 32-bit : data[0:7] + rsvd[31:08] + DCST_RANGE_16BIT, // 32-bit x 2 + DCST_RANGE_24BIT, // 32-bit x 3 + /* */ + DCST_ZERO_CHECK = (1<<16), +}; + +#if defined(__SIW_WATCH_MODE_DCST) +static int ext_watch_read_dcst(struct device *dev, u32 addr, u32 *value, int range) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct watch_data *watch = (struct watch_data *)chip->watch; + u32 data[4]; + u32 _value = 0; + int i; + int ret; + + if (!watch->dcst_offset || !watch->dcst_data) { + t_watch_err(dev, "wrong dcst setup: o %Xh, d %Xh\n", + watch->dcst_offset, watch->dcst_data); + return -EFAULT; + } + + ret = siw_hal_write_value(dev, watch->dcst_offset, addr); + if (ret < 0) { + t_watch_err(dev, "dcst: read offset err[%04Xh, 0x%X], %d\n", + watch->dcst_offset, addr, ret); + goto out; + } + + ret = siw_hal_reg_read(dev, watch->dcst_data, data, sizeof(data[0]) * range); + if (ret < 0) { + t_watch_err(dev, "dcst: read data err[%04Xh], %d\n", + watch->dcst_data, ret); + goto out; + } + + for (i = 0; i < range; i++) { + _value |= (data[i] & 0xFF)<<(i<<3); + } + + if (value != NULL) + *value = _value; + +out: + return ret; +} + +static int ext_watch_write_dcst(struct device *dev, u32 addr, u32 value, int range) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; +// struct siw_hal_reg *reg = chip->reg; + struct watch_data *watch = (struct watch_data *)chip->watch; + u32 data[4]; + u32 _value = value; + int i; + int ret = 0; + + if (!watch->dcst_offset || !watch->dcst_data) { + t_watch_err(dev, "wrong dcst setup: o %Xh, d %Xh\n", + watch->dcst_offset, watch->dcst_data); + return -EFAULT; + } + + for (i = 0; i < range; i++) { + data[i] = _value & 0xFF; + _value >>= 8; + } + + ret = siw_hal_write_value(dev, watch->dcst_offset, addr); + if (ret < 0) { + t_watch_err(dev, "dcst: write offset err[%04Xh, %Xh], %d\n", + watch->dcst_offset, addr, ret); + goto out; + } + + ret = siw_hal_reg_write(dev, watch->dcst_data, data, sizeof(data[0]) * range); + if (ret < 0) { + t_watch_err(dev, "dcst: write data err[%04Xh, %Xh], %d\n", + watch->dcst_data, value, ret); + goto out; + } + +out: + return ret; +} + +#define WATCH_MODE_RD_STR "get mode(dcst): " +#define WATCH_MODE_WR_STR "set mode(dcst): " +#define WATCH_MODE_LUT_REG_GAP 3 + +static int ext_watch_do_mode_read(struct device *dev, u32 addr, u32 *value, int range) +{ + return ext_watch_read_dcst(dev, addr, value, range); +} + +static int ext_watch_do_mode_write(struct device *dev, u32 addr, u32 value, int range) +{ + return ext_watch_write_dcst(dev, addr, value, range); +} +#else /* __SIW_WATCH_MODE_DCST */ +#define WATCH_MODE_RD_STR "get mode: " +#define WATCH_MODE_WR_STR "set mode: " +#define WATCH_MODE_LUT_REG_GAP 1 + +static int ext_watch_do_mode_read(struct device *dev, u32 addr, u32 *value, int range) +{ + return siw_hal_read_value(dev, addr, value); +} + +static int ext_watch_do_mode_write(struct device *dev, u32 addr, u32 value, int range) +{ + return siw_hal_write_value(dev, addr, value); +} +#endif /* __SIW_WATCH_MODE_DCST */ + +static int ext_watch_mode_read(struct device *dev, u32 addr, u32 *value, + const char *name, int range) +{ + u32 _value; + int zero_check = !!(range & DCST_ZERO_CHECK); + int ret = 0; + + range &= 0xFF; + + if (zero_check & !value) { + if (name != NULL) + t_watch_warn(dev, WATCH_MODE_RD_STR "%s skip\n", name); + return 0; + } + + ret = ext_watch_do_mode_read(dev, addr, &_value, range); + if (ret < 0) { + goto out; + } + + if (name != NULL) { + switch (range) { + case DCST_RANGE_24BIT: + t_watch_info(dev, WATCH_MODE_RD_STR "%s %06Xh\n", name, _value); + break; + case DCST_RANGE_16BIT: + t_watch_info(dev, WATCH_MODE_RD_STR "%s %04Xh\n", name, _value); + break; + default: + t_watch_info(dev, WATCH_MODE_RD_STR "%s %02Xh\n", name, _value); + break; + } + } + + if (value != NULL) + *value = _value; + +out: + return ret; +} + +static int ext_watch_mode_write(struct device *dev, u32 addr, u32 value, + const char *name, int range) +{ + int zero_check = !!(range & DCST_ZERO_CHECK); + + range &= 0xFF; + + if (zero_check & !value) { + if (name != NULL) + t_watch_warn(dev, WATCH_MODE_WR_STR "%s skip\n", name); + return 0; + } + + if (name != NULL) { + switch (range) { + case DCST_RANGE_24BIT: + t_watch_info(dev, WATCH_MODE_WR_STR "%s %06Xh\n", name, value); + break; + case DCST_RANGE_16BIT: + t_watch_info(dev, WATCH_MODE_WR_STR "%s %04Xh\n", name, value); + break; + default: + t_watch_info(dev, WATCH_MODE_WR_STR "%s %02Xh\n", name, value); + break; + } + } + + return ext_watch_do_mode_write(dev, addr, value, range); +} + +static int ext_watch_rtc_start(struct device *dev, u8 start) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; + u32 rtc_ctrl; + int ret = 0; + + rtc_ctrl = (start == EXT_WATCH_RTC_START) ? + EXT_WATCH_RTC_START : + EXT_WATCH_RTC_STOP; + + ret = siw_hal_write_value(dev, + reg->ext_watch_rtc_run, + rtc_ctrl); + if (ret < 0) { + t_watch_err(dev, "failed to run rtc, %d\n", ret); + goto out; + } + + t_watch_info(dev, "rtc %s\n", + (rtc_ctrl == EXT_WATCH_RTC_START) ? "on" : "off"); + +out: + return ret; +} + +static int ext_watch_mem_ctrl(struct device *dev, int on_off, const char *msg) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct watch_data *watch = (struct watch_data *)chip->watch; +// u32 value = !!on_off; + int ret; + + if (WATCH_FLAG(watch) & WATCH_FLAG_SKIP_MEM_CTRL) { + return 0; + } + + ret = siw_hal_write_value(dev, + reg->ext_watch_mem_ctrl, + !!on_off); + if (ret < 0) { + return ret; + } + t_watch_info(dev, "%s, watch memory clk %s\n", + msg, (on_off) ? "on" : "off"); + return 0; +} + +static void __store_log_buf(struct device *dev, char *buf, + char *log, int *buflen, int loglen) +{ + if (buf) { + memcpy(&buf[*buflen], log, loglen); + *buflen += loglen; + } else { + t_watch_info(dev, "%s", log); + } +} + +static int ext_watch_get_mode(struct device *dev, char *buf, int *len) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_cfg_mode *mode_r = &watch->state.mode_r; + struct ext_watch_bits_lut *lut; + char log[FONT_TEMP_LOG_SZ] = {0, }; + int loglen = 0; + int buflen = 0; + u32 val; + int i; + int ret = 0; + + if (WATCH_FLAG(watch) & WATCH_FLAG_SKIP_GET_MODE) { + return 0; + } + + if (len) + buflen = *len; + + ret = ext_watch_mode_read(dev, reg->ext_watch_ctrl, &val, + "watch_ctrl", DCST_RANGE_16BIT); + if (ret < 0) { + goto out; + } + memcpy((void *)&mode_r->watch_ctrl, (void *)&val, sizeof(mode_r->watch_ctrl)); + loglen = siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, 0, + "watch_ctrl[%04Xh] dispmode %d, alpha %d\n", + reg->ext_watch_ctrl, + mode_r->watch_ctrl.dispmode, + mode_r->watch_ctrl.alpha); + + __store_log_buf(dev, buf, log, &buflen, loglen); + + ret = ext_watch_mode_read(dev, reg->ext_watch_area_x, &val, + "watch_area_x", DCST_RANGE_24BIT); + if (ret < 0) { + goto out; + } + memcpy((void *)&mode_r->watch_area_x, (void *)&val, sizeof(mode_r->watch_area_x)); + loglen = siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, 0, + "watch_area_x[%04Xh] sx %d, ex %d\n", + reg->ext_watch_area_x, + mode_r->watch_area_x.watstart, + mode_r->watch_area_x.watend); + + __store_log_buf(dev, buf, log, &buflen, loglen); + + ret = ext_watch_mode_read(dev, reg->ext_watch_area_y, &val, + "watch_area_y", DCST_RANGE_24BIT); + if (ret < 0) { + goto out; + } + t_watch_dbg(dev, "get mode: watch_area_y %Xh\n", val); + memcpy((void *)&mode_r->watch_area_y, (void *)&val, sizeof(mode_r->watch_area_y)); + loglen = siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, 0, + "watch_area_y[%04Xh] sy %d, ey %d\n", + reg->ext_watch_area_y, + mode_r->watch_area_y.watstart, + mode_r->watch_area_y.watend); + + __store_log_buf(dev, buf, log, &buflen, loglen); + + ret = ext_watch_mode_read(dev, reg->ext_watch_blink_area, &val, + "blink_area", DCST_RANGE_24BIT); + if (ret < 0) { + goto out; + } + memcpy((void *)&mode_r->blink_area, (void *)&val, sizeof(mode_r->blink_area)); + loglen = siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, 0, + "blink_area[%04Xh] bsx %d, bex %d\n", + reg->ext_watch_blink_area, + mode_r->blink_area.bstartx, + mode_r->blink_area.bendx); + + __store_log_buf(dev, buf, log, &buflen, loglen); + + mode_r->lut = mode_r->__lut; + lut = mode_r->lut; + for (i = 0; i < EXT_WATCH_LUT_NUM; i++) { + ret = ext_watch_mode_read(dev, + reg->ext_watch_lut + (WATCH_MODE_LUT_REG_GAP*i), &val, + "lut", DCST_RANGE_24BIT); + if (ret < 0) { + goto out; + } + memcpy((void *)lut, (void *)&val, sizeof(*lut)); + loglen = siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, 0, + "LUT[%d][%04Xh] b %02X, g %02X, r %02X\n", + i, reg->ext_watch_blink_area + i, + lut->b, lut->g, lut->r); + + __store_log_buf(dev, buf, log, &buflen, loglen); + + lut++; + } + + if (len) + *len = buflen; + + return 0; + +out: + return ret; +} + +static int ext_watch_get_position(struct device *dev, char *buf, int *len) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct touch_xfer_msg *xfer = ts->xfer; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_cfg_position *position_r = &watch->state.position_r; + struct ext_watch_cfg_status *status_r = &watch->state.status_r; + char log[FONT_TEMP_LOG_SZ] = {0, 0}; + int loglen = 0; + int buflen = 0; + int ret = 0; + +#if 0 + if (!buf) { + t_watch_err(dev, "get position: NULL buffer"); + ret = -EINVAL; + goto out; + } +#endif + + if (len) + buflen = *len; + + siw_hal_xfer_init(dev, xfer); + + siw_hal_xfer_add_rx(xfer, + reg->ext_watch_position_r, + (void *)position_r, sizeof(u32)*3); + + siw_hal_xfer_add_rx(xfer, + reg->ext_watch_state, + (void *)status_r, sizeof(u32)); + + ret = siw_hal_xfer_msg(dev, xfer); + if (ret < 0) { + goto out; + } + + loglen = siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, 0, + "position[%04Xh ~ %04Xh] h10x %d, h1x %d, m10x %d, m1x %d, clx %d\n", + reg->ext_watch_position_r, + reg->ext_watch_position_r + 3, + position_r->h10x_pos, position_r->h1x_pos, + position_r->m10x_pos, position_r->m1x_pos, + position_r->clx_pos); + __store_log_buf(dev, buf, log, &buflen, loglen); + + position_r->zero_disp = status_r->zero_en; + position_r->h24_en = status_r->en_24; + position_r->clock_disp_mode = status_r->disp_mode; + position_r->midnight_hour_zero_en = status_r->midnight_hour_zero_en; + position_r->bhprd = status_r->bhprd; + + loglen = siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, 0, + "state[%04Xh] zero disp %d, 24H mode %d, clock mode %d\n", + reg->ext_watch_state, + position_r->zero_disp, position_r->h24_en, + position_r->clock_disp_mode); + + __store_log_buf(dev, buf, log, &buflen, loglen); + + loglen = siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, 0, + "state[%04Xh] midnight mode %d, blink period %d\n", + reg->ext_watch_state, + position_r->midnight_hour_zero_en, + position_r->bhprd); + + __store_log_buf(dev, buf, log, &buflen, loglen); + + loglen = siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, 0, + "state[%04Xh] step %d, enable %d\n", + reg->ext_watch_state, + status_r->step, status_r->en); + + __store_log_buf(dev, buf, log, &buflen, loglen); + + if (len) + *len = buflen; + + return 0; + +out: + t_watch_err(dev, "get position: failed, %d\n", ret); + return ret; +} + +static int ext_watch_get_curr_time(struct device *dev, char *buf, int *len) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct touch_xfer_msg *xfer = ts->xfer; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_bits_time *rtc_ctst = &watch->ext_wdata.time.rtc_ctst; + struct ext_watch_cfg_status status = {0, 0}; + char log[FONT_TEMP_LOG_SZ] = {0, 0}; + int loglen = 0; + int buflen = 0; + int ret = 0; + +#if 0 + if (!buf) { + t_watch_err(dev, "get curr time: NULL buffer"); + ret = -EINVAL; + goto out; + } +#endif + + if (len) + buflen = *len; + + siw_hal_xfer_init(dev, xfer); + + siw_hal_xfer_add_rx(xfer, + reg->ext_watch_state, + (void *)&status, sizeof(u32)); + + siw_hal_xfer_add_rx(xfer, + reg->ext_watch_rtc_ctst, + (void *)rtc_ctst, sizeof(u32)); + + ret = siw_hal_xfer_msg(dev, xfer); + if (ret < 0) { + goto out; + } + + loglen = siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, 0, + "state[%04Xh, %04Xh] display %02d:%02d:%02d, rtc %02d:%02d:%02d\n", + reg->ext_watch_state, reg->ext_watch_rtc_ctst, + status.cur_hour, status.cur_min, status.cur_sec, + rtc_ctst->hour, rtc_ctst->min, rtc_ctst->sec); + + __store_log_buf(dev, buf, log, &buflen, loglen); + + if (len) + *len = buflen; + + return 0; + +out: + t_watch_err(dev, "get curr time: failed, %d\n", ret); + return ret; +} + +static int ext_watch_get_dic_st(struct device *dev, char *buf, int *len) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + u32 dic_status = 0; + u32 watch_status = 0; + char log[FONT_TEMP_LOG_SZ] = {0, }; + int loglen = 0; + int buflen = 0; + int ret = 0; + + if (len) + buflen = *len; + + ret = siw_hal_read_value(dev, + reg->sys_dispmode_status, + &dic_status); + if (ret < 0) { + goto out; + } + + ret = siw_hal_read_value(dev, + reg->ext_watch_display_status, + &watch_status); + if (ret < 0) { + goto out; + } + + if (dic_status == 2) { + loglen = siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, 0, + "display mode U2, watch display %s\n", + (watch_status & 1) ? "on" : "off"); + } else { + loglen = siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, 0, + "display mode U%d\n", + dic_status); + } + + __store_log_buf(dev, buf, log, &buflen, loglen); + + if (len) + *len = buflen; + + return 0; + +out: + t_watch_err(dev, "get dic st: failed, %d\n", ret); + + return ret; +} + +static int ext_watch_set_mode(struct device *dev, int log) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_cfg_mode *mode = &watch->ext_wdata.mode; + struct ext_watch_cfg_position *position = &watch->ext_wdata.position; +// int watch_type = WATCH_TYPE(watch); + u32 val = 0; + int i; + int ret = 0; + + if (WATCH_FLAG(watch) & WATCH_FLAG_SKIP_SET_MODE) { + /* SKIP : all handled by MIPI, no effect */ + return 0; + } + + if (__check_pos_not_set(position)) { + t_watch_info(dev, "set mode: skip\n"); + return 0; + } + + mode->watch_ctrl.alpha = 1; +// mode->watch_ctrl.alpha = !!position->bhprd; + + val = 0; + memcpy((void *)&val, (void *)&mode->watch_ctrl, sizeof(mode->watch_ctrl)); + ret = ext_watch_mode_write(dev, reg->ext_watch_ctrl, val, + (log) ? "watch_ctrl" : NULL, DCST_RANGE_16BIT); + if (ret < 0) { + goto out; + } + + val = 0; + memcpy((void *)&val, (void *)&mode->watch_area_x, sizeof(mode->watch_area_x)); + ret = ext_watch_mode_write(dev, reg->ext_watch_area_x, val, + (log) ? "watch_area_x" : NULL, DCST_ZERO_CHECK | DCST_RANGE_24BIT); + if (ret < 0) + goto out; + + val = 0; + memcpy((void *)&val, (void *)&mode->watch_area_y, sizeof(mode->watch_area_y)); + ret = ext_watch_mode_write(dev, reg->ext_watch_area_y, val, + (log) ? "watch_area_y" : NULL, DCST_ZERO_CHECK | DCST_RANGE_24BIT); + if (ret < 0) + goto out; + + val = 0; + memcpy((void *)&val, (void *)&mode->blink_area, sizeof(mode->blink_area)); + ret = ext_watch_mode_write(dev, reg->ext_watch_blink_area, val, + (log) ? "blink_area" : NULL, DCST_ZERO_CHECK | DCST_RANGE_24BIT); + if (ret < 0) + goto out; + + if ((WATCH_FLAG(watch) & WATCH_FLAG_SKIP_LUT_UPDATE)) { + goto skip_lut; + } + + if (mode->lut == NULL) { + goto skip_lut; + } + + val = 0; + for (i = 0; i < EXT_WATCH_LUT_NUM; i++) { + memcpy((void *)&val, (void *)&mode->lut[i], sizeof(mode->lut[0])-sizeof(u8)); + ret = ext_watch_mode_write(dev, + reg->ext_watch_lut + (WATCH_MODE_LUT_REG_GAP*i), val, + (log) ? "lut" : NULL, DCST_RANGE_24BIT); + if (ret < 0) + goto out; + } + +skip_lut: + t_watch_info(dev, WATCH_MODE_WR_STR "done\n"); + + return 0; + +out: + t_watch_err(dev, WATCH_MODE_WR_STR "failed, %d\n", ret); + return ret; + +} + +static int ext_watch_set_curr_time(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct touch_xfer_msg *xfer = ts->xfer; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_cfg_time *time = &watch->ext_wdata.time; +// struct ext_watch_cfg_status status = {0, 0}; + u32 rtc_ctrl = 0; + u32 rtc_sctcnt = 0; + u32 rtc_count = EXT_WATCH_SET_RTC_COUNT; /* 30.5 us */ + int ret = 0; + + if (WATCH_FLAG(watch) & WATCH_FLAG_SKIP_SET_TIME) { + return 0; + } + + time->rtc_ecnt = EXT_WATCH_SET_RTC_ECNT; + rtc_sctcnt = (int)((time->rtc_sctcnt * rtc_count) / 10); + rtc_ctrl = time->rtc_ecnt & 0xFFFF; + + ret = ext_watch_rtc_start(dev, EXT_WATCH_RTC_STOP); + if (ret < 0) { + goto out; + } + + siw_hal_xfer_init(dev, xfer); + + siw_hal_xfer_add_tx(xfer, + reg->ext_watch_rtc_sct, + (void *)&time->rtc_sct, sizeof(u32)); + + siw_hal_xfer_add_tx(xfer, + reg->ext_watch_rtc_sctcnt, + (void *)&rtc_sctcnt, sizeof(u32)); + + siw_hal_xfer_add_tx(xfer, + reg->ext_watch_rtc_ecnt, + (void *)&rtc_ctrl, sizeof(u32)); + + ret = siw_hal_xfer_msg(dev, xfer); + if (ret < 0) { + goto out; + } + + t_watch_info(dev, "set time : %02d:%02d:%02d, clk[%d Hz], cnt %d\n", + time->rtc_sct.hour, + time->rtc_sct.min, + time->rtc_sct.sec, + time->rtc_ecnt, + time->rtc_sctcnt); + + ret = ext_watch_rtc_start(dev, EXT_WATCH_RTC_START); + if (ret < 0) { + goto out; + } + + atomic_set(&watch->state.rtc_status, RTC_RUN); + + return 0; + +out: + t_watch_err(dev, "set curr time, failed, %d\n", ret); + return ret; +} + +static int ext_watch_set_position(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_cfg_position *position = &watch->ext_wdata.position; + u8 *ptr = (u8 *)position; + int i; + int ret = 0; + + if (WATCH_FLAG(watch) & WATCH_FLAG_SKIP_SET_POS) { + return 0; + } + +#if 0 + if (chip->lcd_mode != LCD_MODE_U3) { + t_watch_info(dev, "lcd mode is %s, skip\n", + siw_lcd_driving_mode_str(chip->lcd_mode)); + return 0; + } +#endif + + if (__check_pos_not_set(position)) { + t_watch_info(dev, "set position: skip\n"); + return 0; + } + +#if 1 + /* + * Single access for wide compatibility + * LG4946 can't support burst access + */ + for (i = 0; i < 5; i++) { + ret = siw_hal_reg_write(dev, + reg->ext_watch_position + i, + (void *)&ptr[i<<2], sizeof(u32)); + if (ret < 0) { + break; + } + } +#else + ret = siw_hal_reg_write(dev, + reg->ext_watch_position, + (void *)position, sizeof(u32)*5); +#endif + if (ret < 0) { + goto out; + } + + t_watch_info(dev, "set position: done\n"); + + return 0; + +out: + t_watch_err(dev, "set position: failed, %d\n", ret); + return ret; +} + +static int __used __ext_watch_display_onoff_type_1(struct device *dev, u32 data) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct watch_data *watch = (struct watch_data *)chip->watch; + int waton = data; + int ret = 0; + + if (atomic_read(&watch->state.rtc_status) != RTC_RUN) { + ret = ext_watch_set_curr_time(dev); + if (ret < 0) { + goto out; + } + } + + switch (chip->lcd_mode) { + case LCD_MODE_U2: + case LCD_MODE_U2_UNBLANK: + if (waton) { + ret = siw_hal_write_value(dev, + reg->ext_watch_dcs_ctrl, + waton); + if (ret < 0) { + goto out; + } + + t_watch_info(dev, "display[%04Xh] : %s for U2\n", + reg->ext_watch_dcs_ctrl, + waton ? "on" : "off"); + } + break; + } + +out: + return ret; +} + +static int __ext_watch_display_onoff(struct device *dev, u32 data) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_cfg_time *time = &watch->ext_wdata.time; + int watch_type = WATCH_TYPE(watch); + int ret = 0; + + if (WATCH_FLAG(watch) & WATCH_FLAG_SKIP_SET_DISP) { + return 0; + } + + if (data == ~0) { + data = time->disp_waton; + } + + switch (watch_type) { + case WATCH_TYPE_1: + ret = __ext_watch_display_onoff_type_1(dev, data ); + if (ret < 0) { + goto out; + } + break; + } + + ret = siw_hal_write_value(dev, + reg->ext_watch_display_on, + data); + if (ret < 0) { + goto out; + } + + t_watch_info(dev, "display[%04Xh] : %s\n", + reg->ext_watch_display_on, + data ? "on" : "off"); + + return 0; + +out: + t_watch_err(dev, "failed to control display on/off(%d), %d\n", + time->disp_waton, ret); + return ret; +} + +static int ext_watch_display_onoff(struct device *dev) +{ + return __ext_watch_display_onoff(dev, ~0); +} + +static void ext_watch_display_off(struct device *dev) +{ + __ext_watch_display_onoff(dev, 0); +} + +static int ext_watch_set_cfg(struct device *dev, char log) +{ + int ret = 0; + + ret = ext_watch_set_mode(dev, log); + if (ret < 0) { + goto out; + } + + ret = ext_watch_set_position(dev); + if (ret < 0) { + goto out; + } + +out: + return ret; +} +#else /* __SUPPORT_WATCH_CTRL_ACCESS */ +static int __used ext_watch_rtc_start(struct device *dev, u8 start) +{ + return 0; +} + +static int __used ext_watch_mem_ctrl(struct device *dev, int on_off, const char *msg) +{ + return 0; +} + +static int __used ext_watch_get_curr_time(struct device *dev, char *buf, int *len) +{ + return 0; +} + +static int __used ext_watch_display_onoff(struct device *dev) +{ + return 0; +} + +static void __used ext_watch_display_off(struct device *dev) +{ + +} + +static int __used ext_watch_set_cfg(struct device *dev, char log) +{ + return 0; +} +#endif /* __SUPPORT_WATCH_CTRL_ACCESS */ + +static u16 ext_watch_cal_crc16(const u16 *data, u32 size, u16 init_val) +{ + const u16 *crc16lut = ext_watch_crc16lut; + u16 crc_sum = 0; + u8 temp = 0; + int i; + + crc_sum = init_val; + for (i = 0; i < size; i += 2) { + temp = (data[i]>>8) & 0xFF; + crc_sum = (crc_sum<<8) ^ crc16lut[((crc_sum>>8)&0xFF)^temp]; + + temp = data[i]&0xFF; + crc_sum = (crc_sum<<8) ^ crc16lut[((crc_sum>>8)&0xFF)^temp]; + } + + return crc_sum; +} + +static u32 ext_watch_font_crc_cal(char *data, u32 size) +{ + u32 crc_value = 0; + + /*CRC Calculation*/ + crc_value = ext_watch_cal_crc16((const u16*)&data[0], size>>1, 0); + crc_value |= (ext_watch_cal_crc16((const u16*)&data[2], size>>1, 0) << 16); + + return crc_value & 0x3FFFFFFF; +} + +#if defined(__SUPPORT_WATCH_FONT_STS_CHK) +static int ext_watch_do_chk_font_status(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; + u32 status = 0; + int ret = 0; + + ret = siw_hal_read_value(dev, + reg->tc_status, + &status); + if (ret < 0) { + return ret; + } + + if (!(status & FONT_MEM_CRC)) { + t_watch_err(dev, "font crc fail [tc_status %08Xh]\n", status); + return 1; + } + + t_watch_info(dev, "font crc ok\n"); + return 0; +} + +static int ext_watch_chk_font_status(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct watch_data *watch = (struct watch_data *)chip->watch; + int ret = 0; + + if (siw_touch_boot_mode_check(dev) >= MINIOS_MFTS_FOLDER) { + return -EBUSY; + } + + switch (atomic_read(&watch->state.font_status)) { + case FONT_EMPTY : + return -EBUSY; + + case FONT_DOWNLOADING : + mod_delayed_work(ts->wq, &chip->font_download_work, + msecs_to_jiffies(20)); + return 0; + } + + ret = ext_watch_do_chk_font_status(dev); + if (ret == 1) { + mod_delayed_work(ts->wq, &chip->font_download_work, + msecs_to_jiffies(20)); + goto out; + } + +out: + return ret; +} +#else /* __SUPPORT_WATCH_FONT_STS_CHK */ +static int ext_watch_chk_font_status(struct device *dev) +{ + return 0; +} + +#define ext_watch_do_chk_font_status ext_watch_chk_font_status +#endif /* __SUPPORT_WATCH_FONT_STS_CHK */ + +enum { + FONT_TYPE_0 = 0, + FONT_TYPE_1, +}; + +#define FONT_DN_WORK_MSG "font dn work(%d): " + +#define t_watch_info_font_dn_x(_dev, fmt, _idx, args...) \ + t_watch_info(_dev, FONT_DN_WORK_MSG fmt, _idx, ##args) + +#define t_watch_info_font_dn_0(_dev, fmt, args...) \ + t_watch_info_font_dn_x(_dev, fmt, 0, ##args) +#define t_watch_info_font_dn_1(_dev, fmt, args...) \ + t_watch_info_font_dn_x(_dev, fmt, 1, ##args) + +#define t_watch_err_font_dn_x(_dev, fmt, _idx, args...) \ + t_watch_err(_dev, FONT_DN_WORK_MSG fmt, _idx, ##args) + +#define t_watch_err_font_dn_0(_dev, fmt, args...) \ + t_watch_err_font_dn_x(_dev, fmt, 0, ##args) +#define t_watch_err_font_dn_1(_dev, fmt, args...) \ + t_watch_err_font_dn_x(_dev, fmt, 1, ##args) + +static int ext_watch_font_dn_type_0(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; +// struct siw_ts *ts = chip->ts; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_cfg *ext_wdata = &watch->ext_wdata; + struct ext_watch_font_header __font_hdr; + struct ext_watch_font_header *font_hdr = NULL; + u8 *font_data; + int font_size; + int curr_size; + u32 offset = 0; + u32 crc_addr = 0; + u32 crc_file = 0; + u32 font_crc_check = 0; + u32 magic_addr = 0; + u32 font_magic_check = 0; + int ret = 0; + + t_watch_info_font_dn_0(dev, "begins\n"); + + memcpy((void *)&__font_hdr.width_num, (void *)watch->ext_wdata.font_data, + sizeof(__font_hdr) - sizeof(__font_hdr.magic_code)); + + font_hdr = (struct ext_watch_font_header *)&__font_hdr; + + magic_addr = font_hdr->size; + + t_watch_info_font_dn_0(dev, + "magic addr %08Xh\n", + magic_addr); + + if (magic_addr > watch->font_written_size) { + t_watch_err_font_dn_0(dev, "wrong magic addr, %08Xh\n", magic_addr); + goto out; + } + + memcpy((void *)&font_hdr->magic_code, + (void *)&ext_wdata->font_data[magic_addr], sizeof(u32)); + + if (font_hdr->magic_code != watch->font_magic_code) { + t_watch_err_font_dn_0(dev, "wrong magic code: %08Xh (%08Xh)\n", + font_hdr->magic_code, watch->font_magic_code); + goto out; + } + ext_wdata->magic_code = watch->font_magic_code; + + t_watch_info_font_dn_0(dev, + "id %d, size %d\n", + font_hdr->font_id, font_hdr->size); + + t_watch_info_font_dn_0(dev, + "width_num %d, width_colon %d, height %d\n", + font_hdr->width_num, font_hdr->width_colon, font_hdr->height); + +// crc_addr = font_hdr->size + SIW_FONT_MAGIC_CODE_SIZE; + crc_addr = font_hdr->size + watch->font_crc_offset; + + memcpy((void *)&crc_file, (void *)&ext_wdata->font_data[crc_addr], sizeof(u32)); + t_watch_info_font_dn_0(dev, + "input crc %08Xh[addr %08Xh]\n", + crc_file, crc_addr); + + ext_wdata->font_crc = + ext_watch_font_crc_cal(ext_wdata->font_data, crc_addr); + t_watch_info_font_dn_0(dev, + "result crc %08Xh\n", + ext_wdata->font_crc); + + memcpy((void *)&ext_wdata->font_data[crc_addr], + (void *)&ext_wdata->font_crc, sizeof(u32)); + + magic_addr >>= 2; + crc_addr >>= 2; + offset = 0; + font_data = ext_wdata->font_data; + font_size = watch->font_written_size; + while (font_size) { + curr_size = min(font_size, WATCH_MAX_RW_SIZE); + + /* write font */ + ret = siw_hal_write_value(dev, + reg->ext_watch_font_offset, + offset); + if (ret < 0) { + goto out; + } + + ret = siw_hal_reg_write(dev, + reg->ext_watch_font_addr, + (void *)font_data, curr_size); + if (ret < 0) { + goto out; + } + + /* dump font */ + ret = siw_hal_write_value(dev, + reg->ext_watch_font_offset, + magic_addr); + if (ret < 0) { + goto out; + } + + ret = siw_hal_read_value(dev, + reg->ext_watch_font_addr, + &font_magic_check); + if (ret < 0) { + goto out; + } + + t_watch_info_font_dn_0(dev, + "font magic return check : %Xh\n", + font_magic_check); + + ret = siw_hal_write_value(dev, + reg->ext_watch_font_offset, + crc_addr); + if (ret < 0) { + goto out; + } + + ret = siw_hal_read_value(dev, + reg->ext_watch_font_addr, + &font_crc_check); + if (ret < 0) { + goto out; + } + + t_watch_info_font_dn_0(dev, + "font crc return check : %Xh\n", + font_crc_check); + + + font_size -= curr_size; + font_data += curr_size; + offset += (curr_size>>2); + } + +#if defined(__SUPPORT_WATCH_FONT_STS_CHK) + ret = siw_hal_write_value(dev, + reg->ext_watch_font_crc, 1); + if (ret < 0) { + goto out; + } +#endif + + atomic_set(&watch->state.font_status, FONT_READY); + + if (chip->lcd_mode == LCD_MODE_U2) { + ret = ext_watch_mem_ctrl(dev, 0, "U2 mode"); + if (ret < 0) { + goto out; + } + } + + t_watch_info_font_dn_0(dev, + "done(%d)\n", + watch->font_written_size); + + return 0; + +out: + atomic_set(&watch->state.font_status, FONT_EMPTY); + + t_watch_err_font_dn_0(dev, "failed, %d\n", ret); + + return ret; +} + +static int ext_watch_font_dn_type_1(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_hal_reg *reg = chip->reg; +// struct siw_ts *ts = chip->ts; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_cfg *ext_wdata = &watch->ext_wdata; + struct ext_watch_font_header *font_hdr = NULL; + u8 *font_data; + int font_size; + int curr_size; + u32 offset = 0; + u32 crc_addr = 0; + u32 crc_file = 0; + int ret = 0; + + t_watch_info_font_dn_1(dev, "begins\n"); + + font_hdr = (struct ext_watch_font_header *)watch->ext_wdata.font_data; + + if (font_hdr->magic_code != watch->font_magic_code) { + t_watch_err_font_dn_1(dev, "wrong magic code: %08Xh (%08Xh)\n", + font_hdr->magic_code, watch->font_magic_code); + goto out; + } + ext_wdata->magic_code = watch->font_magic_code; + + t_watch_info_font_dn_1(dev, + "id %d, size %d\n", + font_hdr->font_id, font_hdr->size); + + t_watch_info_font_dn_1(dev, + "width_num %d, width_colon %d, height %d\n", + font_hdr->width_num, font_hdr->width_colon, font_hdr->height); + +// crc_addr = font_hdr->size + sizeof(struct ext_watch_font_header); + crc_addr = font_hdr->size + watch->font_crc_offset; + + memcpy((void *)&crc_file, (void *)&ext_wdata->font_data[crc_addr], sizeof(u32)); + t_watch_info_font_dn_1(dev, + "input crc %08Xh[addr %08Xh]\n", + crc_file, crc_addr); + + ext_wdata->font_crc = + ext_watch_font_crc_cal(ext_wdata->font_data, crc_addr); + t_watch_info_font_dn_1(dev, + "result crc %08Xh\n", + ext_wdata->font_crc); + + if (watch->font_written_size == crc_addr) { + watch->font_written_size += sizeof(ext_wdata->font_crc); + } + + memcpy((void *)&ext_wdata->font_data[crc_addr], + (void *)&ext_wdata->font_crc, sizeof(u32)); + + offset = 0; + font_data = ext_wdata->font_data; + font_size = watch->font_written_size; + while (font_size) { + curr_size = min(font_size, WATCH_MAX_RW_SIZE); + + /* write font */ + ret = siw_hal_write_value(dev, + reg->ext_watch_font_offset, + offset); + if (ret < 0) { + goto out; + } + + ret = siw_hal_reg_write(dev, + reg->ext_watch_font_addr, + (void *)font_data, curr_size); + if (ret < 0) { + goto out; + } + + font_size -= curr_size; + font_data += curr_size; + offset += (curr_size>>2); + } + +#if defined(__SUPPORT_WATCH_FONT_STS_CHK) + ret = siw_hal_write_value(dev, + reg->ext_watch_font_crc, 1); + if (ret < 0) { + goto out; + } +#endif + + atomic_set(&watch->state.font_status, FONT_READY); + + if (chip->lcd_mode == LCD_MODE_U2) { + ret = ext_watch_mem_ctrl(dev, 0, "U2 mode"); + if (ret < 0) { + goto out; + } + } + + t_watch_info_font_dn_1(dev, + "done(%d)\n", + watch->font_written_size); + + return 0; + +out: + atomic_set(&watch->state.font_status, FONT_EMPTY); + + t_watch_err_font_dn_1(dev, "failed, %d\n", ret); + + return ret; +} + +static void ext_watch_font_download(struct work_struct *font_download_work) +{ + struct siw_touch_chip *chip = + container_of(to_delayed_work(font_download_work), + struct siw_touch_chip, font_download_work); + struct siw_ts *ts = chip->ts; + struct device *dev = chip->dev; + struct watch_data *watch = (struct watch_data *)chip->watch; + int font_type = FONT_TYPE(watch); +// int watch_type = WATCH_TYPE(watch); +// int watch_type_var = WATCH_TYPE_VAR(watch); + int ret = 0; + + if (atomic_read(&watch->state.font_status) == FONT_EMPTY) { + t_watch_err(dev, "font dn work: data not downloaded\n"); + return; + } + + if (atomic_read(&ts->state.sleep) == IC_DEEP_SLEEP) { + atomic_set(&watch->state.font_status, FONT_DOWNLOADING); + + t_watch_info(dev, "font dn work: IC deep sleep\n"); + return; + } + + mutex_lock(&ts->lock); + mutex_lock(&ts->reset_lock); + + if (chip->lcd_mode == LCD_MODE_U2) { + watch->ext_wdata.time.disp_waton = 0; + ret = ext_watch_display_onoff(dev); + if (ret < 0) { + goto out; + } + + ret = ext_watch_mem_ctrl(dev, 1, "U2 mode"); + if (ret < 0) { + goto out; + } + } + + switch (font_type) { + case 1: + ext_watch_font_dn_type_1(dev); + break; + default: + ext_watch_font_dn_type_0(dev); + break; + } + + touch_msleep(5); + + ext_watch_do_chk_font_status(dev); + +out: + mutex_unlock(&ts->reset_lock); + mutex_unlock(&ts->lock); +} + +static int ext_watch_font_dump(struct device *dev, u8 *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct siw_hal_reg *reg = chip->reg; + struct watch_data *watch = (struct watch_data *)chip->watch; + u8 *font_data; + int font_size; + int curr_size; + u32 offset = 0; + int ret = 0; + + mutex_lock(&ts->lock); + + t_watch_info(dev, "font dump: begins\n"); + + if (chip->lcd_mode == LCD_MODE_U2) { + ret = ext_watch_mem_ctrl(dev, 1, "font dump: U2 mode"); + if (ret < 0) { + goto out; + } + } + + font_data = buf; + font_size = watch->font_written_size; + while (font_size) { + curr_size = min(font_size, WATCH_MAX_RW_SIZE); + + ret = siw_hal_write_value(dev, + reg->ext_watch_font_offset, + offset); + if (ret < 0) { + goto out; + } + + ret = siw_hal_reg_write(dev, + reg->ext_watch_font_addr, + (void *)font_data, curr_size); + if (ret < 0) { + goto out; + } + + font_size -= curr_size; + font_data += curr_size; + offset += (curr_size>>2); + } + + if (chip->lcd_mode == LCD_MODE_U2) { + ret = ext_watch_mem_ctrl(dev, 0, "font dump: U2 mode"); + if (ret < 0) { + goto out; + } + } + + t_watch_info(dev, "font dump: done(%d)\n", + watch->font_written_size); + + mutex_unlock(&ts->lock); + + return 0; + +out: + t_watch_err(dev, "font dump: failed, %d\n", ret); + + mutex_unlock(&ts->lock); + + return ret; +} + +static int ext_watch_fontdata_check(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct watch_data *watch = chip->watch; + struct siw_touch_font_bin *font_bin = NULL; + u8 *font_buf = NULL; + u32 font_size = 0; + + watch->font_num = 0; + watch->font_idx = 0; + + if (!(touch_flags(ts) & TOUCH_USE_FONT_BINARY)) { + return 0; + } + + t_watch_info(dev, "font_check: binary\n"); + + font_bin = touch_font_bin(ts); + if (font_bin == NULL) { + t_watch_err(dev, "font_check: empty font info\n"); + return -ENOMEM; + } + + while (1) { + font_buf = font_bin->font_data; + font_size = font_bin->font_size; + + if ((font_buf == NULL) || !font_size) { + break; + } + + watch->font_num++; + + font_bin++; + } + + t_watch_info(dev, + "font_check: %d binary detected\n", + watch->font_num); + + return 0; +} + +static int ext_watch_fontdata_read(struct device *dev, + const char *name, u8 *buf, u32 max_size) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct watch_data *watch = chip->watch; + struct siw_touch_font_bin *font_bin = NULL; + struct file *filp = NULL; + u8 *font_buf = NULL; + u32 font_size = 0; + int font_idx = 0; + loff_t fsize; + int rd_size; + int ret = 0; + + if (touch_flags(ts) & TOUCH_USE_FONT_BINARY) { + t_watch_info(dev, "font_preload: font info - binary\n"); + + if (!watch->font_num) { + t_watch_err(dev, "font_preload: no valid font info\n"); + return -EFAULT; + } + + font_idx = watch->font_idx; + if (font_idx >= watch->font_num) { + t_watch_err(dev, "font_preload: invalid font index: %d > %d\n", + font_idx, watch->font_num - 1); + return -EFAULT; + } + + font_bin = touch_font_bin(ts); + if (font_bin == NULL) { + t_watch_err(dev, "font_preload: empty font info\n"); + return -ENOMEM; + } + + font_buf = font_bin[font_idx].font_data; + font_size = font_bin[font_idx].font_size; + + if ((font_buf == NULL) || !font_size) { + t_watch_err(dev, "font_preload: invalid font info\n"); + return -ENOMEM; + } + + if (font_size > max_size) { + t_watch_err(dev, "font_preload: size overflow, %d > %d\n", + font_size, max_size); + return -EOVERFLOW; + } + + memcpy(buf, font_buf, font_size); + return font_size; + } + + t_watch_info(dev, "font_preload: font info - file\n"); + + if (!name) { + t_watch_info(dev, "font_preload: watch_font_image not defined\n"); + return -EFAULT; + } + + filp = filp_open(name, O_RDONLY, 0); + if (IS_ERR(filp)) { + t_watch_err(dev, "font_preload: font_image open failed\n"); + return (int)PTR_ERR(filp); + } + + fsize = vfs_llseek(filp, 0, SEEK_END); + if ((u32)fsize > max_size) { + t_watch_err(dev, "font_preload: size overflow, %d > %d\n", + (int)fsize, max_size); + ret = -EOVERFLOW; + goto out; + } + + rd_size = kernel_read(filp, 0, (char *)buf, (unsigned long)fsize); + if (rd_size != (int)fsize) { + t_watch_err(dev, "font_preload: failed to read[%d], %d\n", + (int)fsize, rd_size); + memset(buf, 0, max_size); + ret = (rd_size < 0) ? rd_size : -EFAULT; + goto out; + } + + ret = rd_size; + +out: + filp_close(filp, 0); + return ret; +} + +static int ext_watch_fontdata_preload(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct watch_data *watch = chip->watch; + u8 *buf; + int size = 0; + + if (!watch->ext_wdata.font_data) { + t_watch_info(dev, "font_preload: no font data buffer! check!\n"); + return 0; + } + buf = watch->ext_wdata.font_data; + + size = ext_watch_fontdata_read(dev, + ts->watch_font_image, + buf, + watch->font_max_size); + if (size < 0) { + return size; + } + + watch->font_written_size = (u32)size; + + atomic_set(&watch->state.font_status, FONT_DOWNLOADING); + + if (touch_flags(ts) & TOUCH_USE_FONT_BINARY) { + t_watch_info(dev, "font image preload done (%d)\n", + size); + } else { + t_watch_info(dev, "font image preload done (%d, %s)\n", + size, ts->watch_font_image); + } + t_watch_info(dev, + " %02X %02X %02X %02X" + " %02X %02X %02X %02X" + " ...\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + return 0; +} + + +#if defined(__SUPPORT_WATCH_CTRL_ACCESS) +static ssize_t store_ext_watch_rtc_onoff(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + u8 buf_val, value = 0; + u8 zero = '0'; + int ret = 0; + + buf_val = buf[0]; + value = !(!buf_val || (buf_val == zero)); + + mutex_lock(&ts->lock); + ret = ext_watch_rtc_start(dev, value); + mutex_unlock(&ts->lock); + + return count; +} + +static ssize_t show_ext_watch_block_cfg(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + int ret = 0; + + ret = snprintf(buf, PAGE_SIZE, "block_watch_cfg : %d\n", + atomic_read(&chip->block_watch_cfg)); + + return ret; +} + +static ssize_t store_ext_watch_block_cfg(struct device *dev, + const char *buf, size_t count) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + u8 buf_val, buf_val_org, value; + + buf_val = buf[0]; + buf_val_org = buf_val; + if (buf_val >= '0') + buf_val -= '0'; + if (buf_val > BLOCKED) { + t_watch_err(dev, "invalid param, %d\n", buf_val_org); + goto out; + }; + + value = buf_val; + + atomic_set(&chip->block_watch_cfg, value); + + t_watch_info(dev, "%s\n", (value) ? "BLOCKED" : "UNBLOCKED"); + +out: + return count; +} + +static ssize_t store_ext_watch_font_onoff(struct device *dev, + const char *buf, size_t count) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct watch_data *watch = (struct watch_data *)chip->watch; + u8 buf_val, value; + int ret = 0; + + if (atomic_read(&chip->block_watch_cfg) == BLOCKED) { + t_watch_err(dev, "store font onoff blocked\n"); + return __ret_val_blocked(count); + } + + mutex_lock(&ts->lock); + + buf_val = buf[0]; + if ((count == 2 ) && (buf_val == EXT_WATCH_CFG_DEBUG)) { + buf_val = 1; + } else if ((buf_val >= '0') && (buf_val <= '9')) { + buf_val -= '0'; + } + + value = !!buf_val; + + ret = ext_watch_set_cfg(dev, 1); + if (ret < 0) { + t_watch_err(dev, "failed to set watch cfg, %d\n", ret); + goto out; + } + + watch->ext_wdata.time.disp_waton = value; + + ret = ext_watch_display_onoff(dev); + if (ret < 0) { + t_watch_err(dev, + "failed to set watch display control, %d\n", ret); + goto out; + } + + t_watch_info(dev, + "Power %s(%d)\n", (value) ? "ON" : "OFF", value); + + ret = count; + +out: + mutex_unlock(&ts->lock); + return ret; +} + +static ssize_t store_ext_watch_config_font_effect(struct device *dev, + const char *buf, size_t count) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_cfg_mode *mode = &watch->ext_wdata.mode; + struct ext_watch_cfg_time *time = &watch->ext_wdata.time; + struct ext_watch_cfg_position *position = &watch->ext_wdata.position; + struct ext_watch_config_font_effect cfg = { 0, }; + char period[16]; + int blink_type, blink_max, blink_boundary, blink_unit; + int watch_type = WATCH_TYPE(watch); + int watch_type_var = WATCH_TYPE_VAR(watch); + + if (atomic_read(&chip->block_watch_cfg) == BLOCKED) { + t_watch_err(dev, "store font effect blocked\n"); + return __ret_val_blocked(count); + } + + //for test using echo command + if ((count == 2) && (buf[0] == EXT_WATCH_CFG_DEBUG)) { + memset((char *)&cfg, 0, sizeof(cfg)); + + cfg.midnight_hour_zero_en = 0; + cfg.blink.blink_type = 3; + + switch (watch_type) { + case WATCH_TYPE_1: + cfg.blink.bstartx = 696; + cfg.blink.bendx = 744; + break; + default: + /* + * blink position is handled by MIPI. + */ + switch (watch_type_var) { + case 1: //SW49407 + cfg.blink.bstartx = 1092 + 132; + cfg.blink.bendx = 1092 + 156; + break; + } + break; + } + } else { + memcpy((char *)&cfg, buf, sizeof(cfg)); + } + + blink_type = cfg.blink.blink_type; + + /* + * 4:1s, 5:2s, 6:4s, 7:8s + * 1:125ms, 2:250ms, 3:500ms + */ + blink_max = 7; + blink_boundary = 4; + blink_unit = 125; + + if (blink_type > blink_max) + blink_type = blink_max; + + if (blink_type >= blink_boundary) { + snprintf(period, sizeof(period), "%d s", + 1<<(blink_type - blink_boundary)); + } else if (blink_type) { + snprintf(period, sizeof(period), "%d ms", + (1<<(blink_type - 1))*blink_unit); + } else { + snprintf(period, sizeof(period), "off"); + } + + position->h24_en = cfg.h24_en; + position->zero_disp = cfg.zero_disp; + position->clock_disp_mode = cfg.clock_disp_type; + position->midnight_hour_zero_en = cfg.midnight_hour_zero_en; + position->bhprd = blink_type; + + mode->blink_area.bstartx = cfg.blink.bstartx; + mode->blink_area.bendx = cfg.blink.bendx; + + t_watch_info(dev, + "24h mode %s, zero dispaly %s, %s type, %s mode\n", + (cfg.h24_en) ? "on" : "off", + (cfg.zero_disp) ? "on" : "off", + (cfg.clock_disp_type) ? "MM:SS" : "HH:MM", + (cfg.midnight_hour_zero_en) ? "00:00" : "12:00"); + + switch (watch_type) { + case WATCH_TYPE_1: + t_watch_info(dev, + "blink area [%d , %d] period %s\n", + cfg.blink.bstartx, cfg.blink.bendx, period); + break; + case WATCH_TYPE_0: + time->disp_waton = cfg.watchon; + t_watch_info(dev, + "blink area [%d , %d] period %s, watch display %s\n", + cfg.blink.bstartx, cfg.blink.bendx, period, + (time->disp_waton) ? "on" : "off"); + break; + } + + return count; +} + +static int __ext_watch_chk_font_pos(struct device *dev, + struct ext_watch_config_font_pos *cfg, + struct reset_area *watch_win) +{ +#if 1 + if (watch_win) { + if ((cfg->watstartx < watch_win->x1) || (cfg->watendx > watch_win->x2) || + (cfg->watstarty < watch_win->y1) || (cfg->watendy > watch_win->y2)) { + t_watch_err(dev, "check pos: invalid win: %d %d %d %d - %d %d %d %d\n", + watch_win->x1, watch_win->x2, watch_win->y1, watch_win->y2, + cfg->watstartx, cfg->watendx, cfg->watstarty, cfg->watendy); + return -EINVAL; + } + } +#endif + + return 0; +} + +static const struct reset_area watch_win_test = { + .x1 = 420, + .y1 = 0, + .x2 = 1020, + .y2 = 680, +}; + +static const struct ext_watch_config_font_pos watch_pos_test_1 = { + .watstartx = 464, + .watendx = 976, + .watstarty = 244, + .watendy = 428, + .h1x_pos = 114, + .h10x_pos = 0, + .m1x_pos = 398, + .m10x_pos = 284, + .clx_pos = 232, +}; + +static const struct ext_watch_config_font_pos watch_pos_test_0 = { + .watstartx = 522, + .watendx = 630, + .watstarty = 9, + .watendy = 59, + .h1x_pos = 16, + .h10x_pos = 0, + .m1x_pos = 64, + .m10x_pos = 48, + .clx_pos = (32+4), +}; + +static ssize_t store_ext_watch_config_font_position(struct device *dev, + const char *buf, size_t count) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_cfg_mode *mode = &watch->ext_wdata.mode; + struct ext_watch_cfg_position *position = &watch->ext_wdata.position; + struct ext_watch_config_font_pos cfg = {0, }; + struct reset_area *watch_win = NULL; + int watch_type = WATCH_TYPE(watch); + int watch_type_var = WATCH_TYPE_VAR(watch); + int ret = 0; + + if (atomic_read(&chip->block_watch_cfg) == BLOCKED) { + t_watch_err(dev, "store font position blocked\n"); + return __ret_val_blocked(count); + } + + if (watch->win_fixed != NULL) { + watch_win = (struct reset_area *)watch->win_fixed; + } else { + watch_win = pdata_watch_win(ts->pdata); + if (!watch_win) { + watch_win = (struct reset_area *)&watch_win_test; + } + } + + //for test using echo command + if ((count == 2) && (buf[0] == EXT_WATCH_CFG_DEBUG)) { + void *pos_dbg = NULL; + + switch (watch_type) { + case WATCH_TYPE_1: + pos_dbg = (void *)&watch_pos_test_1; + break; + default: + pos_dbg = (void *)&watch_pos_test_0; + + switch (watch_type_var) { + case 1: //SW49407 + cfg.watstartx = 1092; // <= 1152 + cfg.watendx = 1092 + 288; + cfg.watstarty = 0; + cfg.watendy = 160; + cfg.h10x_pos = 0; + cfg.h1x_pos = 66; + cfg.clx_pos = 132; + cfg.m10x_pos = 156; + cfg.m1x_pos = 222; + pos_dbg = &cfg; + break; + } + break; + } + if (pos_dbg) { + memcpy((void *)&cfg, pos_dbg, sizeof(cfg)); + } else { + memset((char *)&cfg, 0, sizeof(cfg)); + } + } else { + memcpy((char *)&cfg, buf, sizeof(cfg)); + } + + ret = __ext_watch_chk_font_pos(dev, &cfg, watch_win); + if (ret < 0) { + goto out; + } + + mode->watch_area_x.watstart = cfg.watstartx; + mode->watch_area_x.watend = cfg.watendx; + mode->watch_area_y.watstart = cfg.watstarty; + mode->watch_area_y.watend = cfg.watendy; + + position->h10x_pos = cfg.h10x_pos; + position->h1x_pos = cfg.h1x_pos; + position->m10x_pos = cfg.m10x_pos; + position->m1x_pos = cfg.m1x_pos; + position->clx_pos = cfg.clx_pos; + + t_watch_info(dev, + "watch area: sx %d, ex %d, sy %d, ey %d\n", + mode->watch_area_x.watstart, + mode->watch_area_x.watend, + mode->watch_area_y.watstart, + mode->watch_area_y.watend); + t_watch_info(dev, + "watch position: h10x %d, h1x %d, m10x %d, m1x %d, c1x %d\n", + position->h10x_pos, + position->h1x_pos, + position->m10x_pos, + position->m1x_pos, + position->clx_pos); + + if (ret < 0) + goto out; + + /* Transfer pos data via notifier */ + siw_touch_blocking_notifier_call(LCD_EVENT_TOUCH_WATCH_POS_UPDATE, (void*)(&cfg)); + + ret = count; + +out: + return ret; +} + +static const struct ext_watch_config_font_prop watch_prop_test_lg4946 = { + .max_num = EXT_WATCH_LUT_NUM, + .lut = { + [0] = { 0x01, 0x01, 0x01 }, + [1] = { 0x36, 0x36, 0x36 }, + [2] = { 0x4F, 0x4F, 0x4F }, + [3] = { 0x73, 0x73, 0x73 }, + [4] = { 0x8E, 0x8E, 0x8E }, + [5] = { 0xA2, 0xA2, 0xA2 }, + [6] = { 0xC0, 0xC0, 0xC0 }, + }, +}; + +static const struct ext_watch_config_font_prop watch_prop_test = { + .max_num = EXT_WATCH_LUT_NUM, + .lut = { + [0] = { 0xFF, 0x00, 0x00 }, /* blue */ + [1] = { 0x00, 0xFF, 0x00 }, /* green */ + [2] = { 0xFF, 0xFF, 0x00 }, /* cyan */ + [3] = { 0x00, 0x00, 0xFF }, /* red */ + [4] = { 0xFF, 0x00, 0xFF }, /* magenda */ + [5] = { 0x00, 0xFF, 0xFF }, /* yellow */ + [6] = { 0xFF, 0xFF, 0xFF }, /* white */ + }, +}; + +static ssize_t store_ext_watch_config_font_property(struct device *dev, + const char *buf, size_t count) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_cfg_mode *mode = &watch->ext_wdata.mode; + struct ext_watch_bits_lut *lut = NULL; + struct ext_watch_config_font_prop cfg = {0, }; + struct ext_watch_config_font_lut *lut_src = NULL; + void *prop_src = NULL; + char log[FONT_TEMP_LOG_SZ] = {0, }; + int loglen = 0; + int idx = 0; +// int ret = 0; + + if (atomic_read(&chip->block_watch_cfg) == BLOCKED) { + t_watch_err(dev, "store font property blocked\n"); + return __ret_val_blocked(count); + } + + //for test using echo command + if ((count == 2) && (buf[0] == EXT_WATCH_CFG_DEBUG)) { + switch (touch_chip_type(ts)) { + case CHIP_LG4946: + prop_src = (void *)&watch_prop_test_lg4946; + break; + default: + prop_src = (void *)&watch_prop_test; + break; + } + } else { + prop_src = (void *)buf; + } + memcpy((char *)&cfg, prop_src, sizeof(cfg)); + + if (WATCH_FLAG(watch) & WATCH_FLAG_SKIP_LUT_UPDATE) { + /* SKIP : lut setup is handled by MIPI, no effect */ + t_watch_info(dev, "skip font property\n"); + goto out; + } + + if (!cfg.max_num || (cfg.max_num > EXT_WATCH_LUT_NUM)) { + t_watch_err(dev, "invalid lut count, %d\n", cfg.max_num); + goto out; + } + + loglen += siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, loglen, + "lut[%d] ", cfg.max_num); + + mode->lut = mode->__lut; + lut = mode->lut; + lut_src = cfg.lut; + for (idx = 0; idx < (int)cfg.max_num; idx++) { + lut->b = lut_src->rgb_blue; + lut->g = lut_src->rgb_green; + lut->r = lut_src->rgb_red; + + loglen += siw_watch_snprintf(log, FONT_TEMP_LOG_SZ, loglen, + "%d:%02X%02X%02X ", + idx + 1, + lut_src->rgb_blue, + lut_src->rgb_green, + lut_src->rgb_red); + + lut++; + lut_src++; + } + + t_watch_info(dev, "%s\n", log); + +out: + /* Transfer LUT data via notifier */ + siw_touch_blocking_notifier_call(LCD_EVENT_TOUCH_WATCH_LUT_UPDATE, (void*)(&cfg)); + + return count; +} + + +static ssize_t store_ext_watch_config_time_sync(struct device *dev, + const char *buf, size_t count) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_cfg_time *time = &watch->ext_wdata.time; + struct ext_watch_config_time_sync cfg = {0, }; + int ret = 0; + + if (atomic_read(&chip->block_watch_cfg) == BLOCKED) { + t_watch_err(dev, "store time sync blocked\n"); + return __ret_val_blocked(count); + } + + mutex_lock(&ts->lock); + + t_watch_info(dev, "time sync\n"); + + //for test using echo command + if ((count == 2) && (buf[0] == EXT_WATCH_CFG_DEBUG)) { + struct timespec my_time; + struct tm my_date; + + my_time = current_kernel_time(); + time_to_tm(my_time.tv_sec, + sys_tz.tz_minuteswest * 60 * (-1), + &my_date); + + t_watch_info(dev, "get system time\n"); + + memset((char *)&cfg, 0, sizeof(cfg)); + + cfg.rtc_cwhour = my_date.tm_hour; + cfg.rtc_cwmin = my_date.tm_min; + cfg.rtc_cwsec = my_date.tm_sec; + cfg.rtc_cwmilli = my_time.tv_nsec/1000000; + } else { + memcpy((char *)&cfg, buf, sizeof(cfg)); + } + + time->rtc_sct.hour = cfg.rtc_cwhour; + time->rtc_sct.min = cfg.rtc_cwmin; + time->rtc_sct.sec = cfg.rtc_cwsec; + time->rtc_sctcnt = cfg.rtc_cwmilli; + + ret = ext_watch_set_curr_time(dev); + if (ret < 0) { + goto out; + } + + ret = ext_watch_get_curr_time(dev, NULL, NULL); + if (ret < 0) { + goto out; + } + + ret = ext_watch_display_onoff(dev); + if (ret < 0) { + goto out; + } + + ret = count; + +out: + mutex_unlock(&ts->lock); + return ret; +} + +static ssize_t show_ext_watch_query_font_data(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_query_font_data query; + ssize_t size = sizeof(struct ext_watch_query_font_data); + int watch_type = WATCH_TYPE(watch); + + query.Font_supported = SUPPORT; + + /* + * Not fixed, depends on F/W spec. + */ + switch (watch_type) { + case WATCH_TYPE_1: + query.max_font_x_size = 255; + query.max_font_y_size = 184; + query.max_cln_x_size = 255; + query.max_cln_y_size = 48; + break; + default: + query.max_font_x_size = 160; + query.max_font_y_size = 64; + query.max_cln_x_size = 160; + query.max_cln_y_size = 24; + break; + }; + + memcpy(buf, (void *)&query, size); + + return size; +} + +static ssize_t show_ext_watch_query_font_position(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct ext_watch_cfg_mode *mode = &watch->ext_wdata.mode; + struct ext_watch_cfg_position *position = &watch->ext_wdata.position; + struct ext_watch_query_font_position query; + ssize_t size = sizeof(struct ext_watch_query_font_position); + int watch_type = WATCH_TYPE(watch); + + switch (watch_type) { + case WATCH_TYPE_1: + query.vertical_position_supported = SUPPORT; + break; + default: + query.vertical_position_supported = NOT_SUPPORT; + break; + } + query.horizontal_position_supported = SUPPORT; + + memcpy(buf, (void *)&query, size); + + t_watch_info(dev, + "watch area: sx %d, ex %d, sy %d, ey %d\n", + mode->watch_area_x.watstart, + mode->watch_area_x.watend, + mode->watch_area_y.watstart, + mode->watch_area_y.watend); + + t_watch_info(dev, + "blink area: sx %d, ex %d\n", + mode->blink_area.bstartx, + mode->blink_area.bendx); + + t_watch_info(dev, + "watch pos: h10x %d, h1x %d, m10x %d, m1x %d, c1x %d\n", + position->h10x_pos, + position->h1x_pos, + position->m10x_pos, + position->m1x_pos, + position->clx_pos); + + return size; +} + +static ssize_t show_ext_watch_query_font_time(struct device *dev, char *buf) +{ + struct ext_watch_query_font_time query; + ssize_t size = sizeof(struct ext_watch_query_font_time); + + query.h24_supported = SUPPORT; + query.ampm_supported = NOT_SUPPORT; + + t_watch_info(dev, "query font time: %2d %2d\n", + query.h24_supported, query.ampm_supported); + + memcpy(buf, (void *)&query, size); + + return size; +} + +static ssize_t show_ext_watch_query_font_color(struct device *dev, char *buf) +{ + struct ext_watch_query_font_color query; + ssize_t size = sizeof(struct ext_watch_query_font_color); + + query.max_num = EXT_WATCH_LUT_NUM; + query.lut_supported = SUPPORT; + query.alpha_supported = SUPPORT; + query.gradation_supported = SUPPORT; + + memcpy(buf, (void *)&query, size); + + return size; +} + +static ssize_t show_ext_watch_query_font_effect(struct device *dev, char *buf) +{ + struct ext_watch_query_font_effect query; + ssize_t size = sizeof(struct ext_watch_query_font_effect); + + query.zero_supported = SUPPORT; + query.blink_type = 2; + + memcpy(buf, (void *)&query, size); + + return size; +} + +static ssize_t show_ext_watch_curr_time(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + int len = 0; + + if (chip == NULL) + return 0; + + ext_watch_get_curr_time(dev, buf, &len); + + return len; +} + + +static ssize_t show_ext_watch_setting(struct device *dev, char *buf) +{ + int len = 0; + + t_watch_dbg(dev, "show watch setting\n"); + + ext_watch_get_mode(dev, buf, &len); + ext_watch_get_position(dev, buf, &len); + ext_watch_get_curr_time(dev, buf, &len); + ext_watch_get_dic_st(dev, buf, &len); + + return len; +} +#endif /* __SUPPORT_WATCH_CTRL_ACCESS */ + +static ssize_t store_ext_watch_ctrl(struct device *dev, + const char *buf, size_t count) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct watch_data *watch = (struct watch_data *)chip->watch; + char command[6] = {0}; + u32 value = 0; + int ret = 0; + + if (sscanf(buf, "%5s %X", command, &value) <= 0) { + return count; + } + + if (!strcmp(command, "p")) { + watch->font_idx = value; + ret = ext_watch_fontdata_preload(dev); + if (ret >= 0) { + mod_delayed_work(ts->wq, &chip->font_download_work, + msecs_to_jiffies(20)); + } + } else if (!strcmp(command, "d")) { + mod_delayed_work(ts->wq, &chip->font_download_work, + msecs_to_jiffies(20)); + } + + return count; +} + + +#define SIW_WATCH_ATTR(_name, _show, _store) \ + TOUCH_ATTR(_name, _show, _store) + +#define _SIW_WATCH_ATTR_T(_name) \ + touch_attr_##_name + +static SIW_WATCH_ATTR(control, NULL, + store_ext_watch_ctrl); + +#if defined(__SUPPORT_WATCH_CTRL_ACCESS) +static SIW_WATCH_ATTR(rtc_onoff, NULL, store_ext_watch_rtc_onoff); +static SIW_WATCH_ATTR(block_cfg, show_ext_watch_block_cfg, + store_ext_watch_block_cfg); +static SIW_WATCH_ATTR(config_fontonoff, NULL, + store_ext_watch_font_onoff); +static SIW_WATCH_ATTR(config_fonteffect, NULL, + store_ext_watch_config_font_effect); +static SIW_WATCH_ATTR(config_fontposition, NULL, + store_ext_watch_config_font_position); +static SIW_WATCH_ATTR(config_fontproperty, NULL, + store_ext_watch_config_font_property); +static SIW_WATCH_ATTR(config_timesync, NULL, + store_ext_watch_config_time_sync); +static SIW_WATCH_ATTR(current_time, show_ext_watch_curr_time, NULL); +static SIW_WATCH_ATTR(query_fontdata, + show_ext_watch_query_font_data, NULL); +static SIW_WATCH_ATTR(query_fontposition, + show_ext_watch_query_font_position, NULL); +static SIW_WATCH_ATTR(query_timesync, + show_ext_watch_query_font_time, NULL); +static SIW_WATCH_ATTR(query_fontcolor, + show_ext_watch_query_font_color, NULL); +static SIW_WATCH_ATTR(query_fonteffect, + show_ext_watch_query_font_effect, NULL); +static SIW_WATCH_ATTR(get_cfg, show_ext_watch_setting, NULL); + +static struct attribute *ext_watch_attribute_list[] = { + &_SIW_WATCH_ATTR_T(control).attr, + &_SIW_WATCH_ATTR_T(rtc_onoff).attr, + &_SIW_WATCH_ATTR_T(block_cfg).attr, + &_SIW_WATCH_ATTR_T(config_fontonoff).attr, + &_SIW_WATCH_ATTR_T(config_fonteffect).attr, + &_SIW_WATCH_ATTR_T(config_fontproperty).attr, + &_SIW_WATCH_ATTR_T(config_fontposition).attr, + &_SIW_WATCH_ATTR_T(config_timesync).attr, + &_SIW_WATCH_ATTR_T(current_time).attr, + &_SIW_WATCH_ATTR_T(query_fontdata).attr, + &_SIW_WATCH_ATTR_T(query_fontposition).attr, + &_SIW_WATCH_ATTR_T(query_timesync).attr, + &_SIW_WATCH_ATTR_T(query_fontcolor).attr, + &_SIW_WATCH_ATTR_T(query_fonteffect).attr, + &_SIW_WATCH_ATTR_T(get_cfg).attr, + NULL, +}; +#else /* __SUPPORT_WATCH_CTRL_ACCESS */ + +static struct attribute *ext_watch_attribute_list[] = { + &_SIW_WATCH_ATTR_T(control).attr, + NULL, +}; +#endif /* __SUPPORT_WATCH_CTRL_ACCESS */ + +static const struct attribute_group ext_watch_attribute_group = { + .attrs = ext_watch_attribute_list, +}; + +static ssize_t ext_watch_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct siw_touch_chip *chip = + container_of(kobj, struct siw_touch_chip, kobj); + struct siw_touch_attribute *priv = + container_of(attr, struct siw_touch_attribute, attr); +// struct siw_ts *ts = chip->ts; + ssize_t ret = 0; + + if (priv->show) + ret = priv->show(chip->dev, buf); + + return ret; +} + +static ssize_t ext_watch_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + struct siw_touch_chip *chip = + container_of(kobj, struct siw_touch_chip, kobj); + struct siw_touch_attribute *priv = + container_of(attr, struct siw_touch_attribute, attr); +// struct siw_ts *ts = chip->ts; + ssize_t ret = 0; + + if (priv->store) + ret = priv->store(chip->dev, buf, count); + + return ret; +} + +static const struct sysfs_ops ext_watch_sysfs_ops = { + .show = ext_watch_attr_show, + .store = ext_watch_attr_store, +}; + +static struct kobj_type ext_watch_kobj_type = { + .sysfs_ops = &ext_watch_sysfs_ops, +}; + +static ssize_t ext_watch_access_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct siw_touch_chip *chip = + container_of(kobj, struct siw_touch_chip, kobj); + struct siw_ts *ts = chip->ts; + struct device *dev = chip->dev; + struct watch_data *watch = (struct watch_data *)chip->watch; + int ret = -EFAULT; + + if (off == 0) { + if (atomic_read(&ts->state.sleep) == IC_DEEP_SLEEP) { + t_watch_info(dev, "IC deep sleep. cannot read\n"); + } else { + while (1) { + if (atomic_read(&watch->state.font_status) != FONT_DOWNLOADING) { + break; + } + + t_watch_info(dev, "font downloading\n"); + touch_msleep(10); + } + ext_watch_font_dump(dev, watch->ext_wdata.font_data); + } + } + + t_watch_info(dev, "font(r): %Xh, %Xh\n", (int)off, (int)count); + + if ((off + count) > watch->font_max_size) { + t_watch_err(dev, "access_read: size overflow : offset[%d] size[%d]\n", + (int)off, (int)count); + ret = -EOVERFLOW; + } else { + memcpy(buf, &watch->ext_wdata.font_data[off], count); + ret = count; + } + + return (ssize_t)ret; +} + +static ssize_t ext_watch_access_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct siw_touch_chip *chip = + container_of(kobj, struct siw_touch_chip, kobj); + struct siw_ts *ts = chip->ts; + struct device *dev = chip->dev; + struct watch_data *watch = (struct watch_data *)chip->watch; + int ret = -EFAULT; + + if (atomic_read(&chip->block_watch_cfg) == BLOCKED) { + t_watch_warn(dev, "access_write: blocked\n"); + return count; + } + + if (buf == NULL) { + t_watch_err(dev, "access_write: NULL buf\n"); + goto out; + } + + if (watch->ext_wdata.font_data == NULL) { + t_watch_err(dev, "access_write: NULL font_data\n"); + goto out; + } + + if ((off + count) > watch->font_max_size) { + t_watch_err(dev, "access_write: size overflow : offset[%d] size[%d]\n", + (int)off, (int)count); + atomic_set(&watch->state.font_status, FONT_EMPTY); + ret = -EOVERFLOW; + goto out; + } + + t_watch_info(dev, "font(w): %Xh, %Xh\n", (int)off, (int)count); + + watch->font_written_size = off + count; + + memcpy(&watch->ext_wdata.font_data[off], buf, count); + + atomic_set(&watch->state.font_status, FONT_DOWNLOADING); + + mod_delayed_work(ts->wq, &chip->font_download_work, + msecs_to_jiffies(20)); + + ret = count; + + out: + return ret; +} + +static int ext_watch_fontdata_attr_init(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); +// struct siw_ts *ts = chip->ts; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct bin_attribute *fontdata_attr = &watch->fontdata_attr; + u8 *buf; + int size = watch->font_max_size; + int ret = 0; + + watch->font_written_size = 0; + buf = touch_kzalloc(dev, size, GFP_KERNEL); + if (!buf) { + t_watch_err(dev, "failed to allocate font buffer[%Xh]\n", size); + ret = -ENOMEM; + goto out; + } + watch->ext_wdata.font_data = buf; + + t_dev_dbg_base(dev, "font buffer[%Xh] allocated\n", size); + + sysfs_bin_attr_init(fontdata_attr); + fontdata_attr->attr.name = "config_fontdata"; + fontdata_attr->attr.mode = S_IWUSR | S_IRUSR; + fontdata_attr->read = ext_watch_access_read; + fontdata_attr->write = ext_watch_access_write; + fontdata_attr->size = size; + + ret = sysfs_create_bin_file(&chip->kobj, fontdata_attr); + if (ret < 0) { + t_watch_err(dev, "failed to create %s, %d\n", + fontdata_attr->attr.name, ret); + goto out_bin; + } + + t_dev_dbg_base(dev, "file[%s] file created\n", + fontdata_attr->attr.name); + + return 0; + +out_bin: + touch_kfree(dev, buf); + +out: + + return ret; +} + +static void ext_watch_fontdata_attr_free(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct watch_data *watch = (struct watch_data *)chip->watch; + struct bin_attribute *fontdata_attr = &watch->fontdata_attr; + int size = (int)fontdata_attr->size; + + t_dev_dbg_base(dev, "file[%s] file released\n", + fontdata_attr->attr.name); + + sysfs_remove_bin_file(&chip->kobj, fontdata_attr); + + t_dev_dbg_base(dev, "font buffer[%Xh] released\n", size); + + if (watch->ext_wdata.font_data) { + touch_kfree(dev, watch->ext_wdata.font_data); + watch->ext_wdata.font_data = NULL; + } + watch->font_written_size = 0; +} + +static int ext_watch_init(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct watch_data *watch = (struct watch_data *)chip->watch; + int ret = 0; + + if (ext_quirk_check(dev)) { + return 0; + } + + if (siw_touch_boot_mode_check(dev) >= MINIOS_MFTS_FOLDER){ + return -EBUSY; + } + + if (!watch) { + t_watch_err(dev, "NULL watch\n"); + return -ENOMEM; + } + + if (atomic_read(&watch->state.font_status) != FONT_READY){ + return -EBUSY; + } + + t_watch_info(dev, "init\n"); + + ret = ext_watch_set_cfg(dev, 0); + if (ret < 0) { + t_watch_err(dev, "failed to set watch cfg, %d\n", ret); + goto out; + } + + ret = ext_watch_display_onoff(dev); + if (ret < 0) { + t_watch_err(dev, "failed to set watch display control, %d\n", ret); + goto out; + } + +out: + return ret; +} + +struct siw_watch_type_table { + int chip_type; + int watch_type; + int flag; + /* */ + struct reset_area *win_fixed; /* fixed 2nd screen window */ + /* */ + int font_type; + int font_max_size; + int font_crc_offset; + u32 font_magic_code; + /* */ + u32 dcst_offset; + u32 dcst_data; +}; + +#define WATCH_FLAG_SKIP_FOR_LG4895 (0 | \ + WATCH_FLAG_SKIP_GET_MODE | \ + WATCH_FLAG_SKIP_SET_MODE | \ + WATCH_FLAG_SKIP_LUT_UPDATE | \ + 0) + +#define WATCH_FLAG_SKIP_FOR_SW49407 (0 | \ + WATCH_FLAG_SKIP_MEM_CTRL | \ + 0) + +static const struct reset_area watch_win_fixed_lg4895 = { + .x1 = 200, + .y1 = 0, + .x2 = 720, + .y2 = 80, +}; + +static const struct reset_area watch_win_fixed_sw49407 = { + .x1 = 400, + .y1 = 0, + .x2 = 1440, + .y2 = 160, +}; + +static const struct siw_watch_type_table watch_type_tables[] = { + /* WATCH_TYPE_0 group */ + { .chip_type = CHIP_LG4895, + .watch_type = WATCH_TYPE_0, + .flag = WATCH_FLAG_SKIP_FOR_LG4895, + .win_fixed = (struct reset_area *)&watch_win_fixed_lg4895, + .font_type = FONT_TYPE_0, + .font_max_size = SIW_MAX_FONT_SIZE, + .font_crc_offset = 4, + .font_magic_code = SIW_FONT_MAGIC_CODE, + }, + { .chip_type = CHIP_SW49407, + .watch_type = (WATCH_TYPE_0 | (1<<16)), + .flag = WATCH_FLAG_SKIP_FOR_SW49407, + .win_fixed = (struct reset_area *)&watch_win_fixed_sw49407, + .font_type = FONT_TYPE_1, + .font_max_size = ((45<<10) + 962), //45.94KB + .font_crc_offset = 16, + .font_magic_code = SIW_FONT_MAGIC_CODE, + .dcst_offset = 0x085, //dcst_reg_oft + .dcst_data = 0xFDB, //dcst_reg_data + }, + /* WATCH_TYPE_1 group */ + { .chip_type = CHIP_LG4946, + .watch_type = WATCH_TYPE_1, + .flag = 0, + .font_type = FONT_TYPE_1, + .font_max_size = (87<<10), + .font_crc_offset = 12, + .font_magic_code = SIW_FONT_MAGIC_CODE, + }, + { .chip_type = CHIP_SW49406, + .watch_type = WATCH_TYPE_1, + .flag = 0, + .font_type = FONT_TYPE_1, + .font_max_size = (87<<10), + .font_crc_offset = 12, + .font_magic_code = SIW_FONT_MAGIC_CODE, + }, +#if 0 + { .chip_type = CHIP_SW49408, + .watch_type = (WATCH_TYPE_1 | (1<<16)), + .flag = 0, + .font_type = FONT_TYPE_1, + .font_max_size = (87<<10), + .font_crc_offset = 16, + .font_magic_code = SIW_FONT_MAGIC_CODE, + }, +#endif + /* */ + { CHIP_NONE, 0, }, //End mark +}; + +static int ext_watch_scan_type(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct watch_data *watch = (struct watch_data *)chip->watch; + struct siw_watch_type_table *table = (struct siw_watch_type_table *)watch_type_tables; + + while (1) { + if (table->chip_type == CHIP_NONE) { + break; + } + + if (touch_chip_type(ts) == table->chip_type) { + watch->type = table->watch_type; + watch->flag = table->flag; + + watch->win_fixed = table->win_fixed; + + watch->font_type = table->font_type; + watch->font_max_size = table->font_max_size; + watch->font_crc_offset = table->font_crc_offset; + watch->font_magic_code = table->font_magic_code; + + watch->dcst_offset = table->dcst_offset; + watch->dcst_data = table->dcst_data; + + t_dev_dbg_base(dev, "[%s] watch table: %d(%d), %Xh, %d, %Xh, %Xh\n", + touch_chip_name(ts), + WATCH_TYPE(watch), WATCH_TYPE_VAR(watch), + watch->flag, + watch->font_type, + watch->font_max_size, + watch->font_magic_code); + + return 0; + } + + table++; + } + + watch->font_max_size = SIW_MAX_FONT_SIZE; + watch->flag = WATCH_FLAG_SKIP_FOR_DEFAULT; + watch->type = WATCH_TYPE_0; + watch->font_type = FONT_TYPE_0; + watch->font_max_size = (1<<10); + watch->font_magic_code = SIW_FONT_MAGIC_CODE; + + t_watch_err(dev, + "%s watch table not detected\n", + touch_chip_name(ts)); + + return -EFAULT; +// return 0; +} + +static int ext_watch_create_sysfs(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + struct device *idev = &ts->input->dev; + //Caution : not ts->kobj + struct kobject *kobj = &chip->kobj; + struct kobject *parent; + struct watch_data *watch; + char *name = NULL; + int ret = 0; + + watch = touch_kzalloc(dev, sizeof(*watch), GFP_KERNEL); + if (!watch) { + t_dev_err(dev, "failed to allocate memory for watch data\n"); + ret = -ENOMEM; + goto out; + } + chip->watch = watch; + t_dev_dbg_base(dev, "watch_data allocated\n"); + + ret = ext_watch_scan_type(dev); + if (ret < 0) { + goto out_scan; + } + + name = touch_ext_watch_name(ts); + + if (touch_flags(ts) & TOUCH_USE_VIRT_DIR_WATCH) { + /* + * /sys/devices/virtual/ + */ + parent = idev->kobj.parent->parent; + } else { + /* + * /sys/devices/virtual/input/ + */ + parent = idev->kobj.parent; + } + ret = kobject_init_and_add(kobj, &ext_watch_kobj_type, + parent, "%s", name); + if (ret < 0) { + t_watch_err(dev, "failed to create sysfs kobj\n"); + goto out_kobj; + } + + ret = sysfs_create_group(kobj, &ext_watch_attribute_group); + if (ret < 0) { + t_watch_err(dev, "failed to create sysfs group\n"); + goto out_sys; + } + + ret = ext_watch_fontdata_attr_init(dev); + if (ret < 0) { + t_watch_err(dev, "failed to create fontdata attr\n"); + goto out_attr; + } + + INIT_DELAYED_WORK(&chip->font_download_work, ext_watch_font_download); + + ret = ext_watch_fontdata_check(dev); + if (ret < 0) { + goto out_font; + } + + ret = ext_watch_fontdata_preload(dev); + if (ret < 0) { + /* Just skip */ + // goto out_font; + } + + t_dev_dbg_base(dev, "%s watch sysfs registered\n", + touch_chip_name(ts)); + + return 0; + +out_font: + ext_watch_fontdata_attr_free(dev); + +out_attr: + sysfs_remove_group(kobj, &ext_watch_attribute_group); + +out_sys: + kobject_del(kobj); + +out_kobj: + +out_scan: + touch_kfree(dev, watch); + chip->watch = NULL; + +out: + + return ret; +} + +static void ext_watch_remove_sysfs(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; +// struct device *idev = &ts->input->dev; + //Caution : not ts->kobj + struct kobject *kobj = &chip->kobj; + + if (chip->watch == NULL) { + return; + } + + cancel_delayed_work(&chip->font_download_work); + + ext_watch_fontdata_attr_free(dev); + + sysfs_remove_group(kobj, &ext_watch_attribute_group); + + kobject_del(kobj); + + touch_kfree(dev, chip->watch); + chip->watch = NULL; + + t_dev_dbg_base(dev, "%s watch sysfs unregistered\n", + touch_chip_name(ts)); +} + +int siw_hal_watch_sysfs(struct device *dev, int on_off) +{ + if (ext_quirk_check(dev)) { + return 0; + } + + if (on_off == DRIVER_INIT) { + return ext_watch_create_sysfs(dev); + } + + ext_watch_remove_sysfs(dev); + return 0; +} + +int siw_hal_watch_init(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + + if (ext_quirk_check(dev)) { + return 0; + } + + if (chip->watch == NULL) { + t_watch_err(dev, "NULL watch_data\n"); + return -ENOMEM; + } + + return ext_watch_init(dev); +} + +int siw_hal_watch_get_curr_time(struct device *dev, char *buf, int *len) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + + if (ext_quirk_check(dev)) { + return 0; + } + + if (chip->watch == NULL) { + t_watch_err(dev, "NULL watch_data\n"); + return -ENOMEM; + } + + return ext_watch_get_curr_time(dev, buf, len); +} + +void siw_hal_watch_display_off(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + + if (ext_quirk_check(dev)) { + return; + } + + if (chip->watch == NULL) { + t_watch_err(dev, "NULL watch_data\n"); + return; + } + + ext_watch_display_off(dev); +} + +int siw_hal_watch_chk_font_status(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + + if (ext_quirk_check(dev)) { + return 0; + } + + if (chip->watch == NULL) { + t_watch_err(dev, "NULL watch_data\n"); + return -ENOMEM; + } + + ext_watch_init(dev); + + return ext_watch_chk_font_status(dev); +} + +int siw_hal_watch_is_disp_waton(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct watch_data *watch = (struct watch_data *)chip->watch; + + return (!watch) ? 0 : + watch->ext_wdata.time.disp_waton; +} + +int siw_hal_watch_is_rtc_run(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct watch_data *watch = (struct watch_data *)chip->watch; + + return (!watch) ? 0 : + (atomic_read(&watch->state.rtc_status) == RTC_RUN); +} + +void siw_hal_watch_set_rtc_run(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct watch_data *watch = (struct watch_data *)chip->watch; + + if (watch) + atomic_set(&watch->state.rtc_status, RTC_RUN); +} + +void siw_hal_watch_set_rtc_clear(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct watch_data *watch = (struct watch_data *)chip->watch; + + if (watch) + atomic_set(&watch->state.rtc_status, RTC_CLEAR); +} + +void siw_hal_watch_rtc_on(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct watch_data *watch = (struct watch_data *)chip->watch; + int watch_type; + int rtc_on = 0; + + if (watch == NULL) + return; + + watch_type = WATCH_TYPE(watch); + + switch (watch_type) { + case WATCH_TYPE_1: + rtc_on = 1; + break; + default: + break; + } + + if (rtc_on) { + if (atomic_read(&watch->state.rtc_status) == RTC_CLEAR) { + ext_watch_rtc_start(dev, EXT_WATCH_RTC_START); + } + } +} + +void siw_hal_watch_set_font_empty(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct watch_data *watch = (struct watch_data *)chip->watch; + + if (watch) { + atomic_set(&watch->state.font_status, FONT_EMPTY); + } +} + +void siw_hal_watch_set_cfg_blocked(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + + atomic_set(&chip->block_watch_cfg, BLOCKED); +} + +#endif /* __SIW_SUPPORT_WATCH */ + + diff --git a/drivers/input/touchscreen/siw/siw_touch_irq.c b/drivers/input/touchscreen/siw/siw_touch_irq.c new file mode 100644 index 000000000000..96b34195784a --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_irq.c @@ -0,0 +1,310 @@ +/* + * siw_touch_irq.c - SiW touch interrupt driver + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siw_touch.h" +#include "siw_touch_irq.h" + + +static void siw_touch_irq_pending_onoff(struct device *dev, + unsigned int irq, int onoff) +{ + struct irq_desc *desc = irq_to_desc(irq); + unsigned long flags; + + if (!desc) { + return; + } + + raw_spin_lock_irqsave(&desc->lock, flags); + if (onoff) { + desc->istate |= IRQS_PENDING; + t_dev_info(dev, "set IRQS_PENDING(%d)\n", irq); + } else { + if (desc->istate & IRQS_PENDING) { + t_dev_info(dev, "clr IRQS_PENDING(%d)\n", irq); + } + desc->istate &= ~IRQS_PENDING; + } + raw_spin_unlock_irqrestore(&desc->lock, flags); +} + +static void siw_touch_set_irq_pending(struct device *dev, unsigned int irq) +{ + siw_touch_irq_pending_onoff(dev, irq, 1); +} + +static void siw_touch_clr_irq_pending(struct device *dev, unsigned int irq) +{ + siw_touch_irq_pending_onoff(dev, irq, 0); +} + + +void siw_touch_enable_irq_wake(struct device *dev, + unsigned int irq) +{ + struct siw_ts *ts = to_touch_core(dev); + + if (!ts->role.use_lpwg) + return; + + enable_irq_wake(irq); + t_dev_info(dev, "irq(%d) wake enabled\n", irq); +} + +void siw_touch_disable_irq_wake(struct device *dev, + unsigned int irq) +{ + struct siw_ts *ts = to_touch_core(dev); + + if (!ts->role.use_lpwg) + return; + + disable_irq_wake(irq); + t_dev_info(dev, "irq(%d) wake disabled\n", irq); +} + +void siw_touch_enable_irq(struct device *dev, unsigned int irq) +{ + siw_touch_clr_irq_pending(dev, irq); + enable_irq(irq); + t_dev_info(dev, "irq(%d) enabled\n", irq); +} + +void siw_touch_disable_irq(struct device *dev, unsigned int irq) +{ + disable_irq_nosync(irq); /* No waiting */ + t_dev_info(dev, "irq(%d) disabled\n", irq); +} + +#if 1 +void siw_touch_resume_irq(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + int irq = ts->irq; + + t_dev_info(dev, "resume irq(%d)\n", irq); + +#if 1 +// mutex_lock(&ts->lock); + + disable_irq(irq); + siw_touch_set_irq_pending(dev, irq); + enable_irq(irq); + +// mutex_unlock(&ts->lock); +#else + { + struct irq_desc *desc = irq_to_desc(irq); + if (desc) { + siw_touch_set_irq_pending(dev, rq); + check_irq_resend(desc, irq); + } + } +#endif +} +#else +void siw_touch_resume_irq(struct device *dev) +{ + t_dev_dbg_irq(dev, "nop...\n"); +} +#endif + +void siw_touch_irq_control(struct device *dev, int on_off) +{ + struct siw_ts *ts = to_touch_core(dev); + + t_dev_dbg_irq(dev, "touch_irq_control(%d)\n", on_off); + + if (on_off == INTERRUPT_ENABLE) { + if (atomic_cmpxchg(&ts->state.irq_enable, 0, 1)) { + t_dev_warn(dev, "(warn) already irq enabled\n"); + return; + } + + siw_touch_enable_irq(dev, ts->irq); + + siw_touch_enable_irq_wake(dev, ts->irq); + + return; + } + + if (!atomic_cmpxchg(&ts->state.irq_enable, 1, 0)) { + t_dev_warn(dev, "(warn) already irq disabled\n"); + return; + } + + siw_touch_disable_irq_wake(dev, ts->irq); + + siw_touch_disable_irq(dev, ts->irq); +} + +#if defined(__SIW_CONFIG_OF) +static void siw_touch_irq_work_func(struct work_struct *work) +{ + struct siw_ts *ts = + container_of(work, struct siw_ts, work_irq.work); + struct device *dev = ts->dev; + + t_dev_dbg_irq(dev, "irq_work_func\n"); + + if (ts->thread_fn) + ts->thread_fn(ts->irq, ts); +} + +static irqreturn_t siw_touch_work_irq_handler(int irq, void *dev_id) +{ + struct siw_ts *ts = (struct siw_ts *)dev_id; + struct device *dev = ts->dev; + + t_dev_dbg_irq(dev, "work_irq_handler\n"); + + if (ts->handler_fn(ts->irq, ts) == IRQ_WAKE_THREAD) + queue_delayed_work(ts->wq, &ts->work_irq, 0); + + return IRQ_HANDLED; +} +#endif + +static int siw_touch_request_irq_queue_work(struct siw_ts *ts, + irq_handler_t handler_fn, + irq_handler_t thread_fn, + unsigned long flags, + const char *name) +{ +#if defined(__SIW_CONFIG_OF) + struct device *dev = ts->dev; + struct device_node *node = NULL; + u32 ints[2] = { 0, 0 }; + int ret = 0; + + ts->handler_fn = handler_fn; + ts->thread_fn = thread_fn; + INIT_DELAYED_WORK(&ts->work_irq, siw_touch_irq_work_func); + + //AP specific scheme + node = of_find_compatible_node(NULL, NULL, "vendor,vendor-touch"); + if (!node) { + t_dev_err(dev, "request_irq can not find touch eint device node!.\n"); + ret = -ENXIO; + goto out; + } + + ret = of_property_read_u32_array(node, "debounce", ints, ARRAY_SIZE(ints)); + if (ret < 0) { + t_dev_err(dev, "failed to parse GPIO defaults: %d\n", ret); + goto out; + } + gpio_set_debounce(ints[0], ints[1]); + + /* + * Default flags = 0x2002 : IRQF_ONESHOT | IRQF_TRIGGER_FALLING + */ + ts->irq = irq_of_parse_and_map(node, 0); + ret = request_irq(ts->irq, siw_touch_work_irq_handler, + touch_irqflags(ts), name, (void *)ts); + if (ret) { + t_dev_err(dev, "request_irq IRQ LINE NOT AVAILABLE!, %d\n", ret); + goto out; + } + + t_dev_info(dev, "irq:%d, debounce:%d-%d:\n", ts->irq, ints[0], ints[1]); + +out: + return ret; +#else + return -1; +#endif +} + +int siw_touch_request_irq(struct siw_ts *ts, + irq_handler_t handler, + irq_handler_t thread_fn, + unsigned long flags, + const char *name) +{ + struct device *dev = ts->dev; + u32 irq_use_scheule_work = touch_flags(ts) & IRQ_USE_SCHEDULE_WORK; + int ret = 0; + + if (!ts->irq) { + t_dev_err(dev, "failed to request irq : zero irq\n"); + ret = -EFAULT; + goto out; + } + + ts->irqflags_curr = flags; + + if (irq_use_scheule_work) { + ret = siw_touch_request_irq_queue_work(ts, + handler, thread_fn, + flags, name); + } else { + ret = request_threaded_irq(ts->irq, handler, thread_fn, flags, name, (void *)ts); + } + if (ret) { + t_dev_err(dev, "failed to request irq(%d, %s, 0x%X), %d\n", + ts->irq, name, (u32)flags, ret); + goto out; + } + + t_dev_info(dev, "%s irq request done(%d, %s, 0x%X)\n", + (irq_use_scheule_work) ? \ + "queue work" : "threaded", + ts->irq, name, (u32)flags); + +out: + return ret; +} + +void siw_touch_free_irq(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + + if (ts->irq) { + free_irq(ts->irq, (void *)ts); + t_dev_info(dev, "irq(%d) release done\n", ts->irq); + ts->irq = 0; + return; + } + +// t_dev_warn(dev, "(warn) already free_irq done\n"); +} + + diff --git a/drivers/input/touchscreen/siw/siw_touch_irq.h b/drivers/input/touchscreen/siw/siw_touch_irq.h new file mode 100644 index 000000000000..2cfe7439c8c7 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_irq.h @@ -0,0 +1,43 @@ +/* + * SiW touch interrupt driver + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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_IRQ_H +#define __SIW_TOUCH_IRQ_H + +#include +#include +#include + +/* for irq_desc */ +#define istate core_internal_state__do_not_mess_with_it +#ifndef IRQS_PENDING +#define IRQS_PENDING 0x00000200 +#endif + +//struct siw_ts; + +extern void siw_touch_enable_irq(struct device *dev, unsigned int irq); +extern void siw_touch_disable_irq(struct device *dev, unsigned int irq); + +extern void siw_touch_resume_irq(struct device *dev); + +extern void siw_touch_irq_control(struct device *dev, int on); + +extern int siw_touch_request_irq(struct siw_ts *ts, + irq_handler_t handler, + irq_handler_t thread_fn, + unsigned long flags, + const char *name); +extern void siw_touch_free_irq(struct siw_ts *ts); + + +#endif /* __SIW_TOUCH_IRQ_H */ diff --git a/drivers/input/touchscreen/siw/siw_touch_misc.c b/drivers/input/touchscreen/siw/siw_touch_misc.c new file mode 100644 index 000000000000..d414c24b9cf5 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_misc.c @@ -0,0 +1,329 @@ +/* + * siw_touch_misc.c - SiW touch misc driver + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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 "siw_touch_cfg.h" + +#if defined(__SIW_SUPPORT_MISC) //See siw_touch_cfg.h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "siw_touch.h" +#include "siw_touch_hal.h" +#include "siw_touch_bus.h" +#include "siw_touch_event.h" +#include "siw_touch_gpio.h" +#include "siw_touch_irq.h" +#include "siw_touch_sys.h" + +/* + * Helper layer to support direct access via device node + * This misc layer makes a dedicated device node(/dev/{misc name}) for touch device + * and user app can access the bus regardless the interface type. + * + */ + +#define SIW_MISC_NAME "siw_touch_misc" + +enum { + SIW_MISC_BUF_SZ = (4<<10), + /* */ + SIW_MISC_NAME_SZ = 128, +}; + +struct siw_misc_data { + struct miscdevice misc; + struct device *dev; + /* */ + char name[SIW_MISC_NAME_SZ]; +// spinlock_t bus_lock; + int users; + u8* buf; +}; + +struct siw_misc_data *__siw_misc_data = NULL; + +DEFINE_MUTEX(siw_misc_lock); + +static ssize_t siw_misc_read(struct file *filp, + char __user *buf, + size_t count, loff_t *f_pos) +{ + struct siw_misc_data *misc_data = __siw_misc_data; + struct device *dev = NULL; + u32 addr; + ssize_t result = 0; + ssize_t ret = -EFAULT; + + if (count > SIW_MISC_BUF_SZ) { + return -EMSGSIZE; + } + + mutex_lock(&siw_misc_lock); + + dev = misc_data->dev; + + /* + * buf[0 ~ 3] : addr + */ + ret = copy_from_user(&addr, buf, 4); + if (ret) { + t_dev_err(dev, "rd: can't get addr, %d\n", (int)ret); + goto out; + } + + ret = siw_hal_reg_read(dev, addr, misc_data->buf, count); + if (ret < 0) { + goto out; + } + + result = copy_to_user((void __user *)buf, misc_data->buf, count); + if (result) { + t_dev_err(dev, "rd: can't copy buf(%d), %d\n", + (int)count, (int)result); + ret = result; + goto out; + } + +out: + + mutex_unlock(&siw_misc_lock); + + return ret; +} + +enum { + MISC_WR_DATA_OFFSET = 4, +}; + +static ssize_t siw_misc_write(struct file *filp, + const char __user *buf, + size_t count, loff_t *f_pos) +{ + struct siw_misc_data *misc_data = __siw_misc_data; + struct device *dev = NULL; + u32 addr; + ssize_t ret = -EFAULT; + + if (count > SIW_MISC_BUF_SZ) { + return -EMSGSIZE; + } + + if (count <= MISC_WR_DATA_OFFSET) { + return -EINVAL; + } + + mutex_lock(&siw_misc_lock); + + dev = misc_data->dev; + + /* + * buf[0 ~ 3] : addr + * buf[3 ~ (count - 1)] : data + */ + ret = copy_from_user(misc_data->buf, buf, count); + if (ret) { + t_dev_err(dev, "wr: can't copy buf(%d), %d\n", + (int)count, (int)ret); + goto out; + } + memcpy(&addr, misc_data->buf, MISC_WR_DATA_OFFSET); + + ret = siw_hal_reg_write(dev, addr, + &misc_data->buf[MISC_WR_DATA_OFFSET], + count - MISC_WR_DATA_OFFSET); + if (ret < 0) { + goto out; + } + +out: + mutex_unlock(&siw_misc_lock); + + return ret; +} + +static int siw_misc_open(struct inode *inode, struct file *filp) +{ + struct siw_misc_data *misc_data = __siw_misc_data; + struct device *dev = NULL; + int ret = 0; + + if (misc_data == NULL) { + return -ENOENT; + } + + mutex_lock(&siw_misc_lock); + + dev = misc_data->dev; + + if (!misc_data->users) { + if (!misc_data->buf) { + misc_data->buf = kmalloc(SIW_MISC_BUF_SZ, GFP_KERNEL); + if (!misc_data->buf) { + t_dev_err(dev, "open ENOMEM\n"); + ret = -ENOMEM; + goto out; + } + } + + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + + siw_touch_mon_pause(dev); + } + + misc_data->users++; + +out: + mutex_unlock(&siw_misc_lock); + return ret; +} + +static int siw_misc_release(struct inode *inode, struct file *filp) +{ + struct siw_misc_data *misc_data = __siw_misc_data; + struct device *dev; + int ret = 0; + + if (misc_data == NULL) { + return -ENOENT; + } + + mutex_lock(&siw_misc_lock); + + dev = misc_data->dev; + + if (!misc_data->users) { + goto out; + } + + misc_data->users--; + if (!misc_data->users) { + if (misc_data->buf) { + kfree(misc_data->buf); + misc_data->buf = NULL; + } + + siw_touch_mon_resume(dev); + + siw_touch_irq_control(dev, INTERRUPT_ENABLE); + } + +out: + mutex_unlock(&siw_misc_lock); + + return ret; +} + +static const struct file_operations siw_misc_fops = { + .owner = THIS_MODULE, + .read = siw_misc_read, + .write = siw_misc_write, +// .unlocked_ioctl = siw_misc_ioctl, + .open = siw_misc_open, + .release = siw_misc_release, + .llseek = no_llseek, +}; + +int siw_touch_misc_init(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + struct siw_misc_data *misc_data = NULL; + struct miscdevice *misc = NULL; + char *name; + int ret = 0; + + misc_data = kzalloc(sizeof(*misc_data), GFP_KERNEL); + if (misc_data == NULL) { + t_dev_err(dev, "can't alloacte misc data\n"); + ret = -ENOMEM; + goto out; + } + + misc = &misc_data->misc; + + name = touch_drv_name(ts); + if (!name) { + name = SIW_MISC_NAME; + } + snprintf(misc_data->name, SIW_MISC_NAME_SZ, + "%s", name); + + misc->minor = MISC_DYNAMIC_MINOR; + misc->name = misc_data->name; + misc->fops = &siw_misc_fops; + + /* register misc device */ + ret = misc_register(misc); + if (ret < 0) { + t_dev_err(dev, "siw misc_register failed\n"); + goto out_register; + } + + misc_data->dev = dev; + +// spin_lock_init(&misc_data->bus_lock); + + __siw_misc_data = misc_data; + + t_dev_info(dev, "siw misc register done (%d)\n", misc->minor); + + return 0; + +out_register: + kfree(misc_data); + +out: + + return ret; +} + +void siw_touch_misc_free(struct device *dev) +{ + struct siw_misc_data *misc_data = __siw_misc_data; + + if (misc_data) { + misc_deregister(&misc_data->misc); + + kfree(misc_data); + } + + __siw_misc_data = NULL; +} + +#endif /* __SIW_SUPPORT_MISC */ + diff --git a/drivers/input/touchscreen/siw/siw_touch_notify.c b/drivers/input/touchscreen/siw/siw_touch_notify.c new file mode 100644 index 000000000000..edc699640c07 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_notify.c @@ -0,0 +1,304 @@ +/* + * siw_touch_notify.c - SiW touch notify driver + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "siw_touch.h" +#include "siw_touch_bus.h" +#include "siw_touch_irq.h" +#include "siw_touch_sys.h" + + +static BLOCKING_NOTIFIER_HEAD(siw_touch_blocking_notifier); +static ATOMIC_NOTIFIER_HEAD(siw_touch_atomic_notifier); + +int siw_touch_blocking_notifier_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&siw_touch_blocking_notifier, nb); +} +EXPORT_SYMBOL(siw_touch_blocking_notifier_register); + +int siw_touch_blocking_notifier_unregister(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&siw_touch_blocking_notifier, nb); +} +EXPORT_SYMBOL(siw_touch_blocking_notifier_unregister); + +int siw_touch_blocking_notifier_call(unsigned long val, void *v) +{ + return blocking_notifier_call_chain(&siw_touch_blocking_notifier, val, v); +} +EXPORT_SYMBOL_GPL(siw_touch_blocking_notifier_call); + +int siw_touch_atomic_notifier_register(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&siw_touch_atomic_notifier, nb); +} +EXPORT_SYMBOL(siw_touch_atomic_notifier_register); + +int siw_touch_atomic_notifier_unregister(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&siw_touch_atomic_notifier, nb); +} +EXPORT_SYMBOL(siw_touch_atomic_notifier_unregister); + +int siw_touch_atomic_notifier_call(unsigned long val, void *v) +{ + return atomic_notifier_call_chain(&siw_touch_atomic_notifier, val, v); +} +EXPORT_SYMBOL_GPL(siw_touch_atomic_notifier_call); + +int siw_touch_register_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&siw_touch_blocking_notifier, nb); +} +EXPORT_SYMBOL(siw_touch_register_client); + +int siw_touch_unregister_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&siw_touch_blocking_notifier, nb); +} +EXPORT_SYMBOL(siw_touch_unregister_client); + +int siw_touch_notifier_call_chain(unsigned long val, void *v) +{ + return blocking_notifier_call_chain(&siw_touch_blocking_notifier, val, v); +} +EXPORT_SYMBOL_GPL(siw_touch_notifier_call_chain); + +void siw_touch_notify_connect(u32 type) +{ + siw_touch_atomic_notifier_call(NOTIFY_CONNECTION, &type); +} +EXPORT_SYMBOL(siw_touch_notify_connect); + +void siw_touch_notify_wireless(u32 type) +{ + siw_touch_atomic_notifier_call(NOTIFY_WIRELEES, &type); +} +EXPORT_SYMBOL(siw_touch_notify_wireless); + +void siw_touch_notify_earjack(u32 type) +{ + siw_touch_atomic_notifier_call(NOTIFY_EARJACK, &type); +} +EXPORT_SYMBOL(siw_touch_notify_earjack); + +static int _siw_touch_do_notify(struct siw_ts *ts, + unsigned long event, void *data) +{ +// struct siw_touch_pdata *pdata = ts->pdata; + struct device *dev = ts->dev; + char *noti_str = "(unknown)"; + int call_hal_notify = 1; + u32 value = 0; + int ret = 0; + + if (data) { + value = *((int *)data); + } + + switch (event) { + case LCD_EVENT_TOUCH_INIT_LATE: + ret = siw_touch_init_late(ts, value); + + call_hal_notify = 0; + noti_str = "INIT_LATE"; + goto out; + } + + if (touch_get_dev_data(ts) == NULL) { + return 0; + } + + if (siw_ops_is_null(ts, notify)) { + return 0; + } + + switch (event) { + case NOTIFY_TOUCH_RESET: + ret = siw_ops_notify(ts, event, data); + if (ret != NOTIFY_STOP) { + siw_touch_irq_control(dev, INTERRUPT_DISABLE); + } + break; + + case NOTIFY_CONNECTION: + if (atomic_read(&ts->state.connect) != value) { + atomic_set(&ts->state.connect, value); + ret = siw_ops_notify(ts, event, data); + + #if defined(__SIW_SUPPORT_ASC) + if (ts->asc.use_asc == ASC_ON) { + siw_touch_qd_toggle_delta_work_jiffies(ts, 0); + } + #endif /* __SIW_SUPPORT_ASC */ + } + break; + + case NOTIFY_WIRELEES: + atomic_set(&ts->state.wireless, value); + ret = siw_ops_notify(ts, event, data); + + #if defined(__SIW_SUPPORT_ASC) + if (ts->asc.use_asc == ASC_ON) { + siw_touch_qd_toggle_delta_work_jiffies(ts, 0); + } + #endif /* __SIW_SUPPORT_ASC */ + + break; + + case NOTIFY_FB: + atomic_set(&ts->state.fb, value); + siw_touch_qd_fb_work_now(ts); + + call_hal_notify = 0; + noti_str = "FB"; + break; + + case NOTIFY_EARJACK: + atomic_set(&ts->state.earjack, value); + ret = siw_ops_notify(ts, event, data); + break; + + default: + ret = siw_ops_notify(ts, event, data); + break; + } + +out: + if (!call_hal_notify) { + t_dev_info(dev, "notify: event %s(%Xh), value %Xh\n", + noti_str, (int)event, value); + siwmon_submit_evt(dev, "NOTIFY", 0, noti_str, event, value, ret); + } + + return ret; +} + +int siw_touch_notify(struct siw_ts *ts, unsigned long event, void *data) +{ + int noti_allowed = 0; + int core_state = atomic_read(&ts->state.core); + int ret = 0; + + switch (event) { + case LCD_EVENT_TOUCH_INIT_LATE: + noti_allowed = !!(core_state == CORE_PROBE); + break; + default: + if (ts->is_charger) { + return 0; + } + + noti_allowed = !!(core_state == CORE_NORMAL); + break; + } + + if (!noti_allowed) { + t_dev_dbg_base(ts->dev, + "notify: event(%Xh) is skipped: core %d\n", + (int)event, core_state); + return 0; + } + + mutex_lock(&ts->lock); + ret = _siw_touch_do_notify(ts, event, data); + mutex_unlock(&ts->lock); + + return ret; +} + +static int siw_touch_blocking_notifier_callback(struct notifier_block *this, + unsigned long event, void *data) +{ + struct siw_ts *ts = + container_of(this, struct siw_ts, blocking_notif); + return siw_touch_notify(ts, event, data); +} + +static int siw_touch_atomic_notifier_callback(struct notifier_block *this, + unsigned long event, void *data) +{ + struct siw_ts *ts = + container_of(this, struct siw_ts, atomic_notif); + + ts->notify_event = event; + ts->notify_data = (data == NULL) ? 0 : *(int *)data; + + siw_touch_qd_notify_work_now(ts); + + return 0; +} + +void siw_touch_atomic_notifer_work_func(struct work_struct *work) +{ + struct siw_ts *ts = + container_of(to_delayed_work(work), + struct siw_ts, notify_work); + + siw_touch_notify(ts, ts->notify_event, &ts->notify_data); +} + +int siw_touch_init_notify(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + int ret = 0; + + ts->blocking_notif.notifier_call = siw_touch_blocking_notifier_callback; + ret = siw_touch_blocking_notifier_register(&ts->blocking_notif); + if (ret < 0) { + t_dev_err(dev, + "failed to regiseter touch blocking_notify callback\n"); + goto out; + } + + ts->atomic_notif.notifier_call = siw_touch_atomic_notifier_callback; + ret = siw_touch_atomic_notifier_register(&ts->atomic_notif); + if (ret < 0) { + t_dev_err(dev, "failed to regiseter touch atomic_notify callback\n"); + goto out; + } + +out: + return ret; +} + +void siw_touch_free_notify(struct siw_ts *ts) +{ + (void)siw_touch_atomic_notifier_unregister(&ts->atomic_notif); + (void)siw_touch_blocking_notifier_unregister(&ts->blocking_notif); +} + diff --git a/drivers/input/touchscreen/siw/siw_touch_of.c b/drivers/input/touchscreen/siw/siw_touch_of.c new file mode 100644 index 000000000000..7c5b908035b8 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_of.c @@ -0,0 +1,458 @@ +/* + * siw_touch_of.c - SiW touch device tree driver + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siw_touch.h" + +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + + +#if defined(__SIW_CONFIG_OF) //See siw_touch_cfg.h +static int siw_touch_of_gpio(struct device *dev, void *np, + void *string, enum of_gpio_flags *flags) +{ + int gpio = 0; + + gpio = of_get_named_gpio_flags(np, string, 0, flags); + if (gpio_is_valid(gpio)) { + if (flags) + t_dev_info(dev, "of gpio : %s(0x%X), %d\n", + (char *)string, (*flags), gpio); + else + t_dev_info(dev, "of gpio : %s, %d\n", + (char *)string, gpio); + } + + return gpio; +} + +static bool siw_touch_of_bool(struct device *dev, void *np, + void *string) +{ + u32 val = 0; + int ret = 0; + + ret = of_property_read_u32(np, string, &val); + if (ret < 0) { + return 0; + } + + t_dev_dbg_of(dev, "of bool : %s, %d\n", (char *)string, val); + return (bool)val; +} + +static u32 siw_touch_of_u32(struct device *dev, void *np, + void *string) +{ + u32 val = 0; + int ret = 0; + + ret = of_property_read_u32(np, string, &val); + if (ret < 0) { + return 0; + } + + t_dev_dbg_of(dev, "of u32 : %s, %d\n", (char *)string, val); + return val; +} + +/* Caution : MSB(bit31) unavailable */ +static int siw_touch_of_int(struct device *dev, void *np, + void *string) +{ + u32 val = 0; + int ret = 0; + + ret = of_property_read_u32(np, string, &val); + if (ret < 0) { + return -ENXIO; + } + + t_dev_dbg_of(dev, "of int : %s, %d\n", (char *)string, val); + return val; +} + +static void siw_touch_of_array(struct device *dev, void *np, + void *string, const char **name, int *cnt) +{ + int i; + int ret = 0; + + (*cnt) = 0; + + ret = of_property_count_strings(np, string); + if (ret < 0) { + t_dev_warn(dev, "of count : %s not found\n", (char *)string); + return; + } + + (*cnt) = ret; + t_dev_dbg_of(dev, "of count : %s, %d\n", (char *)string, (*cnt)); + for (i = 0; i < (*cnt); i++) { + ret = of_property_read_string_index(np, string, i, (const char **)&name[i]); + if (!ret) { + t_dev_info(dev, "of string[%d/%d] : %s\n", i+1, (*cnt), name[i]); + } + } +} + +static int siw_touch_of_string(struct device *dev, void *np, + void *string, const char **name) +{ + int ret; + + ret = of_property_read_string(np, string, name); + if (!ret) { + t_dev_info(dev, "of_string : %s, %s\n", (char *)string, *name); + } else { + t_dev_warn(dev, "of_string : %s not found\n", (char *)string); + } + + return ret; +} + +#if defined(__SIW_SUPPORT_WATCH) +static int siw_touch_parse_dts_watch(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + struct device_node *np = dev->of_node; + int ret = 0; + + if (touch_test_quirks(ts, CHIP_QUIRK_NOT_SUPPORT_WATCH)) { + goto out; + } + + ret = siw_touch_of_string(dev, np, "font_image", &ts->watch_font_image); + +out: + return 0; +} +#else /* __SIW_SUPPORT_WATCH */ +#define siw_touch_parse_dts_watch(_ts) ({ int _r = 0; _r; }) +#endif /* __SIW_SUPPORT_WATCH */ + +#if defined(__SIW_SUPPORT_PRD) +/* + * weak(dummy) function for PRD control + * These are deactivated by enabling __SIW_SUPPORT_PRD + * and the actual functions can be found in siw_touch_hal_prd.c + */ +int __weak siw_hal_set_prd_file(struct device *dev, const char *path, int idx) +{ + t_dev_warn(dev, "PRD disabled\n"); + return 0; +} + +static int siw_touch_parse_dts_prd(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + struct device_node *np = dev->of_node; + int ret = 0; + + ret = siw_touch_of_string(dev, np, "prd_in_file", &ts->prd_in_file_path); + if (!ret) { + siw_hal_set_prd_file(dev, ts->prd_in_file_path, 0); + } + ret = siw_touch_of_string(dev, np, "prd_in_file_m", &ts->prd_in_file_m_path); + if (!ret) { + siw_hal_set_prd_file(dev, ts->prd_in_file_m_path, 1); + } + ret = siw_touch_of_string(dev, np, "prd_out_file", &ts->prd_out_file_path); + if (!ret) { + siw_hal_set_prd_file(dev, ts->prd_out_file_path, 2); + } + ret = siw_touch_of_string(dev, np, "prd_out_file_mo_aat", &ts->prd_out_file_mo_aat_path); + if (!ret) { + siw_hal_set_prd_file(dev, ts->prd_out_file_mo_aat_path, 3); + } + ret = siw_touch_of_string(dev, np, "prd_out_file_mo_mfo", &ts->prd_out_file_mo_mfo_path); + if (!ret) { + siw_hal_set_prd_file(dev, ts->prd_out_file_mo_mfo_path, 4); + } + ret = siw_touch_of_string(dev, np, "prd_out_file_mo_mfl", &ts->prd_out_file_mo_mfl_path); + if (!ret) { + siw_hal_set_prd_file(dev, ts->prd_out_file_mo_mfl_path, 5); + } + ret = siw_touch_of_string(dev, np, "prd_out_file_mo_mcv", &ts->prd_out_file_mo_mcv_path); + if (!ret) { + siw_hal_set_prd_file(dev, ts->prd_out_file_mo_mcv_path, 6); + } + + return 0; +} +#else /* __SIW_SUPPORT_PRD */ +#define siw_touch_parse_dts_prd(_ts) ({ int _r = 0; _r; }) +#endif /* __SIW_SUPPORT_PRD */ + +static int siw_touch_do_parse_dts(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + struct device_node *np = dev->of_node; + struct touch_pins *pins = &ts->pins; + struct touch_device_caps *caps = &ts->caps; + struct touch_operation_role *role = &ts->role; + int tmp; + u32 p_flags = 0; + int chip_flags = 0; + int irq_flags = 0; + enum of_gpio_flags pin_flags = 0; + int pin_val = -1; + + if (!ts->dev->of_node) { + t_dev_err(dev, "No dts data\n"); + return -ENOENT; + } + + t_dev_dbg_of(dev, "start dts parsing\n"); + + chip_flags = siw_touch_of_int(dev, np, "chip_flags"); + p_flags = pdata_flags(ts->pdata); + if ((p_flags & TOUCH_IGNORE_DT_FLAGS) || (chip_flags < 0)) { + ts->flags = p_flags; + } else { + ts->flags = chip_flags & 0xFFFF; + ts->flags |= p_flags & (0xFFFFUL<<16); + } + if (chip_flags >= 0) { + t_dev_info(dev, "flags(of) = 0x%08X (0x%08X, 0x%08X)\n", + ts->flags, p_flags, chip_flags); + } else { + t_dev_info(dev, "flags(of) = 0x%08X (0x%08X, %d)\n", + ts->flags, p_flags, chip_flags); + } + + if (ts->flags & TOUCH_SKIP_RESET_PIN) { + pins->reset_pin = -1; + pins->reset_pin_pol = 0; + } else { + pin_val = siw_touch_of_gpio(dev, np, "reset-gpio", &pin_flags); + if (!gpio_is_valid(pin_val)) { + t_dev_err(dev, "of gpio : reset-gpio invalid, %d\n", pin_val); + return -EINVAL; + } + pins->reset_pin = pin_val; + pins->reset_pin_pol = !!(pin_flags & OF_GPIO_ACTIVE_LOW); + } + + pin_val = siw_touch_of_gpio(dev, np, "irq-gpio", NULL); + if (!gpio_is_valid(pin_val)) { + t_dev_err(dev, "of gpio : irq-gpio invalid, %d\n", pin_val); + return -EINVAL; + } + pins->irq_pin= pin_val; + + pins->maker_id_pin = siw_touch_of_gpio(dev, np, "maker_id-gpio", NULL); + + /* Power */ + pins->vdd_pin = siw_touch_of_gpio(dev, np, "vdd-gpio", NULL); + pins->vio_pin = siw_touch_of_gpio(dev, np, "vio-gpio", NULL); + + irq_flags = siw_touch_of_u32(dev, np, "irqflags"); + p_flags = pdata_irqflags(ts->pdata); + if ((p_flags & TOUCH_IGNORE_DT_FLAGS) || (irq_flags < 0)) { + ts->irqflags = p_flags & ~TOUCH_IGNORE_DT_FLAGS; + } else { + ts->irqflags = irq_flags; + } + if (irq_flags >= 0) { + t_dev_info(dev, "irqflags(of) = 0x%08X (0x%08X, 0x%08X)\n", + (u32)ts->irqflags, p_flags, irq_flags); + } else { + t_dev_info(dev, "irqflags(of) = 0x%08X (0x%08X, %d)\n", + (u32)ts->irqflags, p_flags, irq_flags); + } + + /* Caps */ + caps->max_x = siw_touch_of_u32(dev, np, "max_x"); + caps->max_y = siw_touch_of_u32(dev, np, "max_y"); + caps->max_pressure = siw_touch_of_u32(dev, np, "max_pressure"); + caps->max_width = siw_touch_of_u32(dev, np, "max_width"); + caps->max_orientation = siw_touch_of_u32(dev, np, "max_orientation"); + caps->max_id = siw_touch_of_u32(dev, np, "max_id"); + caps->hw_reset_delay = siw_touch_of_u32(dev, np, "hw_reset_delay"); + caps->sw_reset_delay = siw_touch_of_u32(dev, np, "sw_reset_delay"); + + memcpy((void *)&ts->i_id, (void *)&ts->pdata->i_id, sizeof(ts->i_id)); + tmp = siw_touch_of_int(dev, np, "i_product"); + if (tmp >= 0) + ts->i_id.product = tmp; + tmp = siw_touch_of_int(dev, np, "i_vendor"); + if (tmp >= 0) + ts->i_id.vendor = tmp; + tmp = siw_touch_of_int(dev, np, "i_version"); + if (tmp >= 0) + ts->i_id.version = tmp; + + /* Role */ +#if defined(__SIW_CONFIG_KNOCK) + if (touch_test_quirks(ts, CHIP_QUIRK_NOT_SUPPORT_LPWG)) { + role->use_lpwg = 0; + role->use_lpwg_test = 0; + } else { + role->use_lpwg = siw_touch_of_bool(dev, np, "use_lpwg"); + role->use_lpwg_test = siw_touch_of_u32(dev, np, "use_lpwg_test"); + } +#else /* __SIW_CONFIG_KNOCK */ + role->use_lpwg = 0; + role->use_lpwg_test = 0; +#endif /* __SIW_CONFIG_KNOCK */ + + /* Firmware */ + role->use_firmware = siw_touch_of_bool(dev, np, "use_firmware"); + if (role->use_firmware == 1) { + role->use_fw_upgrade = siw_touch_of_bool(dev, np, "use_fw_upgrade"); + + siw_touch_of_array(dev, np, "fw_image", ts->def_fwpath, &ts->def_fwcnt); + + siw_touch_of_string(dev, np, "panel_spec", &ts->panel_spec); + siw_touch_of_string(dev, np, "panel_spec_mfts", &ts->panel_spec_mfts); + } else { + role->use_fw_upgrade = 0; + ts->def_fwcnt = 0; + memset(ts->def_fwpath, 0, sizeof(ts->def_fwpath)); + ts->panel_spec = NULL; + ts->panel_spec_mfts = NULL; + } + + siw_touch_parse_dts_watch(ts); + + siw_touch_parse_dts_prd(ts); + + t_dev_info(dev, "caps max_x = %d\n", caps->max_x); + t_dev_info(dev, "caps max_y = %d\n", caps->max_y); + t_dev_dbg_of(dev, "caps max_pressure = %d\n", caps->max_pressure); + t_dev_dbg_of(dev, "caps max_width = %d\n", caps->max_width); + t_dev_dbg_of(dev, "caps max_orientation = %d\n", caps->max_orientation); + t_dev_dbg_of(dev, "caps max_id = %d\n", caps->max_id); + t_dev_dbg_of(dev, "caps hw_reset_delay = %d\n", caps->hw_reset_delay); + t_dev_dbg_of(dev, "caps sw_reset_delay = %d\n", caps->sw_reset_delay); + t_dev_dbg_of(dev, "role use_lpwg = %d\n", role->use_lpwg); + t_dev_dbg_of(dev, "role use_lpwg_test = %d\n", role->use_lpwg_test); + t_dev_dbg_of(dev, "role use_firmware = %d\n", role->use_firmware); + t_dev_dbg_of(dev, "role use_fw_upgrade = %d\n", role->use_fw_upgrade); + + t_dev_dbg_of(dev, "dts parsing done\n"); + + return 0; +} + +static int siw_touch_parse_dts(struct siw_ts *ts) +{ + struct siw_touch_fquirks *fquirks = touch_fquirks(ts); + int ret = 0; + + if (fquirks->parse_dts) { + ret = fquirks->parse_dts(ts); + if (ret != -EAGAIN) { + goto out; + } + } + + ret = siw_touch_do_parse_dts(ts); + +out: + return ret; +} + + +#else /* __SIW_CONFIG_OF */ + +static int siw_touch_parse_dts(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + struct touch_pins *pins = &ts->pins; + + t_dev_info(dev, "__SIW_CONFIG_OF disabled\n"); + + ts->irqflags = pdata_irqflags(ts->pdata); + + ts->flags = pdata_flags(ts->pdata); + t_dev_info(dev, "flags = 0x%08X", ts->flags); + + touch_set_pins(ts, &ts->pdata->pins); + if (ts->flags & TOUCH_SKIP_RESET_PIN) { + pins->reset_pin = -1; + pins->reset_pin_pol = 0; + } else { + if (!gpio_is_valid(pins->reset_pin)) { + t_dev_err(dev, "reset_pin invalid, %d\n", pins->reset_pin); + return -EINVAL; + } + t_dev_info(dev, "reset_pin = %d\n", pins->reset_pin); + } + if (!gpio_is_valid(pins->irq_pin)) { + t_dev_err(dev, "irq_pin invalid, %d\n", pins->irq_pin); + return -EINVAL; + } + t_dev_info(dev, "irq_pin = %d\n", pins->irq_pin); + + touch_set_caps(ts, &ts->pdata->caps); + + memcpy((void *)&ts->i_id, (void *)&ts->pdata->i_id, sizeof(ts->i_id)); + + /* Role */ +#if defined(__SIW_CONFIG_KNOCK) + ts->role.use_lpwg = !touch_test_quirks(ts, CHIP_QUIRK_NOT_SUPPORT_LPWG); +#else /* __SIW_CONFIG_KNOCK */ + ts->role.use_lpwg = 0; +#endif /* __SIW_CONFIG_KNOCK */ + + ts->role.use_firmware = 0; + ts->role.use_fw_upgrade = 0; + ts->def_fwcnt = 0; + memset(ts->def_fwpath, 0, sizeof(ts->def_fwpath)); + ts->panel_spec = NULL; + ts->panel_spec_mfts = NULL; + + ts->vdd_id = -1; + ts->vio_id = -1; + + return 0; +} +#endif /* __SIW_CONFIG_OF */ + +int siw_touch_parse_data(struct siw_ts *ts) +{ + int ret = 0; + + ret = siw_touch_parse_dts(ts); + if (ret) { + t_dev_err(ts->dev, "!! DTS parsing failed !!\n"); + } + + return ret; +} + + + diff --git a/drivers/input/touchscreen/siw/siw_touch_sys.c b/drivers/input/touchscreen/siw/siw_touch_sys.c new file mode 100644 index 000000000000..544dc4ca1626 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_sys.c @@ -0,0 +1,284 @@ +/* + * siw_touch_sys.c - SiW touch system interface + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_PLAT_SAMSUNG) +#include +#include +#include +#include +#include +#endif + +#include "siw_touch.h" +#include "siw_touch_gpio.h" +#include "siw_touch_sys.h" + +#if defined(CONFIG_TOUCHSCREEN_SIW_LG4895_F650) || \ + defined(CONFIG_TOUCHSCREEN_SIW_LG4946_F700) +#include +#endif + + +/* + * depends on AP bus + */ +int siw_touch_sys_bus_use_dma(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + + if (touch_bus_type(ts) != BUS_IF_SPI) { + return 0; + } + +#if defined(__If_the_bus_of_your_chipset_needs_dma_control__) + { + return 1; + } +#elif defined(CONFIG_ARCH_EXYNOS5) + { + return 0; + } +#endif + + return 0; +} + +int siw_touch_get_boot_mode(void) +{ +#if defined(CONFIG_SIW_GET_BOOT_MODE) + if (sys_get_boot_mode() == BOOT_MODE_CHARGERLOGO) { + return SIW_TOUCH_CHARGER_MODE; + } +#elif defined(CONFIG_TOUCHSCREEN_SIW_LG4895_F650) || \ + defined(CONFIG_TOUCHSCREEN_SIW_LG4946_F700) + if (lge_get_boot_mode() == LGE_BOOT_MODE_CHARGERLOGO) { + return SIW_TOUCH_CHARGER_MODE; + } +#endif + + return 0; +} + +int siw_touch_boot_mode_check(struct device *dev) +{ + struct siw_ts *ts = to_touch_core(dev); + int ret = NORMAL_BOOT; + +#if defined(CONFIG_SIW_GET_FACTORY_MODE) + ret = sys_get_factory_boot(); +#elif defined(CONFIG_TOUCHSCREEN_SIW_LG4895_F650) || \ + defined(CONFIG_TOUCHSCREEN_SIW_LG4946_F700) + ret = lge_get_factory_boot(); +#endif + + if (ret != NORMAL_BOOT) { + switch (atomic_read(&ts->state.mfts)) { + case MFTS_NONE : + ret = MINIOS_AAT; + break; + case MFTS_FOLDER : + ret = MINIOS_MFTS_FOLDER; + break; + case MFTS_FLAT : + ret = MINIOS_MFTS_FLAT; + break; + case MFTS_CURVED : + ret = MINIOS_MFTS_CURVED; + break; + default : + ret = MINIOS_AAT; + break; + } + } + + return ret; +} + +int siw_touch_boot_mode_tc_check(struct device *dev) +{ + int ret = 0; + +#if defined(CONFIG_SIW_GET_BOOT_MODE) + if (sys_get_boot_mode() == BOOT_MODE_CHARGERLOGO) { + return 1; + } +#elif defined(CONFIG_TOUCHSCREEN_SIW_LG4895_F650) + if ((lge_get_boot_mode() == LGE_BOOT_MODE_QEM_910K) || + (lge_get_boot_mode() == LGE_BOOT_MODE_PIF_910K)) { + return 1; + } +#endif + + return ret; +} + +int siw_touch_sys_gpio_set_pull(int pin, int value) +{ + int ret = 0; + +#if defined(CONFIG_PLAT_SAMSUNG) + { + int pull_val; + + switch (value) { + case GPIO_PULL_UP: + pull_val = S3C_GPIO_PULL_UP; + break; + case GPIO_PULL_DOWN: + pull_val = S3C_GPIO_PULL_DOWN; + break; + default: + pull_val = S3C_GPIO_PULL_NONE; + break; + } + ret = s3c_gpio_setpull(pin, pull_val); + } +#endif + + return ret; +} + +int siw_touch_sys_panel_reset(struct device *dev) +{ + return 0; +} + +int siw_touch_sys_get_panel_bl(struct device *dev) +{ + return 0; +} + +int siw_touch_sys_set_panel_bl(struct device *dev, int level) +{ + if (level == 0){ + t_dev_info(dev, "BLU control OFF\n"); + /* To Do : screen off */ + } else { + t_dev_info(dev, "BLU control ON (level:%d)\n", level); + /* To Do : screen on with level */ + } + return 0; +} + +int siw_touch_sys_osc(struct device *dev, int onoff) +{ + return 0; +} + +#if defined(__SIW_CONFIG_USER_FB) +#if defined(CONFIG_DRM_SDE_SPECIFIC_PANEL) +#include + +#define __SIW_SUPPORT_DRM_TYPE_1 +#endif /* CONFIG_DRM_SDE_SPECIFIC_PANEL */ +#endif /* __SIW_CONFIG_USER_FB */ + +#if defined(__SIW_SUPPORT_DRM_TYPE_1) +static int drm_notifier_callback( + struct notifier_block *self, + unsigned long event, void *data) +{ + struct siw_ts *ts = + container_of(self, struct siw_ts, user_fb_notif); + struct device *dev = ts->dev; + struct drm_ext_event *ev = (struct drm_ext_event *)data; + int *blank; + + if (!ev || !ev->data) { + return 0; + } + + blank = (int *)ev->data; + +#if defined(__SIW_CONFIG_SYSTEM_PM) + if (event == DRM_EXT_EVENT_BEFORE_BLANK) { + if (*blank == DRM_BLANK_UNBLANK) { + t_dev_info(dev, "drm_unblank(early)\n"); + } else if (*blank == DRM_BLANK_POWERDOWN) { + t_dev_info(dev, "drm_blank(early)\n"); + siw_touch_suspend_call(dev); + } + } + + if (event == DRM_EXT_EVENT_AFTER_BLANK) { + if (*blank == DRM_BLANK_UNBLANK) { + t_dev_info(dev, "drm_unblank\n"); + siw_touch_resume_call(dev); + } else if (*blank == DRM_BLANK_POWERDOWN) { + t_dev_info(dev, "drm_blank\n"); + } + } +#else /* __SIW_CONFIG_SYSTEM_PM */ + if (event == DRM_EXT_EVENT_AFTER_BLANK) { + if (*blank == DRM_BLANK_UNBLANK) { + t_dev_info(dev, "drm_unblank\n"); + siw_touch_resume_call(dev); + } else if (*blank == DRM_BLANK_POWERDOWN) { + t_dev_info(dev, "drm_blank\n"); + siw_touch_suspend_call(dev); + } + } +#endif /* __SIW_CONFIG_SYSTEM_PM */ + + return 0; +} +#endif /* __SIW_SUPPORT_DRM_TYPE_1 */ + +int siw_touch_sys_fb_register_client(struct device *dev) +{ +#if defined(__SIW_SUPPORT_DRM_TYPE_1) + struct siw_ts *ts = to_touch_core(dev); + + t_dev_info(dev, "drm_register_client - user_fb_notif\n"); + + ts->user_fb_notif.notifier_call = drm_notifier_callback; + + return drm_register_client(&ts->user_fb_notif); +#else + return 0; +#endif +} + +int siw_touch_sys_fb_unregister_client(struct device *dev) +{ +#if defined(__SIW_SUPPORT_DRM_TYPE_1) + struct siw_ts *ts = to_touch_core(dev); + + t_dev_info(dev, "drm_unregister_client - user_fb_notif\n"); + + return drm_unregister_client(&ts->user_fb_notif); +#else + return 0; +#endif +} + diff --git a/drivers/input/touchscreen/siw/siw_touch_sys.h b/drivers/input/touchscreen/siw/siw_touch_sys.h new file mode 100644 index 000000000000..e85bbebc664b --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_sys.h @@ -0,0 +1,35 @@ +/* + * SiW touch system interface + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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_SYS_H +#define __SIW_TOUCH_SYS_H + +extern int siw_touch_sys_bus_use_dma(struct device *dev); + +extern int siw_touch_get_boot_mode(void); +extern int siw_touch_boot_mode_check(struct device *dev); + +extern int siw_touch_boot_mode_tc_check(struct device *dev); + +extern int siw_touch_sys_gpio_set_pull(int pin, int value); + +extern int siw_touch_sys_panel_reset(struct device *dev); +extern int siw_touch_sys_get_panel_bl(struct device *dev); +extern int siw_touch_sys_set_panel_bl(struct device *dev, int level); + +extern int siw_touch_sys_osc(struct device *dev, int onoff); + +extern int siw_touch_sys_fb_register_client(struct device *dev); +extern int siw_touch_sys_fb_unregister_client(struct device *dev); + +#endif /* __SIW_TOUCH_SYS_H */ + diff --git a/drivers/input/touchscreen/siw/siw_touch_sysfs.c b/drivers/input/touchscreen/siw/siw_touch_sysfs.c new file mode 100644 index 000000000000..92208e315741 --- /dev/null +++ b/drivers/input/touchscreen/siw/siw_touch_sysfs.c @@ -0,0 +1,1932 @@ +/* + * siw_touch_sysfs.c - SiW touch sysfs driver + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siw_touch.h" +#include "siw_touch_hal.h" +#include "siw_touch_irq.h" +#include "siw_touch_sys.h" + + +//#define __SYS_USE_LOCKSCREEN +//#define __SYS_USE_IME_STATE + +#if defined(__SYS_USE_IME_STATE) +static const char *siw_ime_str[] = { + "OFF", + "ON", + "SWYPE", +}; +#endif + +#if defined(__SIW_CONFIG_KNOCK) +static const char *siw_mfts_str[] = { + "NONE", + "FOLDER", + "FLAT", + "CURVED", +}; +#endif + +#if defined(__SIW_SUPPORT_ASC) +static const char *siw_incoming_call_str[] = { + "IDLE", + "RINGING", + "OFFHOOK", +}; + +static const char *siw_onhand_str[] = { + "IN_HAND_ATTN", + "NOT_IN_HAND", + "IN_HAND_NO_ATTN", +}; +#endif /* __SIW_SUPPORT_ASC */ + + +#define siw_sysfs_err_invalid_param(_dev) \ + t_dev_err(_dev, "Invalid param\n"); + +#define _plat_data_snprintf(_buf, _size, args...) \ + siw_snprintf(_buf, _size, " %-25s = %d\n", ##args) + +static void siw_touch_sysfs_gen_symlink(struct siw_ts *ts, char *name) +{ + struct device *dev = ts->dev; + struct kobject *kobj = &ts->kobj; + int ret = 0; + + if (!name || !strlen(name)) { + return; + } + + if (strcmp(kobj->name, name) != 0) { + ret = sysfs_create_link(kobj->parent, kobj, name); + if (ret >= 0) { + t_dev_info(dev, "symlink generated, %s\n", name); + } + } +} + +static void siw_touch_sysfs_del_symlink(struct siw_ts *ts, char *name) +{ + struct kobject *kobj = &ts->kobj; + + if (!name || !strlen(name)) { + return; + } + + if (strcmp(kobj->name, name) != 0) { + sysfs_remove_link(kobj->parent, name); + } +} + + +static ssize_t _show_do_plat_data(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + struct touch_device_caps *caps = &ts->caps; + int i; + int size = 0; + + size += siw_snprintf(buf, size, "=== Platform Data ===\n"); + size += _plat_data_snprintf(buf, size, + "reset_pin", touch_reset_pin(ts)); + size += _plat_data_snprintf(buf, size, + "reset_pin_pol", touch_reset_pin_pol(ts)); + size += _plat_data_snprintf(buf, size, + "irq_pin", touch_irq_pin(ts)); + size += _plat_data_snprintf(buf, size, + "maker_id_pin", touch_maker_id_pin(ts)); + + size += siw_snprintf(buf, size, "power:\n"); + size += _plat_data_snprintf(buf, size, + "vdd-gpio", touch_vdd_pin(ts)); + size += _plat_data_snprintf(buf, size, + "vio-gpio", touch_vio_pin(ts)); + + size += siw_snprintf(buf, size, "caps:\n"); + size += _plat_data_snprintf(buf, size, + "max_x", caps->max_x); + size += _plat_data_snprintf(buf, size, + "max_y", caps->max_y); + size += _plat_data_snprintf(buf, size, + "max_pressure", caps->max_pressure); + size += _plat_data_snprintf(buf, size, + "max_width", caps->max_width); + size += _plat_data_snprintf(buf, size, + "max_orientation", caps->max_orientation); + size += _plat_data_snprintf(buf, size, + "max_id", caps->max_id); + size += _plat_data_snprintf(buf, size, + "hw_reset_delay", caps->hw_reset_delay); + size += _plat_data_snprintf(buf, size, + "sw_reset_delay", caps->sw_reset_delay); + + size += siw_snprintf(buf, size, "role:\n"); + size += _plat_data_snprintf(buf, size, + "use_lpwg", ts->role.use_lpwg); + size += _plat_data_snprintf(buf, size, + "use_firmware", ts->role.use_firmware); + size += _plat_data_snprintf(buf, size, + "use_fw_upgrade", ts->role.use_fw_upgrade); + + size += siw_snprintf(buf, size, "firmware:\n"); + size += _plat_data_snprintf(buf, size, + "def_fwcnt", ts->def_fwcnt); + for (i = 0; i < ts->def_fwcnt; i++) + size += siw_snprintf(buf, size, " %-25s : [%d] %s\n", + "def_fwpath", i, ts->def_fwpath[i]); + + return size; +} + +static ssize_t _show_plat_data(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int size = 0; + + mutex_lock(&ts->lock); + size = _show_do_plat_data(dev, buf); + mutex_unlock(&ts->lock); + + return size; +} + +static ssize_t _show_do_driver_data(struct device *dev, char *buf) +{ +// struct siw_ts *ts = to_touch_core(dev); + int size = 0; + + size += siw_snprintf(buf, size, "=== Driver Data ===\n"); + + size += siw_snprintf(buf, size, + "Version : %s\n", SIW_DRV_VERSION); + + return size; +} + +static ssize_t _show_driver_data(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int size = 0; + + mutex_lock(&ts->lock); + size = _show_do_driver_data(dev, buf); + mutex_unlock(&ts->lock); + + return size; +} + +static ssize_t _store_upgrade(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + + if (sscanf(buf, "%255s", ts->test_fwpath) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + ts->force_fwup |= FORCE_FWUP_SYS_STORE; + + siw_touch_qd_upgrade_work_now(ts); + + return count; +} + +static ssize_t _show_upgrade(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + + ts->force_fwup |= FORCE_FWUP_SYS_SHOW; + ts->test_fwpath[0] = '\0'; + + siw_touch_qd_upgrade_work_now(ts); + + return 0; +} + +#if defined(__SIW_CONFIG_KNOCK) +static int __show_do_lpwg_data(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + struct lpwg_info *lpwg = &ts->lpwg; + int i; + int size = 0; + + for (i = 0; i < MAX_LPWG_CODE; i++) { + if ((lpwg->code[i].x == -1) && (lpwg->code[i].y == -1)) { + break; + } + + size += siw_snprintf(buf, size, "%d %d\n", + lpwg->code[i].x, lpwg->code[i].y); + + if (!lpwg->code[i].x && !lpwg->code[i].y) { + break; + } + } + memset(lpwg->code, 0, sizeof(struct point) * MAX_LPWG_CODE); + + return size; +} + +static ssize_t _show_lpwg_data(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int ret = 0; + + if (siw_ops_is_null(ts, lpwg)) + return ret; + + mutex_lock(&ts->lock); + ret = __show_do_lpwg_data(dev, buf); + mutex_unlock(&ts->lock); + + return ret; +} + +static ssize_t _store_lpwg_data(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int reply = 0; + + if (sscanf(buf, "%d", &reply) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + t_dev_info(dev, "uevent reply %d\n", reply); + + atomic_set(&ts->state.uevent, UEVENT_IDLE); +#if defined(__SIW_SUPPORT_WAKE_LOCK) + wake_unlock(&ts->lpwg_wake_lock); +#endif + + return count; +} + +static ssize_t _store_lpwg_notify(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int code = 0; + int param[4] = {0, }; + int mfts_mode = 0; + + mfts_mode = siw_touch_boot_mode_check(dev); + if ((mfts_mode >= MINIOS_MFTS_FOLDER) && !ts->role.mfts_lpwg) + return count; + + if (sscanf(buf, "%d %d %d %d %d", + &code, ¶m[0], ¶m[1], ¶m[2], ¶m[3]) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + /* only below code notify + 3 active_area + 4 knockcode tap count + 8 knockcode double tap check + 9 update_all + */ + switch (code) { + case LPWG_ENABLE: + case LPWG_LCD: + case LPWG_LENGTH_BETWEEN_TAP: + case LPWG_EARLY_SUSPEND: + case LPWG_SENSOR_STATUS: + goto out; + default: + if ((code >= LPWG_EXT_TCI_INFO_STORE) && + (code < LPWG_EXT_STORE_END)) { + break; + } + + if ((code >= LPWG_EXT_TCI_INFO_SHOW) && + (code < LPWG_EXT_SHOW_END)) { + break; + } + + if (code > LPWG_REPLY) { + t_dev_err(dev, "unknown code, %d\n", code); + goto out; + } + break; + } + + mutex_lock(&ts->lock); + siw_ops_lpwg(ts, code, param); + mutex_unlock(&ts->lock); + +out: + return count; +} + +static ssize_t _show_mfts_state(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + int size = 0; + + value = atomic_read(&ts->state.mfts); + + size += siw_snprintf(buf, size, "%s : %s(%d)\n", + __func__, + siw_mfts_str[value], value); + + return (ssize_t)size; +} + +static ssize_t _store_mfts_state(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + + if (sscanf(buf, "%d", &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + if (value >= MFTS_NONE && value <= MFTS_CURVED) { + atomic_set(&ts->state.mfts, value); + t_dev_info(dev, "MFTS : %s(%d)\n", siw_mfts_str[value], value); + } else { + t_dev_info(dev, "MFTS : Unknown state, %d\n", value); + } + + return count; +} + +static ssize_t _show_mfts_lpwg(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int size = 0; + + size += siw_snprintf(buf, size, "MFTS LPWG : %d\n", + ts->role.use_lpwg_test); + + return (ssize_t)size; +} + +static ssize_t _store_mfts_lpwg(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + + if (sscanf(buf, "%d", &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + ts->role.mfts_lpwg = value; + t_mfts_lpwg = value; + t_dev_info(dev, "MFTS LPWG : %d\n", ts->role.mfts_lpwg); + + return count; +} +#endif /* __SIW_CONFIG_KNOCK */ + +#if defined(__SYS_USE_LOCKSCREEN) +static ssize_t _show_lockscreen_state(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + int size = 0; + + value = atomic_read(&ts->state.lockscreen); + + size += siw_snprintf(buf, size, "Lock-screen : %s(%d)\n", + value ? "LOCK" : "UNLOCK", value); + + return (ssize_t)size; +} + +static ssize_t _store_lockscreen_state(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + + if (sscanf(buf, "%d", &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + if (value == LOCKSCREEN_UNLOCK || value == LOCKSCREEN_LOCK) { + atomic_set(&ts->state.lockscreen, value); + t_dev_info(dev, "Lock-screen : %s(%d)\n", value ? "LOCK" : "UNLOCK", value); + } else { + t_dev_info(dev, "Lock-screen : Unknown state, %d\n", value); + } + + return count; +} +#endif /* __SYS_USE_LOCKSCREEN */ + +#if defined(__SYS_USE_IME_STATE) +static ssize_t _show_ime_state(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + int size = 0; + + value = atomic_read(&ts->state.ime); + + size += siw_snprintf(buf, size, "%s(%d)\n", + siw_ime_str[value], value); + + return (ssize_t)size; +} + +static ssize_t _store_ime_state(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + int ret = 0; + + if (sscanf(buf, "%d", &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + goto out; + } + + if ((value < IME_OFF) || (value > IME_SWYPE)) { + t_dev_warn(dev, "IME : Unknown state, %d\n", value); + goto out; + } + + if (atomic_read(&ts->state.ime) == value) + goto out; + + if (touch_test_quirks(ts, CHIP_QUIRK_NOT_SUPPORT_IME)) { + t_dev_err(dev, "ime control not supporeted in %s\n", + touch_chip_name(ts)); + goto out; + } + + atomic_set(&ts->state.ime, value); + ret = siw_touch_blocking_notifier_call(NOTIFY_IME_STATE, + &ts->state.ime); + t_dev_info(dev, "IME : %s(%d), %d\n", + siw_ime_str[value], value, ret); + +out: + return count; +} +#endif /* __SYS_USE_IME_STATE */ + +#if defined(SOMC_TOUCH_BRINGUP) +static ssize_t _show_quick_cover_mode(struct device *dev, char *buf) +#else +static ssize_t _show_quick_cover_state(struct device *dev, char *buf) +#endif +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + int size = 0; + + value = atomic_read(&ts->state.quick_cover); + + size += siw_snprintf(buf, size, "Qcover : %s(%d)\n", + value ? "CLOSE" : "OPEN", value); + + value = atomic_read(&ts->state.quick_cover_opt); + + size += siw_snprintf(buf, size, "Qcover opt: %s(%d)\n", + (value & QUICKCOVER_OPT_BIT_SKIP_CLOSE) ? "skip" : "pass", value); + + return (ssize_t)size; +} + +#if defined(SOMC_TOUCH_BRINGUP) +static ssize_t _store_quick_cover_mode(struct device *dev, + const char *buf, size_t count) +#else +static ssize_t _store_quick_cover_state(struct device *dev, + const char *buf, size_t count) +#endif +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + + if (sscanf(buf, "%d", &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } +#if defined(SOMC_TOUCH_BRINGUP) + if (value == QUICKCOVER_OPEN) { + value = QUICKCOVER_OPT_BASE + 1; + } else if (value == QUICKCOVER_CLOSE) { + value = QUICKCOVER_OPT_BASE; + } else { + siw_sysfs_err_invalid_param(dev); + return count; + } +#endif + mutex_lock(&ts->lock); + + switch (value) { + case QUICKCOVER_OPEN: + case QUICKCOVER_CLOSE: + atomic_set(&ts->state.quick_cover, value); + t_dev_info(dev, "Qcover : %s(%d)\n", (value) ? "CLOSE" : "OPEN", value); + break; + case QUICKCOVER_OPT_BASE: + case (QUICKCOVER_OPT_BASE+1): + { + int opt = atomic_read(&ts->state.quick_cover_opt); + int sel = value - QUICKCOVER_OPT_BASE; + + switch (sel) { + case 0: + opt &= ~QUICKCOVER_OPT_BIT_SKIP_CLOSE; + break; + case 1: + opt |= QUICKCOVER_OPT_BIT_SKIP_CLOSE; + break; + } + atomic_set(&ts->state.quick_cover_opt, opt); + t_dev_info(dev, "Qcover opt: %s(%d)\n", + (opt & QUICKCOVER_OPT_BIT_SKIP_CLOSE) ? "skip" : "pass", opt); + } + break; + default: + t_dev_info(dev, "Qcover : Unknown state, %d\n", value); + break; + } + + mutex_unlock(&ts->lock); + + return count; +} + +#if defined(SOMC_TOUCH_BRINGUP) +static ssize_t _show_quick_cover_status(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int size = 0; + + size += siw_snprintf(buf, size, "COVER STATUS : %d\n", + ts->cover_status); + + return (ssize_t)size; +} + +static ssize_t _store_quick_cover_status(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int param[4] = {0, }; + int mfts_mode = 0; + int value = 0; + + mfts_mode = siw_touch_boot_mode_check(dev); + if ((mfts_mode >= MINIOS_MFTS_FOLDER) && !ts->role.mfts_lpwg) + return count; + + if (sscanf(buf, "%d", &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + ts->cover_status = value; + param[0] = 0; + param[1] = 1; + param[2] = ts->irq_enable_status; + param[3] = ts->cover_status; + + mutex_lock(&ts->lock); + siw_ops_lpwg(ts, LPWG_UPDATE_ALL, param); + mutex_unlock(&ts->lock); + + return count; +} + +static ssize_t _show_irq_enable_status(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int size = 0; + + size += siw_snprintf(buf, size, "IRQ ENABLE(FORSE SLEEP) : %d\n", + ts->irq_enable_status); + + return (ssize_t)size; +} + +static ssize_t _store_irq_enable_status(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int param[4] = {0, }; + int mfts_mode = 0; + int value = 0; + + mfts_mode = siw_touch_boot_mode_check(dev); + if ((mfts_mode >= MINIOS_MFTS_FOLDER) && !ts->role.mfts_lpwg) + return count; + + if (sscanf(buf, "%d", &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + ts->irq_enable_status = value; + param[0] = 0; + param[1] = 1; + param[2] = ts->irq_enable_status; + param[3] = ts->cover_status; + + mutex_lock(&ts->lock); + siw_ops_lpwg(ts, LPWG_UPDATE_ALL, param); + mutex_unlock(&ts->lock); + + return count; +} +#endif + +static ssize_t _show_version_info(struct device *dev, char *buf) +{ + return siw_touch_get(dev, CMD_VERSION, buf); +} + +static ssize_t _show_atcmd_version_info(struct device *dev, char *buf) +{ + return siw_touch_get(dev, CMD_ATCMD_VERSION, buf); +} + +static ssize_t _show_sp_link_touch_off(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int size = 0; + + size += siw_snprintf(buf, size, "SP link touch status %d\n", + atomic_read(&ts->state.sp_link)); + + return (ssize_t)size; +} + +static ssize_t _store_sp_link_touch_off(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + + if (sscanf(buf, "%d", &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + mutex_lock(&ts->lock); + + siw_touch_irq_control(ts->dev, + (value == SP_CONNECT) ? + INTERRUPT_DISABLE : INTERRUPT_ENABLE); + + t_dev_info(dev, "SP Mirroring %s\n", + (value == SP_CONNECT) ? "Connected" : "Disconnected"); + + atomic_set(&ts->state.sp_link, value); + + mutex_unlock(&ts->lock); + + return count; +} + +static ssize_t _show_irq_state(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int irq_status = atomic_read(&ts->state.irq_enable); + int size = 0; + + size = siw_snprintf(buf, size, + "Irq State : %s\n", + (irq_status) ? "Enabled" : "Disabled"); + + return size; +} + +static ssize_t _store_irq_state(struct device *dev, + const char *buf, size_t count) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int value = 0; + + if (sscanf(buf, "%d", &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + mutex_lock(&ts->lock); + + siw_touch_irq_control(ts->dev, + (value) ? INTERRUPT_ENABLE : INTERRUPT_DISABLE); + + mutex_unlock(&ts->lock); + + return (ssize_t)count; +} + +static ssize_t _show_irq_level(struct device *dev, char *buf) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + int irq_level = gpio_get_value(touch_irq_pin(ts)); + int size = 0; + +// t_dev_dbg_base(dev, "irq_level : %d\n", irq_level); + size = siw_snprintf(buf, size, + "%d\n", + irq_level); + + return size; +} + +static ssize_t _show_debug_tool_state(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + int size = 0; + + value = atomic_read(&ts->state.debug_tool); + + size += siw_snprintf(buf, size, "Debug tool state : %d\n", value); + + return (ssize_t)size; +} + +static ssize_t _store_debug_tool_state(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int data = 0; + + if (sscanf(buf, "%d", &data) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + if (siw_ops_is_null(ts, notify)) + return count; + + if (data >= DEBUG_TOOL_DISABLE && data <= DEBUG_TOOL_ENABLE) { + atomic_set(&ts->state.debug_tool, data); + siw_ops_notify(ts, NOTIFY_DEBUG_TOOL, (void *)&data); + t_dev_info(dev, "Debug tool state : %s\n", + (data == DEBUG_TOOL_ENABLE) ? + "Debug tool Enabled" : "Debug tool Disabled"); + } else { + t_dev_info(dev, "Debug tool state : Invalid value, %d\n", data); + } + + return count; +} + +static ssize_t _show_debug_option_state(struct device *dev, char *buf) +{ +#if defined(__SIW_SUPPORT_DEBUG_OPTION) + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + int size = 0; + + value = atomic_read(&ts->state.debug_option_mask); + + size += siw_snprintf(buf, size, "Debug option : %d\n", value); + + return (ssize_t)size; +#else + return 0; +#endif +} + +static ssize_t _store_debug_option_state(struct device *dev, + const char *buf, size_t count) +{ +#if defined(__SIW_SUPPORT_DEBUG_OPTION) + struct siw_ts *ts = to_touch_core(dev); + int new_mask = 0; + + if (sscanf(buf, "%d", &new_mask) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + if (new_mask >= DEBUG_OPTION_DISABLE + && new_mask <= DEBUG_OPTION_1_2_3) { + atomic_set(&ts->state.debug_option_mask, new_mask); + t_dev_info(dev, "Debug option : Input masking value = %d", + new_mask); + } else { + t_dev_info(dev, "Debug option : Invalid value, %d\n", new_mask); + } +#endif + return count; +} + +#if defined(__SIW_SUPPORT_ASC) +static ssize_t _show_incoming_call_state(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + int size = 0; + + value = atomic_read(&ts->state.incoming_call); + + size += siw_snprintf(buf, size, "%s : %s(%d)\n", + __func__, + siw_incoming_call_str[value], value); + + return (ssize_t)size; +} + +static ssize_t _store_incoming_call_state(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + int ret = 0; + + if (sscanf(buf, "%d", &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + if (value >= INCOMING_CALL_IDLE && value <= INCOMING_CALL_OFFHOOK) { + if (atomic_read(&ts->state.incoming_call) == value) + return count; + + atomic_set(&ts->state.incoming_call, value); + + ret = siw_touch_blocking_notifier_call(NOTIFY_CALL_STATE, + &ts->state.incoming_call); + + if (ts->asc.use_asc == ASC_ON) + siw_touch_qd_toggle_delta_work_jiffies(ts, 0); + + t_dev_info(dev, "Incoming-call : %s(%d)\n", + siw_incoming_call_str[value], value); + } else { + t_dev_info(dev, "Incoming-call : Unknown %d\n", value); + } + + return count; +} + +static ssize_t _show_asc_param(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + struct asc_info *asc = &(ts->asc); + int size = 0; + + size += siw_snprintf(buf, size, "use_asc = %d\n", + asc->use_asc); + size += siw_snprintf(buf, size, "low_delta_thres = %d\n", + asc->low_delta_thres); + size += siw_snprintf(buf, size, "high_delta_thres = %d\n", + asc->high_delta_thres); + + return (ssize_t)size; +} + +static ssize_t _store_asc_param(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + struct asc_info *asc = &(ts->asc); + unsigned char string[30] = {0, }; + u32 value = 0; + + if (touch_test_quirks(ts, CHIP_QUIRK_NOT_SUPPORT_ASC)) { + t_dev_err(dev, "asc control not supporeted in %s\n", + touch_chip_name(ts)); + goto out; + } + + if (sscanf(buf, "%s %d", string, &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + if (!strcmp(string, "use_asc")) { + if (value == ASC_OFF) { + asc->use_delta_chk = DELTA_CHK_OFF; + siw_touch_change_sensitivity(ts, NORMAL_SENSITIVITY); + asc->use_asc = ASC_OFF; + } else if (value == ASC_ON) { + asc->use_asc = ASC_ON; + mutex_lock(&ts->lock); + siw_ops_asc(ts, ASC_GET_FW_SENSITIVITY, 0); + mutex_unlock(&ts->lock); + siw_touch_qd_toggle_delta_work_jiffies(ts, 0); + } else { + t_dev_info(dev, "ASC : Invalid value, %d\n", value); + } + + } else if (!strcmp(string, "low_delta_thres")) { + asc->low_delta_thres = value; + } else if (!strcmp(string, "high_delta_thres")) { + asc->high_delta_thres = value; + } + +out: + return count; +} + +static ssize_t _show_onhand(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + int size = 0; + + value = atomic_read(&ts->state.onhand); + + t_dev_info(dev, "Hand : %s(%d)\n", siw_onhand_str[value], value); + + size += siw_snprintf(buf, size, "%d\n", value); + + return (ssize_t)size; +} + +static ssize_t _store_onhand(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + + if (sscanf(buf, "%d", &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + if (value >= IN_HAND_ATTN && value <= IN_HAND_NO_ATTN) { + atomic_set(&ts->state.onhand, value); + + if (ts->asc.use_asc == ASC_ON) + siw_touch_qd_toggle_delta_work_jiffies(ts, 0); + + t_dev_info(dev, "Hand : %s(%d)\n", siw_onhand_str[value], value); + } else { + t_dev_info(dev, "Hand : Invalid value, %d\n", value); + } + + return count; +} +#endif /* __SIW_SUPPORT_ASC */ + +static ssize_t _show_module_info(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int size = 0; + + size += siw_snprintf(buf, size, "%s/%s/%s, %s\n", + dev_name(dev->parent->parent), + dev_name(dev->parent), + dev_name(dev), + dev_name(&ts->input->dev)); + + return (ssize_t)size; +} + +u32 __weak t_mon_dbg_mask; //instead of using extern +u32 __weak t_bus_dbg_mask; //instead of using extern + +static ssize_t _show_dbg_mask(struct device *dev, char *buf) +{ +// struct siw_ts *ts = to_touch_core(dev); + int size = 0; + + size += siw_snprintf(buf, size, + "t_dev_dbg_mask %08Xh\n", t_dev_dbg_mask); + size += siw_snprintf(buf, size, + "t_pr_dbg_mask %08Xh\n", t_pr_dbg_mask); + size += siw_snprintf(buf, size, + "t_mon_dbg_mask %08Xh\n", t_mon_dbg_mask); + size += siw_snprintf(buf, size, + "t_bus_dbg_mask %08Xh\n", t_bus_dbg_mask); + + size += siw_snprintf(buf, size, + "Usage:\n"); + size += siw_snprintf(buf, size, + " t_dev_dbg_mask : echo 0 {mask_value} > dbg_mask\n"); + size += siw_snprintf(buf, size, + " t_pr_dbg_mask : echo 1 {mask_value} > dbg_mask\n"); + size += siw_snprintf(buf, size, + " t_mon_dbg_mask : echo 2 {mask_value} > dbg_mask\n"); + size += siw_snprintf(buf, size, + " t_bus_dbg_mask : echo 3 {mask_value} > dbg_mask\n"); + + return (ssize_t)size; +} + +static void _store_dbg_mask_usage(struct device *dev) +{ + t_dev_info(dev, "Usage:\n"); + t_dev_info(dev, " t_dev_dbg_mask : echo 0 {mask_value(hex)} > dbg_mask\n"); + t_dev_info(dev, " t_pr_dbg_mask : echo 1 {mask_value(hex)} > dbg_mask\n"); + t_dev_info(dev, " t_mon_dbg_mask : echo 2 {mask_value(hex)} > dbg_mask\n"); + t_dev_info(dev, " t_bus_dbg_mask : echo 3 {mask_value(hex)} > dbg_mask\n"); +} + +static ssize_t _store_dbg_mask(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int type = 0; + u32 old_value, new_value = 0; + + if (sscanf(buf, "%d %X", &type, &new_value) <= 0) { + siw_sysfs_err_invalid_param(dev); + _store_dbg_mask_usage(dev); + return count; + } + + switch (type) { + case 0 : + old_value = t_dev_dbg_mask; + t_dev_dbg_mask = new_value; + t_dev_info(dev, "t_dev_dbg_mask changed : %08Xh -> %08xh\n", + old_value, new_value); + break; + case 1 : + old_value = t_pr_dbg_mask; + t_pr_dbg_mask = new_value; + t_dev_info(dev, "t_pr_dbg_mask changed : %08Xh -> %08xh\n", + old_value, new_value); + break; + case 2 : + old_value = t_mon_dbg_mask; + t_mon_dbg_mask = new_value; + t_dev_info(dev, "t_mon_dbg_mask changed : %08Xh -> %08xh\n", + old_value, new_value); + if (!(touch_flags(ts) & TOUCH_USE_MON_THREAD)) { + t_dev_info(dev, "(but, mon thread not activated)\n"); + } + break; + case 3 : + old_value = t_bus_dbg_mask; + t_bus_dbg_mask = new_value; + t_dev_info(dev, "t_bus_dbg_mask changed : %08Xh -> %08xh\n", + old_value, new_value); + break; + default : + _store_dbg_mask_usage(dev); + break; + } + + return count; +} + +static ssize_t _show_dbg_flag(struct device *dev, char *buf) +{ +// struct siw_ts *ts = to_touch_core(dev); + int size = 0; + + size += siw_snprintf(buf, size, + "t_dbg_flag %08Xh\n\n", + t_dbg_flag); + + return (ssize_t)size; +} + +static ssize_t _store_dbg_flag(struct device *dev, + const char *buf, size_t count) +{ +// struct siw_ts *ts = to_touch_core(dev); + u32 old_value, new_value = 0; + + if (sscanf(buf, "%X", &new_value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + old_value = t_dbg_flag; + t_dbg_flag = new_value; + t_dev_info(dev, "t_dbg_flag changed : %08Xh -> %08xh\n", + old_value, new_value); + + return count; +} + +static ssize_t _show_irq_flag(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int size = 0; + + size += siw_snprintf(buf, size, + "irq flag : %08Xh\n\n", + (u32)ts->irqflags_curr); + + return (ssize_t)size; +} + + +static ssize_t _store_irq_flag(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + u32 old_value, new_value = 0; + + if (sscanf(buf, "%X", &new_value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + old_value = (u32)ts->irqflags_curr; + + mutex_lock(&ts->lock); + + irq_set_irq_type(ts->irq, new_value); + ts->irqflags_curr &= ~IRQ_TYPE_SENSE_MASK; + ts->irqflags_curr |= (new_value & IRQ_TYPE_SENSE_MASK); + + mutex_unlock(&ts->lock); + + t_dev_info(dev, "irq flag changed : %08Xh -> %08xh\n", + old_value, (int)ts->irqflags_curr); + + return count; +} + +static ssize_t _store_init_late(struct device *dev, + const char *buf, size_t count) +{ +// struct siw_ts *ts = to_touch_core(dev); + int value = 0; + + if (sscanf(buf, "%X", &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + if (value != 0x55AA) { + goto out; + } + + siw_touch_atomic_notifier_call( + LCD_EVENT_TOUCH_INIT_LATE, + &value); + +out: + return count; +} + +#if defined(SOMC_TOUCH_BRINGUP) +static ssize_t _store_init_late_session(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + + t_dev_info(dev, "%s:start\n", __func__); + + if (ts->session.started) + goto out; + + ts->session.started = sysfs_streq(buf, "0") ? false : true; + + if (ts->session.done) { + t_dev_err(dev, "already session done\n"); + goto out; + } else { + t_dev_info(dev, "start init late session " + "with init late session sysfs\n"); + schedule_delayed_work(&ts->session.work, 0); + } + +out: + return count; +} +#endif + +extern int siw_touch_notify(struct siw_ts *ts, unsigned long event, void *data); + +static ssize_t _store_dbg_notify(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int magic = 0; + int event = 0; + int data = 0; + + if (sscanf(buf, "%X %X %X", + &magic, &event, &data) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + if (magic != 0x5A) { //magic code + goto out; + } + + siw_touch_notify(ts, + (unsigned long)event, (void *)&data); + +out: + return count; +} + +static ssize_t _show_dbg_mon(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + struct siw_ts_thread *ts_thread = &ts->mon_thread; + const char *name = NULL; + int size = 0; + + if (!(touch_flags(ts) & TOUCH_USE_MON_THREAD)) { + size += siw_snprintf(buf, size, "mon thread not enabled\n"); + return (ssize_t)size; + } + + name = (atomic_read(&ts_thread->state) == TS_THREAD_ON) ? "resume" : "pause"; + + size += siw_snprintf(buf, size, "mon thread is %s state\n", name); + + size += siw_snprintf(buf, size, "\nUsage:\n"); + size += siw_snprintf(buf, size, " pause : echo 1 > dbg_mon\n"); + size += siw_snprintf(buf, size, " resume : echo 0 > dbg_mon\n"); + + return (ssize_t)size; +} + +static ssize_t _store_dbg_mon(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + struct siw_touch_chip *chip = NULL; + int pause = 0; + + if (!(touch_flags(ts) & TOUCH_USE_MON_THREAD)) { + t_dev_info(dev, "mon thread not enabled\n"); + return count; + } + + if (sscanf(buf, "%d", &pause) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + chip = to_touch_chip(dev); + + if (chip->driving_mode != LCD_MODE_U3) { + t_dev_info(dev, "can be controlled only in U3\n"); + return count; + } + + if (pause) + siw_touch_mon_pause(dev); + else + siw_touch_mon_resume(dev); + + return count; +} + +enum { + SYSFS_DBG_TEST_NONE = 0, + SYSFS_DBG_TEST_SYMLINK, +}; + +static ssize_t _store_dbg_test(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + char name[16+1] = {0, }; + int magic = 0; + int code = 0; + int value = 0; + + if (sscanf(buf, "%X %X %X %16s", + &magic, &code, &value, name) <= 0) { + siw_sysfs_err_invalid_param(dev); + return count; + } + + if (magic != 0x5A) { //magic code + goto out; + } + + switch (code) { + case SYSFS_DBG_TEST_SYMLINK : + siw_touch_sysfs_del_symlink(ts, name); + if (value) { + siw_touch_sysfs_gen_symlink(ts, name); + } + break; + default : + break; + } + +out: + return count; +} + +static int __store_g_not_allowed(struct device *dev) +{ + struct siw_touch_chip *chip = to_touch_chip(dev); + struct siw_ts *ts = chip->ts; + +#if defined(__SIW_CONFIG_SYSTEM_PM) + if (atomic_read(&ts->state.fb) != FB_RESUME) { + return 1; + } +#endif /* __SIW_CONFIG_SYSTEM_PM */ + + if (atomic_read(&ts->state.pm) != DEV_PM_RESUME) { + return 1; + } + + if (atomic_read(&chip->init) != IC_INIT_DONE) { + return 1; + } + + return 0; +} + +static ssize_t __show_g_state(struct device *dev, char *buf, + int value, const char *name) +{ + int size = 0; + + size += siw_snprintf(buf, size, "%s chk: %s(%08Xh)\n", + name, (value & 0x1) ? "ON" : "OFF", value); + + return (ssize_t)size; +} + +static ssize_t __store_g_state(struct device *dev, + const char *buf, size_t count, + int *val, const char *name) +{ + int value = 0; + + if (sscanf(buf, "%X", &value) <= 0) { + siw_sysfs_err_invalid_param(dev); + return -EINVAL; + } + + t_dev_dbg_base(dev, "%s set: %s(%08Xh)\n", + name, (value & 0x1) ? "ON" : "OFF", value); + + if (val != NULL) + *val = value; + + return count; +} + +static ssize_t _show_glove_state(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = atomic_read(&ts->state.glove); + + return __show_g_state(dev, buf, value, "Glove"); +} + +static ssize_t _store_glove_state(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + int ret = 0; + + ret = __store_g_state(dev, buf, count, &value, "Glove"); + if (ret >= 0) { + mutex_lock(&ts->lock); + atomic_set(&ts->state.glove, value); + if (__store_g_not_allowed(dev)) { + t_dev_warn(dev, "Glove skipped\n"); + goto skip_glove_con; + } + +#if defined(SOMC_TOUCH_BRINGUP) + siw_hal_tc_driving(dev, LCD_MODE_STOP); + siw_hal_tc_driving(dev, LCD_MODE_U3); +#endif + siw_ops_tc_con(ts, TCON_GLOVE, NULL); + +skip_glove_con: + mutex_unlock(&ts->lock); + } + + return count; +} + +static ssize_t _show_grab_state(struct device *dev, char *buf) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = atomic_read(&ts->state.grab); + + return __show_g_state(dev, buf, value, "Grab"); +} + +static ssize_t _store_grab_state(struct device *dev, + const char *buf, size_t count) +{ + struct siw_ts *ts = to_touch_core(dev); + int value = 0; + int ret = 0; + + ret = __store_g_state(dev, buf, count, &value, "Grab"); + if (ret >= 0) { + mutex_lock(&ts->lock); + atomic_set(&ts->state.grab, value); + if (__store_g_not_allowed(dev)) { + t_dev_warn(dev, "Grab skipped\n"); + goto skip_grab_con; + } + + siw_ops_tc_con(ts, TCON_GRAB, NULL); + +skip_grab_con: + mutex_unlock(&ts->lock); + } + + return count; +} + +ssize_t __weak show_sys_con(struct device *dev, char *buf) +{ + return 0; +} + +ssize_t __weak store_sys_con(struct device *dev, + const char *buf, size_t count) +{ + return count; +} + + +#define SIW_TOUCH_ATTR(_name, _show, _store) \ + TOUCH_ATTR(_name, _show, _store) + +#define _SIW_TOUCH_ATTR_T(_name) \ + touch_attr_##_name + + +static SIW_TOUCH_ATTR(platform_data, + _show_plat_data, NULL); +static SIW_TOUCH_ATTR(driver_data, + _show_driver_data, NULL); +static SIW_TOUCH_ATTR(fw_upgrade, + _show_upgrade, + _store_upgrade); +#if defined(__SIW_CONFIG_KNOCK) +static SIW_TOUCH_ATTR(lpwg_data, + _show_lpwg_data, + _store_lpwg_data); +static SIW_TOUCH_ATTR(lpwg_notify, NULL, + _store_lpwg_notify); +static SIW_TOUCH_ATTR(mfts, + _show_mfts_state, + _store_mfts_state); +static SIW_TOUCH_ATTR(mfts_lpwg, + _show_mfts_lpwg, + _store_mfts_lpwg); +#endif /* __SIW_CONFIG_KNOCK */ +#if defined(__SYS_USE_LOCKSCREEN) +static SIW_TOUCH_ATTR(keyguard, + _show_lockscreen_state, + _store_lockscreen_state); +#endif /* __SYS_USE_LOCKSCREEN */ +#if defined(__SYS_USE_IME_STATE) +static SIW_TOUCH_ATTR(ime_status, + _show_ime_state, + _store_ime_state); +#endif /* __SYS_USE_LOCKSCREEN */ +#if defined(SOMC_TOUCH_BRINGUP) +static SIW_TOUCH_ATTR(cover_mode, + _show_quick_cover_mode, + _store_quick_cover_mode); +static SIW_TOUCH_ATTR(cover_status, + _show_quick_cover_status, + _store_quick_cover_status); +static SIW_TOUCH_ATTR(irq_enable, + _show_irq_enable_status, + _store_irq_enable_status); +#else +static SIW_TOUCH_ATTR(quick_cover_status, + _show_quick_cover_state, + _store_quick_cover_state); +#endif +static SIW_TOUCH_ATTR(firmware, + _show_version_info, NULL); +static SIW_TOUCH_ATTR(version, + _show_version_info, NULL); +static SIW_TOUCH_ATTR(testmode_ver, + _show_atcmd_version_info, NULL); +static SIW_TOUCH_ATTR(sp_link_touch_off, + _show_sp_link_touch_off, + _store_sp_link_touch_off); +static SIW_TOUCH_ATTR(irq_state, + _show_irq_state, + _store_irq_state); +static SIW_TOUCH_ATTR(irq_level, + _show_irq_level, NULL); +static SIW_TOUCH_ATTR(debug_tool, + _show_debug_tool_state, + _store_debug_tool_state); +static SIW_TOUCH_ATTR(debug_tool_t, + _show_debug_tool_state, + _store_debug_tool_state); +static SIW_TOUCH_ATTR(debug_option, + _show_debug_option_state, + _store_debug_option_state); +#if defined(__SIW_SUPPORT_ASC) +static SIW_TOUCH_ATTR(incoming_call, + _show_incoming_call_state, + _store_incoming_call_state); +static SIW_TOUCH_ATTR(asc, + _show_asc_param, + _store_asc_param); +static SIW_TOUCH_ATTR(onhand, + _show_onhand, + _store_onhand); +#endif /* __SIW_SUPPORT_ASC */ +static SIW_TOUCH_ATTR(module_info, + _show_module_info, NULL); +static SIW_TOUCH_ATTR(dbg_mask, + _show_dbg_mask, + _store_dbg_mask); +static SIW_TOUCH_ATTR(dbg_flag, + _show_dbg_flag, + _store_dbg_flag); +static SIW_TOUCH_ATTR(irq_flag, + _show_irq_flag, + _store_irq_flag); +static SIW_TOUCH_ATTR(init_late, NULL, + _store_init_late); +static SIW_TOUCH_ATTR(dbg_notify, NULL, + _store_dbg_notify); +static SIW_TOUCH_ATTR(dbg_mon, + _show_dbg_mon, + _store_dbg_mon); +static SIW_TOUCH_ATTR(dbg_test, NULL, + _store_dbg_test); +#if defined(SOMC_TOUCH_BRINGUP) +static SIW_TOUCH_ATTR(glove_mode, + _show_glove_state, + _store_glove_state); +#else +static SIW_TOUCH_ATTR(glove_status, + _show_glove_state, + _store_glove_state); +#endif +static SIW_TOUCH_ATTR(grab_status, + _show_grab_state, + _store_grab_state); +static SIW_TOUCH_ATTR(sys_con, + show_sys_con, + store_sys_con); + +#if defined(SOMC_TOUCH_BRINGUP) +static SIW_TOUCH_ATTR(init_late_session, NULL, + _store_init_late_session); +#endif + +static struct attribute *siw_touch_attribute_list[] = { + &_SIW_TOUCH_ATTR_T(platform_data).attr, + &_SIW_TOUCH_ATTR_T(driver_data).attr, + &_SIW_TOUCH_ATTR_T(module_info).attr, + &_SIW_TOUCH_ATTR_T(dbg_mask).attr, + &_SIW_TOUCH_ATTR_T(dbg_flag).attr, + &_SIW_TOUCH_ATTR_T(init_late).attr, + &_SIW_TOUCH_ATTR_T(dbg_notify).attr, +#if defined(SOMC_TOUCH_BRINGUP) + &_SIW_TOUCH_ATTR_T(init_late_session).attr, +#endif + NULL, +}; + +static const struct attribute_group siw_touch_attribute_group = { + .attrs = siw_touch_attribute_list, +}; + +static struct attribute *siw_touch_attribute_list_normal[] = { + &_SIW_TOUCH_ATTR_T(fw_upgrade).attr, +#if defined(__SIW_CONFIG_KNOCK) + &_SIW_TOUCH_ATTR_T(lpwg_data).attr, + &_SIW_TOUCH_ATTR_T(lpwg_notify).attr, + &_SIW_TOUCH_ATTR_T(mfts).attr, + &_SIW_TOUCH_ATTR_T(mfts_lpwg).attr, +#endif /* __SIW_CONFIG_KNOCK */ +#if defined(__SYS_USE_LOCKSCREEN) + &_SIW_TOUCH_ATTR_T(keyguard.attr), +#endif /* __SYS_USE_LOCKSCREEN */ +#if defined(__SYS_USE_IME_STATE) + &_SIW_TOUCH_ATTR_T(ime_status).attr, +#endif /* __SYS_USE_IME_STATE */ +#if defined(SOMC_TOUCH_BRINGUP) + &_SIW_TOUCH_ATTR_T(cover_mode).attr, + &_SIW_TOUCH_ATTR_T(cover_status).attr, + &_SIW_TOUCH_ATTR_T(irq_enable).attr, +#else + &_SIW_TOUCH_ATTR_T(quick_cover_status).attr, +#endif + &_SIW_TOUCH_ATTR_T(firmware).attr, + &_SIW_TOUCH_ATTR_T(version).attr, + &_SIW_TOUCH_ATTR_T(testmode_ver).attr, + &_SIW_TOUCH_ATTR_T(sp_link_touch_off).attr, + &_SIW_TOUCH_ATTR_T(irq_state).attr, + &_SIW_TOUCH_ATTR_T(irq_level).attr, + &_SIW_TOUCH_ATTR_T(irq_flag).attr, + &_SIW_TOUCH_ATTR_T(debug_tool).attr, + &_SIW_TOUCH_ATTR_T(debug_tool_t).attr, + &_SIW_TOUCH_ATTR_T(debug_option).attr, +#if defined(__SIW_SUPPORT_ASC) + &_SIW_TOUCH_ATTR_T(incoming_call).attr, + &_SIW_TOUCH_ATTR_T(asc).attr, + &_SIW_TOUCH_ATTR_T(onhand).attr, +#endif /* __SIW_SUPPORT_ASC */ + &_SIW_TOUCH_ATTR_T(dbg_mon).attr, + &_SIW_TOUCH_ATTR_T(dbg_test).attr, +#if defined(SOMC_TOUCH_BRINGUP) + &_SIW_TOUCH_ATTR_T(glove_mode).attr, +#else + &_SIW_TOUCH_ATTR_T(glove_status).attr, +#endif + &_SIW_TOUCH_ATTR_T(grab_status).attr, + &_SIW_TOUCH_ATTR_T(sys_con).attr, + NULL, +}; + +static const struct attribute_group siw_touch_attribute_group_normal = { + .attrs = siw_touch_attribute_list_normal, +}; + +static struct attribute *siw_touch_attribute_list_charger[] = { +#if defined(__SIW_CONFIG_KNOCK) + &_SIW_TOUCH_ATTR_T(mfts).attr, + &_SIW_TOUCH_ATTR_T(mfts_lpwg).attr, +#endif + NULL, +}; + +static const struct attribute_group siw_touch_attribute_group_charger = { + .attrs = siw_touch_attribute_list_charger, +}; + +static ssize_t siw_touch_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct siw_ts *ts = + container_of(kobj, struct siw_ts, kobj); + struct siw_touch_attribute *priv = + container_of(attr, struct siw_touch_attribute, attr); + ssize_t ret = 0; + + if (priv->show) + ret = priv->show(ts->dev, buf); + + return ret; +} + +static ssize_t siw_touch_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + struct siw_ts *ts = + container_of(kobj, struct siw_ts, kobj); + struct siw_touch_attribute *priv = + container_of(attr, struct siw_touch_attribute, attr); + ssize_t ret = count; + + if (priv->store) + ret = priv->store(ts->dev, buf, count); + + return ret; +} + +/* + * To reegister SiW's unique sysfs functions + */ +static const struct sysfs_ops siw_touch_sysfs_ops = { + .show = siw_touch_attr_show, + .store = siw_touch_attr_store, +}; + +static struct kobj_type siw_touch_kobj_type = { + .sysfs_ops = &siw_touch_sysfs_ops, +}; + + +int siw_touch_init_sysfs(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + struct device *idev = &ts->input->dev; + struct kobject *kobj = &ts->kobj; + char *name = NULL; + int ret = 0; + + name = (touch_flags(ts) & TOUCH_USE_DRV_NAME_SYSFS) ? + touch_drv_name(ts) : touch_idrv_name(ts); + if (!name) { + name = SIW_TOUCH_INPUT; + } + + ret = kobject_init_and_add(kobj, &siw_touch_kobj_type, + idev->kobj.parent, "%s", name); + if (ret < 0) { + t_dev_err(dev, "failed to create sysfs entry\n"); + goto out; + } + + ret = sysfs_create_group(kobj, &siw_touch_attribute_group); + if (ret < 0) { + t_dev_err(dev, "failed to add sysfs(initial)\n"); + goto out_sys; + } + + return 0; + +out_sys: + kobject_del(kobj); + +out: + return ret; +} + +void siw_touch_free_sysfs(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + struct device *idev = &ts->input->dev; + + if (ts->kobj.parent != idev->kobj.parent) { + t_dev_warn(dev, "Invalid kobject\n"); + return; + } + + sysfs_remove_group(&ts->kobj, &siw_touch_attribute_group); + + kobject_del(&ts->kobj); +} + +int __weak siw_touch_misc_init(struct device *dev) +{ + return 0; +} + +void __weak siw_touch_misc_free(struct device *dev) +{ + +} + +static int siw_touch_add_sysfs_normal(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + struct kobject *kobj = &ts->kobj; + int ret; +#if defined(SOMC_TOUCH_BRINGUP) + char *path_name = "common_touch"; +#endif + + ret = sysfs_create_group(kobj, &siw_touch_attribute_group_normal); + if (ret < 0) { + t_dev_err(dev, "failed to add sysfs(normal)\n"); + goto out; + } +#if defined(SOMC_TOUCH_BRINGUP) + dev_set_name(&ts->virtdev, "%s", path_name); + ret = device_register(&ts->virtdev); + if (ret) + t_dev_err(dev, "failed virtdev register error\n"); + dev_set_drvdata(&ts->virtdev, ts); + ret = sysfs_create_link(&ts->virtdev.kobj, kobj, "touch"); + if (ret) + t_dev_err(dev, "failed sysfs link error\n"); +#endif + ret = siw_ops_sysfs(ts, DRIVER_INIT); + if (ret < 0) { + t_dev_err(dev, "failed to add sysfs(ops)\n"); + goto out_ops_sysfs; + } + + return 0; + +out_ops_sysfs: + sysfs_remove_group(kobj, &siw_touch_attribute_group_normal); + +out: + return ret; +} + +static void siw_touch_del_sysfs_normal(struct siw_ts *ts) +{ +// struct device *dev = ts->dev; + struct kobject *kobj = &ts->kobj; + + siw_ops_sysfs(ts, DRIVER_FREE); + + sysfs_remove_group(kobj, &siw_touch_attribute_group_normal); +} + +static int siw_touch_add_sysfs_charger(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + struct kobject *kobj = &ts->kobj; + int ret; + + ret = sysfs_create_group(kobj, &siw_touch_attribute_group_charger); + if (ret < 0) { + t_dev_err(dev, "failed to add sysfs(charger)\n"); + goto out; + } + +out: + return ret; +} + +static void siw_touch_del_sysfs_charger(struct siw_ts *ts) +{ +// struct device *dev = ts->dev; + struct kobject *kobj = &ts->kobj; + + sysfs_remove_group(kobj, &siw_touch_attribute_group_charger); +} + +static int siw_touch_do_add_sysfs(struct siw_ts *ts) +{ + if (ts->is_charger) { + return siw_touch_add_sysfs_charger(ts); + } + + return siw_touch_add_sysfs_normal(ts); +} + +static void siw_touch_do_del_sysfs(struct siw_ts *ts) +{ + if (ts->is_charger) { + siw_touch_del_sysfs_charger(ts); + return; + } + + siw_touch_del_sysfs_normal(ts); +} + +int siw_touch_add_sysfs(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + struct device *idev = &ts->input->dev; + struct kobject *kobj = &ts->kobj; + int ret = 0; + + if (kobj->parent != idev->kobj.parent) { + t_dev_err(dev, "Invalid kobject\n"); + return -EINVAL; + } + + ret = siw_touch_do_add_sysfs(ts); + if (ret < 0) { + return ret; + } + + siw_touch_misc_init(dev); + + return 0; +} + +void siw_touch_del_sysfs(struct siw_ts *ts) +{ + struct device *dev = ts->dev; + struct device *idev = &ts->input->dev; + struct kobject *kobj = &ts->kobj; + + if (kobj->parent != idev->kobj.parent) { + t_dev_warn(dev, "Invalid kobject\n"); + return; + } + + siw_touch_misc_free(dev); + + siw_touch_do_del_sysfs(ts); +} + + diff --git a/drivers/input/touchscreen/siw/touch_sw49501.c b/drivers/input/touchscreen/siw/touch_sw49501.c new file mode 100644 index 000000000000..8957ce3b7d74 --- /dev/null +++ b/drivers/input/touchscreen/siw/touch_sw49501.c @@ -0,0 +1,313 @@ +/* + * touch_sw49501.c - SiW touch driver glue for SW49501 + * + * Copyright (C) 2016 Silicon Works - http://www.siliconworks.co.kr + * Copyright (C) 2018 Sony Mobile Communications Inc. + * Author: Hyunho Kim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siw_touch.h" +#include "siw_touch_hal.h" +#include "siw_touch_bus.h" + +#define CHIP_ID "9501" +#define CHIP_DEVICE_NAME "SW49501" +#define CHIP_COMPATIBLE_NAME "siw,sw49501" +#define CHIP_DEVICE_DESC "SiW Touch SW49501 Driver" + +#define CHIP_TYPE CHIP_SW49501 + +#define CHIP_MODE_ALLOWED (0 | \ + LCD_MODE_BIT_U3 | \ + LCD_MODE_BIT_U3_QUICKCOVER | \ + LCD_MODE_BIT_STOP | \ + 0) + +#define CHIP_FW_SIZE (96<<10) + +#if defined (SOMC_TOUCH_BRINGUP) +#define CHIP_FLAGS (0 | \ + TOUCH_SKIP_ESD_EVENT | \ + TOUCH_USE_PROBE_INIT_LATE | \ + 0) +#else +#define CHIP_FLAGS (0 | \ + TOUCH_SKIP_ESD_EVENT | \ + 0) +#endif + +#define CHIP_IRQFLAGS (IRQF_TRIGGER_FALLING | IRQF_ONESHOT) + + +#define CHIP_INPUT_ID_BUSTYPE BUS_I2C +#define CHIP_INPUT_ID_VENDOR 0xABCD +#define CHIP_INPUT_ID_PRODUCT 0x9876 +#define CHIP_INPUT_ID_VERSION 0x1234 + +#if defined(CONFIG_ARCH_EXYNOS5) +#define __CHIP_QUIRK_ADD CHIP_QUIRK_NOT_SUPPORT_XFER +#else +#define __CHIP_QUIRK_ADD 0 +#endif + +#define CHIP_QUIRKS (0 | \ + CHIP_QUIRK_NOT_SUPPORT_ASC | \ + CHIP_QUIRK_NOT_SUPPORT_WATCH | \ + CHIP_QUIRK_NOT_SUPPORT_IME | \ + __CHIP_QUIRK_ADD | \ + 0) + +#define CHIP_BUS_TYPE BUS_IF_I2C +#define CHIP_BUF_SIZE 0 +#define CHIP_SPI_MODE -1 +#define CHIP_BPW -1 +#define CHIP_MAX_FREQ -1 +#define CHIP_TX_HDR_SZ I2C_BUS_TX_HDR_SZ +#define CHIP_RX_HDR_SZ I2C_BUS_RX_HDR_SZ +#define CHIP_TX_DUMMY_SZ I2C_BUS_TX_DUMMY_SZ +#define CHIP_RX_DUMMY_SZ I2C_BUS_RX_DUMMY_SZ + +static const struct tci_info chip_tci_info[2] = { + [TCI_1] = { + .tap_count = 2, + .min_intertap = 0, + .max_intertap = 50, + .touch_slop = 5, + .tap_distance = 10, + .intr_delay = 0, + }, + [TCI_2] = { + .tap_count = 0, + .min_intertap = 0, + .max_intertap = 70, + .touch_slop = 100, + .tap_distance = 255, + .intr_delay = 20, + }, +}; + +static const struct siw_hal_reg_quirk chip_reg_quirks[] = { + { .old_addr = SPR_CHIP_TEST, .new_addr = 0x07E, }, + { .old_addr = SPR_BOOT_CTL, .new_addr = 0x00E, }, + { .old_addr = SPR_SRAM_CTL, .new_addr = 0x00F, }, + { .old_addr = SPR_BOOT_STS, .new_addr = 0x010, }, + { .old_addr = SPR_SUBDISP_STS, .new_addr = 0x01D, }, + { .old_addr = TC_VERSION, .new_addr = 0x312, }, + { .old_addr = TC_PRODUCT_ID1, .new_addr = 0x314, }, + { .old_addr = TC_PRODUCT_ID2, .new_addr = 0x315, }, + { .old_addr = INFO_FPC_TYPE, .new_addr = 0x32E, }, + { .old_addr = INFO_CHIP_VERSION, .new_addr = 0x001, }, + { .old_addr = INFO_LOT_NUM, .new_addr = 0x32C, }, + { .old_addr = INFO_DATE, .new_addr = 0x32F, }, + { .old_addr = INFO_TIME, .new_addr = 0x330, }, + { .old_addr = SPR_CODE_OFFSET, .new_addr = 0x0AA, }, + { .old_addr = TC_CONFDN_BASE_ADDR, .new_addr = 0x31C, }, + { .old_addr = TC_FLASH_DN_STS, .new_addr = 0x242, }, + /* */ + { .old_addr = (1<<31), .new_addr = 0, }, /* switch : don't show log */ + /* */ + { .old_addr = CMD_RAW_DATA_REPORT_MODE_READ, .new_addr = 0x0B5, }, + { .old_addr = CMD_RAW_DATA_REPORT_MODE_WRITE, .new_addr = 0xC59, }, + { .old_addr = CODE_ACCESS_ADDR, .new_addr = 0xFD0, }, + { .old_addr = SERIAL_DATA_OFFSET, .new_addr = 0x0AF, }, + { .old_addr = TC_TSP_TEST_STS, .new_addr = 0x26C, }, + { .old_addr = TC_TSP_TEST_PF_RESULT, .new_addr = 0x26D, }, + { .old_addr = DATA_I2CBASE_ADDR, .new_addr = 0xFD1, }, + { .old_addr = PRD_TCM_BASE_ADDR, .new_addr = 0xFD3, }, + { .old_addr = PRD_SERIAL_TCM_OFFSET, .new_addr = 0x0B0, }, + { .old_addr = PRD_TC_MEM_SEL, .new_addr = 0x84A, }, + { .old_addr = PRD_TC_TEST_MODE_CTL, .new_addr = 0xC7E, }, + { .old_addr = PRD_M1_M2_RAW_OFFSET, .new_addr = 0x323, }, + { .old_addr = PRD_TUNE_RESULT_OFFSET, .new_addr = 0x325, }, + { .old_addr = PRD_OPEN3_SHORT_OFFSET, .new_addr = 0x324, }, + { .old_addr = PRD_IC_AIT_START_REG, .new_addr = 0xC7C, }, + { .old_addr = PRD_IC_AIT_DATA_READYSTATUS, .new_addr = 0xC74, }, + /* */ + { .old_addr = TCI_ENABLE_W, .new_addr = 0xC30, }, + { .old_addr = TAP_COUNT_W, .new_addr = 0xC31, }, + { .old_addr = MIN_INTERTAP_W, .new_addr = 0xC32, }, + { .old_addr = MAX_INTERTAP_W, .new_addr = 0xC33, }, + { .old_addr = TOUCH_SLOP_W, .new_addr = 0xC34, }, + { .old_addr = TAP_DISTANCE_W, .new_addr = 0xC35, }, + { .old_addr = INT_DELAY_W, .new_addr = 0xC36, }, + { .old_addr = ACT_AREA_X1_W, .new_addr = 0xC37, }, + { .old_addr = ACT_AREA_Y1_W, .new_addr = 0xC38, }, + { .old_addr = ACT_AREA_X2_W, .new_addr = 0xC39, }, + { .old_addr = ACT_AREA_Y2_W, .new_addr = 0xC3A, }, + /* */ + { .old_addr = TCI_DEBUG_FAIL_CTRL, .new_addr = 0xC50, }, + { .old_addr = TCI_DEBUG_FAIL_STATUS, .new_addr = 0xC51, }, + { .old_addr = TCI_DEBUG_FAIL_BUFFER, .new_addr = 0xC52, }, + /* */ + { .old_addr = SPR_CHARGER_STS, .new_addr = 0xC60, }, + { .old_addr = IME_STATE, .new_addr = 0xC61, }, + /* */ + { .old_addr = GLOVE_EN, .new_addr = 0xC62, }, + { .old_addr = GRAB_EN, .new_addr = 0xC64, }, + /* */ + { .old_addr = ~0, .new_addr = ~0 }, // End signal +}; + +#if defined(__SIW_CONFIG_OF) +/* + * of_device_is_compatible(dev->of_node, CHIP_COMPATIBLE_NAME) + */ +static const struct of_device_id chip_match_ids[] = { + { .compatible = CHIP_COMPATIBLE_NAME }, + { }, +}; +#else +enum CHIP_CAPABILITY { + CHIP_MAX_X = 2160, + CHIP_MAX_Y = 3840, + CHIP_MAX_PRESSURE = 255, + CHIP_MAX_WIDTH = 15, + CHIP_MAX_ORI = 1, + CHIP_MAX_ID = 10, + /* */ + CHIP_HW_RST_DELAY = 210, + CHIP_SW_RST_DELAY = 90, +}; + +#define CHIP_PIN_RESET 0 +#define CHIP_PIN_IRQ 0 +#define CHIP_PIN_MAKER -1 +#define CHIP_PIN_VDD -1 +#define CHIP_PIN_VIO -1 + +#if (CHIP_PIN_RESET == 0) || (CHIP_PIN_IRQ == 0) +// #error Assign external pin & flag first!!! +#endif +#endif /* __SIW_CONFIG_OF */ + +/* use eg. cname=arc1 to change name */ +static char chip_name[32] = CHIP_DEVICE_NAME; +module_param_string(cname, chip_name, sizeof(chip_name), 0); + +/* use eg. dname=arc1 to change name */ +static char chip_drv_name[32] = SIW_TOUCH_NAME; +module_param_string(dname, chip_drv_name, sizeof(chip_drv_name), 0); + +/* use eg. iname=arc1 to change input name */ +static char chip_idrv_name[32] = SIW_TOUCH_INPUT; +module_param_string(iname, chip_idrv_name, sizeof(chip_idrv_name), 0); + +static const struct siw_touch_pdata chip_pdata = { + /* Configuration */ + .chip_id = CHIP_ID, + .chip_name = chip_name, + .drv_name = chip_drv_name, + .idrv_name = chip_idrv_name, + .owner = THIS_MODULE, + .chip_type = CHIP_TYPE, + .mode_allowed = CHIP_MODE_ALLOWED, + .fw_size = CHIP_FW_SIZE, + .flags = CHIP_FLAGS, /* Caution : MSB(bit31) unavailable */ + .irqflags = CHIP_IRQFLAGS, + .quirks = CHIP_QUIRKS, + /* */ + .bus_info = { + .bus_type = CHIP_BUS_TYPE, + .buf_size = CHIP_BUF_SIZE, + .spi_mode = CHIP_SPI_MODE, + .bits_per_word = CHIP_BPW, + .max_freq = CHIP_MAX_FREQ, + .bus_tx_hdr_size = CHIP_TX_HDR_SZ, + .bus_rx_hdr_size = CHIP_RX_HDR_SZ, + .bus_tx_dummy_size = CHIP_TX_DUMMY_SZ, + .bus_rx_dummy_size = CHIP_RX_DUMMY_SZ, + }, +#if defined(__SIW_CONFIG_OF) + .of_match_table = of_match_ptr(chip_match_ids), +#else + .pins = { + .reset_pin = CHIP_PIN_RESET, + .reset_pin_pol = OF_GPIO_ACTIVE_LOW, + .irq_pin = CHIP_PIN_IRQ, + .maker_id_pin = CHIP_PIN_MAKER, + .vdd_pin = CHIP_PIN_VDD, + .vio_pin = CHIP_PIN_VIO, + }, + .caps = { + .max_x = CHIP_MAX_X, + .max_y = CHIP_MAX_Y, + .max_pressure = CHIP_MAX_PRESSURE, + .max_width = CHIP_MAX_WIDTH, + .max_orientation = CHIP_MAX_ORI, + .max_id = CHIP_MAX_ID, + .hw_reset_delay = CHIP_HW_RST_DELAY, + .sw_reset_delay = CHIP_SW_RST_DELAY, + }, +#endif + /* Input Device ID */ + .i_id = { + .bustype = CHIP_INPUT_ID_BUSTYPE, + .vendor = CHIP_INPUT_ID_VENDOR, + .product = CHIP_INPUT_ID_PRODUCT, + .version = CHIP_INPUT_ID_VERSION, + }, + /* */ + //See 'siw_hal_get_default_ops' [siw_touch_hal.c] + .ops = NULL, + /* */ + //See 'siw_hal_get_tci_info' [siw_touch_hal.c] + .tci_info = (void *)chip_tci_info, + .tci_qcover_open = NULL, + .tci_qcover_close = NULL, + //See 'siw_hal_get_swipe_info' [siw_touch_hal.c] + .swipe_ctrl = NULL, + //See 'store_ext_watch_config_font_position' [siw_touch_hal_watch.c] + .watch_win = NULL, + //See 'siw_setup_operations' [siw_touch.c] + .reg_quirks = (void *)chip_reg_quirks, +}; + +static struct siw_touch_chip_data chip_data = { + .pdata = &chip_pdata, + .bus_drv = NULL, +}; + +siw_chip_module_init(CHIP_DEVICE_NAME, + chip_data, + CHIP_DEVICE_DESC, + "kimhh@siliconworks.co.kr"); + + +__siw_setup_str("siw_chip_name=", siw_setup_chip_name, chip_name); +__siw_setup_str("siw_drv_name=", siw_setup_drv_name, chip_drv_name); +__siw_setup_str("siw_idrv_name=", siw_setup_idrv_name, chip_idrv_name); + + +