/*
 *
 * Copyright (C) 2012-2019, Samsung Electronics Co., Ltd.
 *
 * RMI4 func.0x12 implementation
 */

#include "rmi.h"
#include "rmi_driver.h"

#define F12_MAX_NUM_OF_FINGERS  10

#define RPT_TYPE                (1 << 0)
#define RPT_X_LSB               (1 << 1)
#define RPT_X_MSB               (1 << 2)
#define RPT_Y_LSB               (1 << 3)
#define RPT_Y_MSB               (1 << 4)
#define RPT_Z                   (1 << 5)
#define RPT_WX                  (1 << 6)
#define RPT_WY                  (1 << 7)
#define RPT_DEFAULT             (RPT_TYPE | RPT_X_LSB | RPT_X_MSB | RPT_Y_LSB | RPT_Y_MSB)

#define INVALID_X               65535
#define INVALID_Y               65535

#define GET_MIN(x, y) (((x) <= (y)) ? (x) : (y))
#define GET_MAX(x, y) (((x) >= (y)) ? (x) : (y))
#define DO_SWAP(x, y, tmp) { tmp = x; x = y; y = tmp; }
#define MK_WORD(high, low) ((((uint16_t)(high)) << 8) | (low))

struct f12_query5 {
    union {
        struct {
            uint8_t size_of_query6;
            struct {
                uint8_t ctrl0_exists:1;
                uint8_t ctrl1_exists:1;
                uint8_t ctrl2_exists:1;
                uint8_t ctrl3_exists:1;
                uint8_t ctrl4_exists:1;
                uint8_t ctrl5_exists:1;
                uint8_t ctrl6_exists:1;
                uint8_t ctrl7_exists:1;
            } PACKED_STRUCT;
            struct {
                uint8_t ctrl8_exists:1;
                uint8_t ctrl9_exists:1;
                uint8_t ctrl10_exists:1;
                uint8_t ctrl11_exists:1;
                uint8_t ctrl12_exists:1;
                uint8_t ctrl13_exists:1;
                uint8_t ctrl14_exists:1;
                uint8_t ctrl15_exists:1;
            } PACKED_STRUCT;
            struct {
                uint8_t ctrl16_exists:1;
                uint8_t ctrl17_exists:1;
                uint8_t ctrl18_exists:1;
                uint8_t ctrl19_exists:1;
                uint8_t ctrl20_exists:1;
                uint8_t ctrl21_exists:1;
                uint8_t ctrl22_exists:1;
                uint8_t ctrl23_exists:1;
            } PACKED_STRUCT;
            struct {
                uint8_t ctrl24_exists:1;
                uint8_t ctrl25_exists:1;
                uint8_t ctrl26_exists:1;
                uint8_t ctrl27_exists:1;
                uint8_t ctrl28_exists:1;
                uint8_t ctrl29_exists:1;
                uint8_t ctrl30_exists:1;
                uint8_t ctrl31_exists:1;
            } PACKED_STRUCT;
        };
        uint8_t data[5];
    };
};

struct f12_query8 {
    union {
        struct {
            uint8_t size_of_query9;
            struct {
                uint8_t data0_exists:1;
                uint8_t data1_exists:1;
                uint8_t data2_exists:1;
                uint8_t data3_exists:1;
                uint8_t data4_exists:1;
                uint8_t data5_exists:1;
                uint8_t data6_exists:1;
                uint8_t data7_exists:1;
            } PACKED_STRUCT;
            struct {
                uint8_t data8_exists:1;
                uint8_t data9_exists:1;
                uint8_t data10_exists:1;
                uint8_t data11_exists:1;
                uint8_t data12_exists:1;
                uint8_t data13_exists:1;
                uint8_t data14_exists:1;
                uint8_t data15_exists:1;
            } PACKED_STRUCT;
        };
        uint8_t data[3];
    };
};

