/*
 *
 * Copyright (C) 2012-2019, Samsung Electronics Co., Ltd.
 *
 * Melfas touchscreen routines
 */

#include <errno.h>
#include <driver/interrupt/interrupt.h>
#include <string.h>
#include <tee_internal_api.h>

#include "board.h"
#include "bsp_common.h"
#include "dbg.h"
#include "device.h"
#include "i2c.h"
#include "melfas_driver.h"
#include "secmap.h"
#include "touch_gpio.h"

/* Touch controller commands */
#define MMS_CMD0_INFO                           0x01
#define MMS_CMD1_INFO_PRODUCT_NAME              0x00
#define MMS_CMD1_INFO_RESOLUTION_X              0x10

#define MMS_CMD0_EVENT                          0x02
#define MMS_CMD1_EVENT_FORMAT                   0x04
#define MMS_CMD1_EVENT_PACKET_INFO              0x10
#define MMS_CMD1_EVENT_PACKET_DATA              0x11

#define MMS_COORDINATE_ACTION_RELEASE           0
#define MMS_COORDINATE_ACTION_PRESS_MOVE        1
#define MMS_COORDINATE_ACTION_PRESS_MOVE_16BYTE 2
#define MMS_COORDINATE_ACTION_RELEASE_16BYTE    3

#define MMS_EVENT_INPUT_TYPE_KEY                0
#define MMS_EVENT_INPUT_TYPE_SCREEN             1

/* event format */
#define MMS_EVENT_FORMAT_BASIC                  0
#define MMS_EVENT_FORMAT_WITH_RECT              1
#define MMS_EVENT_FORMAT_WITH_PRESSURE          3
#define MMS_EVENT_FORMAT_KEY_ONLY               4
#define MMS_EVENT_FORMAT_WITH_PRESSURE_2BYTE    8
#define MMS_EVENT_FORMAT_16BYTE                 10

/* event size */
#define MMS_EVENT_SIZE                          12

#define MMS_MAX_X                               4095
#define MMS_MAX_Y                               4095

struct melfas_ts_info melfas_data;

/******************************************************************************/

static int mms_i2c_rw(uint8_t *wr_buff, int wr_size, uint8_t *rd_buff, int rd_size)
{
    TEE_Result ret;
    deviceInfo_t *i2cDev = &board.i2c;
    int try_count = MMS_RETRY_COUNT;

    if (!wr_buff || !wr_size || !rd_buff || !rd_size) {
        errPrintf("null buffer or wrong size\n");
        return -EINVAL;
    }

    do {
        ret = i2c_read_write(i2cDev, i2cDev->bus.slave_address,
                             wr_buff, wr_size, rd_buff, rd_size);
        if (ret == TEE_SUCCESS) {
            return 0;
        }
    } while (--try_count);

    errPrintf("failed ret = %d\n", ret);

    return -EIO;
}

static int get_event_type(uint8_t *packet, uint8_t *type)
{
    switch (melfas_data.event_format) {
    case MMS_EVENT_FORMAT_BASIC:
    case MMS_EVENT_FORMAT_WITH_RECT:
        *type = (packet[0] & 0x40) >> 6;
        break;
    case MMS_EVENT_FORMAT_WITH_PRESSURE:
    case MMS_EVENT_FORMAT_WITH_PRESSURE_2BYTE:
        *type = (packet[0] & 0xF0) >> 4;
        break;
    case MMS_EVENT_FORMAT_KEY_ONLY:
        *type = MMS_EVENT_INPUT_TYPE_KEY;
        break;
    case MMS_EVENT_FORMAT_16BYTE:
        *type = (packet[0] & 0x03);
        break;
    default:
        errPrintf("unknown event format=%d\n", melfas_data.event_format);
        return -1;
    }

    dbgPrintf("%s type=%d\n", __func__, *type);

    return 0;
}

