/*
 *
 * Copyright (C) 2012-2021, Samsung Electronics Co., Ltd.
 *
 * STM touch device implementation
 */

#include <tee_internal_api.h>
#include <errno.h>
#include <string.h>
#include "board.h"
#include "bsp_common.h"
#include "dbg.h"
#include "device.h"
#include "i2c.h"
#include "touch_gpio.h"
#include "secmap.h"
#include "stm_driver.h"

/* STM_TS read register address */
#define CMD_SENSE_ON              0x10
#define CMD_CALIBRATION           0x13
#define READ_DEVICE_ID            0x22
#define READ_PANEL_INFO           0x23
#define READ_TS_READ_ID           0x52
#define READ_ONE_EVENT            0x60
#define READ_ALL_EVENT            0x61

/* SEC_TS cmd register address */
#define CMD_CLEAR_EVENT_STACK     0x62

#define COORDINATE_EVENT          0
#define STATUS_EVENT              1

#define COORDINATE_ACTION_NONE    0
#define COORDINATE_ACTION_PRESS   1
#define COORDINATE_ACTION_RELEASE 3

#define TOUCHTYPE_NORMAL          0
#define TOUCHTYPE_HOVER           1
#define TOUCHTYPE_FLIPCOVER       2
#define TOUCHTYPE_GLOVE           3
#define TOUCHTYPE_STYLUS          4
#define TOUCHTYPE_PALM            5
#define TOUCHTYPE_WET             6
#define TOUCHTYPE_PROXIMITY       7
#define TOUCHTYPE_JIG             8
#define TOUCHTYPE_RESERVED        9

#define TYPE_STATUS_EVENT_ERR     1
#define TYPE_STATUS_EVENT_INFO    2

/* SEC_TS_INFO : Info acknowledge event */
#define ACK_BOOT_COMPLETE         0x00

/* SEC_TS_ERROR : Error event */
#define ERR_EVENT_QUEUE_FULL      0x01

#define EVENT_BUFF_SIZE           16
#define PANEL_INFO_SIZE           11
#define DEVICE_ID_SIZE            5
#define TS_READ_ID_SIZE           3
#define MAX_EVENT_COUNT           31
#define READ_EVENT_MASK           0x1F

struct stm_ts_info ts_data;

#define SEC_RETRY_COUNT       3

static int stm_i2c_read(uint16_t addr, uint8_t *buff, uint16_t count)
{
    TEE_Result ret;
    deviceInfo_t *i2cDev = &board.i2c;
    int try_count = SEC_RETRY_COUNT;

    if (!buff || !count) {
        return -EINVAL;
    }
    do {
        ret = i2c_read_write(i2cDev, i2cDev->bus.slave_address, (uint8_t *)&addr, 1, buff, count);
        if (ret == TEE_SUCCESS) {
            return count;
        }
    } while(--try_count);
    return -EIO;
}

static int stm_i2c_write(uint16_t addr, uint8_t *buff, uint16_t count)
{
    TEE_Result ret;
    deviceInfo_t *i2cDev = &board.i2c;
    static uint8_t local_buff[256];
    int try_count = SEC_RETRY_COUNT;

    if ((!buff && count) || (size_t)count >= sizeof(local_buff)) {
        return -EINVAL;
    }
    local_buff[0] = addr;
    if (count) {
        TEE_MemMove(local_buff+1, buff, count);
    }
    do {
        ret = i2c_send(i2cDev, i2cDev->bus.slave_address, local_buff, count+1);
        if (ret == TEE_SUCCESS) {
            return count;
        }
    } while(--try_count);
    return -EIO;
}

/**
 * Compute x, y Coordinate
 * @param[in] sec_ts_event_coordinate
 * @param[out] x current x coordinate
 * @param[out] y current y coordinate
 * @return none
 */
static void compute_coordinate(struct stm_ts_event_coordinate *p_event_coord,
                               uint16_t *x, uint16_t *y)
{
    uint16_t tx;
    uint16_t ty;

    tx = (p_event_coord->x_11_4 << 4) | (p_event_coord->x_3_0);
    ty = (p_event_coord->y_11_4 << 4) | (p_event_coord->y_3_0);

    if (ts_data.max_x != ts_data.width && ts_data.max_x != 0) {
        *x = (tx * ts_data.width) / ts_data.max_x;
    } else {
        *x = tx;
    }

    if (ts_data.max_y != ts_data.height && ts_data.max_y != 0) {
        *y =  (ty * ts_data.height) / ts_data.max_y;
    } else {
        *y = ty;
    }
}

