/*
 *
 * Copyright (C) 2012-2019, Samsung Electronics Co., Ltd.
 *
 * SEC touch device implementation
 */

#include <errno.h>
#include <string.h>
#include <tee_internal_api.h>

#include "board.h"
#include "dbg.h"
#include "sec_driver.h"

/* SEC_TS read register address */
#define CMD_SENSE_ON              0x10
#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

#if defined(TUI_MODEL_BEYOND2)
#define EVENT_BUFF_SIZE           8
#define PANEL_INFO_SIZE           11
#define DEVICE_ID_SIZE            3
#define TS_READ_ID_SIZE           3
#define MAX_EVENT_COUNT           31
#define READ_EVENT_MASK           0x1F
#endif /* TUI_MODEL_BEYOND2 */

#if defined(TUI_MODEL_PICASSO) || defined(TUI_MODEL_A71X) || defined(TUI_MODEL_N2S) || defined(TUI_MODEL_A32)
#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
#endif /* TUI_MODEL_PICASSO */

struct sec_ts_info ts_data;

/**
 * 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 sec_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
 */
int sec_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 sec_ts_event_coordinate *p_event_coord;
    struct sec_ts_event_status *p_event_status;
    dbgPrintf("%s +\n", __func__);

    if (get_touch_queue_wsize() < 1) {
        return -1;
    }

    ret = ts_data.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;
    }

    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 = ts_data.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;
    }

    if (left_event_count > 0) {
        ret = ts_data.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;
        }
    }

    do {
        event_buff = read_event_buff[curr_pos];
        event_id = event_buff[0] & 0x3;

        switch (event_id) {
        case STATUS_EVENT:
            p_event_status = (struct sec_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 = ts_data.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 sec_ts_event_coordinate *)event_buff;
            t_id = (p_event_coord->tid - 1);
            ttype = p_event_coord->ttype_3_2 << 2 | p_event_coord->ttype_1_0 << 0;

            if (t_id >= 0 && 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 SEC device driver
 * @param[in] sec_dev sec device pointer
 * @return error status
 */
int sec_driver_init(struct sec_ts_info *sec_dev)
{
    int ret;
    uint8_t data[PANEL_INFO_SIZE];
    uint16_t rX;
    uint16_t rY;

    ret = sec_dev->i2c_read(READ_TS_READ_ID, data, TS_READ_ID_SIZE);
    if (ret < 0) {
        errPrintf("%s: read id fail %d\n", __func__, ret);
        return ret;
    }
    dbgPrintf("%s: read_boot_id = %02X%02X%02X\n", __func__, data[0], data[1], data[2]);

    memset(data, 0x0, PANEL_INFO_SIZE);

    ret = sec_dev->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 - 1;
    } else {
        ts_data.max_x = SENSOR_MAX_X_DEFAULT;
    }

    if (rY > 0) {
        ts_data.max_y = rY - 1;
    } else {
        ts_data.max_y = SENSOR_MAX_Y_DEFAULT;
    }

    ret = sec_dev->i2c_write(CMD_CLEAR_EVENT_STACK, NULL, 0);
    if (ret < 0) {
        errPrintf("%s: i2c write clear event failed %d\n", __func__, ret);
    }

    init_touch_queue();

    return 0;
}

/**
 * Uninitialize SEC device driver
 * @param[in] sec_dev sec device pointer
 * @return error status
 */
int sec_driver_release(struct sec_ts_info *sec_dev)
{
    int ret = 0;

    if (ts_data.initialized) {
        ret = sec_dev->i2c_write(CMD_CLEAR_EVENT_STACK, NULL, 0);
        if (ret < 0) {
            errPrintf("%s: i2c write clear event failed %d\n", __func__, ret);
        }
    }

    return ret;
}
