/* * Copyright (c) 2017 Actions Semiconductor Co., Ltd * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief TP Keyboard driver for Actions SoC */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(tpkey, CONFIG_SYS_LOG_INPUT_DEV_LEVEL); #define DT_DRV_COMPAT actions_acts_tpkey #define tp_slaver_addr (0x2A>>1) #define TP_RESET_PIN DT_GPIO_PIN_BY_IDX(DT_DRV_INST(0), reset_gpios, 0) #define TP_RESET_FLAGS DT_GPIO_FLAGS_BY_IDX(DT_DRV_INST(0), reset_gpios, 0) #define TP_ISR_PIN DT_GPIO_PIN_BY_IDX(DT_DRV_INST(0), isr_gpios, 0) #define TP_ISR_FLAGS DT_GPIO_FLAGS_BY_IDX(DT_DRV_INST(0), isr_gpios, 0) #if DT_NODE_HAS_PROP(DT_DRV_INST(0), power_gpios) #define TP_POWER_PIN DT_GPIO_PIN_BY_IDX(DT_DRV_INST(0), power_gpios, 0) #define TP_POWER_FLAGS DT_GPIO_PIN_BY_IDX(DT_DRV_INST(0), power_gpios, 0) #endif struct acts_tpkey_data { input_notify_t notify; struct device *i2c_dev; struct device *gpio_dev; struct device *this_dev; struct gpio_callback key_gpio_cb; struct k_delayed_work timer; }; struct acts_tpkey_config { uint16_t poll_interval_ms; uint32_t poll_total_ms; uint8_t pinmux_size; const struct acts_pin_config *pinmux; }; struct k_timer tpkey_inquiry_timer; u8_t tpkey_flag_timeout; static struct acts_tpkey_data tpkey_acts_ddata; static const struct acts_pin_config pins_tpkey[] = {FOREACH_PIN_MFP(0)}; static const struct acts_tpkey_config tpkey_acts_cdata = { .poll_interval_ms = DT_INST_PROP(0, poll_interval_ms), .poll_total_ms = DT_INST_PROP(0, poll_total_ms), .pinmux = pins_tpkey, .pinmux_size = ARRAY_SIZE(pins_tpkey), }; static void tpkey_acts_enable(struct device *dev); static void tpkey_acts_disable(struct device *dev); static void read_touch(struct input_value *val); void tpkey_detect(struct k_timer *timer) { LOG_DBG("go into tpkey_detect\n"); tpkey_flag_timeout = 1; k_timer_stop(&tpkey_inquiry_timer); } static void KEY_IRQ_callback(struct device *port, struct gpio_callback *cb, uint32_t pins) { sys_trace_void(SYS_TRACE_ID_TP_IRQ); if(DT_INST_PROP(0, lowpower)) { LOG_DBG("inquiry mode\n"); tpkey_flag_timeout = 0; k_timer_start(&tpkey_inquiry_timer, K_MSEC(20), K_MSEC(20)); } if(DT_INST_PROP(0, cb_en)) k_delayed_work_submit(&tpkey_acts_ddata.timer, K_NO_WAIT); sys_trace_end_call(SYS_TRACE_ID_TP_IRQ); } static void tpkey_acts_poll(struct k_work *work) { struct acts_tpkey_data *tpkey = CONTAINER_OF(work, struct acts_tpkey_data, timer); struct device *dev = tpkey->this_dev; struct input_value val; read_touch(&val); tpkey->notify(dev,&val); k_delayed_work_cancel(&tpkey->timer); } static void tpkey_acts_enable(struct device *dev) { struct acts_tpkey_data *tpkey = dev->data; const struct acts_tpkey_config *cfg = dev->config; const struct acts_pin_config *pinconf = cfg->pinmux; gpio_pin_interrupt_configure(tpkey->gpio_dev , TP_ISR_PIN, GPIO_INT_EDGE_FALLING);//GPIO_INT_DISABLE LOG_DBG("enable tpkey"); } static void tpkey_acts_disable(struct device *dev) { struct acts_tpkey_data *tpkey = dev->data; const struct acts_tpkey_config *cfg = dev->config; const struct acts_pin_config *pinconf = cfg->pinmux; gpio_pin_interrupt_configure(tpkey->gpio_dev , TP_ISR_PIN, GPIO_INT_DISABLE);//GPIO_INT_DISABLE LOG_DBG("disable tpkey"); } static void tpkey_acts_inquiry(struct device *dev, struct input_value *val) { struct acts_tpkey_data *tpkey = dev->data; if(tpkey_flag_timeout) return; read_touch(val); LOG_DBG("inquiry tpkey"); } static void tpkey_acts_register_notify(struct device *dev, input_notify_t notify) { struct acts_tpkey_data *tpkey = dev->data; LOG_DBG("register notify 0x%x", (uint32_t)notify); tpkey->notify = notify; } static void tpkey_acts_unregister_notify(struct device *dev, input_notify_t notify) { struct acts_tpkey_data *tpkey = dev->data; LOG_DBG("unregister notify 0x%x", (uint32_t)notify); tpkey->notify = NULL; } const struct input_dev_driver_api tpkey_acts_driver_api = { .enable = tpkey_acts_enable, .disable = tpkey_acts_disable, .inquiry = tpkey_acts_inquiry, .register_notify = tpkey_acts_register_notify, .unregister_notify = tpkey_acts_unregister_notify, }; #define mode_pin (GPIO_PULL_UP | GPIO_INPUT | GPIO_INT_DEBOUNCE) void tp_reset(struct device *dev) { struct device *gpios_temp = NULL; gpios_temp = device_get_binding(DT_GPIO_LABEL_BY_IDX(DT_DRV_INST(0), reset_gpios, 0)); gpio_pin_configure(gpios_temp , TP_RESET_PIN, GPIO_OUTPUT| GPIO_PULL_UP); gpio_pin_set_raw(gpios_temp , TP_RESET_PIN, 1); k_busy_wait(1000*1000); gpio_pin_set_raw(gpios_temp , TP_RESET_PIN, 0); k_busy_wait(10*1000); gpio_pin_set_raw(gpios_temp , TP_RESET_PIN, 1); } static uint8_t tp_enter_bootmode(struct device *dev) { struct acts_tpkey_data *tpkey = dev->data; uint8_t retrycnt = 10; tp_reset(dev); k_busy_wait(5*1000); while(retrycnt--){ uint8_t cmd[4] = {0}; cmd[0] = 0xA0; cmd[1] = 0x01; cmd[2] = 0xab; if(-1 == i2c_write(tpkey->i2c_dev,cmd, 3, 0X6A)) { k_busy_wait(4*1000); continue; } cmd[0] = 0xA0; cmd[1] = 0x03; if(-1 == i2c_write(tpkey->i2c_dev,cmd, 2, 0X6A)) { k_busy_wait(4*1000); continue; } if(-1 == i2c_read(tpkey->i2c_dev,cmd, 1, 0X6A)) { k_busy_wait(2*1000); continue; } else { if(cmd[0] != 0x55) { k_busy_wait(2*1000); continue; } else { return 0; } } } return -1; } #if 0 void read_touch(struct input_value *val) { uint8_t cmd[5] = {0}; cmd[0] = 0x02; if(-1 == i2c_write(tpkey_acts_ddata.i2c_dev, cmd, 1, tp_slaver_addr)) { return; } else if(-1 == i2c_read(tpkey_acts_ddata.i2c_dev, cmd, 5, tp_slaver_addr)) {//tp_slaver_addr return; } val->point.loc_x = (((uint16_t)(cmd[1]&0x0f))<<8) | cmd[2]; val->point.loc_y = (((uint16_t)(cmd[3]&0x0f))<<8) | cmd[4]; val->point.pessure_value = cmd[0]; LOG_DBG("finger_num = %d, local:(%d,%d)\n",cmd[0], val->point.loc_x, val->point.loc_y); } #endif static void read_touch(struct input_value *val) { uint8_t cmd[6] = {0}; sys_trace_void(SYS_TRACE_ID_TP_READ); cmd[0] = 0x01; if(-1 == i2c_write(tpkey_acts_ddata.i2c_dev, cmd, 1, tp_slaver_addr)) { return; } else if(-1 == i2c_read(tpkey_acts_ddata.i2c_dev, cmd, 6, tp_slaver_addr)) {//tp_slaver_addr return; } val->point.loc_x = (((uint16_t)(cmd[2]&0x0f))<<8) | cmd[3]; val->point.loc_y = (((uint16_t)(cmd[4]&0x0f))<<8) | cmd[5]; val->point.pessure_value = cmd[1]; val->point.gesture = cmd[0]; LOG_DBG("finger_num = %d, gesture = %d,local:(%d,%d)\n",cmd[0], val->point.gesture,val->point.loc_x, val->point.loc_y); sys_trace_end_call(SYS_TRACE_ID_TP_READ); } static void enter_low_power_mode(const struct device *dev) { uint8_t cmd[6] = {0}; printk("go into low power mode\n"); cmd[0] = 0xfe; cmd[1] = 0; i2c_write(tpkey_acts_ddata.i2c_dev, cmd, 2, tp_slaver_addr); } static void turn_off_detector_mode() { uint8_t cmd[6] = {0}; cmd[0] = 0xfb; cmd[1] = 0; i2c_write(tpkey_acts_ddata.i2c_dev, cmd, 2, tp_slaver_addr); cmd[0] = 0xfc; cmd[1] = 0; i2c_write(tpkey_acts_ddata.i2c_dev, cmd, 2, tp_slaver_addr); } int tpkey_acts_init(const struct device *dev) { struct acts_tpkey_data *tpkey = dev->data; const struct acts_tpkey_config *cfg = dev->config; const struct acts_pin_config *pinconf = cfg->pinmux; struct device *gpios_power = NULL; #if DT_NODE_HAS_PROP(DT_DRV_INST(0), power_gpios) gpios_power = device_get_binding(DT_GPIO_LABEL_BY_IDX(DT_DRV_INST(0), power_gpios, 0)); gpio_pin_configure(gpios_power , TP_POWER_PIN, GPIO_OUTPUT| GPIO_PULL_UP); gpio_pin_set_raw(gpios_power , TP_RESET_PIN, 0); #endif tpkey->i2c_dev = (struct device *)device_get_binding(DT_INST_BUS_LABEL(0)); tpkey->gpio_dev = (struct device *)device_get_binding(DT_GPIO_LABEL_BY_IDX(DT_DRV_INST(0), isr_gpios, 0)); if(tpkey->i2c_dev == NULL) { printk("can not access right i2c device\n"); return -1; } if(tpkey->gpio_dev == NULL) { printk("can not access right gpio device\n"); return -1; } tpkey->this_dev = (struct device *)dev; gpio_pin_configure(tpkey->gpio_dev , TP_ISR_PIN, mode_pin); gpio_init_callback(&tpkey->key_gpio_cb , KEY_IRQ_callback, BIT(TP_ISR_PIN)); gpio_add_callback(tpkey->gpio_dev , &tpkey->key_gpio_cb); //if(-1 == tp_enter_bootmode(dev)) { // printk("error return \n"); //} tpkey_flag_timeout = 0; tp_reset(dev); k_busy_wait(100*1000);//the time waiting too short,and the device can not wake up if(DT_INST_PROP(0, lowpower)) { enter_low_power_mode(dev); k_timer_init(&tpkey_inquiry_timer, tpkey_detect, NULL); k_timer_start(&tpkey_inquiry_timer, K_MSEC(20), K_MSEC(20)); } turn_off_detector_mode(); k_delayed_work_init(&tpkey->timer, tpkey_acts_poll); return 0; } #if DT_NODE_HAS_STATUS(DT_DRV_INST(0), okay) DEVICE_DEFINE(tpkey, DT_INST_LABEL(0), tpkey_acts_init, NULL, &tpkey_acts_ddata, &tpkey_acts_cdata, POST_KERNEL, 60, &tpkey_acts_driver_api); #endif // DT_NODE_HAS_STATUS