static void convert_coordinate(uint16_t *x, uint16_t *y)
{
    uint16_t tx = *x;
    uint16_t ty = *y;

    if (melfas_data.max_x != melfas_data.width && melfas_data.max_x != 0) {
        *x = (tx * melfas_data.width) / melfas_data.max_x;
    }

    if (melfas_data.max_y != melfas_data.height && melfas_data.max_y != 0) {
        *y = (ty * melfas_data.height) / melfas_data.max_y;
    }
}

static int read_screen_event(uint8_t *packet, uint8_t *state, uint8_t *id,
                             uint16_t *x, uint16_t *y)
{
    switch (melfas_data.event_format) {
    case MMS_EVENT_FORMAT_BASIC:
        *state = (packet[0] & 0x80) >> 7;
        *id = (packet[0] & 0x0F) - 1;
        *x = ((packet[1] & 0x0F) << 8) | packet[2];
        *y = (((packet[1] >> 4) & 0x0F) << 8) | packet[3];
        break;
    case MMS_EVENT_FORMAT_WITH_RECT:
        *state = (packet[0] & 0x80) >> 7;
        *id = (packet[0] & 0x0F) - 1;
        *x = ((packet[1] & 0x0F) << 8) | packet[2];
        *y = (((packet[1] >> 4) & 0x0F) << 8) | packet[3];
        break;
    case MMS_EVENT_FORMAT_WITH_PRESSURE:
        *state = (packet[1] & 0x01);
        *id = (packet[0] & 0x0F) - 1;
        *x = ((packet[2] & 0x0F) << 8) | packet[3];
        *y = (((packet[2] >> 4) & 0x0F) << 8) | packet[4];
        break;
    case MMS_EVENT_FORMAT_WITH_PRESSURE_2BYTE:
        *state = (packet[1] & 0x01);
        *id = (packet[0] & 0x0F) - 1;
        *x = ((packet[2] & 0x0F) << 8) | packet[3];
        *y = (((packet[2] >> 4) & 0x0F) << 8) | packet[4];
        break;
    case MMS_EVENT_FORMAT_16BYTE:
        *state = packet[0] >> 6;
        *id = ((packet[0] >> 2) & 0x0F) - 1;
        *x = (packet[1] << 4) | ((packet[3] >> 4) & 0x0F);
        *y = (packet[2] << 4) | (packet[3] & 0x0F);
        break;
    default:
        errPrintf("unknown event format=%d\n", melfas_data.event_format);
        return -1;
    }

    return 0;
}

static int process_event_queue(uint8_t *queue_ptr, uint8_t queue_size)
{
    uint16_t x;
    uint16_t y;
    uint8_t id;
    uint8_t state;
    uint8_t type;
    uint8_t *packet;
    int ret;

    for (unsigned int i = 0; i < queue_size; i += melfas_data.event_size) {
        packet = &queue_ptr[i];

        ret = get_event_type(packet, &type);
        if (ret < 0) {
            return ret;
        }

        /* Report input event */
        if (type == MMS_EVENT_INPUT_TYPE_SCREEN) {
            ret = read_screen_event(packet, &state, &id, &x, &y);
            if (ret < 0) {
                return ret;
            }

            convert_coordinate(&x, &y);

            if (state == MMS_COORDINATE_ACTION_RELEASE || state == MMS_COORDINATE_ACTION_RELEASE_16BYTE) {
                add_touch_event(id, x, y, tui_ts_release);
            } else if (state == MMS_COORDINATE_ACTION_PRESS_MOVE || state == MMS_COORDINATE_ACTION_PRESS_MOVE_16BYTE) {
                if (get_finger_state(id) == tui_ts_release) {
                    add_touch_event(id, x, y, tui_ts_press);
                } else {
                    add_touch_event(id, x, y, tui_ts_update);
                }
            }
        } else {
            errPrintf("not supported event format=%d\n", type);
        }
    }

    return 0;
}

/**
 * Get touch data from controller and push it to the touch queue
 * @return error status
 */