struct f12_ctrl8 {
    union {
        struct {
            uint8_t max_x_coord_lsb;
            uint8_t max_x_coord_msb;
            uint8_t max_y_coord_lsb;
            uint8_t max_y_coord_msb;
            uint8_t rx_pitch_lsb;
            uint8_t rx_pitch_msb;
            uint8_t tx_pitch_lsb;
            uint8_t tx_pitch_msb;
            uint8_t low_rx_clip;
            uint8_t high_rx_clip;
            uint8_t low_tx_clip;
            uint8_t high_tx_clip;
            uint8_t num_of_rx;
            uint8_t num_of_tx;
        };
        uint8_t data[14];
    };
};

struct f12_ctrl23 {
    union {
        struct {
            uint8_t obj_type_enable;
            uint8_t max_reported_objects;
        };
        uint8_t data[2];
    };
};

struct f12_finger_data {
    uint8_t object_type_and_status;
    uint8_t x_lsb;
    uint8_t x_msb;
    uint8_t y_lsb;
    uint8_t y_msb;
};

struct f12_data {
    int sensor_max_x;
    int sensor_max_y;
    uint8_t num_of_rx;
    uint8_t num_of_tx;
    uint8_t max_touch_width;
    uint8_t fingers_number;
    uint8_t data1_offset;
    uint8_t data15_offset;
    uint8_t data15_size;
    uint8_t data15_data[(F12_MAX_NUM_OF_FINGERS + 7) / 8];
    int finger_data_size;
    uint8_t *finger_data;
    uint8_t finger_tracker[F12_MAX_NUM_OF_FINGERS];
    uint8_t fingers_already_present;
};

static int rmi_f12_init(struct rmi_function_container *fc);
static int rmi_f12_attention(rmi_function_container_t *fc, uint8_t irq_bits);
static void rmi_f12_remove(rmi_function_container_t *fc);

static rmi_function_container_t function_handler = {
    .func      = 0x12,
    .init      = rmi_f12_init,
    .attention = rmi_f12_attention,
    .remove    = rmi_f12_remove
};

