diff options
-rw-r--r-- | nt36xxx/nt36xxx.c | 115 | ||||
-rw-r--r-- | nt36xxx/nt36xxx.h | 28 | ||||
-rw-r--r-- | nt36xxx/nt36xxx_ext_usi.c | 169 | ||||
-rw-r--r-- | nt36xxx/nt36xxx_mem_map.h | 2 |
4 files changed, 291 insertions, 23 deletions
diff --git a/nt36xxx/nt36xxx.c b/nt36xxx/nt36xxx.c index a2437cd..0a54594 100644 --- a/nt36xxx/nt36xxx.c +++ b/nt36xxx/nt36xxx.c @@ -31,6 +31,8 @@ #define NVT_PRODUCT_ID 0x7806 #define NVT_VERSION 0x0100 +#define INFO_BUF_SIZE (64 + 1) + #if defined(CONFIG_DRM_PANEL) #include <drm/drm_panel.h> #elif defined(CONFIG_DRM_MSM) @@ -1453,6 +1455,9 @@ static enum power_supply_property pen_battery_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_LEVEL, +#if NVT_TOUCH_EXT_USI + POWER_SUPPLY_PROP_SERIAL_NUMBER, +#endif POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_SCOPE, }; @@ -1498,6 +1503,15 @@ static int pen_get_battery_property(struct power_supply *psy, break; +#if NVT_TOUCH_EXT_USI + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + /* the latest serial number */ + mutex_lock(&ts->lock); + val->strval = ts->battery_serial_number_str; + mutex_unlock(&ts->lock); + + break; +#endif case POWER_SUPPLY_PROP_STATUS: val->intval = POWER_SUPPLY_STATUS_UNKNOWN; break; @@ -1550,6 +1564,50 @@ static void pen_clean_battery(struct power_supply *battery) kfree(psy_desc); } +#if NVT_TOUCH_EXT_USI +static void process_usi_responses(uint16_t info_buf_flags, const uint8_t *info_buf) +{ + uint32_t pen_serial_high; + uint32_t pen_serial_low; + uint8_t pen_bat_capa; + + if (info_buf_flags & USI_GID_FLAG) { + nvt_usi_store_gid(info_buf + USI_GID_OFFSET); + nvt_usi_get_serial_number(&pen_serial_high, &pen_serial_low); + if (ts->pen_serial_high != pen_serial_high || + ts->pen_serial_low != pen_serial_low) { + int idx = 0; + int sz = sizeof(ts->battery_serial_number_str); + + idx += scnprintf(ts->battery_serial_number_str + idx, sz - idx, + "%08X", pen_serial_high); + idx += scnprintf(ts->battery_serial_number_str + idx, sz - idx, + "%08X", pen_serial_low); + + ts->pen_serial_high = pen_serial_high; + ts->pen_serial_low = pen_serial_low; + + power_supply_changed(ts->pen_bat_psy); + } + } + + if (info_buf_flags & USI_BATTERY_FLAG) { + nvt_usi_store_battery(info_buf + USI_BATTERY_OFFSET); + nvt_usi_get_battery(&pen_bat_capa); + if (ts->pen_bat_capa != pen_bat_capa) { + ts->pen_bat_capa = pen_bat_capa; + power_supply_changed(ts->pen_bat_psy); + } + } + + if (info_buf_flags & USI_FW_VERSION_FLAG) + nvt_usi_store_fw_version(info_buf + USI_FW_VERSION_OFFSET); + + if (info_buf_flags & USI_CAPABILITY_FLAG) + nvt_usi_store_capability(info_buf + USI_CAPABILITY_OFFSET); +} +#endif + static irqreturn_t nvt_ts_isr(int irq, void *handle) { struct nvt_ts_data *ts = (struct nvt_ts_data *)handle; @@ -1592,6 +1650,11 @@ static irqreturn_t nvt_ts_work_func(int irq, void *data) uint32_t pen_btn2 = 0; uint8_t touch_freq_index; uint8_t pen_freq_index; +#if NVT_TOUCH_EXT_USI + uint8_t info_buf[INFO_BUF_SIZE] = {0}; + uint16_t info_buf_flags; + uint32_t pen_serial_low; +#endif if (!ts->probe_done) return IRQ_HANDLED; @@ -1871,10 +1934,6 @@ static irqreturn_t nvt_ts_work_func(int irq, void *data) #endif pen_btn1 = (uint32_t)(point_data[77] & 0x01); pen_btn2 = (uint32_t)((point_data[77] >> 1) & 0x01); - if (point_data[78] && ts->pen_bat_capa != (uint32_t)point_data[78]) { - ts->pen_bat_capa = (uint32_t)point_data[78]; - power_supply_changed(ts->pen_bat_psy); - } // printk("x=%d,y=%d,p=%d,tx=%d,ty=%d,d=%d,b1=%d,b2=%d,bat=%d\n", pen_x, pen_y, pen_pressure, // pen_tilt_x, pen_tilt_y, pen_distance, pen_btn1, pen_btn2, pen_battery); @@ -1902,13 +1961,35 @@ static irqreturn_t nvt_ts_work_func(int irq, void *data) input_report_key(ts->pen_input_dev, BTN_TOOL_PEN, 1); input_report_key(ts->pen_input_dev, BTN_STYLUS, pen_btn1); input_report_key(ts->pen_input_dev, BTN_STYLUS2, pen_btn2); +#if NVT_TOUCH_EXT_USI + /* + * Input Subsystem doesn't support 64bits serial number. + * So we only reports the lower 32bit. + */ + if (!nvt_usi_get_serial_number(NULL, &pen_serial_low)) + input_event(ts->pen_input_dev, EV_MSC, + MSC_SERIAL, pen_serial_low); +#endif + input_sync(ts->pen_input_dev); +#if NVT_TOUCH_EXT_USI + info_buf_flags = point_data[63] + (point_data[64] << 8); + + if (info_buf_flags) { + nvt_set_page(ts->mmap->EB_INFO_ADDR); + info_buf[0] = ts->mmap->EB_INFO_ADDR & 0x7F; + CTP_SPI_READ(ts->client, info_buf, INFO_BUF_SIZE); + nvt_set_page(ts->mmap->EVENT_BUF_ADDR); + + process_usi_responses(info_buf_flags, info_buf); + } +#endif } else if (ts->pen_format_id == 0xF0) { // report Pen ID } else { NVT_ERR("Unknown pen format id!\n"); goto XFER_ERROR; } - } else { // pen_format_id = 0xFF, i.e. no pen present + } else if (ts->pen_active) { // pen_format_id = 0xFF and a pen was reporting input_set_timestamp(ts->pen_input_dev, ts->timestamp); /* Snapshot some stylus context information for offload */ @@ -1929,9 +2010,15 @@ static irqreturn_t nvt_ts_work_func(int irq, void *data) input_report_key(ts->pen_input_dev, BTN_TOOL_PEN, 0); input_report_key(ts->pen_input_dev, BTN_STYLUS, 0); input_report_key(ts->pen_input_dev, BTN_STYLUS2, 0); +#if NVT_TOUCH_EXT_USI + if (!nvt_usi_get_serial_number(NULL, &pen_serial_low)) + input_event(ts->pen_input_dev, EV_MSC, MSC_SERIAL, pen_serial_low); +#endif + input_sync(ts->pen_input_dev); +#if NVT_TOUCH_EXT_USI + nvt_usi_clear_stylus_read_map(); +#endif } - - input_sync(ts->pen_input_dev); } /* if (ts->pen_support) */ /* Check any sensing freq hopping for touch or stylus. */ @@ -2400,6 +2487,10 @@ static int32_t nvt_ts_probe(struct spi_device *client) input_set_abs_params(ts->pen_input_dev, ABS_TILT_Y, PEN_TILT_MIN, PEN_TILT_MAX, 0, 0); +#if NVT_TOUCH_EXT_USI + __set_bit(EV_MSC, ts->pen_input_dev->evbit); + __set_bit(MSC_SERIAL, ts->pen_input_dev->mscbit); +#endif snprintf(ts->pen_phys, sizeof(ts->pen_phys), "input/pen"); ts->pen_input_dev->name = NVT_PEN_NAME; ts->pen_input_dev->uniq = ts->pen_input_dev->name; @@ -2883,6 +2974,9 @@ int nvt_ts_suspend(struct device *dev) { uint8_t buf[4] = {0}; uint32_t i = 0; +#if NVT_TOUCH_EXT_USI + uint32_t pen_serial_low; +#endif if (!ts->bTouchIsAwake) { NVT_LOG("Touch is already suspend\n"); @@ -2956,12 +3050,19 @@ int nvt_ts_suspend(struct device *dev) input_report_key(ts->pen_input_dev, BTN_TOOL_PEN, 0); input_report_key(ts->pen_input_dev, BTN_STYLUS, 0); input_report_key(ts->pen_input_dev, BTN_STYLUS2, 0); +#if NVT_TOUCH_EXT_USI + if (!nvt_usi_get_serial_number(NULL, &pen_serial_low)) + input_event(ts->pen_input_dev, EV_MSC, MSC_SERIAL, pen_serial_low); +#endif input_sync(ts->pen_input_dev); ts->pen_active = 0; ts->pen_offload_coord_timestamp = ts->timestamp; memset(&ts->pen_offload_coord, 0, sizeof(ts->pen_offload_coord)); +#if NVT_TOUCH_EXT_USI + nvt_usi_clear_stylus_read_map(); +#endif } #if WAKEUP_GESTURE_DEFAULT diff --git a/nt36xxx/nt36xxx.h b/nt36xxx/nt36xxx.h index 66ce04e..c23f8d8 100644 --- a/nt36xxx/nt36xxx.h +++ b/nt36xxx/nt36xxx.h @@ -265,6 +265,11 @@ struct nvt_ts_data { uint8_t pen_format_id; uint32_t pen_bat_capa; struct power_supply *pen_bat_psy; +#if NVT_TOUCH_EXT_USI + char battery_serial_number_str[17]; /* 16 hex digits */ + uint32_t pen_serial_high; /* transducer serial number high 32 bits */ + uint32_t pen_serial_low; /* transducer serial number low 32 bits */ +#endif #if NVT_TOUCH_EXT_API uint16_t dttw_touch_area_max; uint16_t dttw_touch_area_min; @@ -404,6 +409,29 @@ extern void nvt_set_dttw(bool check_result); #if NVT_TOUCH_EXT_USI extern int32_t nvt_extra_usi_init(void); extern void nvt_extra_usi_deinit(void); +extern int32_t nvt_usi_clear_stylus_read_map(void); +extern int32_t nvt_usi_store_battery(const uint8_t *buf_bat); +extern int32_t nvt_usi_store_capability(const uint8_t *buf_cap); +extern int32_t nvt_usi_store_fw_version(const uint8_t *buf_fw_ver); +extern int32_t nvt_usi_store_gid(const uint8_t *buf_gid); + +extern int32_t nvt_usi_get_battery(uint8_t *bat); +extern int32_t nvt_usi_get_serial_number(uint32_t *serial_high, uint32_t *serial_low); +/* Flags for the responses of the USI read commands */ +enum { + USI_GID_FLAG = 1U << 0, /* C.GetGID() */ + USI_BATTERY_FLAG = 1U << 1, /* C.GetBattery() */ + USI_CAPABILITY_FLAG = 1U << 2, /* C.GetCapacity() */ + USI_FW_VERSION_FLAG = 1U << 3, /* C.GetFirmwareVersion() */ +}; + +/* location of the data in the response buffer */ +enum { + USI_GID_OFFSET = 1, + USI_BATTERY_OFFSET = 13, + USI_FW_VERSION_OFFSET = 15, + USI_CAPABILITY_OFFSET = 17 +}; #endif #if NVT_TOUCH_ESD_PROTECT diff --git a/nt36xxx/nt36xxx_ext_usi.c b/nt36xxx/nt36xxx_ext_usi.c index 56e0eb4..55d3209 100644 --- a/nt36xxx/nt36xxx_ext_usi.c +++ b/nt36xxx/nt36xxx_ext_usi.c @@ -26,6 +26,10 @@ #if NVT_TOUCH_EXT_USI +#define GID_NUM 12 +#define CAP_NUM 12 +#define FW_VER_NUM 2 + /* * The following HID Report Descriptor is copied from * USIv2-HID-Report-Descriptor.h from universalstylus.org. @@ -465,6 +469,21 @@ static uint8_t USI_report_descriptor_v2_0[] = { 0xc0 // END_COLLECTION }; +struct nvt_usi_context { + /* + * Bit Fields: + * A bit represents if the corresponding read command is done or not. + */ + uint32_t stylus_read_map; + + /* responses from a paired stylus. */ + uint8_t stylus_cap[CAP_NUM]; /* C.GetCapability() */ + uint8_t stylus_GID[GID_NUM]; /* C.GetGID() */ + uint8_t stylus_fw_ver[FW_VER_NUM]; /* C.GetFirmwareVersion() */ + uint8_t stylus_battery; /* C.GetBattery() */ +}; + +static struct nvt_usi_context *usi_ctx; #define DEFAULT_STYLUS_INDEX 1 @@ -537,6 +556,121 @@ static int32_t device_release(struct inode *inode, struct file *file) return 0; } +int32_t nvt_usi_store_gid(const uint8_t *buf_gid) +{ + if (!usi_ctx) + return -EINVAL; + + memcpy(usi_ctx->stylus_GID, buf_gid, sizeof(usi_ctx->stylus_GID)); + usi_ctx->stylus_read_map |= USI_GID_FLAG; + + return 0; +} + +int32_t nvt_usi_store_fw_version(const uint8_t *buf_fw_ver) +{ + if (!usi_ctx) + return -EINVAL; + + memcpy(usi_ctx->stylus_fw_ver, buf_fw_ver, + sizeof(usi_ctx->stylus_fw_ver)); + usi_ctx->stylus_read_map |= USI_FW_VERSION_FLAG; + + return 0; +} + +int32_t nvt_usi_store_capability(const uint8_t *buf_cap) +{ + if (!usi_ctx) + return -EINVAL; + + memcpy(usi_ctx->stylus_cap, buf_cap, sizeof(usi_ctx->stylus_cap)); + usi_ctx->stylus_read_map |= USI_CAPABILITY_FLAG; + + return 0; +} + +int32_t nvt_usi_store_battery(const uint8_t *buf_bat) +{ + if (!usi_ctx) + return -EINVAL; + + usi_ctx->stylus_battery = buf_bat[0]; + usi_ctx->stylus_read_map |= USI_BATTERY_FLAG; + + return 0; +} + +int32_t nvt_usi_get_battery(uint8_t *bat) +{ + if (!usi_ctx) + return -EINVAL; + + if (!(usi_ctx->stylus_read_map & USI_BATTERY_FLAG)) + return -ENODATA; + + *bat = usi_ctx->stylus_battery; + + return 0; +} + +int32_t nvt_usi_get_serial_number(uint32_t *serial_high, uint32_t *serial_low) +{ + if (!usi_ctx) + return -EINVAL; + + if (!(usi_ctx->stylus_read_map & USI_GID_FLAG)) + return -ENODATA; + + if (serial_low) + *serial_low = usi_ctx->stylus_GID[0] | + usi_ctx->stylus_GID[1] << 8 | + usi_ctx->stylus_GID[2] << 16 | + usi_ctx->stylus_GID[3] << 24; + + if (serial_high) + *serial_high = usi_ctx->stylus_GID[4] | + usi_ctx->stylus_GID[5] << 8 | + usi_ctx->stylus_GID[6] << 16 | + usi_ctx->stylus_GID[7] << 24; + + return 0; +} + +int32_t nvt_usi_clear_stylus_read_map(void) +{ + if (!usi_ctx) + return -EINVAL; + + usi_ctx->stylus_read_map = 0; + + return 0; +} + +#define USI_HID_FIRMWARE_INFO_READY (USI_GID_FLAG | USI_FW_VERSION_FLAG) +static int32_t get_hid_firmware_info(uint8_t *hid_buf) +{ + if ((usi_ctx->stylus_read_map & USI_HID_FIRMWARE_INFO_READY) != + USI_HID_FIRMWARE_INFO_READY) + return -ENODATA; + + /* USI 2.0 spec. 7.3.3.1.3 Get Stylus Firmware Info */ + /* 64bits Transducer Serial Number : GID0 ~ GID3 */ + memcpy(hid_buf + 2, usi_ctx->stylus_GID, 8); + + /* 32bits Transducer Serial Number Part 2 : GID2 ~ GID3 */ + memcpy(hid_buf + 10, usi_ctx->stylus_GID + 4, 4); + + /* VID/PID : GID4 ~ GID5 */ + memcpy(hid_buf + 14, usi_ctx->stylus_GID + 8, 4); + + /* FW version major/minor */ + hid_buf[18] = usi_ctx->stylus_fw_ver[1]; + hid_buf[19] = usi_ctx->stylus_fw_ver[0]; + + return 0; +} + static struct hid_feature_report_info *get_feature_report_info(int32_t rpt_id) { int32_t i; @@ -673,22 +807,11 @@ static int32_t get_hid_feature_report(uint8_t *hid_buf, int32_t buf_size, } break; case HID_REPORTID_GET_FIRMWARE: - ret = get_usi_data(spi_buf, rpt_info->vendor_get_cmd, 14); - if (ret > 0) { - /* Transducer Serial Number: 64bits */ - memcpy(hid_buf + 2, spi_buf + 1, 8); - /* Transducer Serial Number part 2: 32bits */ - memcpy(hid_buf + 10, spi_buf + 5, 4); - /* Vendor ID: 16bits */ - memcpy(hid_buf + 14, spi_buf + 9, 2); - /* Product ID: 16bits */ - memcpy(hid_buf + 16, spi_buf + 11, 2); - /* Major version: 8bits */ - memcpy(hid_buf + 18, spi_buf + 13, 1); - /* Minor version: 8bits */ - memcpy(hid_buf + 19, spi_buf + 14, 1); + ret = get_hid_firmware_info(hid_buf); + if (!ret) ret = rpt_info->size; - } + else /* data is not ready yet */ + ret = 0; break; case HID_REPORTID_GET_PROTOCOL: hid_buf[2] = 2; @@ -893,12 +1016,24 @@ int32_t nvt_extra_usi_init(void) int32_t ret; NVT_LOG("++\n"); + + usi_ctx = kzalloc(sizeof(*usi_ctx), GFP_KERNEL); + if (!usi_ctx) + return -ENOMEM; + ret = misc_register(&nvt_hid_usi_dev); if (ret < 0) { NVT_ERR("Register %s failed\n", nvt_hid_usi_dev.name); - return ret; + goto usi_init_error; } NVT_LOG("--\n"); + + return ret; + +usi_init_error: + kfree(usi_ctx); + usi_ctx = NULL; + return ret; } @@ -906,6 +1041,8 @@ void nvt_extra_usi_deinit(void) { NVT_LOG("++\n"); misc_deregister(&nvt_hid_usi_dev); + kfree(usi_ctx); + usi_ctx = NULL; NVT_LOG("--\n"); } #endif diff --git a/nt36xxx/nt36xxx_mem_map.h b/nt36xxx/nt36xxx_mem_map.h index a80ff11..559e54f 100644 --- a/nt36xxx/nt36xxx_mem_map.h +++ b/nt36xxx/nt36xxx_mem_map.h @@ -73,6 +73,7 @@ struct nvt_ts_mem_map { uint32_t DMA_CRC_EN_ADDR; uint32_t BLD_ILM_DLM_CRC_ADDR; uint32_t DMA_CRC_FLAG_ADDR; + uint32_t EB_INFO_ADDR; }; struct nvt_ts_hw_info { @@ -81,6 +82,7 @@ struct nvt_ts_hw_info { static const struct nvt_ts_mem_map NT36523_memory_map = { .EVENT_BUF_ADDR = 0x2FE00, + .EB_INFO_ADDR = 0x2B32A, .RAW_PIPE0_ADDR = 0x30FA0, .RAW_PIPE1_ADDR = 0x30FA0, .BASELINE_ADDR = 0x36510, |