/**
 * Get touch data from controller and push it to the touch queue
 * @param[in] sec_dev sec device pointer
 * @return error status
 */
static int stm_irq_process(void)
{
    int ret;
    int t_id;
    int event_id;
    int remain_event_count = 0;
    uint16_t x;
    uint16_t y;
    uint8_t left_event_count;
    uint8_t *event_buff;
    uint8_t read_event_buff[MAX_EVENT_COUNT][EVENT_BUFF_SIZE];
    uint8_t ttype;
    uint8_t curr_pos = 0;
    struct stm_ts_event_coordinate *p_event_coord;
    struct stm_ts_event_status *p_event_status;
    dbgPrintf("%s +\n", __func__);

    if (get_touch_queue_wsize() < 1) {
        return -1;
    }

    ret = stm_i2c_read(READ_ONE_EVENT, read_event_buff[0],  EVENT_BUFF_SIZE);
    if (ret != EVENT_BUFF_SIZE) {
        errPrintf("%s: i2c read one event failed, ret = %d\n", __func__, ret);
        return ret;
    }
    dbgPrintf("%s: READ_ONE_EVENT = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
        __func__, read_event_buff[0][0], read_event_buff[0][1], read_event_buff[0][2], read_event_buff[0][3],
        read_event_buff[0][4], read_event_buff[0][5], read_event_buff[0][6], read_event_buff[0][7],
        read_event_buff[0][8], read_event_buff[0][9], read_event_buff[0][10], read_event_buff[0][11],
        read_event_buff[0][12], read_event_buff[0][13], read_event_buff[0][14], read_event_buff[0][15]);

    if (read_event_buff[0][0] == 0) {
        return 0;
    }

    left_event_count = read_event_buff[0][7] & READ_EVENT_MASK;
    remain_event_count = left_event_count;
    if (left_event_count > (MAX_EVENT_COUNT - 1)) {
        errPrintf("%s: event buffer overflow\n", __func__);
        release_all_fingers();
        ret = stm_i2c_write(CMD_CLEAR_EVENT_STACK, NULL, 0);
        if (ret < 0) {
            errPrintf("%s: i2c write clear event failed, ret = %d\n", __func__, ret);
            return ret;
        }
        return 0;
    }

    if (get_touch_queue_wsize() < (left_event_count + 1)) {
        left_event_count = 0;
        remain_event_count = 0;
    }
    dbgPrintf("%s: left= %d, remain=%d\n",__func__, left_event_count, remain_event_count);

    if (left_event_count > 0) {
        ret = stm_i2c_read(READ_ALL_EVENT, read_event_buff[1], sizeof(uint8_t) *
                               EVENT_BUFF_SIZE * left_event_count);
        if (ret < 0) {
            errPrintf("%s: i2c read all events failed, ret = %d\n", __func__, ret);
            return ret;
        }
        dbgPrintf("%s: READ_ALL_EVENT = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
            __func__, read_event_buff[1][0], read_event_buff[1][1], read_event_buff[1][2], read_event_buff[1][3],
            read_event_buff[1][4], read_event_buff[1][5], read_event_buff[1][6], read_event_buff[1][7],
            read_event_buff[1][8], read_event_buff[1][9], read_event_buff[1][10], read_event_buff[1][11],
            read_event_buff[1][12], read_event_buff[1][13], read_event_buff[1][14], read_event_buff[1][15]);		
    }

    do {
        event_buff = read_event_buff[curr_pos];
        event_id = event_buff[0] & 0x3;
        dbgPrintf("%s: event id= %d, curr_pos=%d, remain=%d\n",__func__, event_id, curr_pos, remain_event_count);

        switch (event_id) {
        case STATUS_EVENT:
            p_event_status = (struct stm_ts_event_status *)event_buff;
            if (p_event_status->stype > 0) {
                dbgPrintf("%s: STATUS %x %x %x %x %x %x %x %x\n", __func__,
                          event_buff[0], event_buff[1], event_buff[2],
                          event_buff[3], event_buff[4], event_buff[5],
                          event_buff[6], event_buff[7]);
            }

            /* watchdog reset -> send SENSEON command */
            if ((p_event_status->stype == TYPE_STATUS_EVENT_INFO) &&
                (p_event_status->status_id == ACK_BOOT_COMPLETE) &&
                (p_event_status->status_data_1 == 0x20)) {
                errPrintf("%s: watchdog reset event\n", __func__);

                release_all_fingers();

                ret = stm_i2c_write(CMD_SENSE_ON, NULL, 0);
                if (ret < 0) {
                    errPrintf("%s: fail to write sense_on\n", __func__);
                }
            }

            /* event queue full-> all finger release */
            if ((p_event_status->stype == TYPE_STATUS_EVENT_ERR) &&
                (p_event_status->status_id == ERR_EVENT_QUEUE_FULL)) {
                errPrintf("%s: IC event queue is full\n", __func__);

                release_all_fingers();
            }
            break;

        case COORDINATE_EVENT:
            p_event_coord = (struct stm_ts_event_coordinate *)event_buff;
            t_id = p_event_coord->tid;
            ttype = p_event_coord->ttype_3_2 << 2 | p_event_coord->ttype_1_0 << 0;

            if (t_id < TUI_MAX_NUM_OF_FINGERS) {
                compute_coordinate(p_event_coord, &x, &y);
                if ((ttype == TOUCHTYPE_NORMAL) || (ttype == TOUCHTYPE_PALM)
                    || (ttype == TOUCHTYPE_GLOVE) || (ttype == TOUCHTYPE_WET)) {
                    if (p_event_coord->tchsta == COORDINATE_ACTION_RELEASE) {
                        add_touch_event(t_id, x, y, tui_ts_release);
                    } else if (p_event_coord->tchsta == COORDINATE_ACTION_PRESS) {
                        add_touch_event(t_id, x, y, tui_ts_press);
                    } else {
                        dbgPrintf("TOUCH %s %d %d\n", __func__, __LINE__, p_event_coord->tchsta);
                    }
                }
            } else {
                dbgPrintf("TOUCH %s tid(%d) is out of range\n", __func__, t_id);
            }
            break;

        default:
            dbgPrintf("TOUCH %s, default case event_id = %d\n", __func__, event_id);
            break;
        }

        ++curr_pos;
        --remain_event_count;
    } while (remain_event_count >= 0);
    dbgPrintf("%s -\n", __func__);
    return 0;
}

/**
 * Initialize STM device driver
 * @param[in] sec_dev sec device pointer
 * @return error status
 */
static int stm_driver_init(void)
{
    int ret;
    uint8_t data[PANEL_INFO_SIZE];
    uint16_t rX;
    uint16_t rY;

    ret = stm_i2c_read(READ_DEVICE_ID, data, DEVICE_ID_SIZE);
    if (ret < 0) {
        errPrintf("%s: read id fail %d\n", __func__, ret);
        return ret;
    }
    dbgPrintf("%s: read_boot_id = %c %c %02X%02X%02X\n", __func__, data[0], data[1], data[2], data[3], data[4]);

    memset(data, 0x0, PANEL_INFO_SIZE);

    ret = stm_i2c_read(READ_PANEL_INFO, data, PANEL_INFO_SIZE);
    if (ret < 0) {
        errPrintf("%s: failed to read panel info(%d)\n", __func__, ret);
        return ret;
    }

    rX = (data[0] << 8) | data[1];
    rY = (data[2] << 8) | data[3];
    dbgPrintf("%s: rX:%d, rY:%d\n", __func__, rX, rY);

    /* Set X,Y Resolution from IC information. */
    if (rX > 0) {
        ts_data.max_x = rX;
    } else {
        ts_data.max_x = SENSOR_MAX_X_DEFAULT;
    }

    if (rY > 0) {
        ts_data.max_y = rY;
    } else {
        ts_data.max_y = SENSOR_MAX_Y_DEFAULT;
    }
#if 0
    ret = stm_i2c_write(CMD_CALIBRATION, NULL, 0);
    if (ret < 0) {
        errPrintf("%s: i2c write clear event failed %d\n", __func__, ret);
    }
#endif
    ret = stm_i2c_write(CMD_SENSE_ON, NULL, 0);
    if (ret < 0) {
        errPrintf("%s: i2c write clear event failed %d\n", __func__, ret);
    }

    init_touch_queue();

    return 0;
}

/**
 * Uninitialize STM device driver
 * @param[in] sec_dev sec device pointer
 * @return error status
 */
static int stm_driver_release(void)
{
    int ret = 0;

    if (ts_data.initialized) {
        ret = stm_i2c_write(CMD_CLEAR_EVENT_STACK, NULL, 0);
        if (ret < 0) {
            errPrintf("%s: i2c write clear event failed %d\n", __func__, ret);
        }
    }

    return ret;
}

TEE_Result stm_get_info(drTouchInfo_ptr touchSize)
{
    touchSize->width = ts_data.max_x;
    touchSize->height = ts_data.max_y;
    return TEE_SUCCESS;
}