int mms_irq_process(void)
{
    uint8_t events_queue_size;
    uint8_t category;
    uint8_t alert_type;
    uint8_t resp_buff[256];
    static uint8_t cmd_buff[2];
    int ret = 0;

    //Read packet info
    cmd_buff[0] = MMS_CMD0_EVENT;
    cmd_buff[1] = MMS_CMD1_EVENT_PACKET_INFO;

    if (mms_i2c_rw(cmd_buff, 2, resp_buff, 1)) {
        errPrintf("Read packet info failed\n");
        return -1;
    }

    events_queue_size = resp_buff[0] & 0x7F;
    category = ((resp_buff[0] >> 7) & 0x1);

    if (melfas_data.event_size_type == 1 && category == 0) {
        events_queue_size = (resp_buff[0] & 0x7F) * melfas_data.event_size;
    } else {
        events_queue_size = (resp_buff[0] & 0x7F);
    }

    if ((events_queue_size <= 0) || (events_queue_size > 200)) {
        return -1;
    }

    if (get_touch_queue_wsize() < (events_queue_size / melfas_data.event_size)) {
        return -1;
    }

    //Read packet data
    cmd_buff[0] = MMS_CMD0_EVENT;
    cmd_buff[1] = MMS_CMD1_EVENT_PACKET_DATA;

    if (mms_i2c_rw(cmd_buff, 2, resp_buff, events_queue_size)) {
        errPrintf("Read packet packet failed\n");
        return -1;
    }

    if (category == 0) { /* Touch event received */
        ret = process_event_queue(resp_buff, events_queue_size);
    } else { /* Alert event received */
        alert_type = resp_buff[0];
        infoPrintf("%s alert type=%d\n", __func__, alert_type);
    }

    return ret;
}

/**
 * Initialize MMS device driver
 * @param[in] mms_dev mms device pointer
 * @return error status
 */
int mms_driver_init(struct melfas_ts_info *mms_dev)
{
    int ret;
    uint8_t resp_buff[16];
    uint8_t cmd_buff[2];

    /* get resolution info */
    cmd_buff[0] = MMS_CMD0_INFO;
    cmd_buff[1] = MMS_CMD1_INFO_PRODUCT_NAME;

    ret = mms_i2c_rw(cmd_buff, 2, resp_buff, 16);
    if (ret < 0) {
        errPrintf("get resolution info fail %d\n", ret);
        return ret;
    }
    dbgPrintf("%s product name=%s\n", __func__, resp_buff);

    mms_dev->max_x = MMS_MAX_X;
    mms_dev->max_y = MMS_MAX_Y;
    dbgPrintf("%s resolution info x=%d y=%d\n",
              __func__, mms_dev->max_x, mms_dev->max_y);

#if (defined(TUI_MODEL_A51Y19) || defined(TUI_MODEL_A51X))
    /* read protocol */
    cmd_buff[0] = MMS_CMD0_EVENT;
    cmd_buff[1] = MMS_CMD1_EVENT_FORMAT;

    ret = mms_i2c_rw(cmd_buff, 2, resp_buff, 4);
    if (ret < 0) {
        mms_dev->event_format = MMS_EVENT_FORMAT_WITH_PRESSURE_2BYTE;
        mms_dev->event_size = MMS_EVENT_SIZE;
        mms_dev->event_size_type = 0;
    } else {
        mms_dev->event_format = resp_buff[0] | (resp_buff[1] << 8);
        mms_dev->event_size = resp_buff[2];
        mms_dev->event_size_type = resp_buff[3] & 0x1;
    }
#else
    mms_dev->event_format = MMS_EVENT_FORMAT_WITH_PRESSURE_2BYTE;
    mms_dev->event_size = MMS_EVENT_SIZE;
    mms_dev->event_size_type = 0;
#endif
    dbgPrintf("%s event_format[%d] event_size[%d]\n",
              __func__, mms_dev->event_format, mms_dev->event_size);

    init_touch_queue();

    return ret;
}

/**
 * Uninitialize MMS device driver
 * @return error status
 */
int mms_driver_release(void)
{
    return 0;
}