static int rmi_f12_init(struct rmi_function_container *fc)
{
    struct rmi_device *rmi_dev = fc->rmi_dev;
    int rc;
    uint16_t base_addr_of_query;
    uint16_t base_addr_of_control;
    uint8_t query8_size;
    uint8_t offset_of_ctrl8;
    uint8_t offset_of_ctrl23;
    uint8_t offset_of_ctrl28;
    uint8_t fingers_number;
    uint8_t ctrl_report_type;
    struct f12_data   *data;
    struct f12_query5 query5;
    struct f12_query8 query8;
    struct f12_ctrl8  ctrl8;
    struct f12_ctrl23 ctrl23;

    data = TEE_Malloc(sizeof(struct f12_data), 0);
    if (!data) {
        return ERROR_RMI_NOMEM;
    }
    base_addr_of_query = fc->fd.query_base_addr;
    base_addr_of_control = fc->fd.control_base_addr;

    rc = rmi_read_block(rmi_dev, base_addr_of_query + 5, &query5.data[0], sizeof(query5.data));
    if (rc < 0) {
        goto err_free_data;
    }

    offset_of_ctrl8 = query5.ctrl0_exists +
            query5.ctrl1_exists +
            query5.ctrl2_exists +
            query5.ctrl3_exists +
            query5.ctrl4_exists +
            query5.ctrl5_exists +
            query5.ctrl6_exists +
            query5.ctrl7_exists;

    offset_of_ctrl23 = offset_of_ctrl8 +
            query5.ctrl8_exists +
            query5.ctrl9_exists +
            query5.ctrl10_exists +
            query5.ctrl11_exists +
            query5.ctrl12_exists +
            query5.ctrl13_exists +
            query5.ctrl14_exists +
            query5.ctrl15_exists +
            query5.ctrl16_exists +
            query5.ctrl17_exists +
            query5.ctrl18_exists +
            query5.ctrl19_exists +
            query5.ctrl20_exists +
            query5.ctrl21_exists +
            query5.ctrl22_exists;

    offset_of_ctrl28 = offset_of_ctrl23 +
            query5.ctrl23_exists +
            query5.ctrl24_exists +
            query5.ctrl25_exists +
            query5.ctrl26_exists +
            query5.ctrl27_exists;

    rc = rmi_read_block(rmi_dev, base_addr_of_control + offset_of_ctrl23, &ctrl23.data[0], sizeof(ctrl23.data));
    if (rc < 0) {
        goto err_free_data;
    }
    /* Maximum number of fingers supported */
    fingers_number = GET_MIN(ctrl23.max_reported_objects, (uint8_t)F12_MAX_NUM_OF_FINGERS);

    data->fingers_number = fingers_number;

    rc = rmi_read_block(rmi_dev, base_addr_of_query + 7, &query8_size, sizeof(query8_size));
    if (rc < 0) {
        goto err_free_data;
    }
    rc = rmi_read_block(rmi_dev, base_addr_of_query + 8, &query8.data[0], query8_size);
    if (rc < 0) {
        goto err_free_data;
    }
    /* Determine the presence of the Data0 register */
    data->data1_offset = query8.data0_exists;

    if ((query8_size >= 3) && (query8.data15_exists)) {
        data->data15_offset = query8.data0_exists +
                query8.data1_exists +
                query8.data2_exists +
                query8.data3_exists +
                query8.data4_exists +
                query8.data5_exists +
                query8.data6_exists +
                query8.data7_exists +
                query8.data8_exists +
                query8.data9_exists +
                query8.data10_exists +
                query8.data11_exists +
                query8.data12_exists +
                query8.data13_exists +
                query8.data14_exists;
        data->data15_size = (fingers_number + 7) / 8;
    } else {
        data->data15_size = 0;
    }
    ctrl_report_type = RPT_DEFAULT;

    rc = rmi_write_byte(rmi_dev, base_addr_of_control + offset_of_ctrl28, ctrl_report_type);
    if (rc < 0) {
        goto err_free_data;
    }
    rc = rmi_read_block(rmi_dev, base_addr_of_control + offset_of_ctrl8, &ctrl8.data[0], sizeof(ctrl8.data));
    if (rc < 0) {
        goto err_free_data;
    }
    /* Maximum x and y */
    data->sensor_max_x = MK_WORD(ctrl8.max_x_coord_msb, ctrl8.max_x_coord_lsb);
    data->sensor_max_y = MK_WORD(ctrl8.max_y_coord_msb, ctrl8.max_y_coord_lsb);
    rmi_dev->max_x = data->sensor_max_x;
    rmi_dev->max_y = data->sensor_max_y;
    RMI_DBG("%s() : Function %02x max x = %d max y = %d", __func__, (int)fc->func, rmi_dev->max_x, rmi_dev->max_y);

    data->num_of_rx = ctrl8.num_of_rx;
    data->num_of_tx = ctrl8.num_of_tx;
    data->max_touch_width = GET_MAX(data->num_of_rx, data->num_of_tx);

    /* Allocate memory for finger data storage space */
    data->finger_data_size = fingers_number * sizeof(struct f12_finger_data);
    data->finger_data = TEE_Malloc(data->finger_data_size, 0);
    if (!data->finger_data) {
        goto err_free_data;
    }
    fc->data = data;
    RMI_DBG("F12 Function init done.");
    return rc;

err_free_data:
    if (data) {
        TEE_Free(data);
    }
    fc->data = NULL;
    return rc;
}

static int rmi_f12_abs_pos_report(struct f12_data *f12, struct f12_finger_data *finger_data, uint8_t n_finger)
{
    int finger_state;
    int finger_pressed = 0;
    uint8_t prev_state = f12->finger_tracker[n_finger];
    int x = 0;
    int y = 0;

    finger_state = finger_data->object_type_and_status & 0x07;

    /* Define for Object type and status(F12_2D_data(N)/0).
     * Each 3-bit finger status field represents the following:
     * 000 = finger not present
     * 001 = finger present and data accurate
     * 010 = stylus pen (passive pen)
     * 011 = palm touch
     * 100 = not used
     * 101 = hover
     * 110 = glove touch
     */

    if (finger_state != FN_NO_FINGER) {
        x = MK_WORD(finger_data->x_msb, finger_data->x_lsb);
        y = MK_WORD(finger_data->y_msb, finger_data->y_lsb);
        if ((x == INVALID_X) && (y == INVALID_Y)) {
            finger_state = FN_NO_FINGER;
        }
        if ((x > f12->sensor_max_x) || (y > f12->sensor_max_y)
            || (finger_state == FN_UNCLASSIFIED)) {
            finger_state = FN_NO_FINGER;
        }
    }

    if (finger_state != FN_NO_FINGER) {
        f12->fingers_already_present = n_finger + 1;
        if (prev_state) {
            add_touch_event(n_finger, x, y, tui_ts_update);
        } else {
            add_touch_event(n_finger, x, y, tui_ts_press);
        }
        finger_pressed = 1;
    } else if (prev_state) {
        add_touch_event(n_finger, x, y, tui_ts_release);
    }
    f12->finger_tracker[n_finger] = finger_state;
    return finger_pressed;
}