TEE_Result stm_open(uint32_t width, uint32_t height)
{
    deviceInfo_t *touch_dev = &board.touch;
    deviceInfo_t *i2c_dev = &board.i2c;
    deviceInfo_t *gpio_dev = &board.gpio;
    TEE_Result retHal = TEE_ERROR_GENERIC;
    int ret = TEE_ERROR_GENERIC;
    ts_data.initialized = 0;

    dbgPrintf(">> %s\n", __func__);

    /* Initialization */
    retHal = touch_gpio_init(gpio_dev);
    if (retHal != TEE_SUCCESS) {
        errPrintf("touch_gpio_init() Failed\n");
        goto err_gpio_init;
    }

    retHal = i2c_init(i2c_dev);
    if (retHal != TEE_SUCCESS) {
        errPrintf("i2c_init() Failed\n");
        goto err_init_i2c;
    }

    if (touch_dev->state == DEV_UNCONFIGURED) {
        /* Init touch module */
        dbgPrintf("Touch Init Start\n");
        TEE_MemFill(&ts_data, 0, sizeof(ts_data));

        ts_data.width = width;
        ts_data.height = height;

        /* Initialize SEC touchscreen */
        ret = stm_driver_init();
        if (ret < 0) {
            errPrintf("stm_driver_init() : retval = %d\n", ret);
            retHal = E_TUI_HAL_IO;
            goto err_init_sec;
        }
        ts_data.initialized = 1;
        touch_dev->state |= DEV_SFR_CONFIGURED;
    }

    sec_release();
    dbgPrintf("<< %s\n", __func__);
    return retHal;

err_init_sec:
    ret = i2c_release(i2c_dev);
    if (ret != TEE_SUCCESS) {
        errPrintf("i2c_release Failed!\n");
    }
err_init_i2c:
    ret = touch_gpio_release(gpio_dev);
    if (ret != TEE_SUCCESS) {
        errPrintf("touch_gpio_unregister_int Failed!\n");
    }
err_gpio_init:
    sec_release();
    dbgPrintf("<< %s\n", __func__);
    return retHal;
}

TEE_Result stm_close(void)
{
    dbgPrintf(">> %s\n", __func__);
    deviceInfo_t *i2c_dev = &board.i2c;
    deviceInfo_t *touch_dev = &board.touch;
    deviceInfo_t *gpio_dev = &board.gpio;
    TEE_Result ret;

    if (touch_dev->state == DEV_UNCONFIGURED)
        return TEE_SUCCESS;

    ts_data.initialized = 0;
    ret = touch_gpio_unregister_int(gpio_dev);
    if (ret != TEE_SUCCESS) {
        errPrintf("touch_gpio_unregister_int Failed!\n");
    }
    if (touch_dev->state & DEV_SFR_CONFIGURED) {
        ret = stm_driver_release();
        if (ret != TEE_SUCCESS) {
            errPrintf("sec_driver_release Failed!\n");
        } else {
            touch_dev->state &= ~DEV_SFR_CONFIGURED;
        }
    }
    ret = i2c_release(i2c_dev);
    if (ret != TEE_SUCCESS) {
        errPrintf("i2c_release Failed!\n");
    }
    ret = touch_gpio_release(gpio_dev);
    if (ret != TEE_SUCCESS) {
        errPrintf("touch_gpio_release Failed!\n");
    }

    sec_release();
    touch_dev->state = DEV_UNCONFIGURED;
    dbgPrintf("<< %s\n", __func__);
    return TEE_SUCCESS;
}

TEE_Result stm_process(void)
{
    dbgPrintf(">> %s\n", __func__);

    int retval = 0;
    deviceInfo_t *touch_dev = &board.touch;

    if (touch_dev->state != DEV_SFR_CONFIGURED && touch_dev->state != DEV_CONFIGURED) {
        return TEE_ERROR_GENERIC;
    }

#ifdef USE_TOUCH_INTERRUPT
    deviceInfo_t *gpio_dev = &board.gpio;

    if (touch_dev->state == DEV_SFR_CONFIGURED) {
        retval = touch_gpio_register_int(gpio_dev, stm_irq_process);
        if (retval != TEE_SUCCESS) {
            errPrintf("touch_gpio_register_int() : retval = %d\n", retval);
            return TEE_ERROR_GENERIC;
        }
        touch_dev->state = DEV_CONFIGURED;
    }
    gpio_wait_touch_irq();
#else /* USE_TOUCH_INTERRUPT */
    retval = stm_irq_process();
#endif /* USE_TOUCH_INTERRUPT */
    if (retval != 0) {
        return TEE_ERROR_GENERIC;
    }
    return TEE_SUCCESS;
}
