On Thu, Jul 20, 2017 at 09:22:34AM +0900, Anthony Kim wrote: > The HiDeep touchscreen device is a capacitive multi-touch controller > mainly for multi-touch supported devices use. It use I2C interface for > communication to IC and provide axis X, Y, Z locations for ten finger > touch through input event interface to userspace. > > It support the Crimson and the Lime two type IC. They are different > the number of channel supported and FW size. But the working protocol > is same. > > Signed-off-by: Anthony Kim <anthony.kim@xxxxxxxxxx> > --- > .../bindings/input/touchscreen/hideep.txt | 35 + > .../devicetree/bindings/vendor-prefixes.txt | 1 + > drivers/input/touchscreen/Kconfig | 11 + > drivers/input/touchscreen/Makefile | 2 + > drivers/input/touchscreen/hideep.h | 318 +++++++ > drivers/input/touchscreen/hideep_core.c | 916 +++++++++++++++++++++ > drivers/input/touchscreen/hideep_dbg.c | 405 +++++++++ > drivers/input/touchscreen/hideep_dbg.h | 24 + > drivers/input/touchscreen/hideep_isp.c | 592 +++++++++++++ > drivers/input/touchscreen/hideep_isp.h | 96 +++ > drivers/input/touchscreen/hideep_sysfs.c | 245 ++++++ > 11 files changed, 2645 insertions(+) > create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt > create mode 100644 drivers/input/touchscreen/hideep.h > create mode 100644 drivers/input/touchscreen/hideep_core.c > create mode 100644 drivers/input/touchscreen/hideep_dbg.c > create mode 100644 drivers/input/touchscreen/hideep_dbg.h > create mode 100644 drivers/input/touchscreen/hideep_isp.c > create mode 100644 drivers/input/touchscreen/hideep_isp.h > create mode 100644 drivers/input/touchscreen/hideep_sysfs.c > > diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt > new file mode 100644 > index 0000000..76ea8b1 > --- /dev/null > +++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt > @@ -0,0 +1,35 @@ > +* HiDeep Finger and Stylus touchscreen controller > + > +Required properties: > +- compatible : must be "hideep,hideep_crimson" > + or "hideep,hideep_lime". s/_/-/ > +- reg : I2C slave address, (e.g. 0x6C). > +- interrupt-parent : Interrupt controller to which the chip is connected. > +- interrupts : Interrupt to which the chip is connected. > + > +Optional properties: > +- vdd-supply : It is the controller supply for controlling > + main voltage(3.3V) through the regulator. > +- vid-supply : It is the controller supply for controlling > + IO voltage(1.8V) through the regulator. > +- reset-gpios : Define for reset gpio pin. > + It is to use for reset IC. > +- hideep,max_coords : Max value for axis X, Y, W, Z. s/_/-/ IOW, don't use underscores. > + > +Example: > + > +i2c@00000000 { > + > + /* ... */ > + > + touchscreen@6c { > + compatible = "hideep,hideep_ts"; > + reg = <0x6c>; > + interrupt-parent = <&gpx1>; > + interrupts = <2 0>; > + vdd-supply = <&ldo15_reg>"; > + vid-supply = <&ldo18_reg>; > + reset-gpios = <&gpx1 5 0>; > + hideep,max_coords = <1080 1920 65535 65535>; > + }; > +}; > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt > index c03d201..aa2a301 100644 > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt > @@ -131,6 +131,7 @@ gw Gateworks Corporation > hannstar HannStar Display Corporation > haoyu Haoyu Microelectronic Co. Ltd. > hardkernel Hardkernel Co., Ltd > +hideep HiDeep Inc. > himax Himax Technologies, Inc. > hisilicon Hisilicon Limited. > hit Hitachi Ltd. > diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig > index 64b30fe..13e11c7 100644 > --- a/drivers/input/touchscreen/Kconfig > +++ b/drivers/input/touchscreen/Kconfig > @@ -1246,4 +1246,15 @@ config TOUCHSCREEN_ROHM_BU21023 > To compile this driver as a module, choose M here: the > module will be called bu21023_ts. > > +config TOUCHSCREEN_HIDEEP > + tristate "HiDeep Touch IC" > + depends on I2C > + help > + Say Y here if you have a touchscreen using HiDeep. > + > + If unsure, say N. > + > + To compile this driver as a moudle, choose M here : the > + module will be called hideep_ts. > + > endif > diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile > index 6badce8..3aab466 100644 > --- a/drivers/input/touchscreen/Makefile > +++ b/drivers/input/touchscreen/Makefile > @@ -103,3 +103,5 @@ obj-$(CONFIG_TOUCHSCREEN_ZET6223) += zet6223.o > obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o > obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o > obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o > +obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep_ts.o > +hideep_ts-$(CONFIG_TOUCHSCREEN_HIDEEP) := hideep_core.o hideep_sysfs.o hideep_isp.o hideep_dbg.o > diff --git a/drivers/input/touchscreen/hideep.h b/drivers/input/touchscreen/hideep.h > new file mode 100644 > index 0000000..50306a1 > --- /dev/null > +++ b/drivers/input/touchscreen/hideep.h > @@ -0,0 +1,318 @@ > +/* > + * Copyright (C) 2012-2017 Hideep, Inc. > + * > + * 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 Foudation. > + */ > + > +#ifndef _LINUX_HIDEEP_H > +#define _LINUX_HIDEEP_H > + > +/************************************************************************* > + * this is include special HEAD file. > + *************************************************************************/ > +#ifdef CONFIG_FB > +#include <linux/fb.h> > +#include <linux/notifier.h> > +#endif > + > +/************************************************************************* > + * this is include normal HEAD file. > + *************************************************************************/ > +#include <linux/of_gpio.h> > +#include <linux/of.h> > +#include <linux/of_platform.h> > +#include <linux/input.h> > +#include <linux/uaccess.h> > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/proc_fs.h> > +#include <linux/regulator/consumer.h> > +#include <linux/firmware.h> > +#include <linux/pinctrl/consumer.h> > +#include <linux/pinctrl/pinctrl.h> > +#include <linux/pinctrl/machine.h> > +#include <linux/kthread.h> > +#include <linux/random.h> > +#include <linux/slab.h> > +#include <linux/sched.h> > +#include <linux/sched/rt.h> > +#include <linux/task_work.h> > +#include <linux/rtc.h> > +#include <linux/syscalls.h> > +#include <linux/timer.h> > +#include <linux/time.h> > +#include <linux/delay.h> > +#include <linux/gpio.h> > +#include <linux/hrtimer.h> > +#include <linux/i2c.h> > +#include <linux/irq.h> > +#include <linux/interrupt.h> > +#include <linux/io.h> > +#include <linux/input/mt.h> > +#include <linux/fs.h> > +#include <linux/completion.h> > +#include <linux/platform_device.h> > +#include <linux/miscdevice.h> > +#include <linux/init.h> > +#include <linux/gfp.h> > +#include <linux/kobject.h> > +#include <linux/string.h> > +#include <linux/sysfs.h> > +#include <linux/module.h> > +#include <linux/poll.h> > +#include <linux/fcntl.h> > +#include <linux/unistd.h> > +#include <linux/version.h> > +#include <linux/acpi.h> > + > +/************************************************************************* > + * definition part. > + * define is (open, set, enable) if not, is (close, clear, disable) > + * some special switch of functions. > + *************************************************************************/ > + > +/* HIDEEP_PROTOCOL_2_0 is for protocol 2.0. */ > +#define HIDEEP_PROTOCOL_2_0 > + > +/* HIDEEP_TYPE_B_PROTOCOL is for input_dev, if define , using TYPE_B, > + * otherwise TYPE_A. > + */ > +#define HIDEEP_TYPE_B_PROTOCOL > + > +/* HIDEEP_DWZ_VERSION_CHECK if define, it will check dwz version. */ > +#define HIDEEP_DWZ_VERSION_CHECK > + > +/* HIDEEP_SUPPORT_KE if define, it will use key button. */ > +#define HIDEEP_SUPPORT_KEY > + > +/* HIDEEP_SUPPORT_STYLUS if define, it will use stylus mode. */ > +#define HIDEEP_SUPPORT_STYLUS > + > +/************************************************************************* > + * Buffer size > + *************************************************************************/ > +#define FRAME_HEADER_SIZE 8 > +/* if size of system i2c buffer is smaller than this value, need to modify */ > +#define MAX_I2C_BUFFER_SIZE 512 > + > +/************************************************************************* > + * board porting config > + *************************************************************************/ > +#define HIDEEP_DEBUG_DEVICE_NAME "hideep_debug" > +#define HIDEEP_TS_NAME "HiDeep Touchscreen" > +#define HIDEEP_I2C_NAME "hideep_ts" > + > +/************************************************************************* > + * register addr > + *************************************************************************/ > +/* Touch & key event */ > +#define HIDEEP_EVENT_COUNT_ADDR 0x240 > +#define HIDEEP_TOUCH_DATA_ADDR 0x242 > +#define HIDEEP_KEY_DATA_ADDR 0x2A6 > +#define HIDEEP_RAW_DATA_ADDR 0x1000 > + > +/* command list */ > +#define HIDEEP_RESET_CMD 0x9800 > +#define HIDEEP_INTCLR_CMD 0x9802 > +#define HIDEEP_OPMODE_CMD 0x9804 > +#define HIDEEP_SWTICH_CMD 0x9805 > +#define HIDEEP_SLEEP_CMD 0x980D > + > +/************************************************************************* > + * multi-touch & key definitions. > + *************************************************************************/ > +#define HIDEEP_MT_MAX 10 > +#define HIDEEP_KEY_MAX 3 > + > +/* multi touch event bit */ > +#define HIDEEP_MT_ALWAYS_REPORT 0 > +#define HIDEEP_MT_TOUCHED 1 > +#define HIDEEP_MT_FIRST_CONTACT 2 > +#define HIDEEP_MT_DRAG_MOVE 3 > +#define HIDEEP_MT_RELEASED 4 > +#define HIDEEP_MT_PINCH 5 > +#define HIDEEP_MT_PRESSURE 6 > + > +/* key event bit */ > +#define HIDEEP_KEY_RELEASED 0x20 > +#define HIDEEP_KEY_PRESSED 0x40 > +#define HIDEEP_KEY_FIRST_PRESSED 0x80 > +#define HIDEEP_KEY_PRESSED_MASK 0xC0 > + > +/************************************************************************* > + * HIDEEP PROTOCOL > + *************************************************************************/ > +#ifdef HIDEEP_PROTOCOL_2_0 > +struct hideep_mt_t { > + unsigned short x; > + unsigned short y; > + unsigned short z; > + unsigned char w; > + unsigned char flag; > + unsigned char type; > + unsigned char index; > +}; > +#else > +struct hideep_mt_t { > + unsigned char flag; > + unsigned char index; > + unsigned short x; > + unsigned short y; > + unsigned char z; > + unsigned char w; > +}; > +#endif > + > +struct hideep_key_t { > + unsigned char flag; > + unsigned int key; > +}; > + > +/************************************************************************* > + * IC State > + *************************************************************************/ > +enum e_dev_state { > + state_init = 1, > + state_normal, > + state_sleep, > + state_updating, > + state_debugging, > +}; > + > +/************************************************************************* > + * Firmware info > + *************************************************************************/ > +struct dwz_info_t { > + unsigned int code_start; > + unsigned char code_crc[12]; > + > + unsigned int c_code_start; > + unsigned short c_code_len; > + unsigned short gen_ver; > + > + unsigned int vr_start; > + unsigned short vr_len; > + unsigned short rsv0; > + > + unsigned int ft_start; > + unsigned short ft_len; > + unsigned short vr_version; > + > + unsigned short boot_ver; > + unsigned short core_ver; > + unsigned short custom_ver; > + unsigned short release_ver; > + > + unsigned char factory_id; > + unsigned char panel_type; > + unsigned char model_name[6]; > + unsigned short product_code; > + unsigned short extra_option; > + > + unsigned short product_id; > + unsigned short vendor_id; > +}; > + > +/************************************************************************* > + * driver information for hideep_t by device tree > + *************************************************************************/ > +struct hideep_platform_data_t { > + unsigned int max_x; > + unsigned int max_y; > + unsigned int max_z; > + unsigned int max_w; > + > +#ifdef CONFIG_OF > + int reset_gpio; > + > + struct regulator *vcc_vdd; > + struct regulator *vcc_vid; > +#endif > +}; > + > +struct hideep_debug_dev_t { > + struct miscdevice misc; > + > + wait_queue_head_t i_packet; > + > + unsigned int ready; > + unsigned char *vr_buff; > + unsigned char *img_buff; > + unsigned short vr_size; > + unsigned short img_size; > + > + bool debug_enable; > + bool release_flag; > + > + struct hideep_t *ts; > +}; > + > +struct hideep_function_list_t { > + //core > + int (*i2c_read)(struct hideep_t *, unsigned short, unsigned short, > + unsigned char *); > + int (*i2c_write)(struct hideep_t *, unsigned short, unsigned short, > + unsigned char *); > + void (*reset_ic)(struct hideep_t *); > + void (*power)(struct hideep_t *, int); > + > + //isp > + int (*update_all)(struct hideep_t *, const char *); > + int (*update_part)(struct hideep_t *, unsigned char *, int, int, bool); > + int (*get_dwz_info)(struct hideep_t *); > + void (*sp_func)(struct hideep_t *, unsigned char *, int, int); > +}; > + > +struct hideep_t { > + struct i2c_client *client; > + struct input_dev *input_dev; > + struct hideep_platform_data_t *p_data; > + > + struct mutex dev_mutex; > + struct mutex i2c_mutex; > + > + struct hideep_debug_dev_t debug_dev; > + struct hideep_function_list_t *hideep_api; > + > + struct task_struct *hthread_event; > + > + bool suspended; > + enum e_dev_state dev_state; > + > +#ifdef CONFIG_FB > + struct notifier_block fb_notif; > +#endif > + > + long tch_bit; > + unsigned int tch_count; > + unsigned int key_count; > + unsigned int lpm_count; > + > + struct hideep_mt_t touch_evt[HIDEEP_MT_MAX]; > + > +#ifdef HIDEEP_SUPPORT_KEY > + struct hideep_key_t key_evt[HIDEEP_KEY_MAX]; > + int key_codes[HIDEEP_KEY_MAX]; > +#endif > + > + unsigned char i2c_buf[256]; > + struct dwz_info_t *dwz_info; > + > + int interrupt_state; > + int fw_size; > +}; > + > +/************************************************************************* > + * function define > + *************************************************************************/ > +int hideep_debug_init(struct hideep_t *ts); > +void hideep_debug_uninit(void); > + > +int hideep_sysfs_init(struct hideep_t *ts); > +int hideep_sysfs_exit(struct hideep_t *ts); > + > +void hideep_isp_init(struct hideep_t *ts); > + > +#endif > diff --git a/drivers/input/touchscreen/hideep_core.c b/drivers/input/touchscreen/hideep_core.c > new file mode 100644 > index 0000000..2b71b84d > --- /dev/null > +++ b/drivers/input/touchscreen/hideep_core.c > @@ -0,0 +1,916 @@ > +/* > + * Copyright (C) 2012-2017 Hideep, Inc. > + * > + * 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 Foudation. > + */ > + > +#include "hideep.h" > + > +#define GET_PAYLOAD_SIZE_FROM_HEADER(x) \ > + ((x[1]>>6) ? ((x[2]*256+x[3])*(x[7]+1)):(x[2]*x[3]*(x[7]+1))) > + > +static int hideep_pwr_on(struct hideep_t *ts) > +{ > + struct hideep_platform_data_t *pdata; > + int ret = 0; > + > + pdata = ts->p_data; > + > +#ifdef CONFIG_OF > + if (!IS_ERR(pdata->vcc_vdd)) { > + dev_info(&ts->client->dev, "hideep:vcc_vdd is enable"); > + ret = regulator_enable(pdata->vcc_vdd); > + if (ret) > + dev_err(&ts->client->dev, > + "Regulator vdd enable failed ret=%d", ret); > + } > + usleep_range(999, 1000); > + > + if (!IS_ERR(pdata->vcc_vid)) { > + dev_info(&ts->client->dev, "hideep:vcc_vid is enable"); > + ret = regulator_enable(pdata->vcc_vid); > + if (ret) > + dev_err(&ts->client->dev, > + "Regulator vcc_vid enable failed ret=%d", ret); > + } > + usleep_range(2999, 3000); > +#endif > + > + return ret; > +} > + > +static int hideep_pwr_off(struct hideep_t *ts) > +{ > + struct hideep_platform_data_t *pdata; > + int ret = 0; > + > + pdata = ts->p_data; > + > +#ifdef CONFIG_OF > + if (pdata->reset_gpio > 0) { > + dev_info(&ts->client->dev, "hideep:disable the reset_gpio"); > + gpio_set_value(pdata->reset_gpio, 0); > + } > + > + if (!IS_ERR(pdata->vcc_vid)) { > + dev_info(&ts->client->dev, "hideep:vcc_vid is disable"); > + ret = regulator_disable(pdata->vcc_vid); > + if (ret) > + dev_err(&ts->client->dev, > + "Regulator vcc_vid enable failed ret=%d", ret); > + } > + > + if (!IS_ERR(pdata->vcc_vdd)) { > + dev_info(&ts->client->dev, "hideep:vcc_vdd is disable"); > + ret = regulator_disable(pdata->vcc_vdd); > + if (ret) > + dev_err(&ts->client->dev, > + "Regulator vdd disable failed ret=%d", ret); > + } > +#endif > + return ret; > +} > + > +static void hideep_power(struct hideep_t *ts, int on) > +{ > + int ret = 0; > + > + if (on) { > + dev_info(&ts->client->dev, "power on"); > + ret = hideep_pwr_on(ts); > + } else { > + dev_info(&ts->client->dev, "power off"); > + ret = hideep_pwr_off(ts); > + } > +} > + > +static int hideep_i2c_read(struct hideep_t *ts, unsigned short addr, > + unsigned short len, unsigned char *buf) > +{ > + int ret = -1; > + unsigned short r_len, raddr; > + int length; > + > + length = len; > + raddr = addr; > + > + dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len); > + > + mutex_lock(&ts->i2c_mutex); > + > + do { > + if (length > MAX_I2C_BUFFER_SIZE) > + r_len = MAX_I2C_BUFFER_SIZE; > + else > + r_len = length; > + > + dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", > + raddr, r_len); > + > + ret = i2c_master_send(ts->client, (char *)&raddr, 2); > + > + if (ret < 0) > + goto i2c_err; > + > + ret = i2c_master_recv(ts->client, (char *)buf, r_len); > + length -= MAX_I2C_BUFFER_SIZE; > + buf += MAX_I2C_BUFFER_SIZE; > + raddr += MAX_I2C_BUFFER_SIZE; > + > + if (ret < 0) > + goto i2c_err; > + } while (length > 0); > + > + mutex_unlock(&ts->i2c_mutex); > + > + return 0; > +i2c_err: > + mutex_unlock(&ts->i2c_mutex); > + return -1; > +} > + > +static int hideep_i2c_write(struct hideep_t *ts, unsigned short addr, > + unsigned short len, unsigned char *buf) > +{ > + int ret = -1; > + > + dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len); > + > + mutex_lock(&ts->i2c_mutex); > + > + // data mangling.. > + ts->i2c_buf[0] = (addr >> 0) & 0xFF; > + ts->i2c_buf[1] = (addr >> 8) & 0xFF; > + memcpy(&ts->i2c_buf[2], buf, len); > + > + ret = i2c_master_send(ts->client, (char *)ts->i2c_buf, len + 2); > + > + if (ret < 0) > + goto i2c_err; > + > + mutex_unlock(&ts->i2c_mutex); > + return 0; > + > +i2c_err: > + mutex_unlock(&ts->i2c_mutex); > + return -1; > +} > + > +#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN) > + > +static void pops_mt(struct hideep_t *ts) > +{ > +#ifdef HIDEEP_TYPE_B_PROTOCOL > + int id; > + int i; > + > + for (i = 0; i < ts->tch_count; i++) { > + id = (ts->touch_evt[i].index >> 0) & 0x0F; > + input_mt_slot(ts->input_dev, id); > + input_mt_report_slot_state(ts->input_dev, > + __GET_MT_TOOL_TYPE(ts->touch_evt[i].type), false); > + input_report_key(ts->input_dev, BTN_TOUCH, false); > + } > +#else > + input_report_key(ts->input_dev, BTN_TOUCH, false); > + input_mt_sync(ts->input_dev); > +#endif > + > + input_sync(ts->input_dev); > +} > + > +static void push_mt(struct hideep_t *ts) > +{ > + int id; > + int i; > + bool btn_up = 0; > + bool btn_dn = 0; > + bool btn_mv = 0; > + int evt = 0; > + > + /* load multi-touch event to input system */ > + for (i = 0; i < ts->tch_count; i++) { > + id = (ts->touch_evt[i].index >> 0) & 0x0F; > + btn_up = (ts->touch_evt[i].flag >> HIDEEP_MT_RELEASED) & 0x01; > + btn_dn = (ts->touch_evt[i].flag >> HIDEEP_MT_FIRST_CONTACT) > + & 0x01; > + btn_mv = (ts->touch_evt[i].flag >> HIDEEP_MT_DRAG_MOVE) & 0x01; > + > + if (btn_up) > + clear_bit(id, &ts->tch_bit); > + else > + __set_bit(id, &ts->tch_bit); > + > + dev_dbg(&ts->client->dev, > + "type = %d, id = %d, i = %d, x = %d, y = %d, z = %d", > + ts->touch_evt[i].type, ts->touch_evt[i].index, > + i, ts->touch_evt[i].x, ts->touch_evt[i].y, > + ts->touch_evt[i].z); > + > +#ifdef HIDEEP_TYPE_B_PROTOCOL > + input_mt_slot(ts->input_dev, id); > + input_mt_report_slot_state(ts->input_dev, > + __GET_MT_TOOL_TYPE(ts->touch_evt[i].type), > + (btn_up == 0)); > + > + if (btn_up == 0) { > + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, > + ts->touch_evt[i].x); > + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, > + ts->touch_evt[i].y); > + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, > + ts->touch_evt[i].z); > + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, > + ts->touch_evt[i].w); > + evt++; > + } > +#else > + if (btn_up) { > + input_mt_sync(ts->input_dev); > + } else { > + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id); > + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, > + ts->touch_evt[i].x); > + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, > + ts->touch_evt[i].y); > + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, > + ts->touch_evt[i].z); > + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, > + ts->touch_evt[i].w); > + input_mt_sync(ts->input_dev); > + evt++; > + } > +#endif > + } > + > + if (ts->tch_bit == 0) > + evt = 0; > + > + input_report_key(ts->input_dev, BTN_TOUCH, evt); > + input_mt_sync_frame(ts->input_dev); > + input_sync(ts->input_dev); > +} > + > +#ifdef HIDEEP_SUPPORT_KEY > +static void pops_ky(struct hideep_t *ts) > +{ > + int i; > + > + for (i = 0; i < ts->tch_count; i++) { > + input_report_key(ts->input_dev, ts->key_codes[i], false); > + input_report_key(ts->input_dev, BTN_TOUCH, false); > + } > + > + input_sync(ts->input_dev); > +} > + > +static void push_ky(struct hideep_t *ts) > +{ > + int i; > + int pressed; > + int key_code; > + int key_status; > + > + for (i = 0; i < ts->key_count; i++) { > + key_code = ts->key_evt[i].flag & 0x0F; > + key_status = ts->key_evt[i].flag & 0xF0; > + pressed = false; > + > + if (key_status & HIDEEP_KEY_PRESSED_MASK) > + pressed = true; > + else > + pressed = false; > + > + input_report_key(ts->input_dev, ts->key_codes[key_code], > + pressed); > + input_report_key(ts->input_dev, BTN_TOUCH, pressed); > + } > + > + input_sync(ts->input_dev); > +} > +#endif > + > +static void hideep_put_event(struct hideep_t *ts) > +{ > + /* mangling touch information */ > + if (ts->tch_count > 0) > + push_mt(ts); > + > +#ifdef HIDEEP_SUPPORT_KEY > + if (ts->key_count > 0) > + push_ky(ts); > +#endif > +} > + > +static int hideep_get_event(struct hideep_t *ts) > +{ > + int ret; > + int touch_count; > + int info_size; > + > + > + /* get touch event count */ > + dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x", > + ts->tch_count, ts->key_count, ts->lpm_count); > + > + /* get touch event information */ > + if (ts->tch_count > HIDEEP_MT_MAX) > + ts->tch_count = 0; > + > + if (ts->key_count > HIDEEP_KEY_MAX) > + ts->key_count = 0; > + > + touch_count = ts->tch_count + ts->key_count; > + > + if (ts->tch_count > 0) { > + info_size = ts->tch_count * sizeof(struct hideep_mt_t); > + ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR, > + info_size, (unsigned char *)ts->touch_evt); > + if (ret < 0) { > + dev_err(&ts->client->dev, "read I2C error."); > + return -1; > + } > + } > + > +#ifdef HIDEEP_SUPPORT_KEY > + if (ts->key_count > 0) { > + info_size = ts->key_count * sizeof(struct hideep_key_t); > + ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR, > + info_size, (unsigned char *)ts->key_evt); > + if (ret < 0) { > + dev_err(&ts->client->dev, "read I2C error."); > + return -1; > + } > + } > +#endif > + > + return touch_count; > +} > + > +static int hideep_event_thread(void *arg) > +{ > + int t_evt; > + > + struct hideep_t *ts = arg; > + > + dev_info(&ts->client->dev, "start event thread"); > + > + while (!kthread_should_stop()) { > + set_current_state(TASK_INTERRUPTIBLE); > + > + schedule(); > + > + t_evt = hideep_get_event(ts); > + > + if (t_evt >= 0) > + hideep_put_event(ts); > + > + if (kthread_should_stop()) > + break; > + } > + > + dev_info(&ts->client->dev, "end thread"); > + return 0; > +} > + > +static irqreturn_t hideep_irq_task(int irq, void *handle) > +{ > + unsigned char i2c_buff[2]; > + int ret; > + > + struct hideep_t *ts = (struct hideep_t *) handle; > + > + dev_dbg(&ts->client->dev, "state = 0x%x, debug = %d", > + ts->dev_state, ts->debug_dev.debug_enable); > + > + if (ts->debug_dev.debug_enable == false && > + ts->dev_state == state_normal) { > + ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR, > + 2, (u8 *)&i2c_buff); > + if (ret < 0) { > + disable_irq(ts->client->irq); > + ts->interrupt_state = 0; > + return IRQ_HANDLED; > + } > + > + ts->tch_count = i2c_buff[0]; > + ts->key_count = i2c_buff[1] & 0x0f; > + ts->lpm_count = i2c_buff[1] & 0xf0; > + > + wake_up_process(ts->hthread_event); > + > + } else if (ts->debug_dev.debug_enable == true > + && ts->dev_state == state_debugging) { > + ret = ts->hideep_api->i2c_read(ts, HIDEEP_RAW_DATA_ADDR, > + FRAME_HEADER_SIZE + ts->debug_dev.img_size, > + ts->debug_dev.img_buff); > + ts->debug_dev.img_size = > + GET_PAYLOAD_SIZE_FROM_HEADER(ts->debug_dev.img_buff); > + ts->debug_dev.ready = 1; > + wake_up_interruptible(&ts->debug_dev.i_packet); > + } > + dev_dbg(&ts->client->dev, "end."); > + > + return IRQ_HANDLED; > +} > + > +static int hideep_capability(struct hideep_t *ts) > +{ > +#ifdef HIDEEP_SUPPORT_KEY > + int i; > + > + ts->key_codes[0] = KEY_MENU; > + ts->key_codes[1] = KEY_HOME; > + ts->key_codes[2] = KEY_BACK; > + > + for (i = 0; i < HIDEEP_KEY_MAX; i++) > + set_bit(ts->key_codes[i], ts->input_dev->keybit); > +#endif > + > + ts->input_dev->name = HIDEEP_TS_NAME; > + ts->input_dev->id.bustype = BUS_I2C; > + > + set_bit(EV_ABS, ts->input_dev->evbit); > + set_bit(EV_KEY, ts->input_dev->evbit); > + set_bit(EV_SYN, ts->input_dev->evbit); > + set_bit(BTN_TOUCH, ts->input_dev->keybit); > + set_bit(MT_TOOL_FINGER, ts->input_dev->keybit); > + set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); > + > + input_set_abs_params(ts->input_dev, ABS_X, 0, ts->p_data->max_x, 0, 0); > + input_set_abs_params(ts->input_dev, ABS_Y, 0, ts->p_data->max_y, 0, 0); > + input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, > + ts->p_data->max_z, 0, 0); > + > +#ifdef HIDEEP_TYPE_B_PROTOCOL > + input_mt_init_slots(ts->input_dev, > + HIDEEP_MT_MAX, INPUT_MT_DIRECT); > +#else > + input_set_abs_params(ts->input_dev, > + ABS_MT_TRACKING_ID, 0, 10, 0, 0); > +#endif > + input_set_abs_params(ts->input_dev, > + ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0); > + input_set_abs_params(ts->input_dev, > + ABS_MT_POSITION_X, 0, ts->p_data->max_x - 1, 0, 0); > + input_set_abs_params(ts->input_dev, > + ABS_MT_POSITION_Y, 0, ts->p_data->max_y - 1, 0, 0); > + input_set_abs_params(ts->input_dev, > + ABS_MT_PRESSURE, 0, ts->p_data->max_z, 0, 0); > + input_set_abs_params(ts->input_dev, > + ABS_MT_TOUCH_MAJOR, 0, ts->p_data->max_w, 0, 0); > + > + return 0; > +} > + > +static void hideep_reset_ic(struct hideep_t *ts) > +{ > + struct hideep_platform_data_t *pdata; > + int ret; > + unsigned char cmd = 0x01; > + > + pdata = ts->p_data; > + > + dev_info(&ts->client->dev, "start!!"); > + > +#ifdef CONFIG_OF > + if (pdata->reset_gpio > 0) { > + dev_info(&ts->client->dev, "hideep:enable the reset_gpio"); > + gpio_set_value(pdata->reset_gpio, 0); > + mdelay(20); > + gpio_set_value(pdata->reset_gpio, 1); > + } else > +#endif > + { > + ret = hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd); > + } > + mdelay(50); > + dev_info(&ts->client->dev, "end!!"); > +} > + > +static void hideep_get_info(struct hideep_t *ts) > +{ > + struct hideep_platform_data_t *pdata; > + unsigned char val[4]; > + > + pdata = ts->p_data; > + ts->hideep_api->i2c_read(ts, 0x28, 4, val); > + > + pdata->max_x = val[3] << 8 | val[2]; > + pdata->max_y = val[1] << 8 | val[0]; > + pdata->max_w = 255; > + pdata->max_z = 255; > + > + dev_info(&ts->client->dev, "X : %d, Y : %d", > + pdata->max_x, pdata->max_y); > +} > + > +static void hideep_init_ic(struct hideep_t *ts) > +{ > + struct hideep_platform_data_t *pdata; > + struct device dev; > + > + pdata = ts->p_data; > + dev = ts->client->dev; > + > + /* power on */ > + ts->hideep_api->power(ts, true); > + ts->dev_state = state_init; > + mdelay(30); > + > + /* ic reset */ > + ts->hideep_api->reset_ic(ts); > +} > + > +static int hideep_resume(struct device *dev) > +{ > + struct hideep_t *ts = dev_get_drvdata(dev); > + unsigned char sleep_cmd = 0x00; > + > + dev_info(dev, "enter"); > + > + mutex_lock(&ts->dev_mutex); > + > + if (ts->dev_state == state_normal) > + goto hideep_resume_exit; > + > + dev_info(dev, "not waiting."); > + ts->dev_state = state_normal; > + > + hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd); > + enable_irq(ts->client->irq); > + ts->interrupt_state = 1; > + > +hideep_resume_exit: > + mdelay(10); > + ts->hideep_api->reset_ic(ts); > + > + mutex_unlock(&ts->dev_mutex); > + dev_info(dev, "exit."); > + return 0; > +} > + > +static int hideep_suspend(struct device *dev) > +{ > + struct hideep_t *ts = dev_get_drvdata(dev); > + unsigned char sleep_cmd = 0x01; > + > + dev_info(dev, "enter"); > + > + mutex_lock(&ts->dev_mutex); > + if (ts->dev_state == state_sleep) > + goto hideep_suspend_exit; > + > + dev_info(dev, "not waiting."); > + ts->dev_state = state_sleep; > + > + /* send sleep command.. */ > + pops_mt(ts); > +#ifdef HIDEEP_SUPPORT_KEY > + pops_ky(ts); > +#endif > + > + /* default deep sleep */ > + hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd); > + disable_irq(ts->client->irq); > + ts->interrupt_state = 0; > + > +hideep_suspend_exit: > + mutex_unlock(&ts->dev_mutex); > + dev_info(dev, "exit."); > + return 0; > +} > + > +#ifdef CONFIG_FB > +static int fb_notifier_callback(struct notifier_block *self, > + unsigned long event, void *data) > +{ > + struct fb_event *evdata = data; > + int *blank; > + > + struct hideep_t *ts = container_of(self, struct hideep_t, fb_notif); > + > + if ((evdata) && (evdata->data) && > + (event == FB_EVENT_BLANK) && > + (ts) && (ts->client)) { > + blank = evdata->data; > + if (*blank == FB_BLANK_UNBLANK) { > + dev_info(&ts->client->dev, "resume"); > + if (ts->suspended == 1) { > + hideep_resume(&ts->client->dev); > + ts->suspended = 0; > + } > + } else if (*blank == FB_BLANK_POWERDOWN) { > + dev_info(&ts->client->dev, "suspend"); > + if (ts->suspended == 0) { > + ts->suspended = 1; > + hideep_suspend(&ts->client->dev); > + } > + } > + } > + > + return 0; > +} > +#endif > + > +#ifdef CONFIG_OF > +static int hideep_parse_dts(struct device *dev, > + struct hideep_platform_data_t *pdata) > +{ > + int ret = 0; > + unsigned int coords[4]; > + struct device_node *np; > + > + dev_info(dev, "enter"); > + np = dev->of_node; > + > + ret = of_property_read_u32_array(np, "hideep,max_coords", coords, 4); > + if (ret) { > + dev_err(dev, "Failed to get max_coords property\n"); > + return ret; > + } > + > + pdata->max_x = coords[0]; > + pdata->max_y = coords[1]; > + pdata->max_w = coords[2]; > + pdata->max_z = coords[3]; > + > + dev_info(dev, "max coord data x : %d\n", pdata->max_x); > + dev_info(dev, "max coord data y : %d\n", pdata->max_y); > + dev_info(dev, "max coord data w : %d\n", pdata->max_w); > + dev_info(dev, "max coord data z : %d\n", pdata->max_z); > + > + /* device tree information get */ > + pdata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); > + dev_info(dev, "reset_gpio = %d, is %s specified", > + pdata->reset_gpio, pdata->reset_gpio < 0 ? "not" : ""); > + if (pdata->reset_gpio) > + ret = gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH, > + "reset-gpios"); > + > + pdata->vcc_vdd = regulator_get(dev, "vdd"); > + pdata->vcc_vid = regulator_get(dev, "vid"); > + > + return ret; > +} > +#endif > + > +static int hideep_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + int ret = 0; > + struct hideep_platform_data_t *p_data; > + struct dwz_info_t *dwz; > + struct hideep_function_list_t *function; > + struct hideep_t *ts; > + > + dev_info(&client->dev, "enter"); > + > + /* check i2c bus */ > + if (!i2c_check_functionality(client->adapter, > + I2C_FUNC_I2C)) { > + dev_err(&client->dev, "check i2c device error"); > + ret = -ENODEV; > + return ret; > + } > + > + /* init platform data */ > + p_data = devm_kzalloc(&client->dev, > + sizeof(struct hideep_platform_data_t), GFP_KERNEL); > + > +#ifdef CONFIG_OF > + if (client->dev.of_node) { > + ret = hideep_parse_dts(&client->dev, p_data); > + if (ret) > + return ret; > + } > +#endif > + > + /* init hideep_t */ > + ts = devm_kzalloc(&client->dev, > + sizeof(struct hideep_t), GFP_KERNEL); > + dwz = devm_kzalloc(&client->dev, > + sizeof(struct dwz_info_t), GFP_KERNEL); > + function = devm_kzalloc(&client->dev, > + sizeof(struct hideep_function_list_t), GFP_KERNEL); > + > + ts->client = client; > + ts->p_data = p_data; > + ts->dwz_info = dwz; > + ts->hideep_api = function; > + > + ts->hideep_api->i2c_read = hideep_i2c_read; > + ts->hideep_api->i2c_write = hideep_i2c_write; > + ts->hideep_api->reset_ic = hideep_reset_ic; > + ts->hideep_api->power = hideep_power; > + > + /* init for isp function */ > + hideep_isp_init(ts); > + > + i2c_set_clientdata(client, ts); > + > + mutex_init(&ts->i2c_mutex); > + mutex_init(&ts->dev_mutex); > + > + if (!client->dev.of_node) > + hideep_get_info(ts); > + > + /* hardware init */ > + hideep_init_ic(ts); > + > +#ifdef HIDEEP_DWZ_VERSION_CHECK > + /* read info */ > + ret = ts->hideep_api->get_dwz_info(ts); > + if (ret < 0) { > + dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret); > + goto hideep_probe_read_dwz_err; > + } > +#endif > + > + /* init input device */ > + ts->input_dev = devm_input_allocate_device(&client->dev); > + if (!ts->input_dev) { > + dev_err(&client->dev, "can't allocate memory for input_dev"); > + ret = -ENOMEM; > + goto hideep_probe_input_dev_memory_err; > + } > + > + hideep_capability(ts); > + > + ret = input_register_device(ts->input_dev); > + if (ret) { > + dev_err(&client->dev, "can't register input_dev"); > + ret = -ENOMEM; > + goto hideep_probe_register_input_dev_err; > + } > + > + input_set_drvdata(ts->input_dev, ts); > + > + /* init event thread */ > + ts->hthread_event = kthread_run(hideep_event_thread, > + ts, "hideep_event_thread"); > + if (IS_ERR(ts->hthread_event)) { > + dev_err(&client->dev, "can't create event thread !!!"); > + ret = PTR_ERR(ts->hthread_event); > + goto hideep_probe_create_thread_err; > + } > + > + dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq); > + if (ts->client->irq <= 0) { > + dev_err(&client->dev, "can't be assigned irq"); > + goto hideep_probe_assigned_irq_err; > + } > + > + if (ts->client->irq) { > + ret = request_threaded_irq(ts->client->irq, NULL, > + hideep_irq_task, > + (IRQF_TRIGGER_LOW | IRQF_ONESHOT), > + ts->client->name, ts); > + disable_irq(ts->client->irq); > + ts->interrupt_state = 0; > + if (ret < 0) { > + dev_err(&client->dev, "fail to get irq, ret = 0x%08x", > + ret); > + goto hideep_probe_request_irq_err; > + } > + } > + > + ret = hideep_debug_init(ts); > + if (ret) { > + dev_err(&client->dev, "fail init debug, ret = 0x%x", ret); > + ret = -1; > + goto hideep_probe_debug_init_err; > + } > + > + ret = hideep_sysfs_init(ts); > + if (ret) { > + dev_err(&client->dev, "fail init sys, ret = 0x%x", ret); > + ret = -1; > + goto hideep_probe_sysfs_init_err; > + } > + > +#ifdef CONFIG_FB > + ts->suspended = 0; > + ts->fb_notif.notifier_call = fb_notifier_callback; > + ret = fb_register_client(&ts->fb_notif); > + if (ret) { > + dev_err(&client->dev, "Unable to register fb_notifier: ret = %d", > + ret); > + ret = -1; > + goto hideep_probe_register_fb_err; > + } > +#endif > + > + > + ts->dev_state = state_normal; > + enable_irq(ts->client->irq); > + ts->interrupt_state = 1; > + > + dev_info(&client->dev, "probe is ok!"); > + return 0; > + > +hideep_probe_register_fb_err: > + hideep_sysfs_exit(ts); > + > +hideep_probe_sysfs_init_err: > + hideep_debug_uninit(); > + > +hideep_probe_debug_init_err: > +hideep_probe_request_irq_err: > + free_irq(ts->client->irq, ts); > + > +hideep_probe_assigned_irq_err: > +hideep_probe_create_thread_err: > + input_unregister_device(ts->input_dev); > + > +hideep_probe_register_input_dev_err: > + if (ts->input_dev) > + input_free_device(ts->input_dev); > + > +hideep_probe_input_dev_memory_err: > +#ifdef HIDEEP_DWZ_VERSION_CHECK > +hideep_probe_read_dwz_err: > +#endif > + > +#ifdef CONFIG_OF > + ts->hideep_api->power(ts, false); > +#endif > + dev_err(&client->dev, "probe err!"); > + return ret; > +} > + > +static int hideep_remove(struct i2c_client *client) > +{ > + struct hideep_t *ts = i2c_get_clientdata(client); > + > + kthread_stop(ts->hthread_event); > + > +#ifdef CONFIG_FB > + if (fb_unregister_client(&ts->fb_notif)) > + dev_info(&client->dev, > + "Error occurred while unregistering fb_notifier"); > +#endif > + > +#ifdef CONFIG_OF > + ts->hideep_api->power(ts, false); > +#endif > + free_irq(client->irq, ts); > + > + input_unregister_device(ts->input_dev); > + hideep_sysfs_exit(ts); > + > + hideep_debug_uninit(); > + devm_kfree(&client->dev, ts); > + return 0; > +} > + > +#ifdef CONFIG_PM > +static const struct dev_pm_ops hideep_pm_ops = { > + .suspend = hideep_suspend, > + .resume = hideep_resume, > +}; > +#endif > + > +static const struct i2c_device_id hideep_dev_idtable[] = { > + { HIDEEP_I2C_NAME, 0 }, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable); > + > +#ifdef CONFIG_ACPI > +static const struct acpi_device_id hideep_acpi_id[] = { > + { "HIDP0001", 0 }, > + {} > +}; > +MODULE_DEVICE_TABLE(acpi, hideep_acpi_id); > +#endif > + > +#ifdef CONFIG_OF > +static const struct of_device_id hideep_match_table[] = { > + { .compatible = "hideep,hideep_lime" }, > + { .compatible = "hideep,hideep_crimson" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, hideep_match_table); > +#endif > + > +static struct i2c_driver hideep_driver = { > + .probe = hideep_probe, > + .remove = hideep_remove, > + .id_table = hideep_dev_idtable, > + .driver = { > + .name = HIDEEP_I2C_NAME, > + .owner = THIS_MODULE, > + .of_match_table = of_match_ptr(hideep_match_table), > + .acpi_match_table = ACPI_PTR(hideep_acpi_id), > + .pm = &hideep_pm_ops, > + }, > +}; > + > +module_i2c_driver(hideep_driver); > + > +MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller"); > +MODULE_AUTHOR("anthony.kim@xxxxxxxxxx"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/input/touchscreen/hideep_dbg.c b/drivers/input/touchscreen/hideep_dbg.c > new file mode 100644 > index 0000000..549f2ad2 > --- /dev/null > +++ b/drivers/input/touchscreen/hideep_dbg.c > @@ -0,0 +1,405 @@ > +/* > + * Copyright (C) 2012-2017 Hideep, Inc. > + * > + * 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 Foudation. > + */ > + > +#include "hideep.h" > +#include "hideep_isp.h" > +#include "hideep_dbg.h" > + > +static struct hideep_debug_dev_t *hdd; > + > +static int hideep_i2c_recv(unsigned int len) > +{ > + int ret = 0; > + > + mutex_lock(&hdd->ts->i2c_mutex); > + ret = i2c_master_recv(hdd->ts->client, hdd->vr_buff, len); > + mutex_unlock(&hdd->ts->i2c_mutex); > + if (ret < 0) > + goto i2c_err; > + > + dev_info(&hdd->ts->client->dev, "(%d)", len); > + return ret; > + > +i2c_err: > + dev_err(&hdd->ts->client->dev, "i2c_err"); > + return ret; > +} > + > +static int hideep_i2c_send(unsigned int len) > +{ > + int ret = 0; > + unsigned char *buff = hdd->vr_buff; > + > + mutex_lock(&hdd->ts->i2c_mutex); > + ret = i2c_master_send(hdd->ts->client, buff, len); > + mutex_unlock(&hdd->ts->i2c_mutex); > + if (ret < 0) > + goto i2c_err; > + > + dev_info(&hdd->ts->client->dev, "(%d)", len); > + return ret; > + > +i2c_err: > + dev_err(&hdd->ts->client->dev, "i2c_err"); > + return ret; > +} > + > +static int hideep_get_vreg(unsigned int addr, unsigned int len) > +{ > + int ret = 0; > + > + ret = hdd->ts->hideep_api->i2c_read(hdd->ts, addr, len, > + hdd->vr_buff); > + if (ret < 0) > + goto i2c_err; > + > + dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len); > + return ret; > + > +i2c_err: > + dev_err(&hdd->ts->client->dev, "i2c_err"); > + return ret; > +} > + > +static int hideep_set_vreg(unsigned int addr, unsigned int len) > +{ > + int ret = 0; > + int wr_remain = len; > + int vr_addr = addr; > + int wr_len = len; > + unsigned char *buff = hdd->vr_buff; > + > + do { > + if (wr_remain >= MAX_VR_BUFF) > + wr_len = MAX_VR_BUFF; > + else > + wr_len = wr_remain; > + > + ret = hdd->ts->hideep_api->i2c_write(hdd->ts, vr_addr, > + wr_len, buff); > + if (ret < 0) > + goto i2c_err; > + > + wr_remain -= MAX_VR_BUFF; > + vr_addr += MAX_VR_BUFF; > + buff += MAX_VR_BUFF; > + } while (wr_remain > 0); > + > + dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len); > + return ret; > + > +i2c_err: > + dev_err(&hdd->ts->client->dev, "i2c_err"); > + return ret; > +} > + > +static int hideep_download_uc(const char __user *uc, size_t count, int offset) > +{ > + int ret; > + unsigned char *ucode; > + > + dev_dbg(&hdd->ts->client->dev, "count = %zu", count); > + > + if (count > hdd->ts->fw_size) { > + dev_err(&hdd->ts->client->dev, "over size data!!!"); > + return -1; > + } > + > + ucode = kmalloc(count, GFP_KERNEL); > + > + ret = copy_from_user(ucode + offset, uc, count); > + if (ret < 0) { > + dev_err(&hdd->ts->client->dev, "ADDR_UC : copy_to_user"); > + kfree(ucode); > + return 0; > + } > + > + disable_irq(hdd->ts->client->irq); > + hdd->ts->interrupt_state = 0; > + hdd->ts->hideep_api->update_part(hdd->ts, ucode, count, offset, false); > + enable_irq(hdd->ts->client->irq); > + hdd->ts->interrupt_state = 1; > + kfree(ucode); > + > + dev_dbg(&hdd->ts->client->dev, "Download_uc(%zu)", count); > + > + return count; > +} > + > +static int hideep_debug_open(struct inode *inode, struct file *file) > +{ > + hdd->release_flag = false; > + > + file->private_data = hdd; > + dev_dbg(&hdd->ts->client->dev, "hideep_debug_open"); > + > + return 0; > +} > + > +static int hideep_debug_release(struct inode *inode, struct file *file) > +{ > + if (!hdd->release_flag) > + return -1; > + hdd->release_flag = false; > + file->private_data = NULL; > + return 0; > +} > + > +static unsigned int hideep_debug_poll(struct file *file, > + struct poll_table_struct *wait) > +{ > + unsigned int mask = 0; > + struct hideep_debug_dev_t *drv_info; > + > + if (file->private_data == NULL) > + return 0; > + > + drv_info = file->private_data; > + > + poll_wait(file, &drv_info->i_packet, wait); > + > + if (drv_info->ready) { > + disable_irq(drv_info->ts->client->irq); > + drv_info->ts->interrupt_state = 0; > + mask |= POLLIN | POLLRDNORM; > + drv_info->ready = 0; > + } > + > + return mask; > +} > + > +static ssize_t hideep_debug_read(struct file *file, char __user *buf, > + size_t count, loff_t *offset) > +{ > + int ret = -1; > + ssize_t rd_len = 0; > + unsigned char *rd_buffer = NULL; > + unsigned char *data = NULL; > + struct hideep_debug_dev_t *drv_info = file->private_data; > + > + dev_dbg(&hdd->ts->client->dev, "count = %zu", count); > + > + if (file->private_data == NULL) > + return 0; > + > + drv_info->vr_size = count; > + rd_len = count; > + > + data = kmalloc(rd_len, GFP_KERNEL); > + > + if (*offset <= HIDEEP_VR_ADDR_LEN) { > + // if offset is not belong to any special command > + if ((*offset & HIDEEP_MAX_RAW_LEN) && > + (*offset < HIDEEP_MAX_RAW_LEN) && > + (drv_info->debug_enable == true)) { > + mutex_lock(&drv_info->ts->dev_mutex); > + rd_buffer = drv_info->img_buff; > + ret = 0; > + mutex_unlock(&drv_info->ts->dev_mutex); > + } else { > + ret = hideep_get_vreg(*offset, rd_len); > + rd_buffer = drv_info->vr_buff; > + } > + if (ret < 0) > + rd_len = 0; > + } else if (*offset & (HIDEEP_I2C_BYPASS)) { > + // if offset is belong to special command "i2c bypass" > + ret = hideep_i2c_recv(rd_len); > + if (ret < 0) { > + dev_err(&hdd->ts->client->dev, "ret = %d", ret); > + rd_len = 0; > + } else { > + rd_buffer = drv_info->vr_buff; > + } > + } else if (*offset & HIDEEP_NVM_DOWNLOAD) { > + // if offset is belong to special command "nvm download" > + rd_len = count; > + memset(data, 0x0, rd_len); > + > + disable_irq(drv_info->ts->client->irq); > + drv_info->ts->interrupt_state = 0; > + mutex_lock(&drv_info->ts->dev_mutex); > + mutex_lock(&drv_info->ts->i2c_mutex); > + > + drv_info->ts->hideep_api->sp_func(drv_info->ts, data, rd_len, > + *offset & 0xfffff); > + > + mutex_unlock(&drv_info->ts->dev_mutex); > + mutex_unlock(&drv_info->ts->i2c_mutex); > + enable_irq(drv_info->ts->client->irq); > + drv_info->ts->interrupt_state = 1; > + > + rd_buffer = data; > + } else { > + dev_err(&hdd->ts->client->dev, "undefined address"); > + kfree(data); > + return 0; > + } > + > + ret = copy_to_user(buf, rd_buffer, rd_len); > + > + if (drv_info->debug_enable == true && drv_info->ready == 0) { > + enable_irq(drv_info->ts->client->irq); > + drv_info->ts->interrupt_state = 1; > + } > + > + if (ret < 0) { > + dev_err(&hdd->ts->client->dev, "error : copy_to_user"); > + kfree(data); > + return -EFAULT; > + } > + > + kfree(data); > + return rd_len; > +} > + > +static ssize_t hideep_debug_write(struct file *file, const char __user *buf, > + size_t count, loff_t *offset) > +{ > + int ret; > + struct hideep_debug_dev_t *drv_info = file->private_data; > + int wr_len = 0; > + > + dev_dbg(&hdd->ts->client->dev, "count = %zu", count); > + if (file->private_data == NULL) > + return 0; > + > + if (*offset <= HIDEEP_VR_ADDR_LEN) { > + // if offset is not belong to any special command > + wr_len = count; > + > + ret = copy_from_user(drv_info->vr_buff, buf, wr_len); > + if (ret < 0) { > + dev_err(&hdd->ts->client->dev, "error : copy_to_user"); > + return -EFAULT; > + } > + > + ret = hideep_set_vreg(*offset, wr_len); > + if (ret < 0) > + wr_len = 0; > + } else if (*offset & (HIDEEP_I2C_BYPASS)) { > + // if offset is belong to special command "i2c bypass" > + wr_len = count; > + ret = copy_from_user(drv_info->vr_buff, buf, wr_len); > + ret = hideep_i2c_send(wr_len); > + } else if (*offset & HIDEEP_NVM_DOWNLOAD) { > + // if offset is belong to special command "nvm download" > + wr_len = hideep_download_uc(buf, count, *offset & 0xfffff); > + } else { > + dev_err(&hdd->ts->client->dev, > + "hideep_write : undefined address, 0x%08x", > + (int)*offset); > + return 0; > + } > + > + return wr_len; > +} > + > +static loff_t hideep_debug_llseek(struct file *file, loff_t off, int whence) > +{ > + loff_t newpos; > + struct hideep_debug_dev_t *drv_info = file->private_data; > + > + dev_dbg(&hdd->ts->client->dev, "off = 0x%08x, whence = %d", > + (unsigned int)off, whence); > + if (file->private_data == NULL) > + return -EFAULT; > + > + switch (whence) { > + /* SEEK_SET */ > + case 0: > + newpos = off; > + break; > + /* SEEK_CUR */ > + case 1: > + dev_dbg(&hdd->ts->client->dev, "set mode off = 0x%08x", > + (unsigned int)off); > + if (off & HIDEEP_RELEASE_FLAG) { > + dev_dbg(&hdd->ts->client->dev, "set release flag"); > + drv_info->release_flag = true; > + } > + newpos = file->f_pos; > + break; > + /* SEEK_END */ > + case 2: > + newpos = file->f_pos; > + break; > + default: > + return -EINVAL; > + } > + > + if (newpos < 0) > + return -EINVAL; > + > + file->f_pos = newpos; > + > + return newpos; > +} > + > +static const struct file_operations hideep_debug_fops = { > + .owner = THIS_MODULE, > + .open = hideep_debug_open, > + .poll = hideep_debug_poll, > + .release = hideep_debug_release, > + .read = hideep_debug_read, > + .write = hideep_debug_write, > + .llseek = hideep_debug_llseek, > +}; > + > +static struct miscdevice hideep_debug_dev = { > + .minor = MISC_DYNAMIC_MINOR, > + .name = HIDEEP_DEBUG_DEVICE_NAME, > + .fops = &hideep_debug_fops > +}; > + > +void hideep_debug_uninit(void) > +{ > + kfree(hdd->vr_buff); > + kfree(hdd->img_buff); > + > + misc_deregister(&hideep_debug_dev); > +} > + > +int hideep_debug_init(struct hideep_t *ts) > +{ > + int ret = 0; > + > + hdd = &ts->debug_dev; > + > + ret = misc_register(&hideep_debug_dev); > + if (ret) { > + dev_err(&ts->client->dev, > + "hideep debug device register fail!!!"); > + goto fail; > + } > + > + init_waitqueue_head(&hdd->i_packet); > + > + hdd->ts = ts; > + hdd->debug_enable = false; > + > + hdd->img_size = 0; > + hdd->vr_size = 0; > + > + hdd->vr_buff = kmalloc(MAX_VR_BUFF, GFP_KERNEL); > + hdd->img_buff = kmalloc(MAX_RAW_SIZE, GFP_KERNEL); > + > + memset(hdd->vr_buff, 0x0, MAX_VR_BUFF); > + memset(hdd->img_buff, 0x0, MAX_RAW_SIZE); > + > + if (!hdd->vr_buff || !hdd->img_buff) > + goto fail; > + > + dev_info(&ts->client->dev, "debug init...."); > + return 0; > + > +fail: > + kfree(hdd->vr_buff); > + kfree(hdd->img_buff); > + return ret; > +} > diff --git a/drivers/input/touchscreen/hideep_dbg.h b/drivers/input/touchscreen/hideep_dbg.h > new file mode 100644 > index 0000000..f18a09f > --- /dev/null > +++ b/drivers/input/touchscreen/hideep_dbg.h > @@ -0,0 +1,24 @@ > +/* > + * Copyright (C) 2012-2017 Hideep, Inc. > + * > + * 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 Foudation. > + */ > + > +#ifndef _LINUX_HIDEEP_DBG_H > +#define _LINUX_HIDEEP_DBG_H > + > +/* Device Driver <==> Application */ > +/* lseek(), write(), read() command */ > +#define HIDEEP_RELEASE_FLAG 0x8000 > +#define HIDEEP_VR_ADDR_LEN 0xFFFF > +#define HIDEEP_MAX_RAW_LEN 0x3000 > +#define HIDEEP_READ_WRITE_VR 0xF000 > +#define HIDEEP_I2C_BYPASS 0x20000000 > + > +#define MAX_VR_BUFF 2048 > + > +/* max tx * max rx * 2 + 8 + 1, 1 is magin size */ > +#define MAX_RAW_SIZE 7369 > +#endif /* _LINUX_HIDEEP_DBG_H */ > diff --git a/drivers/input/touchscreen/hideep_isp.c b/drivers/input/touchscreen/hideep_isp.c > new file mode 100644 > index 0000000..c8d2e932 > --- /dev/null > +++ b/drivers/input/touchscreen/hideep_isp.c > @@ -0,0 +1,592 @@ > +/* > + * Copyright (C) 2012-2017 Hideep, Inc. > + * > + * 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 Foudation. > + */ > + > +#include "hideep.h" > +#include "hideep_isp.h" > + > +static struct pgm_packet packet_w; > +static struct pgm_packet packet_r; > + > +static int hideep_pgm_w_mem(struct hideep_t *ts, unsigned int addr, > + struct pgm_packet *packet, unsigned int len) > +{ > + int ret = 0; > + int i; > + > + if ((len % 4) != 0) > + return -1; > + > + mutex_lock(&ts->i2c_mutex); > + > + packet->header.w[0] = htonl((0x80 | (len / 4-1))); > + packet->header.w[1] = htonl(addr); > + > + for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++) > + packet->payload[i] = htonl(packet->payload[i]); > + > + ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3], > + (len+5)); > + > + if (ret < 0) > + goto err; > + > +err: > + mutex_unlock(&ts->i2c_mutex); > + return ret; > +} > + > +static int hideep_pgm_r_mem(struct hideep_t *ts, unsigned int addr, > + struct pgm_packet *packet, unsigned int len) > +{ > + int ret = 0; > + int i; > + > + if ((len % 4) != 0) > + return -1; > + > + mutex_lock(&ts->i2c_mutex); > + > + packet->header.w[0] = htonl((0x00 | (len / 4-1))); > + packet->header.w[1] = htonl(addr); > + > + ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3], > + 5); > + > + if (ret < 0) > + goto err; > + > + ret = i2c_master_recv(ts->client, (unsigned char *)packet->payload, > + len); > + > + if (ret < 0) > + goto err; > + > + for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++) > + packet->payload[i] = htonl(packet->payload[i]); > + > +err: > + mutex_unlock(&ts->i2c_mutex); > + return ret; > +} > + > +static int hideep_pgm_r_reg(struct hideep_t *ts, unsigned int addr, > + unsigned int *val) > +{ > + int ret = 0; > + struct pgm_packet packet; > + > + packet.header.w[0] = htonl(0x00); > + packet.header.w[1] = htonl(addr); > + > + ret = hideep_pgm_r_mem(ts, addr, &packet, 4); > + > + if (ret < 0) > + goto err; > + > + *val = packet.payload[0]; > + > +err: > + return ret; > +} > + > +static int hideep_pgm_w_reg(struct hideep_t *ts, unsigned int addr, > + unsigned int data) > +{ > + int ret = 0; > + struct pgm_packet packet; > + > + packet.header.w[0] = htonl(0x80); > + packet.header.w[1] = htonl(addr); > + packet.payload[0] = data; > + > + ret = hideep_pgm_w_mem(ts, addr, &packet, 4); > + > + return ret; > +} > + > +#define SW_RESET_IN_PGM(CLK) \ > +{ \ > + hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \ > + hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \ > + hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \ > +} > + > +#define SET_FLASH_PIO(CE) \ > + hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | ((CE) << 1)) > +#define SET_PIO_SIG(X, Y) \ > + hideep_pgm_w_reg(ts, FLASH_BASE + PIO_SIG + (X), Y) > +#define SET_FLASH_HWCONTROL() \ > + hideep_pgm_w_reg(ts, FLASH_CON, 0x00000000) > + > +#define NVM_W_SFR(x, y) \ > +{ \ > + SET_FLASH_PIO(1); \ > + SET_PIO_SIG(x, y); \ > + SET_FLASH_PIO(0); \ > +} > + > +static void get_dwz_from_binary(unsigned char *pres, > + struct dwz_info_t *dwz_info) > +{ > + memcpy(dwz_info, pres + HIDEEP_DWZ_INFO_OFS, > + sizeof(struct dwz_info_t)); > +} > + > +static void hideep_sw_reset(struct hideep_t *ts, unsigned int food) > +{ > + SW_RESET_IN_PGM(food); > +} > + > +static int hideep_enter_pgm(struct hideep_t *ts) > +{ > + int ret = 0; > + int retry_count = 10; > + int retry = 0; > + unsigned int status; > + unsigned int pattern = 0xDF9DAF39; > + > + while (retry < retry_count) { > + i2c_master_send(ts->client, (unsigned char *)&pattern, 4); > + mdelay(1); > + > + /* flush invalid Tx load register */ > + hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01); > + > + hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status); > + > + if (status != htonl(pattern)) { > + retry++; > + dev_err(&ts->client->dev, "enter_pgm : error(%08x):", > + status); > + } else { > + dev_dbg(&ts->client->dev, "found magic code"); > + break; > + } > + } > + > + if (retry < retry_count) { > + hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00); > + hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00); > + hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF); > + hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01); > + hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01); > + hideep_pgm_w_reg(ts, FLASH_TIM, 0x03); > + hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00); > + hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x02); > + > + mdelay(1); > + } else { > + ret = -1; > + } > + > + return ret; > +} > + > +static int hideep_load_dwz(struct hideep_t *ts) > +{ > + int ret = 0; > + struct pgm_packet packet_r; > + > + ret = hideep_enter_pgm(ts); > + > + mdelay(50); > + > + ret = hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO_OFS, &packet_r, > + sizeof(struct dwz_info_t)); > + > + memcpy((unsigned char *)ts->dwz_info, packet_r.payload, > + sizeof(struct dwz_info_t)); > + hideep_sw_reset(ts, 10); > + > + if (ts->dwz_info->product_code & 0x40) { > + /* Crimson IC */ > + ts->fw_size = 1024 * 48; > + } else { > + /* default fw size */ > + ts->fw_size = 1024 * 64; > + } > + > + dev_dbg(&ts->client->dev, "firmware release version : %04x", > + ts->dwz_info->release_ver); > + > + mdelay(50); > + > + return ret; > +} > + > +static int hideep_nvm_unlock(struct hideep_t *ts) > +{ > + int ret = 0; > + unsigned int unmask_code = 0; > + > + ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE); > + > + ret = hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code); > + > + ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE); > + > + /* make it unprotected code */ > + unmask_code &= (~_PROT_MODE); > + > + /* compare unmask code */ > + if (unmask_code != NVM_MASK) > + dev_dbg(&ts->client->dev, "read mask code different 0x%x", > + unmask_code); > + > + ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE); > + SET_FLASH_PIO(0); > + > + NVM_W_SFR(NVM_MASK_OFS, NVM_MASK); > + SET_FLASH_HWCONTROL(); > + ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE); > + > + return ret; > +} > + > +static int hideep_program_page(struct hideep_t *ts, > + unsigned int addr, struct pgm_packet *packet_w) > +{ > + int ret = 0; > + unsigned int pio_cmd = WRONLY; > + unsigned int pio_cmd_page_erase = PERASE; > + unsigned int status; > + int time_out = 0; > + int inf_en = 0; > + unsigned int end_flag = 124; > +#ifndef PGM_BURST_WR > + unsigned int i; > +#endif > + > + hideep_pgm_r_reg(ts, FLASH_STA, &status); > + ret = (status == 0) ? -1:0; > + > + addr = addr & ~(NVM_PAGE_SIZE - 1); > + > + if (addr > INF_SECTION) { > + /* added INF flag set in pio_cmd */ > + addr -= INF_SECTION; > + pio_cmd |= INF; > + pio_cmd_page_erase |= INF; > + inf_en = 1; > + } > + > + SET_FLASH_PIO(0); > + SET_FLASH_PIO(1); > + > + /* first erase */ > + SET_PIO_SIG(pio_cmd_page_erase + addr, 0xFFFFFFFF); > + > + SET_FLASH_PIO(0); > + time_out = 0; > + > + while (1) { > + mdelay(1); > + hideep_pgm_r_reg(ts, FLASH_STA, &status); > + if ((status) != 0) > + break; > + if (time_out++ > 100) > + break; > + } > + SET_FLASH_PIO(1); > + /* first erase end*/ > + > + SET_PIO_SIG(pio_cmd + addr, htonl(packet_w->payload[0])); > + > +#ifdef PGM_BURST_WR > + hideep_pgm_w_mem(ts, (FLASH_BASE + 0x400000) + pio_cmd, > + packet_w, NVM_PAGE_SIZE); > +#else > + for (i = 0; i < NVM_PAGE_SIZE / 4; i++) > + SET_PIO_SIG(pio_cmd + (i<<2), packet_w->payload[i]); > +#endif > + if (inf_en == 0) > + SET_PIO_SIG(end_flag, htonl(packet_w->payload[31])); > + else > + SET_PIO_SIG(end_flag | INF, htonl(packet_w->payload[31])); > + > + SET_FLASH_PIO(0); > + > + mdelay(1); > + > + while (1) { > + hideep_pgm_r_reg(ts, FLASH_STA, &status); > + if ((status) != 0) > + break; > + } > + /* write routine end */ > + > + SET_FLASH_HWCONTROL(); > + > + return ret; > +} > + > +static int hideep_program_nvm(struct hideep_t *ts, const unsigned char *ucode, > + int len, int offset, unsigned char *old_fw) > +{ > + int i; > + int ret = 0; > + int len_r; > + int len_w; > + int addr = 0; > + unsigned int pages; > + > + ret = hideep_enter_pgm(ts); > + > + hideep_nvm_unlock(ts); > + > + pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE; > + addr = offset; > + len_r = len; > + len_w = len_r; > + > + dev_dbg(&ts->client->dev, "pages : %d", pages); > + for (i = 0; i < pages; i++) { > + if (len_r >= NVM_PAGE_SIZE) > + len_w = NVM_PAGE_SIZE; > + > + /* compare */ > + if (old_fw != NULL) > + ret = memcmp(&ucode[addr], &old_fw[addr], len_w); > + > + if (ret != 0 || old_fw == NULL) { > + /* write page */ > + memcpy(packet_w.payload, &(ucode[addr]), len_w); > + > + ret = hideep_program_page(ts, addr, &packet_w); > + mdelay(1); > + if (ret < 0) > + dev_err(&ts->client->dev, > + "hideep_program_nvm : error(%08x):", > + addr); > + } > + > + addr += NVM_PAGE_SIZE; > + len_r -= NVM_PAGE_SIZE; > + len_w = len_r; > + } > + > + return ret; > +} > + > +static int hideep_verify_nvm(struct hideep_t *ts, const unsigned char *ucode, > + int len, int offset) > +{ > + int i, j; > + int ret = 0; > + unsigned char page_chk = 0; > + unsigned int addr = offset; > + unsigned int pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE; > + int len_r = len; > + int len_v = len_r; > + > + for (i = 0; i < pages; i++) { > + if (len_r >= NVM_PAGE_SIZE) > + len_v = NVM_PAGE_SIZE; > + > +#ifdef PGM_BURST_WR > + hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r, > + NVM_PAGE_SIZE); > +#else > + for (j = 0; j < (NVM_PAGE_SIZE >> 2); j++) > + hideep_pgm_r_reg(ts, addr + (j << 2), > + &(packet_r.payload[j])); > +#endif > + page_chk = memcmp(&(ucode[addr]), packet_r.payload, len_v); > + > + if (page_chk != 0) { > + u8 *read = (u8 *)packet_r.payload; > + > + for (j = 0; j < NVM_PAGE_SIZE; j++) > + dev_err(&ts->client->dev, "%02x : %02x", > + ucode[addr+j], read[j]); > + > + dev_err(&ts->client->dev, "verify : error(addr : %d)", > + addr); > + > + ret = -1; > + } > + > + addr += NVM_PAGE_SIZE; > + len_r -= NVM_PAGE_SIZE; > + len_v = len_r; > + } > + > + return ret; > +} > + > +static void hideep_read_nvm(struct hideep_t *ts, unsigned char *data, int len, > + int offset) > +{ > + int ret = 0; > + int pages, i; > + int len_r, len_v; > + int addr = offset; > +#ifndef PGM_BURST_WR > + int j; > +#endif > + > + pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE; > + len_r = len; > + len_v = len_r; > + > + ts->hideep_api->reset_ic(ts); > + > + ret = hideep_enter_pgm(ts); > + > + hideep_nvm_unlock(ts); > + > + for (i = 0; i < pages; i++) { > + if (len_r >= NVM_PAGE_SIZE) > + len_v = NVM_PAGE_SIZE; > + > +#ifdef PGM_BURST_WR > + hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r, > + NVM_PAGE_SIZE); > +#else > + for (j = 0; j < NVM_PAGE_SIZE / 4; j++) > + hideep_pgm_r_reg(ts, addr + (j << 2), > + &(packet_r.payload[j])); > +#endif > + memcpy(&(data[i * NVM_PAGE_SIZE]), &packet_r.payload[0], len_v); > + > + addr += NVM_PAGE_SIZE; > + len_r -= NVM_PAGE_SIZE; > + len_v = len_r; > + } > + > + hideep_sw_reset(ts, 1000); > +} > + > +static int hideep_fw_verify_run(struct hideep_t *ts, unsigned char *fw, > + size_t len, int offset) > +{ > + int ret = 0; > + int retry = 3; > + > + while (retry--) { > + ret = hideep_verify_nvm(ts, fw, len, offset); > + if (ret == 0) { > + dev_dbg(&ts->client->dev, "update success"); > + break; > + } > + dev_err(&ts->client->dev, "download fw failed(%d)", retry); > + } > + > + ret = (retry == 0) ? -1:0; > + > + return ret; > +} > + > +static int hideep_wr_firmware(struct hideep_t *ts, unsigned char *code, > + int len, int offset, bool mode) > +{ > + int ret = 0; > + int firm_len; > + unsigned char *ic_fw; > + unsigned char *dwz_info; > + > + ic_fw = kmalloc(ts->fw_size, GFP_KERNEL); > + dwz_info = kmalloc(sizeof(struct dwz_info_t) + 64, GFP_KERNEL); > + > + memset(dwz_info, 0x0, sizeof(struct dwz_info_t) + 64); > + > + firm_len = len; > + dev_dbg(&ts->client->dev, "fw len : %d, define size : %d", > + firm_len, ts->fw_size); > + > + if (firm_len > ts->fw_size) > + firm_len = ts->fw_size; > + > + dev_dbg(&ts->client->dev, "enter"); > + /* memory dump of target IC */ > + hideep_read_nvm(ts, ic_fw, firm_len, offset); > + /* comparing & programming each page, if the memory of specified > + * page is exactly same, no need to update. > + */ > + ret = hideep_program_nvm(ts, code, firm_len, offset, ic_fw); > + > +#ifdef PGM_VERIFY > + hideep_fw_verify_run(ts, code, firm_len, offset); > + if (ret < 0) { > + if (mode == true) { > + /* clear dwz version, it will be store once again > + * after update success > + */ > + ts->dwz_info->release_ver = 0; > + memcpy(&dwz_info[64], ts->dwz_info, > + sizeof(struct dwz_info_t)); > + ret = hideep_program_nvm(ts, dwz_info, HIDEEP_DWZ_LEN, > + 0x280, NULL); > + } > + } > +#endif > + get_dwz_from_binary(code, ts->dwz_info); > + > + hideep_sw_reset(ts, 1000); > + > + kfree(ic_fw); > + kfree(dwz_info); > + > + return ret; > +} > + > +static int hideep_update_all_firmware(struct hideep_t *ts, const char *fn) > +{ > + int ret = 0; > + const struct firmware *fw_entry; > + unsigned char *fw_buf; > + unsigned int fw_length; > + > + dev_dbg(&ts->client->dev, "enter"); > + ret = request_firmware(&fw_entry, fn, &ts->client->dev); > + > + if (ret != 0) { > + dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret); > + return ret; > + } > + > + fw_buf = (unsigned char *)fw_entry->data; > + fw_length = (unsigned int)fw_entry->size; > + > + /* chip specific code for flash fuse */ > + mutex_lock(&ts->dev_mutex); > + > + ts->dev_state = state_updating; > + > + ret = hideep_wr_firmware(ts, fw_buf, fw_length, 0, true); > + > + ts->dev_state = state_normal; > + > + mutex_unlock(&ts->dev_mutex); > + > + release_firmware(fw_entry); > + > + return ret; > +} > + > +static int hideep_update_part_firmware(struct hideep_t *ts, > + unsigned char *code, int len, int offset, bool mode) > +{ > + int ret = 0; > + > + mutex_lock(&ts->dev_mutex); > + > + ret = hideep_wr_firmware(ts, code, len, offset, false); > + > + mutex_unlock(&ts->dev_mutex); > + > + return ret; > +} > + > +void hideep_isp_init(struct hideep_t *ts) > +{ > + ts->hideep_api->update_all = hideep_update_all_firmware; > + ts->hideep_api->update_part = hideep_update_part_firmware; > + ts->hideep_api->get_dwz_info = hideep_load_dwz; > + ts->hideep_api->sp_func = hideep_read_nvm; > +} > diff --git a/drivers/input/touchscreen/hideep_isp.h b/drivers/input/touchscreen/hideep_isp.h > new file mode 100644 > index 0000000..648f271 > --- /dev/null > +++ b/drivers/input/touchscreen/hideep_isp.h > @@ -0,0 +1,96 @@ > +/* > + * Copyright (C) 2012-2017 Hideep, Inc. > + * > + * 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 Foudation. > + */ > + > +#ifndef _LINUX_HIDEEP_ISP_H > +#define _LINUX_HIDEEP_ISP_H > + > +#define PGM_BURST_WR > +#define PGM_VERIFY > + > +#define NVM_DEFAULT_PAGE 0 > +#define NVM_SFR_WPAGE 1 > +#define NVM_SFR_RPAGE 2 > + > +#define PIO_SIG 0x00400000 > +#define _PROT_MODE 0x03400000 > + > +#define NVM_PAGE_SIZE 128 > + > +#define HIDEEP_NVM_DOWNLOAD 0x10000000 > + > +/************************************************************************* > + * register map > + *************************************************************************/ > +#define YRAM_BASE 0x40000000 > +#define PERIPHERAL_BASE 0x50000000 > +#define ESI_BASE (PERIPHERAL_BASE + 0x00000000) > +#define FLASH_BASE (PERIPHERAL_BASE + 0x01000000) > +#define SYSCON_BASE (PERIPHERAL_BASE + 0x02000000) > + > +#define SYSCON_MOD_CON (SYSCON_BASE + 0x0000) > +#define SYSCON_SPC_CON (SYSCON_BASE + 0x0004) > +#define SYSCON_CLK_CON (SYSCON_BASE + 0x0008) > +#define SYSCON_CLK_ENA (SYSCON_BASE + 0x000C) > +#define SYSCON_RST_CON (SYSCON_BASE + 0x0010) > +#define SYSCON_WDT_CON (SYSCON_BASE + 0x0014) > +#define SYSCON_WDT_CNT (SYSCON_BASE + 0x0018) > +#define SYSCON_PWR_CON (SYSCON_BASE + 0x0020) > +#define SYSCON_PGM_ID (SYSCON_BASE + 0x00F4) > + > +#define FLASH_CON (FLASH_BASE + 0x0000) > +#define FLASH_STA (FLASH_BASE + 0x0004) > +#define FLASH_CFG (FLASH_BASE + 0x0008) > +#define FLASH_TIM (FLASH_BASE + 0x000C) > +#define FLASH_CACHE_CFG (FLASH_BASE + 0x0010) > + > +#define ESI_TX_INVALID (ESI_BASE + 0x0008) > + > +/************************************************************************* > + * flash commands > + *************************************************************************/ > +#define MERASE 0x00010000 > +#define SERASE 0x00020000 > +#define PERASE 0x00040000 > +#define PROG 0x00080000 > +#define WRONLY 0x00100000 > +#define INF 0x00200000 > + > +/************************************************************************* > + * NVM Mask > + *************************************************************************/ > +#ifdef CONFIG_TOUCHSCREEN_HIDEEP_CRIMSON > +#define NVM_MASK_OFS 0x0000000C > +#define NVM_MASK 0x00310000 > +#define INF_SECTION 0x0000C000 > +#endif > +#ifdef CONFIG_TOUCHSCREEN_HIDEEP_LIME > +#define NVM_MASK_OFS 0x0000000C > +#define NVM_MASK 0x0030027B > +#define INF_SECTION 0x00010000 > +#endif > + > +/************************************************************************* > + * DWZ info > + *************************************************************************/ > +#define HIDEEP_BOOT_SECTION 0x00000400 > +#define HIDEEP_BOOT_LEN 0x00000400 > +#define HIDEEP_DWZ_SECTION 0x00000280 > +#define HIDEEP_DWZ_INFO_OFS 0x000002C0 > +#define HIDEEP_DWZ_LEN (HIDEEP_BOOT_SECTION \ > + - HIDEEP_DWZ_SECTION) > + > +struct pgm_packet { > + union { > + unsigned char b[8]; > + unsigned int w[2]; > + } header; > + > + unsigned int payload[NVM_PAGE_SIZE / sizeof(unsigned int)]; > +}; > + > +#endif /* _LINUX_HIDEEP_ISP_H */ > diff --git a/drivers/input/touchscreen/hideep_sysfs.c b/drivers/input/touchscreen/hideep_sysfs.c > new file mode 100644 > index 0000000..7f28fb4 > --- /dev/null > +++ b/drivers/input/touchscreen/hideep_sysfs.c > @@ -0,0 +1,245 @@ > +/* > + * Copyright (C) 2012-2017 Hideep, Inc. > + * > + * 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 Foudation. > + */ > + > +#include "hideep.h" > + > +static ssize_t update_fw(struct device *dev, struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct hideep_t *ts = dev_get_drvdata(dev); > + int mode, ret; > + char *fw_name; > + > + ret = kstrtoint(buf, 8, &mode); > + if (ret) > + return ret; > + > + if (mode == 1) { > + disable_irq(ts->client->irq); > + > + ts->dev_state = state_updating; > + fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin", > + ts->dwz_info->product_id); > + ret = ts->hideep_api->update_all(ts, fw_name); > + > + kfree(fw_name); > + > + enable_irq(ts->client->irq); > + > + ts->dev_state = state_normal; > + if (ret != 0) > + dev_err(dev, "The firmware update failed(%d)", ret); > + } > + > + return count; > +} > + > +static ssize_t fw_version_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + int len = 0; > + struct hideep_t *ts = dev_get_drvdata(dev); > + > + dev_info(dev, "release version : %04x", > + ts->dwz_info->release_ver); > + > + mutex_lock(&ts->dev_mutex); > + len = scnprintf(buf, PAGE_SIZE, > + "%04x\n", ts->dwz_info->release_ver); > + mutex_unlock(&ts->dev_mutex); > + > + return len; > +} > + > +static ssize_t product_id_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + int len = 0; > + struct hideep_t *ts = dev_get_drvdata(dev); > + > + dev_info(dev, "product id : %04x", > + ts->dwz_info->product_id); > + > + mutex_lock(&ts->dev_mutex); > + len = scnprintf(buf, PAGE_SIZE, > + "%04x\n", ts->dwz_info->product_id); > + mutex_unlock(&ts->dev_mutex); > + > + return len; > +} > + > +static ssize_t power_status(struct device *dev, struct device_attribute *attr, > + char *buf) > +{ > + struct hideep_t *ts = dev_get_drvdata(dev); > + int len; > + > + len = scnprintf(buf, PAGE_SIZE, "power status : %s\n", > + (ts->dev_state == state_init) ? "off" : "on"); > + > + return len; > +} > + > +static ssize_t power_control(struct device *dev, struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct hideep_t *ts = dev_get_drvdata(dev); > + int on, ret; > + > + ret = kstrtoint(buf, 8, &on); > + if (ret) > + return ret; > + > + if (on) { > + ts->hideep_api->power(ts, on); > + ts->dev_state = state_normal; > + ts->hideep_api->reset_ic(ts); > + } else { > + ts->hideep_api->power(ts, on); > + ts->dev_state = state_init; > + } > + > + return count; > +} > + > +#ifdef HIDEEP_SUPPORT_STYLUS > +static ssize_t stylus_status(struct device *dev, struct device_attribute *attr, > + char *buf) > +{ > + struct hideep_t *ts = dev_get_drvdata(dev); > + int len; > + unsigned char status[1]; > + > + ts->hideep_api->i2c_read(ts, 0xB00C, 1, status); > + > + len = scnprintf(buf, PAGE_SIZE, "stylus mode enable : %s\n", > + (status[0] == 0x00) ? "off" : "on"); > + > + return len; > +} > + > +static ssize_t stylus_enable(struct device *dev, struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct hideep_t *ts = dev_get_drvdata(dev); > + int on, ret; > + unsigned char data[2]; > + > + ret = kstrtoint(buf, 8, &on); > + if (ret) > + return ret; > + > + data[0] = 0x04; > + > + if (on) { > + data[1] = 0x01; > + ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data); > + } else { > + data[1] = 0x00; > + ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data); > + } > + > + return count; > +} > +#endif > + > +static ssize_t debug_status(struct device *dev, struct device_attribute *attr, > + char *buf) > +{ > + struct hideep_t *ts = dev_get_drvdata(dev); > + int len; > + > + len = scnprintf(buf, PAGE_SIZE, "debug mode : %s\n", > + (ts->debug_dev.debug_enable == 0) ? "off" : "on"); > + > + return len; > +} > + > +static ssize_t debug_mode(struct device *dev, struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct hideep_t *ts = dev_get_drvdata(dev); > + int on, ret; > + unsigned char data[2]; > + > + ret = kstrtoint(buf, 8, &on); > + if (ret) > + return ret; > + > + if (on) { > + ts->debug_dev.debug_enable = 1; > + ts->dev_state = state_debugging; > + } else { > + ts->debug_dev.debug_enable = 0; > + ts->dev_state = state_normal; > + /* set touch mode */ > + data[0] = 0x00; > + data[1] = 0x00; > + ts->hideep_api->i2c_write(ts, HIDEEP_OPMODE_CMD, 2, data); > + if (ts->interrupt_state == 0) { > + data[0] = 0x5A; > + ts->hideep_api->i2c_write(ts, HIDEEP_INTCLR_CMD, 1, > + data); > + enable_irq(ts->client->irq); > + ts->interrupt_state = 1; > + } > + } > + > + return count; > +} > + > +static DEVICE_ATTR(update_fw, 0664, NULL, update_fw); > +static DEVICE_ATTR(version, 0664, fw_version_show, NULL); > +static DEVICE_ATTR(product_id, 0664, product_id_show, NULL); > +static DEVICE_ATTR(power_en, 0664, power_status, power_control); > +static DEVICE_ATTR(debug_en, 0664, debug_status, debug_mode); > +#ifdef HIDEEP_SUPPORT_STYLUS > +static DEVICE_ATTR(stylus_en, 0664, stylus_status, stylus_enable); > +#endif > + > +static struct attribute *hideep_ts_sysfs_entries[] = { > + &dev_attr_update_fw.attr, > + &dev_attr_version.attr, > + &dev_attr_product_id.attr, > + &dev_attr_power_en.attr, > + &dev_attr_debug_en.attr, > +#ifdef HIDEEP_SUPPORT_STYLUS > + &dev_attr_stylus_en.attr, > +#endif > + NULL > +}; > + > +static struct attribute_group hideep_ts_attr_group = { > + .attrs = hideep_ts_sysfs_entries, > +}; > + > +int hideep_sysfs_init(struct hideep_t *ts) > +{ > + int ret; > + struct i2c_client *client = ts->client; > + > + /* Create the files associated with this kobject */ > + ret = sysfs_create_group(&client->dev.kobj, &hideep_ts_attr_group); > + > + dev_info(&ts->client->dev, "device : %s ", client->dev.kobj.name); > + > + if (ret) > + dev_err(&ts->client->dev, "%s: Fail create link error = %d\n", > + __func__, ret); > + > + return ret; > +} > + > +int hideep_sysfs_exit(struct hideep_t *ts) > +{ > + struct i2c_client *client = ts->client; > + > + sysfs_remove_group(&client->dev.kobj, &hideep_ts_attr_group); > + > + return 0; > +} > -- > 2.7.4 > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html