static void rmi4_f12_release_all_finger(struct f12_data *f12)
{
    for (int finger = 0; finger < f12->fingers_number; finger++) {
        if (f12->finger_tracker[finger]) {
            add_touch_event(finger, 0, 0, tui_ts_release);
            f12->finger_tracker[finger] = FN_NO_FINGER;
        }
    }
}

static int rmi_f12_attention(rmi_function_container_t *fc, uint8_t irq_bits)
{
    struct rmi_device *rmi_dev = fc->rmi_dev;
    struct f12_data *f12;
    uint16_t data_base_addr = fc->fd.data_base_addr;
    struct f12_finger_data *finger_data_head, *finger_data;
    int error;
    int finger;
    int fingers_to_process;
    int finger_pressed_count = 0;

    (void)irq_bits;

    f12 = (struct f12_data *)fc->data;
    fingers_to_process = f12->fingers_number;

    /* Determine the total number of fingers to process */
    if(f12->data15_size) {
        unsigned short object_attention = 0;

        error = rmi_read_block(rmi_dev, data_base_addr + f12->data15_offset, f12->data15_data, f12->data15_size);
        if (error <= 0) {
            return (!error) ? ERROR_RMI_FAILED : error;
        }

        /* Object_attention : [000000xx xxxxxxxxx] : "x" represent that finger is on or not
        * Get the highest finger number to read finger data efficently.
        */
        object_attention = f12->data15_data[0];
        if (f12->data15_size > 1)
            object_attention |= (f12->data15_data[1] & 0x7) << 8;

        for (; fingers_to_process > 0; fingers_to_process--) {
            if (object_attention & (1 << (fingers_to_process - 1)))
                break;
        }
        RMI_DBG("fingers[%d] object_attention[0x%02x]\n", fingers_to_process, object_attention);
    }

    fingers_to_process = GET_MAX(fingers_to_process, f12->fingers_already_present);

    if (!fingers_to_process) {
        rmi4_f12_release_all_finger(f12);
        return 0;
    }

    finger_data_head = (struct f12_finger_data *)f12->finger_data;
    error = rmi_read_block(rmi_dev, data_base_addr + f12->data1_offset, (uint8_t *)finger_data_head, fingers_to_process * sizeof(struct f12_finger_data));
    if (error <= 0) {
        return (!error) ? ERROR_RMI_FAILED : error;
    }
    for (finger = 0; finger < fingers_to_process; finger++) {
        finger_data = finger_data_head + finger;

        if (rmi_f12_abs_pos_report(f12, finger_data, finger)) {
            finger_pressed_count++;
        }
    }
    if (finger_pressed_count == 0) {
        f12->fingers_already_present = 0;
    }
    return 0;
}

static void rmi_f12_remove(rmi_function_container_t *fc)
{
    struct f12_data *data = (struct f12_data *)fc->data;
    struct f12_finger_data *finger_data;

    if (data) {
        finger_data = (struct f12_finger_data *)data->finger_data;
        if (finger_data)
            TEE_Free(finger_data);
        data->finger_data = NULL;
        TEE_Free(data);
        fc->data = NULL;
    }
}

int rmi_fn12_register(rmi_device_t *rmi_dev, rmi_function_descriptor_t *fd)
{
    int error;

    error = rmi_bus_register_function(rmi_dev, &function_handler, fd);
    if (error < 0) {
        RMI_ERR("registration failed. err = %d", error);
        return error;
    }
    return 0;
}
