Input: eswin_touch - Import from downstream
This commit is contained in:
parent
aec10556b7
commit
60ca103344
20 changed files with 5901 additions and 0 deletions
|
|
@ -119,3 +119,14 @@ obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5xx.o
|
|||
obj-$(CONFIG_TOUCHSCREEN_IQS7211) += iqs7211.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX83112B) += himax_hx83112b.o
|
||||
|
||||
eswin_ts-y := \
|
||||
eswin_touch/eswin_eph861x.o \
|
||||
eswin_touch/eswin_eph861x_bootloader.o \
|
||||
eswin_touch/eswin_eph861x_comms.o \
|
||||
eswin_touch/eswin_eph861x_eswin.o \
|
||||
eswin_touch/eswin_eph861x_i2c.o \
|
||||
eswin_touch/eswin_eph861x_spi.o \
|
||||
eswin_touch/eswin_eph861x_tlv_command.o \
|
||||
eswin_touch/eswin_eph861x_tlv_report.o
|
||||
obj-y += eswin_ts.o
|
||||
|
|
|
|||
2471
drivers/input/touchscreen/eswin_touch/eswin_eph861x.c
Normal file
2471
drivers/input/touchscreen/eswin_touch/eswin_eph861x.c
Normal file
File diff suppressed because it is too large
Load diff
17
drivers/input/touchscreen/eswin_touch/eswin_eph861x.h
Normal file
17
drivers/input/touchscreen/eswin_touch/eswin_eph861x.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
|
||||
#ifndef __LINUX_PLATFORM_DATA_ESWIN_EPH_H
|
||||
#define __LINUX_PLATFORM_DATA_ESWIN_EPH_H
|
||||
|
||||
bool debug_log_flag = false;
|
||||
#define ts_info(fmt, arg...) \
|
||||
pr_info("[ESWIN-INF][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
|
||||
#define ts_err(fmt, arg...) \
|
||||
pr_err("[ESWIN-ERR][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
|
||||
#define ts_debug(fmt, arg...) \
|
||||
{if (debug_log_flag) \
|
||||
pr_info("[ESWIN-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg);}
|
||||
|
||||
|
||||
|
||||
#endif /* __LINUX_PLATFORM_DATA_ESWIN_EPH_ */
|
||||
383
drivers/input/touchscreen/eswin_touch/eswin_eph861x_bootloader.c
Normal file
383
drivers/input/touchscreen/eswin_touch/eswin_eph861x_bootloader.c
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
* ESWIN EPH861X series Touchscreen driver
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
// uncomment to enable the dev_dbg prints to dmesg
|
||||
#define DEBUG
|
||||
#include <linux/types.h>
|
||||
|
||||
|
||||
|
||||
#include <uapi/asm-generic/errno-base.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <uapi/linux/stat.h>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include "eswin_eph861x_project_config.h"
|
||||
#include "eswin_eph861x_tlv.h"
|
||||
#include "eswin_eph861x_types.h"
|
||||
#include "eswin_eph861x_comms.h"
|
||||
#include "eswin_eph861x_eswin.h"
|
||||
#include "eswin_eph861x_bootloader.h"
|
||||
#include "eswin_eph861x_tlv_command.h"
|
||||
|
||||
/* Bootloader Component defintions */
|
||||
#define BOOTLOADER_COMPONENT_ID (0xFEFEu)
|
||||
|
||||
/* Start Command definitions */
|
||||
#define START_COMMAND_SEQUENCE (0xDCAAu)
|
||||
#define START_COMMAND_SEQUENCE_0 (0xDCu)
|
||||
#define START_COMMAND_SEQUENCE_1 (0xAAu)
|
||||
|
||||
|
||||
#define BOOTLOADER_FRAME_CRC_SIZE (2u)
|
||||
|
||||
|
||||
typedef enum bootloader_state_tag
|
||||
{
|
||||
bootloader_state_INVALID = 0,
|
||||
bootloader_state_APP_CRC_FAILED = 1,
|
||||
bootloader_state_WAITING_START_COMMAND = 2,
|
||||
bootloader_state_WAITING_FRAME_DATA = 3,
|
||||
bootloader_state_PROCESS_FRAME = 4
|
||||
} bootloader_state_e;
|
||||
|
||||
|
||||
static int eph_proc_bootloader_status_report(struct eph_data *ephdata, u8 *message, bootloader_state_e *status_byte);
|
||||
static int eph_write_bootloader_frame(struct eph_data *ephdata, u16 payload_length, u8 *buf, bool last_frame);
|
||||
static int eph_write_bootloader_start(struct eph_data *ephdata);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief Send all the river firmware data from the firmware file.
|
||||
* \pre The file handle in the firmware context structure must already be open and
|
||||
* be "pointing" to the beginning of the file.
|
||||
*/
|
||||
int eph_send_frames(struct eph_data *ephdata)
|
||||
{
|
||||
struct device *dev = &ephdata->commsdevice->dev;
|
||||
struct eph_flash *ephflash = ephdata->ephflash;
|
||||
int ret_val;
|
||||
bool last_frame = false;
|
||||
|
||||
dev_dbg(dev, "%s >\n", __func__);
|
||||
|
||||
|
||||
if (NULL == ephflash)
|
||||
{
|
||||
dev_err(dev, "%s eph_flash = NULL - no fw image found\n", __func__);
|
||||
/* boot fail - restore chg function */
|
||||
(void)eph_bootloader_release_chg(ephdata);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
ret_val = eph_check_bootloader(ephdata, bootloader_state_WAITING_START_COMMAND);
|
||||
if (0 == ret_val)
|
||||
{
|
||||
dev_info(dev, "Unlocking bootloader\n");
|
||||
ret_val = eph_write_bootloader_start(ephdata);
|
||||
(void)eph_bootloader_release_chg(ephdata);
|
||||
|
||||
if (0 != ret_val)
|
||||
{
|
||||
dev_err(dev, "Failured to unlock bootloader\n");
|
||||
return ret_val;
|
||||
}
|
||||
dev_info(dev, "Bootloader unlocked\n");
|
||||
}
|
||||
else if (bootloader_state_WAITING_FRAME_DATA == ret_val)
|
||||
{
|
||||
dev_info(dev, "Bootloader already unlocked\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
dev_err(dev, "Unexpected state\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_info(dev, "Flashing device...\n");
|
||||
|
||||
ephflash->frame_count = 1;
|
||||
#ifdef IC_UPDATE_DETECT
|
||||
// skip dev info in the head of requested fw
|
||||
ephflash->fw_pos = sizeof(struct eph_device_info);
|
||||
#else
|
||||
ephflash->fw_pos = 0;
|
||||
#endif
|
||||
|
||||
while ((ephflash->fw_pos+BOOTLOADER_FRAME_CRC_SIZE) < (loff_t)ephflash->fw->size)
|
||||
{
|
||||
|
||||
ephflash->pframe = (u8*)(ephflash->fw->data + ephflash->fw_pos);
|
||||
ephflash->frame_size = *ephflash->pframe << 8 | *(ephflash->pframe + 1);
|
||||
dev_dbg(dev, "Frame %d: size %ld\n", ephflash->frame_count, ephflash->frame_size);
|
||||
|
||||
/* Allow for CRC bytes at end of frame */
|
||||
ephflash->frame_size += BOOTLOADER_FRAME_CRC_SIZE;
|
||||
|
||||
if (0 != eph_check_bootloader(ephdata, bootloader_state_WAITING_FRAME_DATA))
|
||||
{
|
||||
dev_err(dev, "Unexpected bootloader state\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if((ephflash->fw_pos + BOOTLOADER_FRAME_CRC_SIZE + ephflash->frame_size) >= (loff_t)ephflash->fw->size)
|
||||
{
|
||||
/* we have reached the end of the firmware file after this frame */
|
||||
last_frame = true;
|
||||
}
|
||||
|
||||
usleep_range(1300, 1500);
|
||||
/* Write one frame to device */
|
||||
ret_val = eph_write_bootloader_frame(ephdata, ephflash->frame_size, ephflash->pframe, last_frame);
|
||||
if (0 != ret_val)
|
||||
{
|
||||
return ret_val;
|
||||
}
|
||||
usleep_range(1300, 1500);
|
||||
|
||||
dev_dbg(dev, "frame: %d sent \n", ephflash->frame_count);
|
||||
ephflash->frame_count++;
|
||||
ephflash->fw_pos += ephflash->frame_size;
|
||||
|
||||
}
|
||||
dev_info(dev, "Sent %d frames, %lld bytes\n", ephflash->frame_count, ephflash->fw_pos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read bootloader status and check it is as expected.
|
||||
*
|
||||
* The boot loader returns a 1-byte status via 0x50 type TLV.
|
||||
* There are three status of the bootloader: application CRC fail, Waiting for a start bootloader command and
|
||||
* Waiting for next frame of encrypted firwmare data.
|
||||
*
|
||||
* | Status | Meaning
|
||||
* | 0x01 | Application failed its CRC check. After the status is read, the device will then enter WAITING_START_COMMAND state,.
|
||||
* | 0x02 | Waiting for a start bootloader command.
|
||||
* | 0x03 | Waiting for next frame of encrypted firwmare data.
|
||||
*
|
||||
*/
|
||||
int eph_check_bootloader(struct eph_data *ephdata, unsigned int expected_next_state)
|
||||
{
|
||||
struct device *dev = &ephdata->commsdevice->dev;
|
||||
bootloader_state_e status_byte;
|
||||
int ret_val;
|
||||
|
||||
recheck:
|
||||
/* Wait for the /CHG line to be asserted by the bootloader to tell us that
|
||||
* it is ready to output data. The /CHG line is being used as a flow control
|
||||
* mechanism. Once the bootloader has data to output it will assert the /CHG. */
|
||||
if (0 != eph_wait_for_chg(ephdata))
|
||||
{
|
||||
dev_err(dev, "Bootloader Timeout on CHG expected_next_state=%d\n", expected_next_state);
|
||||
}
|
||||
|
||||
ret_val = eph_read_report(ephdata, ephdata->report_buf);
|
||||
if (0 != ret_val)
|
||||
{
|
||||
dev_err(dev, "Bootloader read FAIL\n");
|
||||
return ret_val;
|
||||
}
|
||||
/* Process report */
|
||||
ret_val = eph_proc_bootloader_status_report(ephdata, ephdata->report_buf, &status_byte);
|
||||
dev_dbg(dev, "Bootloader status_byte %d\n", status_byte);
|
||||
if ( (bootloader_state_WAITING_FRAME_DATA == expected_next_state) ||
|
||||
(bootloader_state_WAITING_START_COMMAND == expected_next_state) )
|
||||
{
|
||||
if((bootloader_state_WAITING_START_COMMAND == expected_next_state) &&
|
||||
(bootloader_state_APP_CRC_FAILED == status_byte))
|
||||
{
|
||||
/* Device is telling us the app had a crc fail -> do another read to move bootloader
|
||||
* state machine to waiting command */
|
||||
|
||||
/* We expected to be waiting for frame data or waiting bootloader start
|
||||
* but we got CRC fail so app failed its crc - start again and we should
|
||||
* be in waiting bootloader start */
|
||||
goto recheck;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (status_byte != expected_next_state)
|
||||
{
|
||||
dev_info(dev, "Invalid bootloader mode state %02X", status_byte);
|
||||
if ( (bootloader_state_WAITING_START_COMMAND == expected_next_state) &&
|
||||
(bootloader_state_WAITING_FRAME_DATA == status_byte) )
|
||||
{
|
||||
return bootloader_state_WAITING_FRAME_DATA;
|
||||
}
|
||||
if (bootloader_state_WAITING_START_COMMAND == expected_next_state)
|
||||
{
|
||||
eph_reset_device(ephdata);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int eph_chg_force_bootloader(struct eph_data *ephdata)
|
||||
{
|
||||
struct device *dev = &ephdata->commsdevice->dev;
|
||||
/* NOTE: interrupts on CHG should be disabled before calling this function */
|
||||
#if 0
|
||||
int ret_val;
|
||||
|
||||
gpio_set_value(ephdata->ephplatform->gpio_reset, GPIO_RESET_YES_LOW);
|
||||
ret_val = gpio_direction_output(ephdata->ephplatform->gpio_chg_irq, CHG_HELD_LOW);
|
||||
/* wait some time for the reset to be seen */
|
||||
msleep(EPH_RESET_HOLD_TIME);
|
||||
gpio_set_value(ephdata->ephplatform->gpio_reset, GPIO_RESET_NO_HIGH);
|
||||
msleep((EPH_POWERON_DELAY));
|
||||
#else
|
||||
for (int i = 0; i < 10; i++) {
|
||||
gpio_set_value(ephdata->ephplatform->gpio_reset, GPIO_RESET_YES_LOW);
|
||||
msleep(EPH_RESET_HOLD_TIME);
|
||||
gpio_set_value(ephdata->ephplatform->gpio_reset, GPIO_RESET_NO_HIGH);
|
||||
msleep((EPH_POWERON_DELAY));
|
||||
}
|
||||
#endif
|
||||
|
||||
dev_info(dev, "%s, Forced device into bl mode.", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int eph_bootloader_release_chg(struct eph_data *ephdata)
|
||||
{
|
||||
int ret_val;
|
||||
ret_val = gpio_direction_input(ephdata->ephplatform->gpio_chg_irq);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
int eph_app_force_bootloader_mode(struct eph_data *ephdata)
|
||||
{
|
||||
int ret_val;
|
||||
u8 cfg[8] = {TLV_CONTROL_DATA_WRITE, /* Type */
|
||||
0x05, 0x00, /* Length */
|
||||
(0x00 & 0xFF), ((0x00>>8) & 0xFF), /* Component ID */
|
||||
0x00,0x00, /* Offset */
|
||||
0x01}; /* Value */
|
||||
u16 length = (sizeof(cfg)/sizeof(cfg[0]));
|
||||
mutex_lock(&ephdata->comms_mutex);
|
||||
ret_val = eph_write_control_config_no_response(ephdata, length, &cfg[0]);
|
||||
mutex_unlock(&ephdata->comms_mutex);
|
||||
|
||||
return ret_val;
|
||||
|
||||
}
|
||||
|
||||
static int eph_write_bootloader_start(struct eph_data *ephdata)
|
||||
{
|
||||
|
||||
int ret_val;
|
||||
u8 cfg[9] = {TLV_CONTROL_DATA_WRITE, /* Type */
|
||||
0x06, 0x00, /* Length */
|
||||
(BOOTLOADER_COMPONENT_ID & 0xFF), ((BOOTLOADER_COMPONENT_ID>>8) & 0xFF), /* Component ID */
|
||||
0x00,0x00, /* Offset */
|
||||
START_COMMAND_SEQUENCE_0,START_COMMAND_SEQUENCE_1}; /* Value */
|
||||
u16 length = (sizeof(cfg)/sizeof(cfg[0]));
|
||||
|
||||
mutex_lock(&ephdata->comms_mutex);
|
||||
ret_val = eph_write_control_config(ephdata, length, &cfg[0]);
|
||||
mutex_unlock(&ephdata->comms_mutex);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int eph_write_bootloader_frame(struct eph_data *ephdata, u16 payload_length, u8 *buf, bool last_frame)
|
||||
{
|
||||
int ret_val = 0;
|
||||
struct eph_device_eng_data_write_command eng_command;
|
||||
u8 data_id = 0;
|
||||
u16 count;
|
||||
|
||||
|
||||
if(last_frame)
|
||||
{
|
||||
/* we use 0x80 engineer data input type to package the firmware data. Data_id is used to represent last frame */
|
||||
data_id = 1;
|
||||
}
|
||||
|
||||
count = payload_length + TLV_HEADER_SIZE + TLV_ENG_DEBUG_HEADER_SIZE;
|
||||
eng_command.header.type = TLV_ENG_DEBUG_DATA_WRITE;
|
||||
eng_command.header.length = cpu_to_le16(payload_length + TLV_ENG_DEBUG_HEADER_SIZE);
|
||||
eng_command.component_id = cpu_to_le16(BOOTLOADER_COMPONENT_ID);
|
||||
eng_command.data_id = data_id;
|
||||
|
||||
/* CRC left clear for bootloading */
|
||||
eng_command.crc = 0x00;
|
||||
|
||||
|
||||
/* Copy accross fixed contents and concatanate with bootloader frame data */
|
||||
memcpy((u8*)&ephdata->comms_send_crc_buf[0], &eng_command, sizeof(struct eph_device_eng_data_write_command));
|
||||
memcpy((u8*)&ephdata->comms_send_crc_buf[sizeof(struct eph_device_eng_data_write_command)], buf, payload_length);
|
||||
|
||||
ret_val = eph_write_engineering_data(ephdata, count, ephdata->comms_send_crc_buf);
|
||||
|
||||
|
||||
if(ret_val)
|
||||
{
|
||||
/* Failed to write engineering data frame. Retry to send the frame once more */
|
||||
ret_val = eph_write_engineering_data(ephdata, count, ephdata->comms_send_crc_buf);
|
||||
}
|
||||
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
static int eph_proc_bootloader_status_report(struct eph_data *ephdata, u8 *message, bootloader_state_e *status_byte)
|
||||
{
|
||||
int ret_val = 0;
|
||||
struct tlv_header tlvheader;
|
||||
|
||||
/* Get the bootloader status type */
|
||||
/* Should only get here if a valid report was received from the device */
|
||||
tlvheader = eph_get_tl_header_info(ephdata, message);
|
||||
|
||||
if (TLV_BOOTLOADER_STATUS_DATA == tlvheader.type)
|
||||
{
|
||||
*status_byte = (bootloader_state_e)message[TLV_HEADER_SIZE];
|
||||
dev_dbg(&ephdata->commsdevice->dev,
|
||||
"TYPE:%d LENGTH:%d STATUS_FLAG:%d\n",
|
||||
tlvheader.type, tlvheader.length, *status_byte);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
dev_err(&ephdata->commsdevice->dev, "Unexpected Report type\n");
|
||||
*status_byte = bootloader_state_INVALID;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef __LINUX_PLATFORM_DATA_ESWIN_EPH_BOOTLOADER_H
|
||||
#define __LINUX_PLATFORM_DATA_ESWIN_EPH_BOOTLOADER_H
|
||||
|
||||
#include "eswin_eph861x_types.h"
|
||||
|
||||
extern int eph_chg_force_bootloader(struct eph_data *ephdata);
|
||||
extern int eph_bootloader_release_chg(struct eph_data *ephdata);
|
||||
extern int eph_send_frames(struct eph_data *ephdata);
|
||||
extern int eph_app_force_bootloader_mode(struct eph_data *ephdata);
|
||||
extern int eph_check_bootloader(struct eph_data *ephdata, unsigned int expected_next_state);
|
||||
|
||||
#endif /* __LINUX_PLATFORM_DATA_ESWIN_EPH_ESWIN_ */
|
||||
342
drivers/input/touchscreen/eswin_touch/eswin_eph861x_comms.c
Normal file
342
drivers/input/touchscreen/eswin_touch/eswin_eph861x_comms.c
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
* ESWIN EPH861X series Touchscreen driver
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
// uncomment to enable the dev_dbg prints to dmesg
|
||||
#define DEBUG
|
||||
// uncomment to test with input forced open
|
||||
//#define INPUT_DEVICE_ALWAYS_OPEN
|
||||
#include <linux/types.h>
|
||||
|
||||
|
||||
|
||||
#include <uapi/asm-generic/errno-base.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <uapi/linux/input-event-codes.h>
|
||||
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "eswin_eph861x_tlv.h"
|
||||
#include "eswin_eph861x_types.h"
|
||||
#include "eswin_eph861x_project_config.h"
|
||||
#include "eswin_eph861x_comms.h"
|
||||
|
||||
struct dma_pool *pool_rx;
|
||||
struct dma_pool *pool_tx;
|
||||
|
||||
|
||||
struct tlv_header eph_get_tl_header_info(struct eph_data *ephdata, u8 *message)
|
||||
{
|
||||
struct tlv_header tlvheader;
|
||||
|
||||
tlvheader.type = message[TLV_TYPE_FIELD];
|
||||
tlvheader.length = (u16)min((int)((int)(message[TLV_LENGTH_FIELD] | ((u16)message[(TLV_LENGTH_FIELD+1)] << 8u)) + TLV_HEADER_SIZE), 0xFFFF); // little endian 16
|
||||
|
||||
return tlvheader;
|
||||
}
|
||||
|
||||
|
||||
int eph_comms_two_stage_read(struct eph_data *ephdata, u8 *buf)
|
||||
{
|
||||
int ret_val = 0;
|
||||
struct tlv_header tlvheader = {0};
|
||||
u16 length;
|
||||
|
||||
/* River will do TL then TLV so header length - Decode length etc */
|
||||
/* offset not used for header */
|
||||
// note: SPI has difference with i2c, SPI read success return 0,but i2c retutn read count // add by hh
|
||||
ret_val = eph_comms_read(ephdata, sizeof(struct tlv_header),(u8*)&tlvheader);
|
||||
#if 0
|
||||
dev_dbg(&ephdata->commsdevice->dev,
|
||||
"ESWIN - eph_comms_two_stage_read first stage type: %d, length: %d, ret_val %d ",
|
||||
tlvheader.type, tlvheader.length, ret_val);
|
||||
#endif
|
||||
if (!ret_val)
|
||||
{
|
||||
/* saturate to 16 bit type */
|
||||
/* if device isnt connected then this can overflow without the saturation */
|
||||
length = (u16)min((int)((int)le16_to_cpu(tlvheader.length) + TLV_HEADER_SIZE), 0xFFFF);
|
||||
|
||||
/* wait a delay for TIC to be ready */
|
||||
udelay(50);
|
||||
#if 0
|
||||
dev_info(&ephdata->commsdevice->dev,
|
||||
"eph_comms_two_stage_read: type: %u length: %u ",
|
||||
tlvheader.type, length);
|
||||
#endif
|
||||
/* do a read */
|
||||
/* Read control configuration response */
|
||||
ret_val = eph_comms_read(ephdata, length, buf);
|
||||
#if 0
|
||||
dev_dbg(&ephdata->commsdevice->dev,
|
||||
"eph_comms_two_stage_read: ESWIN - eph_comms_two_stage_read second stage ret_val %d, Type 0x%x, length_LSB 0x%x, length_MSB 0x%x, buff[3] 0x%x, buff[4] 0x%x, buff[5] 0x%x, buff[6] 0x%x, buff[7] 0x%x, buff[8] 0x%x, buff[9] 0x%x, buff[10] 0x%x, buff[11] 0x%x",
|
||||
ret_val, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]);
|
||||
#endif
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "eph_comms_two_stage_read: Failed to read payload: (%d)", ret_val);
|
||||
} else {
|
||||
// bugfix: spi clk low when start to transfer data
|
||||
if ((buf[0] == buf[0+TLV_HEADER_SIZE]) && (buf[1] == buf[1+TLV_HEADER_SIZE]) && (buf[2] == buf[2+TLV_HEADER_SIZE])) {
|
||||
uint16_t data_length = buf[TLV_LENGTH_FIELD] | ((uint16_t)buf[TLV_LENGTH_FIELD+1] << 8);
|
||||
for (int i = 0; i < data_length; i++) {
|
||||
buf[i] = buf[i+TLV_HEADER_SIZE];
|
||||
}
|
||||
ret_val = eph_comms_read(ephdata, TLV_HEADER_SIZE, &buf[data_length]);
|
||||
//dev_info(&ephdata->commsdevice->dev, "glued the data %d %d %d", buf[0], buf[1], buf[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "eph_comms_two_stage_read: Failed to read header: (%d)", ret_val);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int eph_read_report(struct eph_data *ephdata, u8 *buf)
|
||||
{
|
||||
int ret_val = 0;
|
||||
|
||||
/* Part of interrupt handling - No error handling. If message cant be decoded then it will be discsarded */
|
||||
/* no read offset required ----- always part of the command aspect */
|
||||
ret_val = eph_comms_two_stage_read(ephdata, buf);
|
||||
|
||||
|
||||
return ret_val;
|
||||
|
||||
}
|
||||
|
||||
|
||||
u8 eph_read_chg(struct eph_data *ephdata)
|
||||
{
|
||||
u8 ret_val = (u8)gpio_get_value(ephdata->ephplatform->gpio_chg_irq);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int eph_wait_for_chg(struct eph_data *ephdata)
|
||||
{
|
||||
int timeout_counter = 25000; // 500 msec
|
||||
|
||||
while (0 != eph_read_chg(ephdata) && timeout_counter > 0)
|
||||
{
|
||||
timeout_counter--;
|
||||
udelay(20);
|
||||
}
|
||||
|
||||
return timeout_counter > 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
u32 ctl_data[2] = { 200, 0 };
|
||||
|
||||
int eph_comms_specific_checks(struct comms_device *commsdevice)
|
||||
{
|
||||
int ret_val;
|
||||
#if (ESWIN_EPH861X_SPI)
|
||||
#if 0//KERNEL_VERSION(5, 15, 0) >= LINUX_VERSION_CODE
|
||||
struct spi_delay d;
|
||||
d.value = 100;
|
||||
d.unit = SPI_DELAY_UNIT_USECS;
|
||||
commsdevice->cs_setup = d;
|
||||
pr_err("eph_comms_specific_checks----11a----\n");
|
||||
#else
|
||||
//pr_err("eph_add 100us delay----\n");
|
||||
commsdevice->controller_data = ctl_data;
|
||||
#endif
|
||||
commsdevice->mode = 3;
|
||||
//commsdevice->chip_select = 0;
|
||||
ret_val = eph_comms_specific_checks_spi(commsdevice);
|
||||
#endif
|
||||
#if (ESWIN_EPH861X_I2C)
|
||||
ret_val = eph_comms_specific_checks_i2c(commsdevice);
|
||||
#endif
|
||||
#if ((ESWIN_EPH861X_SPI) && (ESWIN_EPH861X_I2C))
|
||||
#error " Must select only SPI or I2C "
|
||||
#endif
|
||||
#if (!((ESWIN_EPH861X_SPI) || (ESWIN_EPH861X_I2C)))
|
||||
#error " Must select SPI or I2C "
|
||||
#endif
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
void eph_comms_driver_data_set(struct comms_device *commsdevice, struct eph_data *ephdata)
|
||||
{
|
||||
|
||||
#if (ESWIN_EPH861X_SPI)
|
||||
eph_comms_driver_data_set_spi(commsdevice, ephdata);
|
||||
#endif
|
||||
#if (ESWIN_EPH861X_I2C)
|
||||
eph_comms_driver_data_set_i2c(commsdevice, ephdata);
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
struct eph_data* eph_comms_driver_data_get(struct comms_device *commsdevice)
|
||||
{
|
||||
|
||||
#if (ESWIN_EPH861X_SPI)
|
||||
struct eph_data* ephdata = eph_comms_driver_data_get_spi(commsdevice);
|
||||
#endif
|
||||
#if (ESWIN_EPH861X_I2C)
|
||||
struct eph_data* ephdata = eph_comms_driver_data_get_i2c(commsdevice);
|
||||
#endif
|
||||
|
||||
return ephdata;
|
||||
}
|
||||
|
||||
|
||||
struct comms_device* eph_comms_device_get(struct device *dev)
|
||||
{
|
||||
|
||||
#if (ESWIN_EPH861X_SPI)
|
||||
struct comms_device *commsdevice = eph_comms_device_get_spi(dev);
|
||||
#endif
|
||||
#if (ESWIN_EPH861X_I2C)
|
||||
struct comms_device *commsdevice = eph_comms_device_get_i2c(dev);
|
||||
#endif
|
||||
|
||||
return commsdevice;
|
||||
}
|
||||
|
||||
char* eph_comms_devicename_get(struct eph_data *ephdata)
|
||||
{
|
||||
char * commsdevice_name;
|
||||
|
||||
#if (ESWIN_EPH861X_SPI)
|
||||
commsdevice_name = ephdata->commsdevice->modalias;
|
||||
#endif
|
||||
#if (ESWIN_EPH861X_I2C)
|
||||
commsdevice_name = ephdata->commsdevice->name;
|
||||
#endif
|
||||
|
||||
return commsdevice_name;
|
||||
}
|
||||
|
||||
int eph_comms_specific_bootloader_checks(struct eph_data *ephdata)
|
||||
{
|
||||
|
||||
#if (ESWIN_EPH861X_SPI)
|
||||
int ret_val = eph_comms_specific_bootloader_checks_spi(ephdata);
|
||||
#endif
|
||||
#if (ESWIN_EPH861X_I2C)
|
||||
int ret_val = eph_comms_specific_bootloader_checks_i2c(ephdata);
|
||||
#endif
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
u8 eph_get_data_crc(uint8_t* p_msg, size_t size)
|
||||
{
|
||||
u8 calc_crc = 0;
|
||||
int i;
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
calc_crc = get_crc8_iter(calc_crc, p_msg[i]);
|
||||
}
|
||||
return calc_crc;
|
||||
}
|
||||
|
||||
|
||||
u8 get_crc8_iter(u8 crc, u8 data)
|
||||
{
|
||||
static const u8 crcpoly = 0x8c;
|
||||
u8 index = 8;
|
||||
u8 fb;
|
||||
do
|
||||
{
|
||||
fb = (crc ^ data) & 0x01;
|
||||
data >>= 1;
|
||||
crc >>= 1;
|
||||
if (fb)
|
||||
{
|
||||
crc ^= crcpoly;
|
||||
}
|
||||
} while (--index);
|
||||
return crc;
|
||||
}
|
||||
|
||||
int eph_allocate_comms_memory(struct comms_device *commsdevice, struct eph_data *ephdata)
|
||||
{
|
||||
int ret_val = 0;
|
||||
|
||||
ephdata->comms_send_crc_buf = (u8*)kcalloc(COMMS_BUF_WRITE_SIZE, sizeof(ephdata->comms_send_crc_buf), GFP_KERNEL);
|
||||
if (NULL == ephdata->comms_send_crc_buf)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ephdata->report_buf = (u8*)kcalloc(COMMS_BUF_SIZE, sizeof(ephdata->report_buf), GFP_KERNEL);
|
||||
if (NULL == ephdata->report_buf)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#if (ESWIN_EPH861X_SPI && ESWIN_EPH861X_SPI_USE_DMA)
|
||||
//TODO configuration for DMA should not be needed in generic driver and the following can be consolodated into single pool
|
||||
/* Configure DMA for SPI */
|
||||
arch_setup_dma_ops(&commsdevice->dev, 0, U64_MAX, NULL, true);
|
||||
|
||||
ret_val = dma_coerce_mask_and_coherent(&commsdevice->dev,
|
||||
DMA_BIT_MASK(32));
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&commsdevice->dev, "%s. dma_coerce_mask_and_coherent failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Create memory pool */
|
||||
pool_rx = dma_pool_create("create_comms_receive_buf", &ephdata->commsdevice->dev, COMMS_BUF_SIZE,
|
||||
0 /* byte alignment */,
|
||||
0 /* no page-crossing issues */);
|
||||
|
||||
/* Allocate memory from pool */
|
||||
ephdata->comms_receive_buf = (u8*)dma_pool_alloc(pool_rx, GFP_KERNEL, &ephdata->comms_dma_handle_rx);
|
||||
if (NULL == ephdata->comms_receive_buf)
|
||||
{
|
||||
dev_err(&commsdevice->dev, "%s comms_receive_buf memory allocation failed \n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Create memory pool */
|
||||
pool_tx = dma_pool_create("create_comms_send_buf", &ephdata->commsdevice->dev, COMMS_BUF_WRITE_SIZE,
|
||||
0 /* byte alignment */,
|
||||
0 /* no page-crossing issues */);
|
||||
|
||||
/* Allocate memory from pool */
|
||||
ephdata->comms_send_buf = (u8*)dma_pool_alloc(pool_tx, GFP_KERNEL, &ephdata->comms_dma_handle_tx);
|
||||
if (NULL == ephdata->comms_send_buf)
|
||||
{
|
||||
dev_err(&commsdevice->dev, "%s comms_send_buf memory allocation failed \n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
#elif (ESWIN_EPH861X_SPI)
|
||||
// only use spi not use dma
|
||||
ephdata->comms_receive_buf = (u8*)kcalloc(COMMS_BUF_SIZE, sizeof(ephdata->comms_receive_buf), GFP_KERNEL);
|
||||
ephdata->comms_send_buf = (u8*)kcalloc(COMMS_BUF_WRITE_SIZE, sizeof(ephdata->comms_receive_buf), GFP_KERNEL);
|
||||
#endif
|
||||
|
||||
#if (ESWIN_EPH861X_I2C) // modified by hh just define spi here
|
||||
ephdata->comms_receive_buf = (u8*)kcalloc(COMMS_BUF_SIZE, sizeof(ephdata->comms_receive_buf), GFP_KERNEL);
|
||||
#endif
|
||||
return ret_val;
|
||||
|
||||
}
|
||||
48
drivers/input/touchscreen/eswin_touch/eswin_eph861x_comms.h
Normal file
48
drivers/input/touchscreen/eswin_touch/eswin_eph861x_comms.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef __LINUX_PLATFORM_DATA_ESWIN_EPH_COMMS_H
|
||||
#define __LINUX_PLATFORM_DATA_ESWIN_EPH_COMMS_H
|
||||
|
||||
|
||||
#include "eswin_eph861x_types.h"
|
||||
#include "eswin_eph861x_tlv.h"
|
||||
|
||||
#if (ESWIN_EPH861X_SPI)
|
||||
#include "eswin_eph861x_spi.h"
|
||||
#define COMMS_BUF_SIZE SPI_APP_BUF_SIZE_READ
|
||||
#define COMMS_BUF_WRITE_SIZE SPI_APP_BUF_SIZE_WRITE
|
||||
#endif
|
||||
|
||||
#if (ESWIN_EPH861X_I2C)
|
||||
#include "eswin_eph861x_i2c.h"
|
||||
#define COMMS_BUF_SIZE I2C_APP_BUF_SIZE_READ
|
||||
#define COMMS_BUF_WRITE_SIZE I2C_APP_BUF_SIZE_WRITE
|
||||
#endif
|
||||
|
||||
#define COMMS_READ_RETRY_NUM 3
|
||||
|
||||
extern struct dma_pool *pool_rx;
|
||||
extern struct dma_pool *pool_tx;
|
||||
|
||||
|
||||
extern struct tlv_header eph_get_tl_header_info(struct eph_data *ephdata, u8 *message);
|
||||
|
||||
extern int eph_comms_two_stage_read(struct eph_data *ephdata, u8 *buf);
|
||||
extern int eph_read_report(struct eph_data *ephdata, u8 *buf);
|
||||
extern int eph_comms_write(struct eph_data *ephdata, u16 len, u8 *buf);
|
||||
|
||||
extern int eph_wait_for_chg(struct eph_data *ephdata);
|
||||
extern u8 eph_read_chg(struct eph_data *ephdata);
|
||||
|
||||
extern int eph_comms_specific_checks(struct comms_device *commsdevice);
|
||||
extern void eph_comms_driver_data_set(struct comms_device *commsdevice, struct eph_data *ephdata);
|
||||
extern struct eph_data* eph_comms_driver_data_get(struct comms_device *commsdevice);
|
||||
extern struct comms_device* eph_comms_device_get(struct device *dev);
|
||||
extern char* eph_comms_devicename_get(struct eph_data *ephdata);
|
||||
extern int eph_comms_specific_bootloader_checks(struct eph_data *ephdata);
|
||||
|
||||
|
||||
extern u8 get_crc8_iter(u8 crc, u8 data);
|
||||
extern u8 eph_get_data_crc(uint8_t* p_msg, size_t size);
|
||||
|
||||
extern int eph_allocate_comms_memory(struct comms_device *commsdevice, struct eph_data *ephdata);
|
||||
|
||||
#endif /* __LINUX_PLATFORM_DATA_ESWIN_EPH_COMMS_ */
|
||||
447
drivers/input/touchscreen/eswin_touch/eswin_eph861x_eswin.c
Normal file
447
drivers/input/touchscreen/eswin_touch/eswin_eph861x_eswin.c
Normal file
|
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
* ESWIN EPH861X series Touchscreen driver
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
// uncomment to enable the dev_dbg prints to dmesg
|
||||
#define DEBUG
|
||||
// uncomment to test with input forced open
|
||||
//#define INPUT_DEVICE_ALWAYS_OPEN
|
||||
#include <linux/types.h>
|
||||
|
||||
|
||||
|
||||
#include <uapi/asm-generic/errno-base.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <uapi/linux/stat.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include "eswin_eph861x_project_config.h"
|
||||
#include "eswin_eph861x_tlv.h"
|
||||
#include "eswin_eph861x_types.h"
|
||||
#include "eswin_eph861x_eswin.h"
|
||||
#include "eswin_eph861x_comms.h"
|
||||
|
||||
int eph_check_firmware_format(struct device *dev, const struct firmware *fw)
|
||||
{
|
||||
unsigned int pos = 0;
|
||||
char c;
|
||||
|
||||
while (pos < fw->size)
|
||||
{
|
||||
c = *(fw->data + pos);
|
||||
|
||||
if (c < '0' || (c > '9' && c < 'A') || c > 'F')
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
dev_err(dev, "Aborting: firmware file must be in binary format\n");
|
||||
dev_err(dev, "xxd -r -p EPH861x_XXXXX_XXXX.enc > EPH861x_XXXXX_XXXX.bin\n");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef IC_UPDATE_DETECT
|
||||
// return 0 means ic isn't need to update
|
||||
// return 1 means need to update
|
||||
int eph_check_ic_update(struct eph_data *ephdata, const struct firmware *fw)
|
||||
{
|
||||
int ret = 0;
|
||||
struct eph_firmware_info fw_info;
|
||||
struct eph_device_info *dev_info;
|
||||
struct device *dev = &ephdata->commsdevice->dev;
|
||||
|
||||
if (!fw)
|
||||
return -EINVAL;
|
||||
|
||||
dev_info = &ephdata->ephdeviceinfo;
|
||||
|
||||
memcpy(&fw_info, fw->data, sizeof(struct eph_firmware_info));
|
||||
|
||||
if ((fw_info.product_id != dev_info->product_id) && (fw_info.variant_id !=
|
||||
dev_info->variant_id)) {
|
||||
dev_info(dev, "product version diff or requested fw no info in head");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// check crc
|
||||
if (fw_info.crc != eph_get_data_crc((u8 *)&fw_info, sizeof(struct eph_firmware_info) - 1)) {
|
||||
dev_err(dev, "requested fw info is incompete");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// check bootloader
|
||||
if (fw_info.bootloader_version != dev_info->bootloader_version) {
|
||||
dev_warn(dev, "firmware bootloader version diff");
|
||||
}
|
||||
|
||||
// check app version
|
||||
if ((fw_info.application_version_major != dev_info->application_version_major) ||
|
||||
(fw_info.application_version_minor != dev_info->application_version_minor)) {
|
||||
dev_info(dev, "firmware application version diff, wait update");
|
||||
ret = 1;
|
||||
}
|
||||
dev_info(dev, "ic vers: %d %d, requested fw vers: %d %d\n", dev_info->application_version_major,
|
||||
dev_info->application_version_minor, fw_info.application_version_major,
|
||||
fw_info.application_version_minor);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int eph_update_file_name(struct device *dev,
|
||||
char **out_file_name,
|
||||
const char *in_file_name,
|
||||
size_t in_str_len)
|
||||
{
|
||||
char *file_name_tmp;
|
||||
|
||||
/* Simple sanity check */
|
||||
if (in_str_len > 64)
|
||||
{
|
||||
dev_warn(dev, "File name too long\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
file_name_tmp = (char *)krealloc(*out_file_name, in_str_len + 1, GFP_KERNEL);
|
||||
if (!file_name_tmp)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*out_file_name = file_name_tmp;
|
||||
memcpy(*out_file_name, in_file_name, in_str_len);
|
||||
|
||||
/* Echo into the sysfs entry may append newline at the end of buf */
|
||||
if (in_file_name[in_str_len - 1] == '\n')
|
||||
{
|
||||
(*out_file_name)[in_str_len - 1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
(*out_file_name)[in_str_len] = '\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef CONFIG_OF // Open Firmware (Device Tree)
|
||||
const struct eph_platform_data *eph_platform_data_get_from_device_tree(struct comms_device *commsdevice)
|
||||
{
|
||||
struct eph_platform_data *ephplatform;
|
||||
struct device_node *devnode = commsdevice->dev.of_node;
|
||||
int ret_val;
|
||||
|
||||
dev_dbg(&commsdevice->dev, "%s using device tree > \n", __func__);
|
||||
|
||||
if (!devnode)
|
||||
{
|
||||
return (struct eph_platform_data *)ERR_PTR(-ENOENT); /* no device tree device */
|
||||
}
|
||||
|
||||
ephplatform = (struct eph_platform_data *)devm_kzalloc(&commsdevice->dev, sizeof(struct eph_platform_data), GFP_KERNEL);
|
||||
if (!ephplatform)
|
||||
{
|
||||
return (struct eph_platform_data *)ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
ephplatform->gpio_reset = of_get_named_gpio_flags(devnode, "eswin,reset-gpio", 0, NULL);
|
||||
ephplatform->gpio_chg_irq = of_get_named_gpio_flags(devnode, "eswin,irq-gpio", 0, NULL);
|
||||
|
||||
/* returns pointer to already allocated memory containing the string */
|
||||
ret_val = of_property_read_string(devnode, "eswin,regulator_dvdd", &ephplatform->regulator_dvdd);
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&commsdevice->dev, "Couldn't read eswin,regulator_dvdd: %d\n", ret_val);
|
||||
}
|
||||
|
||||
ret_val = of_property_read_string(devnode, "eswin,regulator_avdd", &ephplatform->regulator_avdd);
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&commsdevice->dev, "Couldn't read eswin,regulator_avdd: %d\n", ret_val);
|
||||
}
|
||||
|
||||
ret_val = of_property_read_string(devnode, "eswin,device_settings_name", &ephplatform->device_settings_name);
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&commsdevice->dev, "Couldn't read eswin,device_settings_name: %d\n", ret_val);
|
||||
}
|
||||
|
||||
ret_val = of_property_read_string(devnode, "eswin,fw_name", &ephplatform->fw_name);
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&commsdevice->dev, "Couldn't read eswin,fw_name: %d\n", ret_val);
|
||||
}
|
||||
|
||||
of_property_read_string(devnode, "eswin,input_name", &ephplatform->input_name);
|
||||
|
||||
of_property_read_u32(devnode, "eswin,suspend-mode", &ephplatform->suspend_mode);
|
||||
|
||||
of_property_read_u32(devnode, "eswin,panel-invert-x", &ephplatform->panel_invert_x);
|
||||
|
||||
of_property_read_u32(devnode, "eswin,panel-invert-y", &ephplatform->panel_invert_y);
|
||||
|
||||
ret_val = of_property_read_u32(devnode, "eswin,panel-max-x", &ephplatform->panel_max_x);
|
||||
if (ret_val) {
|
||||
dev_err(&commsdevice->dev, "Couldn't read eswin,panel_max_x: %d\n", ret_val);
|
||||
return (struct eph_platform_data *)ERR_PTR(-EINVAL);
|
||||
}
|
||||
ret_val = of_property_read_u32(devnode, "eswin,panel-max-y", &ephplatform->panel_max_y);
|
||||
if (ret_val) {
|
||||
dev_err(&commsdevice->dev, "Couldn't read eswin,panel_max_y: %d\n", ret_val);
|
||||
return (struct eph_platform_data *)ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
dev_info(&commsdevice->dev, "panel X%uY%u\n", ephplatform->panel_max_x,
|
||||
ephplatform->panel_max_y);
|
||||
|
||||
dev_dbg(&commsdevice->dev, "%s using device tree <\n", __func__);
|
||||
|
||||
return ephplatform;
|
||||
}
|
||||
#else // CONFIG_OF
|
||||
const struct eph_platform_data *eph_platform_data_get_from_device_tree(struct comms_device *commsdevice)
|
||||
{
|
||||
return (struct eph_platform_data *)ERR_PTR(-ENOENT);
|
||||
}
|
||||
#endif // CONFIG_OF
|
||||
|
||||
|
||||
struct eph_platform_data *eph_platform_data_get_default(struct comms_device *commsdevice)
|
||||
{
|
||||
struct eph_platform_data *ephplatform = (struct eph_platform_data *)devm_kzalloc(&commsdevice->dev, sizeof(struct eph_platform_data), GFP_KERNEL);
|
||||
if (!ephplatform)
|
||||
{
|
||||
return (struct eph_platform_data *)ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/* Set default parameters */
|
||||
|
||||
dev_dbg(&commsdevice->dev, "%s <\n", __func__);
|
||||
|
||||
return ephplatform;
|
||||
}
|
||||
|
||||
const struct eph_platform_data * eph_platform_data_get(struct comms_device *commsdevice)
|
||||
{
|
||||
const struct eph_platform_data *ephplatform;
|
||||
|
||||
dev_dbg(&commsdevice->dev, "%s >\n", __func__);
|
||||
|
||||
ephplatform = (struct eph_platform_data *)dev_get_platdata(&commsdevice->dev);
|
||||
if (ephplatform)
|
||||
{
|
||||
return ephplatform;
|
||||
}
|
||||
|
||||
ephplatform = eph_platform_data_get_from_device_tree(commsdevice);
|
||||
if (!IS_ERR(ephplatform) || PTR_ERR(ephplatform) != -ENOENT)
|
||||
{
|
||||
return ephplatform;
|
||||
}
|
||||
|
||||
ephplatform = eph_platform_data_get_default(commsdevice);
|
||||
if (!IS_ERR(ephplatform))
|
||||
{
|
||||
return ephplatform;
|
||||
}
|
||||
|
||||
dev_err(&commsdevice->dev, "No platform data specified\n");
|
||||
return (struct eph_platform_data *)ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
int eph_gpio_setup(struct eph_data *ephdata)
|
||||
{
|
||||
int ret_val;
|
||||
dev_dbg(&ephdata->commsdevice->dev, "%s >\n", __func__);
|
||||
|
||||
ret_val = gpio_request(ephdata->ephplatform->gpio_chg_irq, "irq-gpio");
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "gpio_request %lu (%d)", ephdata->ephplatform->gpio_chg_irq, ret_val);
|
||||
return ret_val;
|
||||
}
|
||||
ret_val = gpio_direction_input(ephdata->ephplatform->gpio_chg_irq);
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "gpio_direction_input %lu (%d)", ephdata->ephplatform->gpio_chg_irq, ret_val);
|
||||
return ret_val;
|
||||
}
|
||||
dev_dbg(&ephdata->commsdevice->dev, "gpio_chg_irq %lu IN %d\n", ephdata->ephplatform->gpio_chg_irq, (u8)gpio_get_value(ephdata->ephplatform->gpio_chg_irq));
|
||||
|
||||
ret_val = gpio_request(ephdata->ephplatform->gpio_reset, "reset-gpio");
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "gpio_request %lu (%d)", ephdata->ephplatform->gpio_reset, ret_val);
|
||||
return ret_val;
|
||||
}
|
||||
/* Initialise so that we are holding the device in reset until power has been applied */
|
||||
ret_val = gpio_direction_output(ephdata->ephplatform->gpio_reset, GPIO_RESET_YES_LOW);
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "gpio_direction_output %lu (%d)", ephdata->ephplatform->gpio_reset, ret_val);
|
||||
return ret_val;
|
||||
}
|
||||
dev_dbg(&ephdata->commsdevice->dev, "gpio_reset %lu OUT %d\n", ephdata->ephplatform->gpio_reset, GPIO_RESET_YES_LOW);
|
||||
|
||||
dev_dbg(&ephdata->commsdevice->dev, "%s <\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eph_wait_for_completion(struct eph_data *ephdata,
|
||||
struct completion *comp,
|
||||
unsigned int timeout_ms,
|
||||
const char *dbg_str)
|
||||
{
|
||||
struct device *dev = &ephdata->commsdevice->dev;
|
||||
unsigned long timeout = msecs_to_jiffies(timeout_ms);
|
||||
long ret_val;
|
||||
|
||||
dev_dbg(&ephdata->commsdevice->dev, "%s %s >\n", __func__, dbg_str);
|
||||
|
||||
ret_val = wait_for_completion_interruptible_timeout(comp, timeout);
|
||||
if (ret_val < 0)
|
||||
{
|
||||
return ret_val;
|
||||
}
|
||||
if (ret_val == 0)
|
||||
{
|
||||
dev_err(dev, "wait_for_completion timeout %s\n", dbg_str);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void eph_regulator_enable(struct eph_data *ephdata)
|
||||
{
|
||||
int ret_val;
|
||||
|
||||
dev_err(&ephdata->commsdevice->dev, "zcy %s >\n", __func__);
|
||||
|
||||
if (!ephdata->reg_vdd || !ephdata->reg_avdd)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "zcy %s out 1\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_set_value(ephdata->ephplatform->gpio_reset, GPIO_RESET_YES_LOW);
|
||||
|
||||
ret_val = regulator_enable(ephdata->reg_vdd);
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "zcy %s out 2\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
ret_val = regulator_enable(ephdata->reg_avdd);
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "zcy %s out 3\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* According to power sequencing specification, RESET line must be kept
|
||||
* low until some time after regulators come up to voltage */
|
||||
msleep(EPH_REGULATOR_DELAY);
|
||||
gpio_set_value(ephdata->ephplatform->gpio_reset, GPIO_RESET_NO_HIGH);
|
||||
//TODO this additional delay should not be needed
|
||||
/* Delay to prevent poor signals after power up. This will allow device time to "settle" before baseline */
|
||||
msleep(EPH_POWERON_DELAY);
|
||||
retry_wait:
|
||||
reinit_completion(&ephdata->chg_completion);
|
||||
ephdata->in_bootloader = true;
|
||||
ret_val = eph_wait_for_completion(ephdata, &ephdata->chg_completion, EPH_POWERON_DELAY, "CHG");
|
||||
if (ret_val == -EINTR)
|
||||
{
|
||||
goto retry_wait;
|
||||
}
|
||||
ephdata->in_bootloader = false;
|
||||
dev_err(&ephdata->commsdevice->dev, "zcy %s out 4\n", __func__);
|
||||
}
|
||||
|
||||
void eph_regulator_disable(struct eph_data *ephdata)
|
||||
{
|
||||
dev_dbg(&ephdata->commsdevice->dev, "%s >\n", __func__);
|
||||
|
||||
if (!ephdata->reg_vdd || !ephdata->reg_avdd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
regulator_disable(ephdata->reg_vdd);
|
||||
regulator_disable(ephdata->reg_avdd);
|
||||
}
|
||||
|
||||
void eph_recovery_device(struct eph_data *ephdata)
|
||||
{
|
||||
int ret_val;
|
||||
|
||||
disable_irq(ephdata->chg_irq);
|
||||
gpio_set_value(ephdata->ephplatform->gpio_reset, GPIO_RESET_YES_LOW);
|
||||
if (ephdata->reg_vdd && regulator_is_enabled(ephdata->reg_vdd))
|
||||
regulator_disable(ephdata->reg_vdd);
|
||||
if (ephdata->reg_avdd && regulator_is_enabled(ephdata->reg_avdd))
|
||||
regulator_disable(ephdata->reg_avdd);
|
||||
|
||||
msleep(500); // at least 100ms for ic discharge
|
||||
|
||||
if (ephdata->reg_vdd)
|
||||
ret_val = regulator_enable(ephdata->reg_vdd);
|
||||
if (ephdata->reg_avdd)
|
||||
ret_val = regulator_enable(ephdata->reg_avdd);
|
||||
|
||||
msleep(20); // ic spec at least 10ms
|
||||
|
||||
gpio_set_value(ephdata->ephplatform->gpio_reset, GPIO_RESET_NO_HIGH);
|
||||
|
||||
// wait ic stable, ic spec should no comms action before ic has
|
||||
// first data ready
|
||||
// sleep over 200ms or wait INT be low
|
||||
msleep(200);
|
||||
|
||||
enable_irq(ephdata->chg_irq);
|
||||
|
||||
dev_dbg(&ephdata->commsdevice->dev, "%s\n", __func__);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void eph_reset_device(struct eph_data *ephdata)
|
||||
{
|
||||
|
||||
dev_dbg(&ephdata->commsdevice->dev, "%s gpio is %ld >\n", __func__, ephdata->ephplatform->gpio_reset);
|
||||
gpio_set_value(ephdata->ephplatform->gpio_reset, GPIO_RESET_YES_LOW);
|
||||
msleep(1);
|
||||
gpio_set_value(ephdata->ephplatform->gpio_reset, GPIO_RESET_NO_HIGH);
|
||||
msleep(EPH_POWERON_DELAY);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
35
drivers/input/touchscreen/eswin_touch/eswin_eph861x_eswin.h
Normal file
35
drivers/input/touchscreen/eswin_touch/eswin_eph861x_eswin.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
#ifndef __LINUX_PLATFORM_DATA_ESWIN_EPH_ESWIN_H
|
||||
#define __LINUX_PLATFORM_DATA_ESWIN_EPH_ESWIN_H
|
||||
|
||||
#include "eswin_eph861x_types.h"
|
||||
|
||||
#define GPIO_RESET_NO_HIGH 1
|
||||
#define GPIO_RESET_YES_LOW 0
|
||||
#define CHG_HELD_LOW 0
|
||||
|
||||
extern int eph_check_firmware_format(struct device* dev, const struct firmware* fw);
|
||||
|
||||
extern int eph_update_file_name(struct device* dev,
|
||||
char** out_file_name,
|
||||
const char* in_file_name,
|
||||
size_t in_str_len);
|
||||
|
||||
extern const struct eph_platform_data* eph_platform_data_get_from_device_tree(struct comms_device* commsdevice);
|
||||
extern struct eph_platform_data* eph_platform_data_get_default(struct comms_device* commsdevice);
|
||||
extern const struct eph_platform_data* eph_platform_data_get(struct comms_device* commsdevice);
|
||||
extern int eph_gpio_setup(struct eph_data* ephdata);
|
||||
|
||||
extern int eph_wait_for_completion(struct eph_data *ephdata,
|
||||
struct completion *comp,
|
||||
unsigned int timeout_ms,
|
||||
const char *dbg_str);
|
||||
extern void eph_regulator_enable(struct eph_data *ephdata);
|
||||
extern void eph_regulator_disable(struct eph_data *ephdata);
|
||||
extern void eph_reset_device(struct eph_data *ephdata);
|
||||
extern void eph_recovery_device(struct eph_data *ephdata);
|
||||
#ifdef IC_UPDATE_DETECT
|
||||
int eph_check_ic_update(struct eph_data *ephdata, const struct firmware *fw);
|
||||
#endif
|
||||
|
||||
#endif /* __LINUX_PLATFORM_DATA_ESWIN_EPH_ESWIN_ */
|
||||
252
drivers/input/touchscreen/eswin_touch/eswin_eph861x_i2c.c
Normal file
252
drivers/input/touchscreen/eswin_touch/eswin_eph861x_i2c.c
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* ESWIN EPH861X series Touchscreen driver
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
// uncomment to enable the dev_dbg prints to dmesg
|
||||
#define DEBUG
|
||||
// uncomment to test with input forced open
|
||||
//#define INPUT_DEVICE_ALWAYS_OPEN
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <uapi/asm-generic/errno-base.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <uapi/linux/input-event-codes.h>
|
||||
#include <uapi/linux/stat.h>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include "eswin_eph861x_types.h"
|
||||
|
||||
#if (ESWIN_EPH861X_I2C)
|
||||
#include "eswin_eph861x_i2c.h"
|
||||
|
||||
#define TLV_RESERVED_INVALID_TYPE 0xFF
|
||||
|
||||
//TODO file untested
|
||||
|
||||
int __eph_read_reg(struct eph_data *ephdata, u16 len, u8 *val)
|
||||
{
|
||||
int ret_val;
|
||||
|
||||
if ((I2C_APP_BUF_SIZE_READ) < len)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Error reading from i2c: length greater than buffer size(%d)", len);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret_val = i2c_master_recv(ephdata->commsdevice, val, len);
|
||||
|
||||
if(ret_val != len)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "%s: i2c_master_recv ret_val %d\n", __func__, ret_val);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __eph_write_reg(struct eph_data *ephdata, u16 len, u8 *val)
|
||||
{
|
||||
int ret_val, retry;
|
||||
|
||||
if (!val)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
if ((I2C_APP_BUF_SIZE_WRITE) < len)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Error writing to i2c: length greater than buffer size(%d)", len);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (retry = 0; retry < 2; retry++)
|
||||
{
|
||||
if (retry > 0)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "%s: retry %d\n", __func__, retry);
|
||||
msleep(EPH_WAKEUP_TIME);
|
||||
}
|
||||
ret_val = i2c_master_send(ephdata->commsdevice, val, len);
|
||||
if (ret_val == len)
|
||||
{
|
||||
ret_val = 0;
|
||||
break;
|
||||
}
|
||||
dev_err(&ephdata->commsdevice->dev, "%s: i2c_master_send ret_val %d\n", __func__, ret_val);
|
||||
ret_val = ret_val < 0 ? ret_val : -EIO;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int eph_comms_write(struct eph_data *ephdata, u16 len, u8 *buf)
|
||||
{
|
||||
int ret_val = 0;
|
||||
|
||||
ret_val = __eph_write_reg(ephdata,
|
||||
len,
|
||||
buf);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int eph_comms_read(struct eph_data *ephdata, u16 len, u8 *buf)
|
||||
{
|
||||
int ret_val = 0;
|
||||
|
||||
|
||||
ret_val = __eph_read_reg(ephdata,
|
||||
len,
|
||||
buf);
|
||||
|
||||
if (ret_val < 0)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Error occured(%d)", ret_val);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/* check valid read - (not a null or reserved message) */
|
||||
if (true == eph_is_report_null_report(buf) || (TLV_RESERVED_INVALID_TYPE == buf[0]))
|
||||
{
|
||||
/* Device response was not ready, try again */
|
||||
/* wait a delay for TIC to be ready - Additional delay as device was not previously ready */
|
||||
udelay(200);
|
||||
|
||||
/* do a read */
|
||||
ret_val = __eph_read_reg(ephdata,
|
||||
len,
|
||||
buf);
|
||||
|
||||
|
||||
if (ret_val < 0)
|
||||
{
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
//dev_info(&ephdata->commsdevice->dev,
|
||||
// "ESWIN SPI READ RETRY------>type: %x length_LSB: %x length_MSB: %x data1: %x data2: %x data3: %x data4: %x ",
|
||||
// buf[0], buf[1],buf[2],buf[3],buf[4],buf[5],buf[6]);
|
||||
|
||||
if (true == eph_is_report_null_report(buf) || (TLV_RESERVED_INVALID_TYPE == buf[0]))
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev,
|
||||
"Failed to read message --- No response type: %u ",
|
||||
buf[0]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int eph_comms_specific_checks_i2c(struct comms_device *commsdevice)
|
||||
{
|
||||
(void)commsdevice;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void eph_comms_driver_data_set_i2c(struct comms_device *commsdevice, struct eph_data *ephdata)
|
||||
{
|
||||
|
||||
snprintf(ephdata->phys, sizeof(ephdata->phys), "i2c-%u-%04x/input0", commsdevice->adapter->nr, commsdevice->addr);
|
||||
dev_info(&commsdevice->dev, "%s %s\n", __func__, ephdata->phys);
|
||||
|
||||
i2c_set_clientdata(commsdevice, ephdata);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
struct eph_data* eph_comms_driver_data_get_i2c(struct comms_device *commsdevice)
|
||||
{
|
||||
|
||||
struct eph_data *ephdata = (struct eph_data *)i2c_get_clientdata(commsdevice);
|
||||
|
||||
|
||||
return ephdata;
|
||||
}
|
||||
|
||||
|
||||
struct comms_device* eph_comms_device_get_i2c(struct device *dev)
|
||||
{
|
||||
|
||||
struct comms_device *commsdevice = to_i2c_client(dev);
|
||||
|
||||
|
||||
return commsdevice;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int eph_comms_specific_bootloader_checks_i2c(struct eph_data *ephdata)
|
||||
{
|
||||
#if 0
|
||||
u8 appmode = ephdata->commsdevice->addr;
|
||||
u8 family_id = ephdata->ephinfo ? ephdata->ephinfo->device_family : 0;
|
||||
|
||||
ephdata->bootloader_addr = 0x26u;
|
||||
|
||||
if ((appmode!=0x4D) || (4 == ephdata->ephinfo->device_family))
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Unrecognised device for boot id:%02x\n",
|
||||
ephdata->ephinfo->device_family);
|
||||
}
|
||||
|
||||
dev_info(&ephdata->commsdevice->dev, "Using bootloader addr:%02x\n",
|
||||
ephdata->bootloader_addr);
|
||||
#else
|
||||
(void)ephdata;
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool eph_is_report_null_report(u8 *buff)
|
||||
{
|
||||
|
||||
bool is_null_report = false;
|
||||
|
||||
if (0 == buff[0])
|
||||
{
|
||||
if((0 == buff[1]) && (0 == buff[2]))
|
||||
{
|
||||
is_null_report = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return is_null_report;
|
||||
|
||||
}
|
||||
|
||||
#endif /* ESWIN_EPH861X_I2C */
|
||||
|
||||
|
||||
21
drivers/input/touchscreen/eswin_touch/eswin_eph861x_i2c.h
Normal file
21
drivers/input/touchscreen/eswin_touch/eswin_eph861x_i2c.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef __LINUX_PLATFORM_DATA_ESWIN_EPH_I2C_H
|
||||
#define __LINUX_PLATFORM_DATA_ESWIN_EPH_I2C_H
|
||||
|
||||
#include <eswin_eph861x_types.h>
|
||||
#define I2C_APP_BUF_SIZE_WRITE (8192)
|
||||
#define I2C_APP_BUF_SIZE_READ (3072)
|
||||
|
||||
extern int __eph_read_reg(struct eph_data *ephdata, u16 len, u8 *val);
|
||||
extern int __eph_write_reg(struct eph_data *ephdata, u16 len, u8 *val);
|
||||
extern int eph_comms_write(struct eph_data *ephdata, u16 len, u8 *buf);
|
||||
extern int eph_comms_read(struct eph_data *ephdata, u16 len, u8 *buf);
|
||||
|
||||
extern int eph_comms_specific_checks_i2c(struct comms_device *commsdevice);
|
||||
extern void eph_comms_driver_data_set_i2c(struct comms_device *commsdevice, struct eph_data *ephdata);
|
||||
extern struct eph_data* eph_comms_driver_data_get_i2c(struct comms_device *commsdevice);
|
||||
extern struct comms_device* eph_comms_device_get_i2c(struct device *dev);
|
||||
extern int eph_comms_specific_bootloader_checks_i2c(struct eph_data *ephdata);
|
||||
|
||||
extern bool eph_is_report_null_report(u8 *buff);
|
||||
|
||||
#endif /* __LINUX_PLATFORM_DATA_ESWIN_EPH_I2C_ */
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
|
||||
|
||||
#define CONFIG_SUPPORTED_TOUCHES (10u)
|
||||
#define CONFIG_COMMS_SLAVE_INPUT_BUFFER_SIZE (1024u)
|
||||
#define CONFIG_COMMS_SLAVE_OUTPUT_BUFFER_SIZE (1024u)
|
||||
#define CONFIG_ESWIN_TOUCH_PROTOCOL_VERSION_3 (3u)
|
||||
#define CONFIG_ESWIN_TOUCH_PROTOCOL_VERSION_2 (2u)
|
||||
#define CONFIG_ESWIN_TOUCH_PROTOCOL_VERSION (CONFIG_ESWIN_TOUCH_PROTOCOL_VERSION_3)
|
||||
293
drivers/input/touchscreen/eswin_touch/eswin_eph861x_spi.c
Normal file
293
drivers/input/touchscreen/eswin_touch/eswin_eph861x_spi.c
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
/*
|
||||
* ESWIN EPH861X series Touchscreen driver
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
// uncomment to enable the dev_dbg prints to dmesg
|
||||
#define DEBUG
|
||||
// uncomment to test with input forced open
|
||||
//#define INPUT_DEVICE_ALWAYS_OPEN
|
||||
#include <linux/types.h>
|
||||
|
||||
|
||||
|
||||
#include <uapi/asm-generic/errno-base.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "eswin_eph861x_tlv.h"
|
||||
#include "eswin_eph861x_types.h"
|
||||
#if (ESWIN_EPH861X_SPI)
|
||||
#include "eswin_eph861x_spi.h"
|
||||
|
||||
#define TLV_RESERVED_INVALID_TYPE 0xFF
|
||||
|
||||
|
||||
|
||||
u8 spi_tx_dummy_buf[SPI_APP_BUF_SIZE_READ];
|
||||
|
||||
|
||||
|
||||
|
||||
int __eph_spi_read(struct eph_data *ephdata, u16 len, u8 *val)
|
||||
{
|
||||
int ret_val;
|
||||
struct spi_message spimsg;
|
||||
struct spi_transfer spitr;
|
||||
|
||||
//dev_info(&ephdata->commsdevice->dev, "%s\n", __func__);
|
||||
/* Read header - T(ype) and L(ength) */
|
||||
spi_message_init(&spimsg);
|
||||
memset(&spitr, 0, sizeof(struct spi_transfer));
|
||||
|
||||
|
||||
if ((SPI_APP_BUF_SIZE_READ) < len)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Error reading from spi: length greater than buffer size(%d)", len);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Ensure dummy tx configured */
|
||||
memcpy(ephdata->comms_send_buf, &spi_tx_dummy_buf[0], len);
|
||||
|
||||
|
||||
/* Output 0xFFs when doing a read */
|
||||
spitr.tx_buf = ephdata->comms_send_buf;
|
||||
spitr.rx_buf = ephdata->comms_receive_buf;
|
||||
spitr.len = len;
|
||||
#if 0
|
||||
spitr.tx_dma = ephdata->comms_dma_handle_tx;
|
||||
spitr.rx_dma = ephdata->comms_dma_handle_rx;
|
||||
#if KERNEL_VERSION(6, 9, 0) >= LINUX_VERSION_CODE
|
||||
spimsg.is_dma_mapped = 1;
|
||||
#endif
|
||||
#endif
|
||||
spitr.cs_change = 0;
|
||||
|
||||
//TODO Kernel version 5.5 introduces support for CS to CLK delay which will be useful when implementing low power sleep modes
|
||||
spi_message_add_tail(&spitr, &spimsg);
|
||||
ret_val = spi_sync(ephdata->commsdevice, &spimsg);
|
||||
|
||||
if (ret_val < 0)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Error reading from spi (%d)", ret_val);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
/* include T and L */
|
||||
memcpy(val, ephdata->comms_receive_buf, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int __eph_spi_write(struct eph_data *ephdata, u16 len, const u8 *val)
|
||||
{
|
||||
int ret_val;
|
||||
struct spi_message spimsg;
|
||||
struct spi_transfer spitr;
|
||||
|
||||
//dev_info(&ephdata->commsdevice->dev, "%s\n", __func__);
|
||||
|
||||
if ((SPI_APP_BUF_SIZE_WRITE) < len)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Error writing to spi: length greater than buffer size(%d)", len);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* SPI_WRITE */
|
||||
memcpy(ephdata->comms_send_buf, val, len);
|
||||
spi_message_init(&spimsg);
|
||||
memset(&spitr, 0, sizeof(struct spi_transfer));
|
||||
spitr.tx_buf = ephdata->comms_send_buf;
|
||||
spitr.rx_buf = ephdata->comms_receive_buf;
|
||||
spitr.len = len;
|
||||
#if (ESWIN_EPH861X_SPI_USE_DMA)
|
||||
spitr.tx_dma = ephdata->comms_dma_handle_tx;
|
||||
spitr.rx_dma = ephdata->comms_dma_handle_rx;
|
||||
#if KERNEL_VERSION(6, 9, 0) >= LINUX_VERSION_CODE
|
||||
spimsg.is_dma_mapped = 1;
|
||||
#endif
|
||||
#endif
|
||||
spitr.cs_change = 0;
|
||||
spi_message_add_tail(&spitr, &spimsg);
|
||||
ret_val = spi_sync(ephdata->commsdevice, &spimsg);
|
||||
if (ret_val < 0)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Error writing to spi (%d)", ret_val);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int eph_comms_read(struct eph_data *ephdata, u16 len, u8 *buf)
|
||||
{
|
||||
int ret_val = 0;
|
||||
|
||||
if ((SPI_APP_BUF_SIZE_READ) < len)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Error reading from spi: length greater than buffer size(%d)", len);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret_val = __eph_spi_read(ephdata,
|
||||
len,
|
||||
buf);
|
||||
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Error occured(%d)", ret_val);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/* check valid read - (not a null or reserved message) */
|
||||
if (true == eph_is_report_null_report(buf) || (TLV_RESERVED_INVALID_TYPE == buf[0]))
|
||||
{
|
||||
/* Device response was not ready, try again */
|
||||
/* wait a delay for TIC to be ready - Additional delay as device was not previously ready */
|
||||
udelay(200);
|
||||
|
||||
/* do a read */
|
||||
ret_val = __eph_spi_read(ephdata,
|
||||
len,
|
||||
buf);
|
||||
|
||||
if (ret_val)
|
||||
{
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
dev_info(&ephdata->commsdevice->dev,
|
||||
"ESWIN SPI READ RETRY------>type: %u length_LSB: %u length_MSB: %u data1: %u data2: %u data3: %u data4: %u ",
|
||||
buf[0], buf[1],buf[2],buf[3],buf[4],buf[5],buf[6]);
|
||||
|
||||
if (true == eph_is_report_null_report(buf) || (TLV_RESERVED_INVALID_TYPE == buf[0]))
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev,
|
||||
"Failed to read message --- No response type: %u ",
|
||||
buf[0]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
int eph_comms_write(struct eph_data *ephdata, u16 len, u8 *buf)
|
||||
{
|
||||
int ret_val = 0;
|
||||
|
||||
ret_val = __eph_spi_write(ephdata,
|
||||
len,
|
||||
buf);
|
||||
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
int eph_comms_specific_checks_spi(struct comms_device *commsdevice)
|
||||
{
|
||||
#if 0
|
||||
if ( (commsdevice->bits_per_word && commsdevice->bits_per_word != 8) ||
|
||||
!(commsdevice->mode & SPI_CPHA) ||
|
||||
!(commsdevice->mode & SPI_CPOL) )
|
||||
{
|
||||
dev_err(&commsdevice->dev, "unexpected spi device setup: SPI mode(%d), bits_per_word(%d) \n", commsdevice->mode, commsdevice->bits_per_word);
|
||||
return -EINVAL;
|
||||
}
|
||||
#else
|
||||
printk("eswin eph skip check spi\n");
|
||||
#endif
|
||||
|
||||
/* initialise SPI dummy buffers */
|
||||
memset(&spi_tx_dummy_buf[0], 0xFF, SPI_APP_BUF_SIZE_READ);
|
||||
|
||||
return spi_setup(commsdevice);
|
||||
}
|
||||
|
||||
void eph_comms_driver_data_set_spi(struct comms_device *commsdevice, struct eph_data *ephdata)
|
||||
{
|
||||
#if KERNEL_VERSION(6, 8, 0) >= LINUX_VERSION_CODE
|
||||
snprintf(ephdata->phys, sizeof(ephdata->phys), "spi-%d/input0", commsdevice->master->bus_num);
|
||||
#endif
|
||||
dev_info(&commsdevice->dev, "%s %s\n", __func__, ephdata->phys);
|
||||
pr_err("eph_comms_driver_data_set_spi------\n");
|
||||
spi_set_drvdata(commsdevice, ephdata);
|
||||
if (0xff != spi_tx_dummy_buf[0])
|
||||
{
|
||||
memset(spi_tx_dummy_buf, 0xff, SPI_APP_BUF_SIZE_READ);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
struct eph_data* eph_comms_driver_data_get_spi(struct comms_device *commsdevice)
|
||||
{
|
||||
|
||||
struct eph_data *ephdata = (struct eph_data *)spi_get_drvdata(commsdevice);
|
||||
|
||||
return ephdata;
|
||||
}
|
||||
|
||||
|
||||
struct comms_device* eph_comms_device_get_spi(struct device *dev)
|
||||
{
|
||||
|
||||
struct comms_device *commsdevice = to_spi_device(dev);
|
||||
|
||||
|
||||
return commsdevice;
|
||||
}
|
||||
|
||||
int eph_comms_specific_bootloader_checks_spi(struct eph_data *ephdata)
|
||||
{
|
||||
(void)ephdata;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool eph_is_report_null_report(u8 *buff)
|
||||
{
|
||||
|
||||
bool is_null_report = false;
|
||||
|
||||
if (0 == buff[0])
|
||||
{
|
||||
if((0 == buff[1]) && (0 == buff[2]))
|
||||
{
|
||||
is_null_report = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return is_null_report;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif /* ESWIN_EPH861X_SPI */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
32
drivers/input/touchscreen/eswin_touch/eswin_eph861x_spi.h
Normal file
32
drivers/input/touchscreen/eswin_touch/eswin_eph861x_spi.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
|
||||
#ifndef __LINUX_PLATFORM_DATA_ESWIN_EPH_SPI_H
|
||||
#define __LINUX_PLATFORM_DATA_ESWIN_EPH_SPI_H
|
||||
|
||||
#include "eswin_eph861x_types.h"
|
||||
#include "eswin_eph861x_tlv.h"
|
||||
|
||||
|
||||
/* Set tx for bootloader frame size plus margin. Set Rx to largest
|
||||
* engineering output plus margin */
|
||||
|
||||
#define SPI_BOOTL_HEADER_LEN 2
|
||||
#define SPI_APP_BUF_SIZE_WRITE (8192)
|
||||
#define SPI_APP_BUF_SIZE_READ (3072)
|
||||
|
||||
extern int __eph_spi_read(struct eph_data *ephdata, u16 len, u8 *val);
|
||||
extern int eph_comms_read(struct eph_data *ephdata, u16 len, u8 *buf);
|
||||
extern int __eph_spi_write(struct eph_data *ephdata, u16 len, const u8 *val);
|
||||
extern int eph_comms_write(struct eph_data *ephdata, u16 len, u8 *buf);
|
||||
|
||||
extern int eph_comms_specific_checks_spi(struct comms_device *commsdevice);
|
||||
extern void eph_comms_driver_data_set_spi(struct comms_device *commsdevice, struct eph_data *ephdata);
|
||||
extern struct eph_data* eph_comms_driver_data_get_spi(struct comms_device *commsdevice);
|
||||
extern struct comms_device* eph_comms_device_get_spi(struct device *dev);
|
||||
extern int eph_comms_specific_bootloader_checks_spi(struct eph_data *ephdata);
|
||||
|
||||
extern bool eph_is_report_null_report(u8 *buff);
|
||||
|
||||
|
||||
|
||||
#endif /* __LINUX_PLATFORM_DATA_ESWIN_EPH_SPI_ */
|
||||
132
drivers/input/touchscreen/eswin_touch/eswin_eph861x_tlv.h
Normal file
132
drivers/input/touchscreen/eswin_touch/eswin_eph861x_tlv.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#include "eswin_eph861x_project_config.h"
|
||||
|
||||
#define TLV_HEADER_SIZE (uint16_t)(sizeof(uint8_t) + sizeof(uint16_t))
|
||||
|
||||
/* Generic TLV types */
|
||||
#define TLV_CONTAINER (0x10u)
|
||||
#define TLV_GENERIC_READ (0x00u)
|
||||
#define TLV_INVALID_COMMAND (0xFEu)
|
||||
|
||||
/* Command input TLV types */
|
||||
#define TLV_NULL (0x00u)
|
||||
#define TLV_DEVICE_INFO_READ (0x01u)
|
||||
#define TLV_EXTENDED_INFO_READ (0x02u)
|
||||
#define TLV_CONTROL_DATA_WRITE (0x08u)
|
||||
#define TLV_CONTROL_DATA_READ (0x09u)
|
||||
#define TLV_CONFIG_DATA_WRITE (0x0Au)
|
||||
#define TLV_CONFIG_DATA_READ (0x0Bu)
|
||||
#define TLV_BOOTLOADER_INFO_READ (0x51u)
|
||||
#define TLV_ENG_DEBUG_DATA_WRITE (0x80u)
|
||||
#define TLV_ALG_IN_LOOP_ENABLE (0x82u)
|
||||
|
||||
|
||||
/* Data output TLV types */
|
||||
#define TLV_DEVICE_INFO_DATA (0x01u)
|
||||
#define TLV_EXTENDED_INFO_DATA (0x02u)
|
||||
#define TLV_CONTROL_DATA (0x09u)
|
||||
#define TLV_CONFIG_DATA (0x0Bu)
|
||||
#define TLV_PACKETISED_DATA (0x21u)
|
||||
#define TLV_BOOTLOADER_INFO_DATA (0x51u)
|
||||
#define TLV_ENG_DEBUG_DATA (0x81u)
|
||||
|
||||
/* Report output TLV types */
|
||||
#define TLV_DEVICE_STATUS_DATA (0x22u)
|
||||
#define TLV_REPORT_DATA (0x23u)
|
||||
#define TLV_AQFE_REPORT_DATA (0x24u)
|
||||
#define TLV_SCREEN_OFF_REPORT_DATA (0x25u)
|
||||
#define TLV_BOOTLOADER_STATUS_DATA (0x50u)
|
||||
|
||||
/* Generic field offsets */
|
||||
#define TLV_TYPE_FIELD (0u)
|
||||
#define TLV_LENGTH_FIELD (1u)
|
||||
#define TLV_PAYLOAD_FIELD TLV_HEADER_SIZE
|
||||
|
||||
|
||||
/* Device status Fields and Masks */
|
||||
#define TLV_DEVICE_STATUS_FLAGS_FIELD (0u)
|
||||
#define TLV_DEVICE_STATUS_RESET_MASK (0x01u)
|
||||
#define TLV_DEVICE_STATUS_CONTROL_MASK (0x02u)
|
||||
#define TLV_DEVICE_STATUS_CONFIG_MASK (0x04u)
|
||||
#define TLV_DEVICE_STATUS_COMPONENT_FIELD (1u)
|
||||
|
||||
/* Read field offsets (within payload) */
|
||||
#define TLV_READ_COMPONENT_FIELD (0u)
|
||||
#define TLV_READ_OFFSET_FIELD (2u)
|
||||
#define TLV_READ_LENGTH_FIELD (4u)
|
||||
#if (CONFIG_ESWIN_TOUCH_PROTOCOL_VERSION >= CONFIG_ESWIN_TOUCH_PROTOCOL_VERSION_3)
|
||||
#define TLV_READ_HEADER_SIZE (7u)
|
||||
#else
|
||||
#define TLV_READ_HEADER_SIZE (6u)
|
||||
#endif
|
||||
|
||||
/* Write field offsets (within payload) */
|
||||
#define TLV_WRITE_COMPONENT_FIELD (0u)
|
||||
#define TLV_WRITE_OFFSET_FIELD (2u)
|
||||
#define TLV_WRITE_HEADER_SIZE (4u)
|
||||
#define TLV_WRITE_RESPONSE_SIZE (2u)
|
||||
|
||||
|
||||
/* Engineering debug field offsets (within payload) */
|
||||
#define TLV_ENG_DEBUG_COMPONENT_FIELD (0u)
|
||||
#define TLV_ENG_DEBUG_ID_FIELD (2u)
|
||||
#define TLV_ENG_DEBUG_CRC_FIELD (3u)
|
||||
#define TLV_ENG_DEBUG_HEADER_SIZE (4u)
|
||||
|
||||
#define TLV_ENG_DEBUG_ID_MASK (0x1Fu)
|
||||
#define TLV_ENG_DEBUG_PRE_COMPONENT_MASK (0x80u)
|
||||
|
||||
/* Engineering Algorithm-in-the-loop enable command size */
|
||||
#define TLV_ALG_IN_LOOP_ENABLE_SIZE (1u)
|
||||
|
||||
/* Packetising field offsets (within payload) */
|
||||
#define TLV_PACKETISING_SEQUENCE_FIELD (0u)
|
||||
#define TLV_PACKETISING_DATA_OFFSET_FIELD (1u)
|
||||
#define TLV_PACKETISING_SEQUENCE_ID_MASK (0x1Fu)
|
||||
#define TLV_PACKETISING_MORE_DATA_MASK (0x80u)
|
||||
#define TLV_PACKETISING_HEADER_SIZE (3u)
|
||||
|
||||
/**************** Event type report defines *************************/
|
||||
#define RESERVED_TYPE (0u)
|
||||
#define CONTACT_TYPE (1u)
|
||||
#define RELEASE_TYPE (2u)
|
||||
#define HOVER_TYPE (3u)
|
||||
#define STYLUS_POSITION_TYPE (4u)
|
||||
#define GESTURE_TYPE (5u)
|
||||
#define STYLUS_STANDARD_DATA_TYPE (6u)
|
||||
#define STYLUS_PERIODIC_DATA_TYPE (7u)
|
||||
#define STYLUS_CAPABILITY_DATA_TYPE (8u)
|
||||
#define STYLUS_RELEASE_TYPE (9u)
|
||||
|
||||
#define EVENT_REPORT_TYPE_OFFSET (4u)
|
||||
#define EVENT_REPORT_TYPE_MASK (0xF0)
|
||||
#define EVENT_REPORT_LENGTH_MASK (0x0F)
|
||||
|
||||
#define MINI_TLV_SIZE (sizeof(uint8_t))
|
||||
|
||||
/* ID X location Y location Minor axis Major Axis */
|
||||
#define TOUCH_PAYLOAD_SIZE (sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint8_t) + sizeof(uint8_t))
|
||||
#define REPORT_SIZE (MINI_TLV_SIZE + TOUCH_PAYLOAD_SIZE)
|
||||
|
||||
/* Type X location Y location Size */
|
||||
#define GESTURE_PAYLOAD_SIZE (sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t))
|
||||
#define GESTURE_SIZE (MINI_TLV_SIZE + GESTURE_PAYLOAD_SIZE)
|
||||
|
||||
#define REPORTING_BUFFER_SIZE (TLV_HEADER_SIZE + (CONFIG_SUPPORTED_TOUCHES * REPORT_SIZE))
|
||||
|
||||
/* gesture type */
|
||||
#define GESTURE_UNUSED 0
|
||||
#define GESTURE_TAP 1
|
||||
#define GESTURE_DOUBLE_TAP 2
|
||||
#define GESTURE_SWIPE_LEFT 3
|
||||
#define GESTURE_SWIPE_RIGHT 4
|
||||
#define GESTURE_SWIPE_UP 5
|
||||
#define GESTURE_SWIPE_DOWN 6
|
||||
#define GESTURE_PINCH 7
|
||||
#define GESTURE_STRETCH 8
|
||||
|
||||
/******************** Finger Region Event Type *********************/
|
||||
#define FINGER_REGION_RESERVED (0u)
|
||||
#define FINGER_REGION_FINGER_DOWN (1u)
|
||||
#define FINGER_REGION_FINGER_UP (2u)
|
||||
#define FINGER_REGION_HOVER_DOWN (3u)
|
||||
#define FINGER_REGION_HOVER_UP (4u)
|
||||
|
|
@ -0,0 +1,524 @@
|
|||
/*
|
||||
* ESWIN EPH861X series Touchscreen driver
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
// uncomment to enable the dev_dbg prints to dmesg
|
||||
#define DEBUG
|
||||
// uncomment to test with input forced open
|
||||
//#define INPUT_DEVICE_ALWAYS_OPEN
|
||||
#include <linux/types.h>
|
||||
|
||||
|
||||
|
||||
#include <uapi/asm-generic/errno-base.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <uapi/linux/input-event-codes.h>
|
||||
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "eswin_eph861x_tlv.h"
|
||||
#include "eswin_eph861x_types.h"
|
||||
#include "eswin_eph861x_project_config.h"
|
||||
#include "eswin_eph861x_comms.h"
|
||||
#include "eswin_eph861x_tlv_report.h"
|
||||
#include "eswin_eph861x_tlv_command.h"
|
||||
|
||||
|
||||
|
||||
|
||||
int eph_read_device_information(struct eph_data *ephdata)
|
||||
{
|
||||
int ret_val = 0;
|
||||
u8 retry = 0;
|
||||
u8 type_match = false;
|
||||
const struct tlv_header tlvheader = {1, 0};
|
||||
|
||||
|
||||
ret_val = eph_comms_write(ephdata,
|
||||
TLV_HEADER_SIZE,
|
||||
(u8*)&tlvheader);
|
||||
|
||||
dev_info(&ephdata->commsdevice->dev,
|
||||
"ESWIN - Device information request::eph_comms_write type: %d, length: %d, ret_val %d ",
|
||||
tlvheader.type, tlvheader.length, ret_val);
|
||||
if (ret_val)
|
||||
{
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/* wait a delay for TIC to be ready */
|
||||
udelay(100);
|
||||
|
||||
while (false == type_match)
|
||||
{
|
||||
memset(ephdata->comms_receive_buf, 0, COMMS_BUF_SIZE);
|
||||
memset(&ephdata->ephdeviceinfo, 0 , sizeof(struct eph_device_info));
|
||||
|
||||
ret_val = eph_comms_two_stage_read(ephdata, ephdata->comms_receive_buf);
|
||||
|
||||
if (!ret_val)
|
||||
{
|
||||
u8 calc_crc;
|
||||
|
||||
type_match = (TLV_DEVICE_INFO_DATA == ephdata->comms_receive_buf[0]);
|
||||
|
||||
if(true == type_match)
|
||||
{
|
||||
/* Only copy the amount for the expected message type and its storage */
|
||||
/* Copy across regardless of CRC failure - rely on retry mechanism */
|
||||
memcpy((u8*)&ephdata->ephdeviceinfo, ephdata->comms_receive_buf, sizeof(struct eph_device_info));
|
||||
}
|
||||
|
||||
/* CRC of payload */
|
||||
calc_crc = eph_get_data_crc((u8*)&ephdata->ephdeviceinfo.product_id, (sizeof(struct eph_device_info) - (TLV_HEADER_SIZE + sizeof(u8))));
|
||||
if (ephdata->ephdeviceinfo.crc != calc_crc)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "CRC mismatch - expected: %d, got: %d", calc_crc, ephdata->ephdeviceinfo.crc);
|
||||
type_match= false;
|
||||
}
|
||||
|
||||
if (false == type_match) {
|
||||
ret_val = eph_handle_report(ephdata, ephdata->comms_receive_buf);
|
||||
dev_info(&ephdata->commsdevice->dev, "Process unexpected response %d\n", ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
if(ret_val || ((retry > COMMS_READ_RETRY_NUM) && (false == type_match)))
|
||||
{
|
||||
/* have already retried and failed to get the correct response - break out the loop */
|
||||
dev_err(&ephdata->commsdevice->dev, "Failed to read expected response on second retry");
|
||||
return -EAGAIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* increment retry each attempt */
|
||||
retry++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
int eph_write_control_config(struct eph_data *ephdata, u16 len, u8 *buf)
|
||||
{
|
||||
int ret_val = 0;
|
||||
struct eph_device_control_config_write_response response;
|
||||
bool type_match = false;
|
||||
u8 retry = 0;
|
||||
/* calculate this before CRC adjustment */
|
||||
u16 payload_length = (len - (TLV_HEADER_SIZE + TLV_WRITE_HEADER_SIZE));
|
||||
|
||||
/* wait a delay for TIC to be ready */
|
||||
udelay(50);
|
||||
|
||||
memcpy(ephdata->comms_send_crc_buf, buf, len);
|
||||
/* Dynamically handle the protocol version of the TIC FW */
|
||||
if(ephdata->ephdeviceinfo.protocol_version > CONFIG_ESWIN_TOUCH_PROTOCOL_VERSION_2)
|
||||
{
|
||||
/* CRC last byte for protocol version 3 */
|
||||
u8 calc_crc;
|
||||
struct tlv_header* header;
|
||||
header = (struct tlv_header*)ephdata->comms_send_crc_buf;
|
||||
/* Protocol version supports CRC in cmd increase payload length and insert CRC */
|
||||
header->length = header->length + cpu_to_le16(1);
|
||||
calc_crc = eph_get_data_crc((u8*)ephdata->comms_send_crc_buf, len);
|
||||
memcpy(&ephdata->comms_send_crc_buf[len], &calc_crc, sizeof(u8));
|
||||
/* increase transaction length to include crc */
|
||||
len++;
|
||||
}
|
||||
|
||||
|
||||
ret_val = eph_comms_write(ephdata, len, ephdata->comms_send_crc_buf);
|
||||
|
||||
dev_info(&ephdata->commsdevice->dev,
|
||||
"ESWIN - Write configuration/control command. type: %d, length: %d",
|
||||
ephdata->comms_send_crc_buf[0],
|
||||
(ephdata->comms_send_crc_buf[1] | ephdata->comms_send_crc_buf[2] << 8u));
|
||||
|
||||
/* wait a delay for TIC to be ready - Extended for large 1k components */
|
||||
udelay(200);
|
||||
|
||||
if (ret_val)
|
||||
{
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
while (false == type_match)
|
||||
{
|
||||
memset(ephdata->comms_receive_buf, 0, COMMS_BUF_SIZE);
|
||||
|
||||
/* Do a 2 stage read, even though we know the format to allow consistant error handling when incorrect response received */
|
||||
/* This way if its an incorrect message format the complete message can be read out and the response can be discarded */
|
||||
ret_val = eph_comms_two_stage_read(ephdata, ephdata->comms_receive_buf);
|
||||
|
||||
if (!ret_val)
|
||||
{
|
||||
/* Only copy the amount for the expected message type and its storage */
|
||||
memcpy((u8*)&response, ephdata->comms_receive_buf, sizeof(struct eph_device_control_config_write_response));
|
||||
|
||||
/* check bytes written matches what was sent */
|
||||
if (le16_to_cpu(response.bytes_written) != payload_length)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Error writing to control config bytes written did not match what was sent");
|
||||
dev_err(&ephdata->commsdevice->dev,
|
||||
"Sent the following number of bytes (%d) but (%d) bytes where written",
|
||||
payload_length, le16_to_cpu(response.bytes_written));
|
||||
}
|
||||
|
||||
type_match = (TLV_CONFIG_DATA_WRITE == response.header.type) || (TLV_CONTROL_DATA_WRITE == response.header.type);
|
||||
|
||||
if (type_match == false) {
|
||||
ret_val = eph_handle_report(ephdata, ephdata->comms_receive_buf);
|
||||
dev_info(&ephdata->commsdevice->dev, "Process unexpected response %d\n", ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(ret_val || ((retry > COMMS_READ_RETRY_NUM) && (false == type_match)))
|
||||
{
|
||||
/* have already retried and failed to get the correct response - break out the loop */
|
||||
dev_err(&ephdata->commsdevice->dev, "Failed to read expected response on second retry");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* increment retry each attempt */
|
||||
retry++;
|
||||
}
|
||||
}
|
||||
|
||||
/* retry */
|
||||
if ((ret_val) || (false == type_match))
|
||||
{
|
||||
/* wait a delay for TIC to be ready */
|
||||
mdelay(10);
|
||||
|
||||
dev_err(&ephdata->commsdevice->dev, "Control or contfiguration write failure. Type Match: (%d)", type_match);
|
||||
/* error or incorrect type received */
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* wait a delay for TIC to be ready, TIC may have prepared touch report so
|
||||
* give it time to prepare before the interrupt handler responds */
|
||||
udelay(200);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
int eph_write_control_config_no_response(struct eph_data *ephdata, u16 len, u8 *buf)
|
||||
{
|
||||
int ret_val = 0;
|
||||
|
||||
/* wait a delay for TIC to be ready */
|
||||
udelay(50);
|
||||
|
||||
memcpy(ephdata->comms_send_crc_buf, buf, len);
|
||||
/* Dynamically handle the protocol version of the TIC FW */
|
||||
if(ephdata->ephdeviceinfo.protocol_version > CONFIG_ESWIN_TOUCH_PROTOCOL_VERSION_2)
|
||||
{
|
||||
/* CRC last byte for protocol version 3 */
|
||||
u8 calc_crc;
|
||||
struct tlv_header* header;
|
||||
header = (struct tlv_header*)ephdata->comms_send_crc_buf;
|
||||
/* Protocol version supports CRC in cmd increase payload length and insert CRC */
|
||||
header->length = header->length + cpu_to_le16(1);
|
||||
calc_crc = eph_get_data_crc((u8*)ephdata->comms_send_crc_buf, len);
|
||||
memcpy(&ephdata->comms_send_crc_buf[len], &calc_crc, sizeof(u8));
|
||||
/* increase transaction length to include crc */
|
||||
len++;
|
||||
}
|
||||
|
||||
ret_val = eph_comms_write(ephdata, len, buf);
|
||||
|
||||
dev_info(&ephdata->commsdevice->dev,
|
||||
"ESWIN - Write configuration/control command. type: %d, length: %d",
|
||||
ephdata->comms_send_crc_buf[0],
|
||||
(ephdata->comms_send_crc_buf[1] | ephdata->comms_send_crc_buf[2] << 8u));
|
||||
|
||||
/* wait a delay for TIC to be ready */
|
||||
udelay(100);
|
||||
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Control or contfiguration write failure.");
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int eph_read_control_config(struct eph_data *ephdata, struct eph_device_control_config_read_command *command_request, u8 *buf)
|
||||
{
|
||||
int ret_val = 0;
|
||||
bool type_match = false;
|
||||
u8 retry = 0;
|
||||
struct tlv_header tlvheader;
|
||||
u16 len = sizeof(struct eph_device_control_config_read_command);
|
||||
|
||||
|
||||
memcpy(ephdata->comms_send_crc_buf, command_request, len);
|
||||
/* Dynamically handle the protocol version of the TIC FW */
|
||||
if(ephdata->ephdeviceinfo.protocol_version > CONFIG_ESWIN_TOUCH_PROTOCOL_VERSION_2)
|
||||
{
|
||||
/* CRC last byte for protocol version 3 */
|
||||
u8 calc_crc;
|
||||
struct tlv_header* header;
|
||||
header = (struct tlv_header*)ephdata->comms_send_crc_buf;
|
||||
/* Protocol version supports CRC in cmd increase payload length and insert CRC */
|
||||
header->length = header->length + cpu_to_le16(1);
|
||||
calc_crc = eph_get_data_crc((u8*)ephdata->comms_send_crc_buf, (sizeof(struct eph_device_control_config_read_command)));
|
||||
memcpy(&ephdata->comms_send_crc_buf[len], &calc_crc, sizeof(u8));
|
||||
/* increase transaction length to include crc */
|
||||
len++;
|
||||
}
|
||||
|
||||
|
||||
/* do a write */
|
||||
ret_val = eph_comms_write(ephdata, len, (u8*)ephdata->comms_send_crc_buf);
|
||||
|
||||
|
||||
dev_info(&ephdata->commsdevice->dev,
|
||||
"ESWIN - Read configuration/control command. type: %d, length: %d",
|
||||
command_request->header.type, command_request->header.length);
|
||||
|
||||
/* wait a delay for TIC to be ready - Extended for large 1k components */
|
||||
udelay(200);
|
||||
|
||||
if (ret_val)
|
||||
{
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
while (false == type_match)
|
||||
{
|
||||
memset(ephdata->comms_receive_buf, 0, COMMS_BUF_SIZE);
|
||||
|
||||
/* Do a 2 stage read, even though we know the format to allow consistant error handling when incorrect response received */
|
||||
/* This way if its an incorrect message format the complete message can be read out and the response can be discarded */
|
||||
ret_val = eph_comms_two_stage_read(ephdata, ephdata->comms_receive_buf);
|
||||
|
||||
|
||||
if (!ret_val)
|
||||
{
|
||||
/* Only copy the amount for the expected message type and its storage */
|
||||
memcpy((u8*)&tlvheader, ephdata->comms_receive_buf, sizeof(struct tlv_header));
|
||||
|
||||
type_match = (TLV_CONFIG_DATA_READ == tlvheader.type) || (TLV_CONTROL_DATA_READ == tlvheader.type);
|
||||
|
||||
if (type_match == false) {
|
||||
ret_val = eph_handle_report(ephdata, ephdata->comms_receive_buf);
|
||||
dev_info(&ephdata->commsdevice->dev, "Process unexpected response %d\n", ret_val);
|
||||
} else {
|
||||
/* no functionality required yet so just print some response bytes */
|
||||
dev_info(&ephdata->commsdevice->dev, "type: %u length: %u ", tlvheader.type, tlvheader.length);
|
||||
|
||||
/* print 4 bytes of payload */
|
||||
dev_info(&ephdata->commsdevice->dev,
|
||||
"payload byte 1: %d payload byte 2: %d payload byte 3: %d payload byte 4: %d",
|
||||
ephdata->comms_receive_buf[4], ephdata->comms_receive_buf[5], ephdata->comms_receive_buf[6], ephdata->comms_receive_buf[7]);
|
||||
|
||||
/* Copy read value into dedicated buffer if needed */
|
||||
//TODO this function not currently needed but has been left is as will likely be needed.
|
||||
}
|
||||
}
|
||||
|
||||
if(ret_val || ((retry > COMMS_READ_RETRY_NUM) && (false == type_match)))
|
||||
{
|
||||
/* have already retried and failed to get the correct response - break out the loop */
|
||||
dev_err(&ephdata->commsdevice->dev, "Failed to read expected response on second retry");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* increment retry each attempt */
|
||||
retry++;
|
||||
}
|
||||
}
|
||||
|
||||
/* retry */
|
||||
if ((ret_val) || (false == type_match))
|
||||
{
|
||||
/* wait a delay for TIC to be ready */
|
||||
mdelay(10);
|
||||
|
||||
dev_err(&ephdata->commsdevice->dev, "Control or contfiguration write failure. Type Match: (%d)", type_match);
|
||||
/* error or incorrect type received */
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
int eph_read_bootloader_information(struct eph_data *ephdata)
|
||||
{
|
||||
int ret_val = 0;
|
||||
u8 retry = 0;
|
||||
u8 type_match = false;
|
||||
const struct tlv_header tlvheader = {1, 0};
|
||||
|
||||
|
||||
ret_val = eph_comms_write(ephdata,
|
||||
TLV_HEADER_SIZE,
|
||||
(u8*)&tlvheader);
|
||||
|
||||
dev_info(&ephdata->commsdevice->dev,
|
||||
"ESWIN - Boot information request type: %d, length: %d",
|
||||
tlvheader.type, tlvheader.length);
|
||||
|
||||
if (ret_val)
|
||||
{
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/* wait a delay for TIC to be ready */
|
||||
udelay(100);
|
||||
|
||||
while (false == type_match)
|
||||
{
|
||||
|
||||
memset(ephdata->comms_receive_buf, 0, COMMS_BUF_SIZE);
|
||||
memset(&ephdata->ephdeviceinfo, 0 , sizeof(struct eph_device_info));
|
||||
|
||||
ret_val = eph_comms_two_stage_read(ephdata, ephdata->comms_receive_buf);
|
||||
|
||||
if (!ret_val)
|
||||
{
|
||||
u8 calc_crc;
|
||||
type_match = (TLV_DEVICE_INFO_DATA == ephdata->comms_receive_buf[0]);
|
||||
|
||||
if(type_match)
|
||||
{
|
||||
/* Only copy the amount for the expected message type and its storage */
|
||||
/* Copy across regardless of CRC failure - rely on retry mechanism */
|
||||
memcpy((u8*)&ephdata->ephdeviceinfo, ephdata->comms_receive_buf, sizeof(struct eph_device_info));
|
||||
}
|
||||
|
||||
/* CRC of payload */
|
||||
calc_crc = eph_get_data_crc((u8*)&ephdata->ephdeviceinfo.product_id, (sizeof(struct eph_device_info) - (TLV_HEADER_SIZE + sizeof(u8))));
|
||||
if (ephdata->ephdeviceinfo.crc != calc_crc)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "CRC mismatch - expected: %d, got: %d", calc_crc, ephdata->ephdeviceinfo.crc);
|
||||
type_match= false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(ret_val || ((retry > COMMS_READ_RETRY_NUM) && (false == type_match)))
|
||||
{
|
||||
/* have already retried and failed to get the correct response - break out the loop */
|
||||
dev_err(&ephdata->commsdevice->dev, "Failed to read expected response on second retry");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* increment retry each attempt */
|
||||
retry++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
int eph_write_engineering_data(struct eph_data *ephdata, u16 len, u8 *buf)
|
||||
{
|
||||
int ret_val = 0;
|
||||
struct eph_device_eng_data_write_response response;
|
||||
bool type_match = false;
|
||||
u8 retry = 0;
|
||||
/* calculate this before CRC adjustment */
|
||||
u16 payload_length = (len - sizeof(struct eph_device_eng_data_write_command));
|
||||
|
||||
/* wait a delay for TIC to be ready */
|
||||
udelay(50);
|
||||
|
||||
ret_val = eph_comms_write(ephdata, len, buf);
|
||||
|
||||
dev_info(&ephdata->commsdevice->dev,
|
||||
"ESWIN - Write engineering data. type: %d, length: %d",
|
||||
buf[0], (buf[1] | buf[2] << 8u));
|
||||
|
||||
/* wait a delay for TIC to be ready */
|
||||
usleep_range(4000, 4500);
|
||||
|
||||
if (ret_val)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Write engineering frame failure. ");
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
while (false == type_match)
|
||||
{
|
||||
memset(ephdata->comms_receive_buf, 0, COMMS_BUF_SIZE);
|
||||
|
||||
/* Do a 2 stage read, even though we know the format to allow consistant error handling when incorrect response received */
|
||||
/* This way if its an incorrect message format the complete message can be read out and the response can be discarded */
|
||||
ret_val = eph_comms_two_stage_read(ephdata, ephdata->comms_receive_buf);
|
||||
|
||||
if (!ret_val)
|
||||
{
|
||||
/* Only copy the amount for the expected message type and its storage */
|
||||
memcpy((u8*)&response, ephdata->comms_receive_buf, sizeof(struct eph_device_eng_data_write_response));
|
||||
|
||||
/* check bytes written matches what was sent */
|
||||
if (le16_to_cpu(response.bytes_written) != payload_length)
|
||||
{
|
||||
dev_err(&ephdata->commsdevice->dev, "Error writing to eng data: bytes written did not match what was sent");
|
||||
dev_err(&ephdata->commsdevice->dev, "Sent the following number of bytes (%d) but (%d) bytes where written", payload_length, le16_to_cpu(response.bytes_written));
|
||||
}
|
||||
|
||||
type_match = (TLV_ENG_DEBUG_DATA_WRITE == response.header.type);
|
||||
if (type_match == false) {
|
||||
ret_val = eph_handle_report(ephdata, ephdata->comms_receive_buf);
|
||||
dev_info(&ephdata->commsdevice->dev, "Process unexpected response %d\n", ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
/* Have 4 Retries at waiting for response */
|
||||
if(ret_val || ((retry > COMMS_READ_RETRY_NUM) && (false == type_match)))
|
||||
{
|
||||
/* have already retried and failed to get the correct response - break out the loop */
|
||||
dev_err(&ephdata->commsdevice->dev, "Failed to read expected response on retry");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* increment retry each attempt */
|
||||
retry++;
|
||||
}
|
||||
}
|
||||
|
||||
/* retry */
|
||||
if ((ret_val) || (false == type_match))
|
||||
{
|
||||
/* wait a delay for TIC to be ready */
|
||||
mdelay(10);
|
||||
|
||||
dev_err(&ephdata->commsdevice->dev, "Write engineering failure. Type Match: (%d)", type_match);
|
||||
/* error or incorrect type received */
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef __LINUX_PLATFORM_DATA_ESWIN_EPH_TLV_COMMAND_H
|
||||
#define __LINUX_PLATFORM_DATA_ESWIN_EPH_TLV_COMMAND_H
|
||||
|
||||
|
||||
#include "eswin_eph861x_types.h"
|
||||
#include "eswin_eph861x_tlv.h"
|
||||
|
||||
|
||||
extern int eph_comms_two_stage_read(struct eph_data *ephdata, u8 *buf);
|
||||
extern int eph_read_report(struct eph_data *ephdata, u8 *buf);
|
||||
extern int eph_comms_write(struct eph_data *ephdata, u16 len, u8 *buf);
|
||||
extern int eph_read_device_information(struct eph_data *ephdata);
|
||||
extern int eph_write_control_config(struct eph_data *ephdata, u16 len, u8 *buf);
|
||||
extern int eph_read_control_config(struct eph_data *ephdata, struct eph_device_control_config_read_command *command_request, u8 *buf);
|
||||
extern int eph_write_control_config_no_response(struct eph_data *ephdata, u16 len, u8 *buf);
|
||||
extern int eph_read_bootloader_information(struct eph_data *ephdata);
|
||||
extern int eph_write_engineering_data(struct eph_data *ephdata, u16 len, u8 *buf);
|
||||
|
||||
|
||||
|
||||
#endif /* __LINUX_PLATFORM_DATA_ESWIN_EPH_TLV_COMMAND_H */
|
||||
564
drivers/input/touchscreen/eswin_touch/eswin_eph861x_tlv_report.c
Normal file
564
drivers/input/touchscreen/eswin_touch/eswin_eph861x_tlv_report.c
Normal file
|
|
@ -0,0 +1,564 @@
|
|||
/*
|
||||
* ESWIN EPH861X series Touchscreen driver
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
// uncomment to enable the dev_dbg prints to dmesg
|
||||
#define DEBUG
|
||||
// uncomment to test with input forced open
|
||||
//#define INPUT_DEVICE_ALWAYS_OPEN
|
||||
#include <linux/types.h>
|
||||
|
||||
|
||||
|
||||
#include <uapi/asm-generic/errno-base.h>
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <uapi/linux/input-event-codes.h>
|
||||
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/input/mt.h>
|
||||
|
||||
#include "eswin_eph861x_project_config.h"
|
||||
#include "eswin_eph861x_tlv.h"
|
||||
#include "eswin_eph861x_types.h"
|
||||
#include "eswin_eph861x_comms.h"
|
||||
#include "eswin_eph861x_tlv_report.h"
|
||||
|
||||
#define EPH_MT_PRESSURE_CONTACTING 1
|
||||
#define EPH_MT_PRESSURE_HOVER 0
|
||||
|
||||
|
||||
u8 sysfs_report_buf[PAGE_SIZE]={0};
|
||||
u8 sysfs_packetised_eng_buf[PAGE_SIZE]={0};
|
||||
|
||||
static void eph_recv_touch_report(struct eph_data* ephdata, u8* message);
|
||||
|
||||
|
||||
|
||||
|
||||
const char *get_touch_type_str(u8 touch_type)
|
||||
{
|
||||
switch (touch_type)
|
||||
{
|
||||
case CONTACT_TYPE:
|
||||
return "Contact touch type";
|
||||
case RELEASE_TYPE:
|
||||
return "Release touch type";
|
||||
case HOVER_TYPE:
|
||||
return "Hover touch type";
|
||||
case STYLUS_POSITION_TYPE:
|
||||
return "Pen touch type";
|
||||
case GESTURE_TYPE:
|
||||
return "Gesture touch type";
|
||||
case STYLUS_RELEASE_TYPE:
|
||||
return "Pen touch release type";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
|
||||
/* gesture report format
|
||||
* Byte | Field | Value
|
||||
* 0 | Type(4-7) Length(0-3) | 5, 7
|
||||
* 1 | Gesture Type | x
|
||||
* 2 | X location Low | x
|
||||
* 3 | X location High | x
|
||||
* 4 | Y location Low | x
|
||||
* 5 | Y location High | x
|
||||
* 6 | Size Low | x
|
||||
* 7 | Size High | x
|
||||
*/
|
||||
static void eph_gesture_event_process(struct eph_data *ephdata, u8 *message)
|
||||
{
|
||||
struct device *dev = &ephdata->commsdevice->dev;
|
||||
struct input_dev *input_dev = ephdata->inputdev;
|
||||
|
||||
u8 gesture_type = message[1];
|
||||
|
||||
printk("eswin gesture report type %d\n", gesture_type);
|
||||
switch (gesture_type) {
|
||||
case GESTURE_TAP:
|
||||
{
|
||||
input_report_key(input_dev, KEY_WAKEUP, 1);
|
||||
input_sync(input_dev);
|
||||
input_report_key(input_dev, KEY_WAKEUP, 0);
|
||||
input_sync(input_dev);
|
||||
break;
|
||||
}
|
||||
case GESTURE_DOUBLE_TAP:
|
||||
{
|
||||
input_report_key(input_dev, KEY_WAKEUP, 1);
|
||||
input_sync(input_dev);
|
||||
input_report_key(input_dev, KEY_WAKEUP, 0);
|
||||
input_sync(input_dev);
|
||||
break;
|
||||
}
|
||||
case GESTURE_SWIPE_LEFT:
|
||||
/* TODO: Implement according to requirement */
|
||||
break;
|
||||
case GESTURE_SWIPE_RIGHT:
|
||||
/* TODO: Implement according to requirement */
|
||||
break;
|
||||
case GESTURE_SWIPE_UP:
|
||||
/* TODO: Implement according to requirement */
|
||||
break;
|
||||
case GESTURE_SWIPE_DOWN:
|
||||
/* TODO: Implement according to requirement */
|
||||
break;
|
||||
case GESTURE_PINCH:
|
||||
/* TODO: Implement according to requirement */
|
||||
break;
|
||||
case GESTURE_STRETCH:
|
||||
/* TODO: Implement according to requirement */
|
||||
break;
|
||||
case GESTURE_UNUSED:
|
||||
default:
|
||||
dev_err(dev, "unexpected gesture type %x\n", gesture_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void eph_recv_event_report_contianer(struct eph_data *ephdata, u8 *message)
|
||||
{
|
||||
|
||||
struct device *dev = &ephdata->commsdevice->dev;
|
||||
struct tlv_header tlvheader;
|
||||
u16 message_offset = TLV_HEADER_SIZE;
|
||||
u8 event_type;
|
||||
u8 event_length;
|
||||
|
||||
tlvheader = eph_get_tl_header_info(ephdata, message);
|
||||
while (message_offset < tlvheader.length)
|
||||
{
|
||||
|
||||
/* Get the event report type and length */
|
||||
event_type = (message[message_offset] & EVENT_REPORT_TYPE_MASK) >> EVENT_REPORT_TYPE_OFFSET;
|
||||
event_length = (message[message_offset] & EVENT_REPORT_LENGTH_MASK);
|
||||
#if 0
|
||||
dev_info(dev,
|
||||
"report - offset: %u event_type: %u event_length: %u ",
|
||||
message_offset, event_type, event_length);
|
||||
#endif
|
||||
switch (event_type)
|
||||
{
|
||||
|
||||
case CONTACT_TYPE:
|
||||
case RELEASE_TYPE:
|
||||
case HOVER_TYPE:
|
||||
case STYLUS_POSITION_TYPE:
|
||||
case STYLUS_RELEASE_TYPE:
|
||||
{
|
||||
eph_recv_touch_report(ephdata, &message[message_offset]);
|
||||
break;
|
||||
}
|
||||
case GESTURE_TYPE:
|
||||
eph_gesture_event_process(ephdata, &message[message_offset]);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
dev_err(dev, "Unsupported event report %d\n", event_type);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
message_offset = message_offset + (event_length + 1);
|
||||
|
||||
}
|
||||
/* Mark the end of the multi-touch transfer */
|
||||
input_sync(ephdata->inputdev);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
void eph_recv_off_event_report_contianer(struct eph_data *ephdata, u8 *message)
|
||||
{
|
||||
|
||||
struct device *dev = &ephdata->commsdevice->dev;
|
||||
|
||||
struct tlv_header tlvheader;
|
||||
u8 finger_event_type;
|
||||
u8 gesture_event_type;
|
||||
|
||||
tlvheader = eph_get_tl_header_info(ephdata, message);
|
||||
|
||||
finger_event_type = message[9];
|
||||
gesture_event_type = message[11];
|
||||
|
||||
dev_info(dev,
|
||||
"finger_event_type: %u gesture_event_type: %u",
|
||||
finger_event_type, gesture_event_type);
|
||||
|
||||
/* 0x25 reporting both fingerprint and gesture event, only process fingerprint event.
|
||||
gesture event is implemented in 0x23 reporting */
|
||||
/* 0x25 Screen off report format
|
||||
* Byte | Field | Value
|
||||
* 0 | Type | 0x25
|
||||
* 1 | Length (LSB) |
|
||||
* 2 | Length (MSB) | 9
|
||||
* 3 | CRC_Value | ---
|
||||
* 4 | Finger ID | ---
|
||||
* 5 | Finger Size | ---
|
||||
* 6 | FP Area Status | ---
|
||||
* 7 | X Position | ---
|
||||
* 8 | Y Position | ---
|
||||
* 9 | Finger Region Event Type | ---
|
||||
* 10 | Interrupt Counter | ---
|
||||
* 11 | Gesture Event Type | ---
|
||||
*/
|
||||
switch (finger_event_type)
|
||||
{
|
||||
case FINGER_REGION_FINGER_DOWN:
|
||||
{
|
||||
/* TODO: Implement finger print down action */
|
||||
dev_err(dev, "finger print type %x -- FINGER_REGION_FINGER_DOWN\n", finger_event_type);
|
||||
break;
|
||||
}
|
||||
case FINGER_REGION_FINGER_UP:
|
||||
{
|
||||
/* TODO: Implement finger print up action */
|
||||
dev_err(dev, "finger print type %x -- FINGER_REGION_FINGER_UP\n", finger_event_type);
|
||||
break;
|
||||
}
|
||||
case FINGER_REGION_HOVER_DOWN:
|
||||
/* TODO: Implement according to requirement */
|
||||
dev_err(dev, "finger print type %x -- FINGER_REGION_HOVER_DOWN\n", finger_event_type);
|
||||
break;
|
||||
case FINGER_REGION_HOVER_UP:
|
||||
/* TODO: Implement according to requirement */
|
||||
dev_err(dev, "finger print type %x -- FINGER_REGION_HOVER_UP\n", finger_event_type);
|
||||
break;
|
||||
case FINGER_REGION_RESERVED:
|
||||
default:
|
||||
dev_err(dev, "unexpected gesture type %x\n", finger_event_type);
|
||||
break;
|
||||
}
|
||||
|
||||
input_sync(ephdata->inputdev);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
static u16 stored_touches = 0;
|
||||
static u16 prev_stored_touches = 0;
|
||||
|
||||
/* https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt */
|
||||
static void eph_recv_touch_report(struct eph_data *ephdata, u8 *message)
|
||||
{
|
||||
struct device *dev = &ephdata->commsdevice->dev;
|
||||
const struct eph_platform_data *ephplatform = ephdata->ephplatform;
|
||||
u8 touch_id_slot;
|
||||
u8 touch_type;
|
||||
u16 position_x;
|
||||
u16 position_y;
|
||||
u8 width;
|
||||
u8 height;
|
||||
int touch_pressure = 0;
|
||||
int touch_tool_type = 0;
|
||||
u8 touch_major_axis = 0;
|
||||
u8 touch_minor_axis = 0;
|
||||
bool is_active = false;
|
||||
|
||||
prev_stored_touches = stored_touches;
|
||||
/* Get the touch report touch type */
|
||||
touch_type = (message[0] & EVENT_REPORT_TYPE_MASK) >> EVENT_REPORT_TYPE_OFFSET;
|
||||
touch_id_slot = message[1];
|
||||
|
||||
position_x = message[2] | ((u16)message[3] << 8); // little endian 16
|
||||
position_y = message[4] | ((u16)message[5] << 8); // little endian 16
|
||||
|
||||
// TODO review the units of this (mm or pixels) and whether they need to switch with touch orientation
|
||||
width = message[6];
|
||||
height = message[7];
|
||||
|
||||
switch (touch_type)
|
||||
{
|
||||
|
||||
case CONTACT_TYPE:
|
||||
{
|
||||
touch_tool_type = MT_TOOL_FINGER;
|
||||
touch_pressure = EPH_MT_PRESSURE_CONTACTING;
|
||||
is_active = true;
|
||||
|
||||
touch_major_axis = height;
|
||||
touch_minor_axis = width;
|
||||
break;
|
||||
}
|
||||
|
||||
case RELEASE_TYPE:
|
||||
{
|
||||
touch_tool_type = MT_TOOL_FINGER;
|
||||
touch_pressure = EPH_MT_PRESSURE_HOVER;
|
||||
is_active = false;
|
||||
|
||||
touch_major_axis = height;
|
||||
touch_minor_axis = width;
|
||||
break;
|
||||
}
|
||||
|
||||
case HOVER_TYPE:
|
||||
{
|
||||
touch_tool_type = MT_TOOL_FINGER;
|
||||
touch_pressure = EPH_MT_PRESSURE_HOVER;
|
||||
is_active = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case STYLUS_POSITION_TYPE:
|
||||
{
|
||||
is_active = true;
|
||||
touch_tool_type = MT_TOOL_PEN;
|
||||
/* For now initialise pressure being set - this would want to change when data supported */
|
||||
touch_pressure = EPH_MT_PRESSURE_CONTACTING;
|
||||
|
||||
touch_major_axis = height;
|
||||
touch_minor_axis = width;
|
||||
|
||||
break;
|
||||
}
|
||||
case STYLUS_RELEASE_TYPE:
|
||||
{
|
||||
touch_tool_type = MT_TOOL_PEN;
|
||||
touch_pressure = EPH_MT_PRESSURE_HOVER;
|
||||
is_active = false;
|
||||
|
||||
touch_major_axis = height;
|
||||
touch_minor_axis = width;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
dev_err(dev, "Unexpected touch_type %d\n", touch_type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate a slot event for updates to this slot */
|
||||
input_mt_slot(ephdata->inputdev, touch_id_slot);
|
||||
|
||||
|
||||
if (RELEASE_TYPE == touch_type)
|
||||
{
|
||||
|
||||
/* close out slot */
|
||||
/* touch tool type on a release will always show MT_TOOL_FINGER - Optional information - Doesnt matter the touch_tool_type for a release */
|
||||
input_mt_report_slot_state(ephdata->inputdev, touch_tool_type, is_active);
|
||||
input_report_abs(ephdata->inputdev, ABS_MT_PRESSURE, touch_pressure);
|
||||
#if 0
|
||||
dev_dbg(dev,
|
||||
"[%u] %s, RELEASE_TYPE, ABS_MT_PRESSURE:%d touch_tool_type:%d is_active:%d BTN_TOUCH:%d\n",
|
||||
touch_id_slot,
|
||||
get_touch_type_str(touch_type), touch_pressure, touch_tool_type, is_active, 0);
|
||||
#endif
|
||||
stored_touches &= (~(1 << touch_id_slot));
|
||||
}
|
||||
else
|
||||
{
|
||||
#if 0
|
||||
dev_dbg(dev,
|
||||
"[%u] %s POSTITION:(%u, %u) ABS_MT_TOUCH_MAJOR:%d ABS_MT_TOUCH_MINOR:%d ABS_MT_PRESSURE:%d touch_tool_type:%d is_active:%d\n",
|
||||
touch_id_slot,
|
||||
get_touch_type_str(touch_type),
|
||||
position_x, position_y, touch_major_axis, touch_minor_axis, touch_pressure, touch_tool_type, is_active);
|
||||
#endif
|
||||
/* Sets ABS_MT_TRACKING_ID if active or -1 if not */
|
||||
input_mt_report_slot_state(ephdata->inputdev, touch_tool_type, is_active);
|
||||
|
||||
if (ephplatform->panel_invert_x)
|
||||
input_report_abs(ephdata->inputdev, ABS_MT_POSITION_X, (ephplatform->panel_max_x - position_x));
|
||||
else
|
||||
input_report_abs(ephdata->inputdev, ABS_MT_POSITION_X, (position_x));
|
||||
|
||||
if (ephplatform->panel_invert_y)
|
||||
input_report_abs(ephdata->inputdev, ABS_MT_POSITION_Y, (ephplatform->panel_max_y - position_y));
|
||||
else
|
||||
input_report_abs(ephdata->inputdev, ABS_MT_POSITION_Y, position_y);
|
||||
|
||||
input_report_abs(ephdata->inputdev, ABS_MT_TOUCH_MAJOR, touch_major_axis);
|
||||
input_report_abs(ephdata->inputdev, ABS_MT_TOUCH_MINOR, touch_minor_axis);
|
||||
input_report_abs(ephdata->inputdev, ABS_MT_PRESSURE, touch_pressure);
|
||||
|
||||
stored_touches |= 1 << touch_id_slot;
|
||||
|
||||
}
|
||||
|
||||
if ((stored_touches) && (!prev_stored_touches))
|
||||
{
|
||||
/* first touch */
|
||||
//TODO android documentation specifieS this should not be needed but host reports do not register otherwise
|
||||
input_report_key(ephdata->inputdev, BTN_TOUCH, 1);
|
||||
}
|
||||
else if ((!stored_touches) && (prev_stored_touches))
|
||||
{
|
||||
/* last touch */
|
||||
input_report_key(ephdata->inputdev, BTN_TOUCH, 0u);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no update required */
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void eph_clear_all_host_touch_slots(struct eph_data *ephdata)
|
||||
{
|
||||
int id;
|
||||
|
||||
dev_dbg(&ephdata->commsdevice->dev, "%s >\n", __func__);
|
||||
|
||||
if (!ephdata->inputdev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (id = 0; id < CONFIG_SUPPORTED_TOUCHES; id++)
|
||||
{
|
||||
input_mt_slot(ephdata->inputdev, id);
|
||||
input_mt_report_slot_state(ephdata->inputdev, 0, false);
|
||||
}
|
||||
input_report_key(ephdata->inputdev, BTN_TOUCH, 0u);
|
||||
input_sync(ephdata->inputdev);
|
||||
stored_touches = 0;
|
||||
prev_stored_touches = 0;
|
||||
}
|
||||
|
||||
|
||||
static void eph_recv_device_state_report(struct eph_data *ephdata, u8 *message)
|
||||
{
|
||||
struct device *dev = &ephdata->commsdevice->dev;
|
||||
u8 device_status_flags;
|
||||
u16 component_id;
|
||||
struct tlv_header tlvheader;
|
||||
|
||||
/* Get the device status type */
|
||||
/* Should only get here if a valid report was received from the device */
|
||||
tlvheader = eph_get_tl_header_info(ephdata, message);
|
||||
device_status_flags = message[(TLV_HEADER_SIZE+TLV_DEVICE_STATUS_FLAGS_FIELD)];
|
||||
component_id = message[(TLV_HEADER_SIZE+TLV_DEVICE_STATUS_COMPONENT_FIELD)] | ((u16)message[(TLV_HEADER_SIZE+TLV_DEVICE_STATUS_COMPONENT_FIELD+1)] << 8);
|
||||
|
||||
|
||||
if (0 != (TLV_DEVICE_STATUS_RESET_MASK & device_status_flags))
|
||||
{
|
||||
complete(&ephdata->reset_completion);
|
||||
dev_dbg(dev,
|
||||
"Reset completed. STATUS_FLAG:%d COMPONENT_ID:%d\n", device_status_flags, component_id);
|
||||
}
|
||||
|
||||
dev_dbg(dev,
|
||||
"TYPE:%d LENGTH:%d STATUS_FLAG:%d COMPONENT_ID:%d\n",
|
||||
tlvheader.type, tlvheader.length, device_status_flags, component_id);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool eph_proc_report(struct eph_data *ephdata, u8 *message)
|
||||
{
|
||||
u8 type = message[0];
|
||||
bool buffer_report = false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case TLV_SCREEN_OFF_REPORT_DATA:
|
||||
{
|
||||
if (ephdata->inputdev)
|
||||
{
|
||||
/* only report messages to host if we have registered as an input device */
|
||||
eph_recv_off_event_report_contianer(ephdata, message);
|
||||
buffer_report = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TLV_REPORT_DATA:
|
||||
{
|
||||
if (ephdata->inputdev)
|
||||
{
|
||||
/* only report messages to host if we have registered as an input device */
|
||||
eph_recv_event_report_contianer(ephdata, message);
|
||||
buffer_report = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TLV_DEVICE_STATUS_DATA:
|
||||
{
|
||||
eph_recv_device_state_report(ephdata, message);
|
||||
buffer_report = true;
|
||||
break;
|
||||
}
|
||||
case TLV_PACKETISED_DATA:
|
||||
case TLV_ENG_DEBUG_DATA:
|
||||
{
|
||||
buffer_report = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
/* No processing required for unsupported types. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer_report;
|
||||
}
|
||||
|
||||
int eph_buffer_report(struct eph_data *ephdata, u8 *message)
|
||||
{
|
||||
struct tlv_header tlvheader;
|
||||
|
||||
/* Should only get here if a valid report was received from the device */
|
||||
tlvheader = eph_get_tl_header_info(ephdata, message);
|
||||
|
||||
if(PAGE_SIZE <= tlvheader.length)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Needs around 200us for eswin app to read message */
|
||||
udelay(200);
|
||||
mutex_lock(&ephdata->sysfs_report_buffer_lock);
|
||||
/* copy message into buffer */
|
||||
memcpy(&sysfs_report_buf[0],message,tlvheader.length);
|
||||
/* Clear report buffer lock as access within the interrupt is complete */
|
||||
mutex_unlock(&ephdata->sysfs_report_buffer_lock);
|
||||
|
||||
//dev_dbg(&ephdata->commsdevice->dev, "eswin T:%d len:%d", tlvheader.type, tlvheader.length);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int eph_handle_report(struct eph_data *ephdata, u8 *message)
|
||||
{
|
||||
bool buffer_report;
|
||||
int ret_val = 0;
|
||||
/* Process report , this is always return 0 after modify */
|
||||
buffer_report = eph_proc_report(ephdata, message);
|
||||
if(buffer_report)
|
||||
{
|
||||
/* Buffer report */
|
||||
ret_val = eph_buffer_report(ephdata, message);
|
||||
}
|
||||
return ret_val;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
|
||||
#ifndef __LINUX_PLATFORM_DATA_ESWIN_EPH_EVENT_REPORT_H
|
||||
#define __LINUX_PLATFORM_DATA_ESWIN_EPH_EVENT_REPORT_H
|
||||
|
||||
#include "eswin_eph861x_types.h"
|
||||
|
||||
extern u8 sysfs_report_buf[PAGE_SIZE];
|
||||
extern u8 sysfs_packetised_eng_buf[PAGE_SIZE];
|
||||
|
||||
|
||||
extern const char* get_touch_type_str(u8 touch_type);
|
||||
|
||||
|
||||
extern void eph_recv_event_report_contianer(struct eph_data* ephdata, u8* message);
|
||||
|
||||
extern void eph_recv_off_event_report_contianer(struct eph_data *ephdata, u8 *message);
|
||||
|
||||
extern void eph_clear_all_host_touch_slots(struct eph_data* ephdata);
|
||||
|
||||
extern bool eph_proc_report(struct eph_data *ephdata, u8 *message);
|
||||
|
||||
extern int eph_buffer_report(struct eph_data *ephdata, u8 *message);
|
||||
|
||||
extern int eph_handle_report(struct eph_data *ephdata, u8 *message);
|
||||
|
||||
#endif /* __LINUX_PLATFORM_DATA_ESWIN_EPH_EVENT_REPORT_ */
|
||||
260
drivers/input/touchscreen/eswin_touch/eswin_eph861x_types.h
Normal file
260
drivers/input/touchscreen/eswin_touch/eswin_eph861x_types.h
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
#ifndef __LINUX_PLATFORM_DATA_ESWIN_EPH_TYPES_H
|
||||
#define __LINUX_PLATFORM_DATA_ESWIN_EPH_TYPES_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <uapi/asm-generic/errno-base.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <uapi/linux/input-event-codes.h>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <generated/autoconf.h>
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
// #if defined(CONFIG_DRM)
|
||||
// #include <linux/msm_drm_notify.h>
|
||||
// #endif
|
||||
|
||||
#include <linux/backlight.h>
|
||||
|
||||
#define ESWIN_EPH861X_SPI_USE_DMA 0
|
||||
#if (ESWIN_EPH861X_SPI_USE_DMA)
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmapool.h>
|
||||
#endif
|
||||
|
||||
//TODO Make this a Kconfig option or read from device tree
|
||||
#define ESWIN_EPH861X_SPI 1
|
||||
#define ESWIN_EPH861X_I2C 0
|
||||
|
||||
#if (ESWIN_EPH861X_SPI)
|
||||
#include <linux/spi/spi.h>
|
||||
#endif
|
||||
|
||||
#if (ESWIN_EPH861X_I2C)
|
||||
#include <linux/i2c.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* Delay times */
|
||||
#define EPH_RESET_TIME 200 /* msec */
|
||||
#define EPH_FW_RESET_TIME 3000 /* msec */
|
||||
#define EPH_WAKEUP_TIME 25 /* msec */
|
||||
#define EPH_REGULATOR_DELAY 150 /* msec */
|
||||
#define EPH_POWERON_DELAY 100 /* msec */
|
||||
#define EPH_RESET_HOLD_TIME 2 /* msec */
|
||||
|
||||
|
||||
#if ESWIN_EPH861X_SPI
|
||||
/* Alias communication adapter to relevant adaptor */
|
||||
#define comms_device spi_device
|
||||
#define EPH_COMMS_BUS_TYPE BUS_SPI
|
||||
#define comms_device_id spi_device_id
|
||||
#define comms_mode_type spi
|
||||
#define comms_driver spi_driver
|
||||
|
||||
#define module_comms_driver module_spi_driver
|
||||
#endif
|
||||
#if ESWIN_EPH861X_I2C
|
||||
/* Alias communication adapter to relevant adaptor */
|
||||
#define comms_device i2c_client
|
||||
#define EPH_COMMS_BUS_TYPE BUS_I2C
|
||||
#define comms_device_id i2c_device_id
|
||||
#define comms_mode_type i2c
|
||||
#define comms_driver i2c_driver
|
||||
|
||||
#define module_comms_driver module_i2c_driver
|
||||
#endif
|
||||
|
||||
|
||||
struct tlv_header {
|
||||
u8 type;
|
||||
u16 length;
|
||||
} __packed;
|
||||
|
||||
|
||||
struct eph_device_control_config_write_response
|
||||
{
|
||||
struct tlv_header header;
|
||||
u16 bytes_written;
|
||||
} __packed;
|
||||
|
||||
struct eph_device_eng_data_write_command
|
||||
{
|
||||
struct tlv_header header;
|
||||
u16 component_id;
|
||||
u8 data_id;
|
||||
u8 crc;
|
||||
} __packed;
|
||||
|
||||
struct eph_device_eng_data_write_response
|
||||
{
|
||||
struct tlv_header header;
|
||||
u16 bytes_written;
|
||||
} __packed;
|
||||
|
||||
struct eph_device_control_config_read_command
|
||||
{
|
||||
struct tlv_header header;
|
||||
u16 command_id;
|
||||
u16 offset;
|
||||
u16 read_length;
|
||||
} __packed;
|
||||
|
||||
|
||||
enum eph_suspend_mode
|
||||
{
|
||||
EPH_SUSPEND_DEEP_SLEEP = 0,
|
||||
EPH_SUSPEND_REGULATOR = 2,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* Platform Data (e.g. populated from Device Tree) */
|
||||
struct eph_platform_data
|
||||
{
|
||||
enum eph_suspend_mode suspend_mode;
|
||||
unsigned long gpio_reset;
|
||||
unsigned long gpio_chg_irq;
|
||||
const char *regulator_dvdd;
|
||||
const char *regulator_avdd;
|
||||
const char *device_settings_name;
|
||||
const char *fw_name;
|
||||
const char *input_name;
|
||||
/* panel info */
|
||||
unsigned int panel_invert_x;
|
||||
unsigned int panel_invert_y;
|
||||
unsigned int panel_max_x;
|
||||
unsigned int panel_max_y;
|
||||
};
|
||||
|
||||
struct eph_device_info
|
||||
{
|
||||
struct tlv_header device_header;
|
||||
u8 product_id;
|
||||
u8 variant_id;
|
||||
u8 application_version_major;
|
||||
u16 application_version_minor;
|
||||
u16 bootloader_version;
|
||||
u8 protocol_version;
|
||||
u8 crc;
|
||||
};
|
||||
|
||||
#ifdef IC_UPDATE_DETECT
|
||||
/* Fixed 8 bytes info */
|
||||
struct eph_firmware_info
|
||||
{
|
||||
u8 product_id;
|
||||
u8 variant_id;
|
||||
u8 application_version_major;
|
||||
u16 application_version_minor;
|
||||
u16 bootloader_version;
|
||||
u8 crc;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Config update context */
|
||||
struct eph_device_settings
|
||||
{
|
||||
u8 *raw;
|
||||
size_t raw_size;
|
||||
off_t raw_pos;
|
||||
};
|
||||
|
||||
/* Firmware update context */
|
||||
struct eph_flash
|
||||
{
|
||||
struct eph_data *ephdata;
|
||||
const struct firmware *fw;
|
||||
u8 *pframe;
|
||||
loff_t fw_pos;
|
||||
size_t frame_size;
|
||||
unsigned int frame_count;
|
||||
};
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct eph_data
|
||||
{
|
||||
struct mutex comms_mutex;
|
||||
struct mutex sysfs_report_buffer_lock;
|
||||
struct comms_device *commsdevice;
|
||||
struct input_dev *inputdev;
|
||||
char phys[64]; /* device physical location */
|
||||
const struct eph_platform_data *ephplatform;
|
||||
struct eph_device_info ephdeviceinfo;
|
||||
unsigned int chg_irq;
|
||||
bool in_bootloader;
|
||||
|
||||
struct pinctrl *pinctrl;
|
||||
|
||||
#if ESWIN_EPH861X_I2C
|
||||
u8 bootloader_addr;
|
||||
#endif
|
||||
u8 *report_buf;
|
||||
u8 *comms_send_buf;
|
||||
u8 *comms_receive_buf;
|
||||
u8 *comms_send_crc_buf;
|
||||
dma_addr_t comms_dma_handle_tx;
|
||||
dma_addr_t comms_dma_handle_rx;
|
||||
|
||||
struct regulator *reg_vdd;
|
||||
struct regulator *reg_avdd;
|
||||
char *fw_name;
|
||||
char *device_settings_name;
|
||||
struct eph_flash *ephflash;
|
||||
|
||||
/* for reset handling */
|
||||
struct completion reset_completion;
|
||||
|
||||
/* for power up handling */
|
||||
struct completion chg_completion;
|
||||
|
||||
/* Indicates whether device is in suspend */
|
||||
bool suspended;
|
||||
|
||||
bool gesture_wakeup_enable;
|
||||
u8 gesture_mode;
|
||||
|
||||
/* low power mode gesture */
|
||||
u8 lp;
|
||||
bool irq_wake;
|
||||
|
||||
struct backlight_device *bl;
|
||||
unsigned int last_brightness;
|
||||
|
||||
struct work_struct force_baseline_work;
|
||||
struct delayed_work heartbeat_work;
|
||||
|
||||
/* Indicates whether device is updating its device settings */
|
||||
bool updating_device_settings;
|
||||
|
||||
#if defined(CONFIG_DRM)
|
||||
struct notifier_block notifier;
|
||||
void *notifier_cookie;
|
||||
// #elif defined(CONFIG_BOARD_CLOUDRIPPER)
|
||||
// bool is_panel_lp_mode;
|
||||
// struct drm_connector *connector;
|
||||
// struct drm_bridge panel_bridge;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __LINUX_PLATFORM_DATA_ESWIN_EPH_TYPES_ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue