mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-10 22:40:54 -04:00
input: synaptics_tcm_mmi: Port Synaptics TCM driver.
This patch port synaptics_tcm_v1.0 origin code. First line of release_notes.txt is: v1.0 Mar 09, 2018 scott.lin@tw.synaptics.com Change-Id: I0f83d23bce4375c609d09ef1f413627fce5f60b7 Signed-off-by: haojl2 <haojl2@motorola.com> Reviewed-on: https://gerrit.mot.com/1171049 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Konstantin Makariev <kmakariev@motorola.com> Submit-Approved: Jira Key
This commit is contained in:
parent
1b0013ece1
commit
0437d00289
18 changed files with 14496 additions and 0 deletions
|
|
@ -0,0 +1,71 @@
|
|||
Synaptics TCM I2C touchscreen controller
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
should be "synaptics,tcm-i2c"
|
||||
- reg:
|
||||
i2c slave address of device
|
||||
- interrupt-parent:
|
||||
hardware controller of interrupt signal
|
||||
- interrupts:
|
||||
gpio number and flags of interrupt signal
|
||||
- vdd-supply:
|
||||
digital power source
|
||||
- avdd-supply:
|
||||
analog power source
|
||||
- pinctrl-names:
|
||||
- pinctrl-0:
|
||||
- pinctrl-1:
|
||||
should be defined if using pinctrl framework
|
||||
"pmx_ts_active": active configuration of pins
|
||||
"pmx_ts_suspend": disabled configuration of pins
|
||||
- synaptics,bus-reg-name:
|
||||
name of digital power source regulator
|
||||
- synaptics,pwr-reg-name:
|
||||
name of analog power source regulator
|
||||
- synaptics,irq-gpio:
|
||||
interrupt hardware controller, gpio number, and flags
|
||||
- synaptics,irq-on-state:
|
||||
active state of interrupt signal
|
||||
|
||||
Optional properties:
|
||||
- synaptics,power-gpio:
|
||||
hardware controller and gpio number of power control signal
|
||||
- synaptics,power-delay-ms:
|
||||
delay time in ms after powering on device
|
||||
- synaptics,reset-gpio:
|
||||
hardware controller and gpio number of reset signal
|
||||
- synaptics,reset-delay-ms:
|
||||
delay time in ms after issuing reset to device
|
||||
- synaptics,reset-on-state:
|
||||
active state of reset signal
|
||||
- synaptics,reset-active-ms:
|
||||
active duration in ms of reset signal
|
||||
- synaptics,x-flip:
|
||||
flip x axis
|
||||
- synaptics,y-flip:
|
||||
flip y axis
|
||||
- synaptics,swap-axes:
|
||||
swap x and y axes
|
||||
- synaptics,ubl-i2c-addr:
|
||||
i2c slave address of device in microbootloader mode
|
||||
|
||||
Example:
|
||||
synaptics_tcm@2c {
|
||||
compatible = "synaptics,tcm-i2c";
|
||||
reg = <0x2c>;
|
||||
interrupt-parent = <&msm_gpio>;
|
||||
interrupts = <65 0x2008>;
|
||||
vdd-supply = <&pm8994_lvs2>;
|
||||
avdd-supply = <&pm8994_l22>;
|
||||
pinctrl-names = "pmx_ts_active", "pmx_ts_suspend";
|
||||
pinctrl-0 = <&ts_active>;
|
||||
pinctrl-1 = <&ts_suspend>;
|
||||
synaptics,pwr-reg-name = "avdd";
|
||||
synaptics,bus-reg-name = "vdd";
|
||||
synaptics,irq-gpio = <&msm_gpio 65 0x2008>;
|
||||
synaptics,irq-on-state = <0>;
|
||||
synaptics,power-delay-ms = <200>;
|
||||
synaptics,reset-delay-ms = <200>;
|
||||
synaptics,ubl-i2c-addr = <0x2c>;
|
||||
};
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
Synaptics TCM SPI touchscreen controller
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
should be "synaptics,tcm-spi"
|
||||
- reg:
|
||||
should be 0
|
||||
- spi-max-frequency:
|
||||
maximum spi clock frequency
|
||||
- interrupt-parent:
|
||||
hardware controller of interrupt signal
|
||||
- interrupts:
|
||||
gpio number and flags of interrupt signal
|
||||
- vdd-supply:
|
||||
digital power source
|
||||
- avdd-supply:
|
||||
analog power source
|
||||
- pinctrl-names:
|
||||
- pinctrl-0:
|
||||
- pinctrl-1:
|
||||
should be defined if using pinctrl framework
|
||||
"pmx_ts_active": active configuration of pins
|
||||
"pmx_ts_suspend": disabled configuration of pins
|
||||
- synaptics,bus-reg-name:
|
||||
name of digital power source regulator
|
||||
- synaptics,pwr-reg-name:
|
||||
name of analog power source regulator
|
||||
- synaptics,irq-gpio:
|
||||
interrupt hardware controller, gpio number, and flags
|
||||
- synaptics,irq-on-state:
|
||||
active state of interrupt signal
|
||||
|
||||
Optional properties:
|
||||
- synaptics,spi-mode:
|
||||
spi mode
|
||||
- synaptics,byte-delay-us:
|
||||
inter-byte delay time in us
|
||||
- synaptics,block-delay-us:
|
||||
inter-block delay time in us
|
||||
- synaptics,power-gpio:
|
||||
hardware controller and gpio number of power control signal
|
||||
- synaptics,power-delay-ms:
|
||||
delay time in ms after powering on device
|
||||
- synaptics,reset-gpio:
|
||||
hardware controller and gpio number of reset signal
|
||||
- synaptics,reset-delay-ms:
|
||||
delay time in ms after issuing reset to device
|
||||
- synaptics,reset-on-state:
|
||||
active state of reset signal
|
||||
- synaptics,reset-active-ms:
|
||||
active duration in ms of reset signal
|
||||
- synaptics,x-flip:
|
||||
flip x axis
|
||||
- synaptics,y-flip:
|
||||
flip y axis
|
||||
- synaptics,swap-axes:
|
||||
swap x and y axes
|
||||
- synaptics,ubl-max-freq:
|
||||
maximum spi clock frequency for microbootloader mode
|
||||
- synaptics,ubl-byte-delay-us:
|
||||
inter-byte delay time in us for microbootloader mode
|
||||
|
||||
Example:
|
||||
synaptics_tcm@0 {
|
||||
compatible = "synaptics,tcm-spi";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <10000000>;
|
||||
interrupt-parent = <&msm_gpio>;
|
||||
interrupts = <65 0x2008>;
|
||||
vdd-supply = <&pm8994_lvs2>;
|
||||
avdd-supply = <&pm8994_l22>;
|
||||
pinctrl-names = "pmx_ts_active", "pmx_ts_suspend";
|
||||
pinctrl-0 = <&ts_active>;
|
||||
pinctrl-1 = <&ts_suspend>;
|
||||
synaptics,bus-reg-name = "vdd";
|
||||
synaptics,pwr-reg-name = "avdd";
|
||||
synaptics,irq-gpio = <&msm_gpio 65 0x2008>;
|
||||
synaptics,irq-on-state = <0>;
|
||||
synaptics,spi-mode = <3>;
|
||||
synaptics,byte-delay-us = <0>;
|
||||
synaptics,block-delay-us = <0>;
|
||||
synaptics,power-delay-ms = <200>;
|
||||
synaptics,reset-delay-ms = <200>;
|
||||
synaptics,ubl-max-freq = <5000000>;
|
||||
synaptics,ubl-byte-delay-us = <20>;
|
||||
};
|
||||
65
drivers/input/touchscreen/synaptics_tcm_mmi/Android.mk
Normal file
65
drivers/input/touchscreen/synaptics_tcm_mmi/Android.mk
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
DLKM_DIR := motorola/kernel/modules
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := synaptics_tcm_spi.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := synaptics_tcm_i2c.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := synaptics_tcm_core.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := synaptics_tcm_device.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := synaptics_tcm_diagnostics.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := synaptics_tcm_recovery.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := synaptics_tcm_reflash.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := synaptics_tcm_testing.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := synaptics_tcm_touch.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := synaptics_tcm_zeroflash.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
|
||||
15
drivers/input/touchscreen/synaptics_tcm_mmi/Kbuild
Normal file
15
drivers/input/touchscreen/synaptics_tcm_mmi/Kbuild
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# add -Wall to try to catch everything we can.
|
||||
EXTRA_CFLAGS += -Wall
|
||||
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \
|
||||
-I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/synaptics_tcm_mmi
|
||||
|
||||
obj-m += synaptics_tcm_spi.o
|
||||
obj-m += synaptics_tcm_i2c.o
|
||||
obj-m += synaptics_tcm_core.o
|
||||
obj-m += synaptics_tcm_device.o
|
||||
obj-m += synaptics_tcm_diagnostics.o
|
||||
obj-m += synaptics_tcm_recovery.o
|
||||
obj-m += synaptics_tcm_reflash.o
|
||||
obj-m += synaptics_tcm_testing.o
|
||||
obj-m += synaptics_tcm_touch.o
|
||||
obj-m += synaptics_tcm_zeroflash.o
|
||||
10
drivers/input/touchscreen/synaptics_tcm_mmi/Makefile
Normal file
10
drivers/input/touchscreen/synaptics_tcm_mmi/Makefile
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
|
||||
|
||||
all:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS)
|
||||
|
||||
modules_install:
|
||||
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean
|
||||
3461
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_core.c
Normal file
3461
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_core.c
Normal file
File diff suppressed because it is too large
Load diff
661
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_core.h
Normal file
661
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_core.h
Normal file
|
|
@ -0,0 +1,661 @@
|
|||
/*
|
||||
* Synaptics TCM touchscreen driver
|
||||
*
|
||||
* Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2017-2018 Scott Lin <scott.lin@tw.synaptics.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
|
||||
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
|
||||
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
|
||||
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
|
||||
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
|
||||
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
|
||||
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
|
||||
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
|
||||
* DOLLARS.
|
||||
*/
|
||||
|
||||
#ifndef _SYNAPTICS_TCM_CORE_H_
|
||||
#define _SYNAPTICS_TCM_CORE_H_
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input/synaptics_tcm.h>
|
||||
#ifdef CONFIG_FB
|
||||
#include <linux/fb.h>
|
||||
#include <linux/notifier.h>
|
||||
#endif
|
||||
|
||||
#define SYNAPTICS_TCM_ID_PRODUCT (1 << 0)
|
||||
#define SYNAPTICS_TCM_ID_VERSION 0x0100
|
||||
#define SYNAPTICS_TCM_ID_SUBVERSION 0
|
||||
|
||||
#define PLATFORM_DRIVER_NAME "synaptics_tcm"
|
||||
|
||||
#define TOUCH_INPUT_NAME "synaptics_tcm_touch"
|
||||
#define TOUCH_INPUT_PHYS_PATH "synaptics_tcm/touch_input"
|
||||
|
||||
#define WAKEUP_GESTURE
|
||||
|
||||
#define RD_CHUNK_SIZE 0 /* read length limit in bytes, 0 = unlimited */
|
||||
#define WR_CHUNK_SIZE 0 /* write length limit in bytes, 0 = unlimited */
|
||||
|
||||
#define MESSAGE_HEADER_SIZE 4
|
||||
#define MESSAGE_MARKER 0xa5
|
||||
#define MESSAGE_PADDING 0x5a
|
||||
|
||||
#define LOGx(func, dev, log, ...) \
|
||||
func(dev, "%s: " log, __func__, ##__VA_ARGS__)
|
||||
|
||||
#define LOGy(func, dev, log, ...) \
|
||||
func(dev, "%s (line %d): " log, __func__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
#define LOGD(dev, log, ...) LOGx(dev_dbg, dev, log, ##__VA_ARGS__)
|
||||
#define LOGI(dev, log, ...) LOGx(dev_info, dev, log, ##__VA_ARGS__)
|
||||
#define LOGN(dev, log, ...) LOGx(dev_notice, dev, log, ##__VA_ARGS__)
|
||||
#define LOGW(dev, log, ...) LOGy(dev_warn, dev, log, ##__VA_ARGS__)
|
||||
#define LOGE(dev, log, ...) LOGy(dev_err, dev, log, ##__VA_ARGS__)
|
||||
|
||||
#define INIT_BUFFER(buffer, is_clone) \
|
||||
mutex_init(&buffer.buf_mutex); \
|
||||
buffer.clone = is_clone
|
||||
|
||||
#define LOCK_BUFFER(buffer) \
|
||||
mutex_lock(&buffer.buf_mutex)
|
||||
|
||||
#define UNLOCK_BUFFER(buffer) \
|
||||
mutex_unlock(&buffer.buf_mutex)
|
||||
|
||||
#define RELEASE_BUFFER(buffer) \
|
||||
do { \
|
||||
if (buffer.clone == false) { \
|
||||
kfree(buffer.buf); \
|
||||
buffer.buf_size = 0; \
|
||||
buffer.data_length = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define MAX(a, b) \
|
||||
({__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a > _b ? _a : _b; })
|
||||
|
||||
#define MIN(a, b) \
|
||||
({__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a < _b ? _a : _b; })
|
||||
|
||||
#define STR(x) #x
|
||||
|
||||
#define CONCAT(a, b) a##b
|
||||
|
||||
#define SHOW_PROTOTYPE(m_name, a_name) \
|
||||
static ssize_t CONCAT(m_name##_sysfs, _##a_name##_show)(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf); \
|
||||
\
|
||||
static struct device_attribute dev_attr_##a_name = \
|
||||
__ATTR(a_name, S_IRUGO, \
|
||||
CONCAT(m_name##_sysfs, _##a_name##_show), \
|
||||
syna_tcm_store_error);
|
||||
|
||||
#define STORE_PROTOTYPE(m_name, a_name) \
|
||||
static ssize_t CONCAT(m_name##_sysfs, _##a_name##_store)(struct device *dev, \
|
||||
struct device_attribute *attr, const char *buf, size_t count); \
|
||||
\
|
||||
static struct device_attribute dev_attr_##a_name = \
|
||||
__ATTR(a_name, (S_IWUSR | S_IWGRP), \
|
||||
syna_tcm_show_error, \
|
||||
CONCAT(m_name##_sysfs, _##a_name##_store));
|
||||
|
||||
#define SHOW_STORE_PROTOTYPE(m_name, a_name) \
|
||||
static ssize_t CONCAT(m_name##_sysfs, _##a_name##_show)(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf); \
|
||||
\
|
||||
static ssize_t CONCAT(m_name##_sysfs, _##a_name##_store)(struct device *dev, \
|
||||
struct device_attribute *attr, const char *buf, size_t count); \
|
||||
\
|
||||
static struct device_attribute dev_attr_##a_name = \
|
||||
__ATTR(a_name, (S_IRUGO | S_IWUSR | S_IWGRP), \
|
||||
CONCAT(m_name##_sysfs, _##a_name##_show), \
|
||||
CONCAT(m_name##_sysfs, _##a_name##_store));
|
||||
|
||||
#define ATTRIFY(a_name) (&dev_attr_##a_name)
|
||||
|
||||
enum module_type {
|
||||
TCM_TOUCH = 0,
|
||||
TCM_DEVICE = 1,
|
||||
TCM_TESTING = 2,
|
||||
TCM_REFLASH = 3,
|
||||
TCM_RECOVERY = 4,
|
||||
TCM_ZEROFLASH = 5,
|
||||
TCM_DIAGNOSTICS = 6,
|
||||
TCM_LAST,
|
||||
};
|
||||
|
||||
enum boot_mode {
|
||||
MODE_APPLICATION = 0x01,
|
||||
MODE_HOST_DOWNLOAD = 0x02,
|
||||
MODE_BOOTLOADER = 0x0b,
|
||||
MODE_TDDI_BOOTLOADER = 0x0c,
|
||||
MODE_PRODUCTION_TEST = 0x0e,
|
||||
};
|
||||
|
||||
enum boot_status {
|
||||
BOOT_STATUS_OK = 0x00,
|
||||
BOOT_STATUS_BOOTING = 0x01,
|
||||
BOOT_STATUS_APP_BAD_DISPLAY_CRC = 0xfc,
|
||||
BOOT_STATUS_BAD_DISPLAY_CONFIG = 0xfd,
|
||||
BOOT_STATUS_BAD_APP_FIRMWARE = 0xfe,
|
||||
BOOT_STATUS_WARM_BOOT = 0xff,
|
||||
};
|
||||
|
||||
enum app_status {
|
||||
APP_STATUS_OK = 0x00,
|
||||
APP_STATUS_BOOTING = 0x01,
|
||||
APP_STATUS_UPDATING = 0x02,
|
||||
APP_STATUS_BAD_APP_CONFIG = 0xff,
|
||||
};
|
||||
|
||||
enum firmware_mode {
|
||||
FW_MODE_BOOTLOADER = 0,
|
||||
FW_MODE_APPLICATION = 1,
|
||||
FW_MODE_PRODUCTION_TEST = 2,
|
||||
};
|
||||
|
||||
enum dynamic_config_id {
|
||||
DC_UNKNOWN = 0x00,
|
||||
DC_NO_DOZE,
|
||||
DC_DISABLE_NOISE_MITIGATION,
|
||||
DC_INHIBIT_FREQUENCY_SHIFT,
|
||||
DC_REQUESTED_FREQUENCY,
|
||||
DC_DISABLE_HSYNC,
|
||||
DC_REZERO_ON_EXIT_DEEP_SLEEP,
|
||||
DC_CHARGER_CONNECTED,
|
||||
DC_NO_BASELINE_RELAXATION,
|
||||
DC_IN_WAKEUP_GESTURE_MODE,
|
||||
DC_STIMULUS_FINGERS,
|
||||
DC_GRIP_SUPPRESSION_ENABLED,
|
||||
DC_ENABLE_THICK_GLOVE,
|
||||
DC_ENABLE_GLOVE,
|
||||
};
|
||||
|
||||
enum command {
|
||||
CMD_NONE = 0x00,
|
||||
CMD_CONTINUE_WRITE = 0x01,
|
||||
CMD_IDENTIFY = 0x02,
|
||||
CMD_RESET = 0x04,
|
||||
CMD_ENABLE_REPORT = 0x05,
|
||||
CMD_DISABLE_REPORT = 0x06,
|
||||
CMD_GET_BOOT_INFO = 0x10,
|
||||
CMD_ERASE_FLASH = 0x11,
|
||||
CMD_WRITE_FLASH = 0x12,
|
||||
CMD_READ_FLASH = 0x13,
|
||||
CMD_RUN_APPLICATION_FIRMWARE = 0x14,
|
||||
CMD_SPI_MASTER_WRITE_THEN_READ = 0x15,
|
||||
CMD_REBOOT_TO_ROM_BOOTLOADER = 0x16,
|
||||
CMD_RUN_BOOTLOADER_FIRMWARE = 0x1f,
|
||||
CMD_GET_APPLICATION_INFO = 0x20,
|
||||
CMD_GET_STATIC_CONFIG = 0x21,
|
||||
CMD_SET_STATIC_CONFIG = 0x22,
|
||||
CMD_GET_DYNAMIC_CONFIG = 0x23,
|
||||
CMD_SET_DYNAMIC_CONFIG = 0x24,
|
||||
CMD_GET_TOUCH_REPORT_CONFIG = 0x25,
|
||||
CMD_SET_TOUCH_REPORT_CONFIG = 0x26,
|
||||
CMD_REZERO = 0x27,
|
||||
CMD_COMMIT_CONFIG = 0x28,
|
||||
CMD_DESCRIBE_DYNAMIC_CONFIG = 0x29,
|
||||
CMD_PRODUCTION_TEST = 0x2a,
|
||||
CMD_SET_CONFIG_ID = 0x2b,
|
||||
CMD_ENTER_DEEP_SLEEP = 0x2c,
|
||||
CMD_EXIT_DEEP_SLEEP = 0x2d,
|
||||
CMD_GET_TOUCH_INFO = 0x2e,
|
||||
CMD_GET_DATA_LOCATION = 0x2f,
|
||||
CMD_DOWNLOAD_CONFIG = 0x30,
|
||||
CMD_ENTER_PRODUCTION_TEST_MODE = 0x31,
|
||||
CMD_GET_FEATURES = 0x32,
|
||||
};
|
||||
|
||||
enum status_code {
|
||||
STATUS_IDLE = 0x00,
|
||||
STATUS_OK = 0x01,
|
||||
STATUS_BUSY = 0x02,
|
||||
STATUS_CONTINUED_READ = 0x03,
|
||||
STATUS_NOT_EXECUTED_IN_DEEP_SLEEP = 0x0b,
|
||||
STATUS_RECEIVE_BUFFER_OVERFLOW = 0x0c,
|
||||
STATUS_PREVIOUS_COMMAND_PENDING = 0x0d,
|
||||
STATUS_NOT_IMPLEMENTED = 0x0e,
|
||||
STATUS_ERROR = 0x0f,
|
||||
STATUS_INVALID = 0xff,
|
||||
};
|
||||
|
||||
enum report_type {
|
||||
REPORT_IDENTIFY = 0x10,
|
||||
REPORT_TOUCH = 0x11,
|
||||
REPORT_DELTA = 0x12,
|
||||
REPORT_RAW = 0x13,
|
||||
REPORT_STATUS = 0x1b,
|
||||
REPORT_PRINTF = 0x82,
|
||||
REPORT_HDL = 0xfe,
|
||||
};
|
||||
|
||||
enum command_status {
|
||||
CMD_IDLE = 0,
|
||||
CMD_BUSY = 1,
|
||||
CMD_ERROR = -1,
|
||||
};
|
||||
|
||||
enum flash_area {
|
||||
BOOTLOADER = 0,
|
||||
BOOT_CONFIG,
|
||||
APP_FIRMWARE,
|
||||
APP_CONFIG,
|
||||
DISP_CONFIG,
|
||||
CUSTOM_OTP,
|
||||
CUSTOM_LCM,
|
||||
CUSTOM_OEM,
|
||||
PPDT,
|
||||
};
|
||||
|
||||
enum flash_data {
|
||||
LCM_DATA = 1,
|
||||
OEM_DATA,
|
||||
PPDT_DATA,
|
||||
};
|
||||
|
||||
enum helper_task {
|
||||
HELP_NONE = 0,
|
||||
HELP_RUN_APPLICATION_FIRMWARE,
|
||||
HELP_SEND_RESET_NOTIFICATION,
|
||||
};
|
||||
|
||||
struct syna_tcm_helper {
|
||||
atomic_t task;
|
||||
struct work_struct work;
|
||||
struct workqueue_struct *workqueue;
|
||||
};
|
||||
|
||||
struct syna_tcm_watchdog {
|
||||
bool run;
|
||||
unsigned char count;
|
||||
struct delayed_work work;
|
||||
struct workqueue_struct *workqueue;
|
||||
};
|
||||
|
||||
struct syna_tcm_buffer {
|
||||
bool clone;
|
||||
unsigned char *buf;
|
||||
unsigned int buf_size;
|
||||
unsigned int data_length;
|
||||
struct mutex buf_mutex;
|
||||
};
|
||||
|
||||
struct syna_tcm_report {
|
||||
unsigned char id;
|
||||
struct syna_tcm_buffer buffer;
|
||||
};
|
||||
|
||||
struct syna_tcm_identification {
|
||||
unsigned char version;
|
||||
unsigned char mode;
|
||||
unsigned char part_number[16];
|
||||
unsigned char build_id[4];
|
||||
unsigned char max_write_size[2];
|
||||
};
|
||||
|
||||
struct syna_tcm_boot_info {
|
||||
unsigned char version;
|
||||
unsigned char status;
|
||||
unsigned char asic_id[2];
|
||||
unsigned char write_block_size_words;
|
||||
unsigned char erase_page_size_words[2];
|
||||
unsigned char max_write_payload_size[2];
|
||||
unsigned char last_reset_reason;
|
||||
unsigned char pc_at_time_of_last_reset[2];
|
||||
unsigned char boot_config_start_block[2];
|
||||
unsigned char boot_config_size_blocks[2];
|
||||
unsigned char display_config_start_block[4];
|
||||
unsigned char display_config_length_blocks[2];
|
||||
unsigned char backup_display_config_start_block[4];
|
||||
unsigned char backup_display_config_length_blocks[2];
|
||||
unsigned char custom_otp_start_block[2];
|
||||
unsigned char custom_otp_length_blocks[2];
|
||||
};
|
||||
|
||||
struct syna_tcm_app_info {
|
||||
unsigned char version[2];
|
||||
unsigned char status[2];
|
||||
unsigned char static_config_size[2];
|
||||
unsigned char dynamic_config_size[2];
|
||||
unsigned char app_config_start_write_block[2];
|
||||
unsigned char app_config_size[2];
|
||||
unsigned char max_touch_report_config_size[2];
|
||||
unsigned char max_touch_report_payload_size[2];
|
||||
unsigned char customer_config_id[16];
|
||||
unsigned char max_x[2];
|
||||
unsigned char max_y[2];
|
||||
unsigned char max_objects[2];
|
||||
unsigned char num_of_buttons[2];
|
||||
unsigned char num_of_image_rows[2];
|
||||
unsigned char num_of_image_cols[2];
|
||||
unsigned char has_hybrid_data[2];
|
||||
};
|
||||
|
||||
struct syna_tcm_touch_info {
|
||||
unsigned char image_2d_scale_factor[4];
|
||||
unsigned char image_0d_scale_factor[4];
|
||||
unsigned char hybrid_x_scale_factor[4];
|
||||
unsigned char hybrid_y_scale_factor[4];
|
||||
};
|
||||
|
||||
struct syna_tcm_message_header {
|
||||
unsigned char marker;
|
||||
unsigned char code;
|
||||
unsigned char length[2];
|
||||
};
|
||||
|
||||
struct syna_tcm_features {
|
||||
unsigned char byte_0_reserved;
|
||||
unsigned char byte_1_reserved;
|
||||
unsigned char dual_firmware:1;
|
||||
unsigned char byte_2_reserved:7;
|
||||
} __packed;
|
||||
|
||||
struct syna_tcm_hcd {
|
||||
pid_t isr_pid;
|
||||
atomic_t command_status;
|
||||
atomic_t host_downloading;
|
||||
wait_queue_head_t hdl_wq;
|
||||
int irq;
|
||||
bool init_okay;
|
||||
bool do_polling;
|
||||
bool in_suspend;
|
||||
bool irq_enabled;
|
||||
bool host_download_mode;
|
||||
unsigned char fb_ready;
|
||||
unsigned char command;
|
||||
unsigned char async_report_id;
|
||||
unsigned char status_report_code;
|
||||
unsigned char response_code;
|
||||
unsigned int read_length;
|
||||
unsigned int payload_length;
|
||||
unsigned int packrat_number;
|
||||
unsigned int rd_chunk_size;
|
||||
unsigned int wr_chunk_size;
|
||||
unsigned int app_status;
|
||||
struct platform_device *pdev;
|
||||
struct regulator *pwr_reg;
|
||||
struct regulator *bus_reg;
|
||||
struct kobject *sysfs_dir;
|
||||
struct kobject *dynamnic_config_sysfs_dir;
|
||||
struct mutex extif_mutex;
|
||||
struct mutex reset_mutex;
|
||||
struct mutex irq_en_mutex;
|
||||
struct mutex io_ctrl_mutex;
|
||||
struct mutex rw_ctrl_mutex;
|
||||
struct mutex command_mutex;
|
||||
struct mutex identify_mutex;
|
||||
struct delayed_work polling_work;
|
||||
struct workqueue_struct *polling_workqueue;
|
||||
struct task_struct *notifier_thread;
|
||||
#ifdef CONFIG_FB
|
||||
struct notifier_block fb_notifier;
|
||||
#endif
|
||||
struct syna_tcm_buffer in;
|
||||
struct syna_tcm_buffer out;
|
||||
struct syna_tcm_buffer resp;
|
||||
struct syna_tcm_buffer temp;
|
||||
struct syna_tcm_buffer config;
|
||||
struct syna_tcm_report report;
|
||||
struct syna_tcm_app_info app_info;
|
||||
struct syna_tcm_boot_info boot_info;
|
||||
struct syna_tcm_touch_info touch_info;
|
||||
struct syna_tcm_identification id_info;
|
||||
struct syna_tcm_helper helper;
|
||||
struct syna_tcm_watchdog watchdog;
|
||||
struct syna_tcm_features features;
|
||||
const struct syna_tcm_hw_interface *hw_if;
|
||||
int (*reset)(struct syna_tcm_hcd *tcm_hcd, bool hw, bool update_wd);
|
||||
int (*sleep)(struct syna_tcm_hcd *tcm_hcd, bool en);
|
||||
int (*identify)(struct syna_tcm_hcd *tcm_hcd, bool id);
|
||||
int (*enable_irq)(struct syna_tcm_hcd *tcm_hcd, bool en, bool ns);
|
||||
int (*switch_mode)(struct syna_tcm_hcd *tcm_hcd,
|
||||
enum firmware_mode mode);
|
||||
int (*read_message)(struct syna_tcm_hcd *tcm_hcd,
|
||||
unsigned char *in_buf, unsigned int length);
|
||||
int (*write_message)(struct syna_tcm_hcd *tcm_hcd,
|
||||
unsigned char command, unsigned char *payload,
|
||||
unsigned int length, unsigned char **resp_buf,
|
||||
unsigned int *resp_buf_size, unsigned int *resp_length,
|
||||
unsigned char *response_code,
|
||||
unsigned int polling_delay_ms);
|
||||
int (*get_dynamic_config)(struct syna_tcm_hcd *tcm_hcd,
|
||||
enum dynamic_config_id id, unsigned short *value);
|
||||
int (*set_dynamic_config)(struct syna_tcm_hcd *tcm_hcd,
|
||||
enum dynamic_config_id id, unsigned short value);
|
||||
int (*get_data_location)(struct syna_tcm_hcd *tcm_hcd,
|
||||
enum flash_area area, unsigned int *addr,
|
||||
unsigned int *length);
|
||||
int (*read_flash_data)(enum flash_area area, bool run_app_firmware,
|
||||
struct syna_tcm_buffer *output);
|
||||
void (*report_touch)(void);
|
||||
void (*update_watchdog)(struct syna_tcm_hcd *tcm_hcd, bool en);
|
||||
};
|
||||
|
||||
struct syna_tcm_module_cb {
|
||||
enum module_type type;
|
||||
int (*init)(struct syna_tcm_hcd *tcm_hcd);
|
||||
int (*remove)(struct syna_tcm_hcd *tcm_hcd);
|
||||
int (*syncbox)(struct syna_tcm_hcd *tcm_hcd);
|
||||
int (*asyncbox)(struct syna_tcm_hcd *tcm_hcd);
|
||||
int (*reset)(struct syna_tcm_hcd *tcm_hcd);
|
||||
int (*suspend)(struct syna_tcm_hcd *tcm_hcd);
|
||||
int (*resume)(struct syna_tcm_hcd *tcm_hcd);
|
||||
int (*early_suspend)(struct syna_tcm_hcd *tcm_hcd);
|
||||
};
|
||||
|
||||
struct syna_tcm_module_handler {
|
||||
bool insert;
|
||||
bool detach;
|
||||
struct list_head link;
|
||||
struct syna_tcm_module_cb *mod_cb;
|
||||
};
|
||||
|
||||
struct syna_tcm_module_pool {
|
||||
bool initialized;
|
||||
bool queue_work;
|
||||
struct mutex mutex;
|
||||
struct list_head list;
|
||||
struct work_struct work;
|
||||
struct workqueue_struct *workqueue;
|
||||
struct syna_tcm_hcd *tcm_hcd;
|
||||
};
|
||||
|
||||
struct syna_tcm_bus_io {
|
||||
unsigned char type;
|
||||
int (*rmi_read)(struct syna_tcm_hcd *tcm_hcd, unsigned short addr,
|
||||
unsigned char *data, unsigned int length);
|
||||
int (*rmi_write)(struct syna_tcm_hcd *tcm_hcd, unsigned short addr,
|
||||
unsigned char *data, unsigned int length);
|
||||
int (*read)(struct syna_tcm_hcd *tcm_hcd, unsigned char *data,
|
||||
unsigned int length);
|
||||
int (*write)(struct syna_tcm_hcd *tcm_hcd, unsigned char *data,
|
||||
unsigned int length);
|
||||
};
|
||||
|
||||
struct syna_tcm_hw_interface {
|
||||
struct syna_tcm_board_data *bdata;
|
||||
const struct syna_tcm_bus_io *bus_io;
|
||||
};
|
||||
|
||||
int syna_tcm_bus_init(void);
|
||||
|
||||
void syna_tcm_bus_exit(void);
|
||||
|
||||
int syna_tcm_add_module(struct syna_tcm_module_cb *mod_cb, bool insert);
|
||||
|
||||
static inline int syna_tcm_rmi_read(struct syna_tcm_hcd *tcm_hcd,
|
||||
unsigned short addr, unsigned char *data, unsigned int length)
|
||||
{
|
||||
return tcm_hcd->hw_if->bus_io->rmi_read(tcm_hcd, addr, data, length);
|
||||
}
|
||||
|
||||
static inline int syna_tcm_rmi_write(struct syna_tcm_hcd *tcm_hcd,
|
||||
unsigned short addr, unsigned char *data, unsigned int length)
|
||||
{
|
||||
return tcm_hcd->hw_if->bus_io->rmi_write(tcm_hcd, addr, data, length);
|
||||
}
|
||||
|
||||
static inline int syna_tcm_read(struct syna_tcm_hcd *tcm_hcd,
|
||||
unsigned char *data, unsigned int length)
|
||||
{
|
||||
return tcm_hcd->hw_if->bus_io->read(tcm_hcd, data, length);
|
||||
}
|
||||
|
||||
static inline int syna_tcm_write(struct syna_tcm_hcd *tcm_hcd,
|
||||
unsigned char *data, unsigned int length)
|
||||
{
|
||||
return tcm_hcd->hw_if->bus_io->write(tcm_hcd, data, length);
|
||||
}
|
||||
|
||||
static inline ssize_t syna_tcm_show_error(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
pr_err("%s: Attribute not readable\n",
|
||||
__func__);
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
static inline ssize_t syna_tcm_store_error(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
pr_err("%s: Attribute not writable\n",
|
||||
__func__);
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size,
|
||||
const unsigned char *src, unsigned int src_size,
|
||||
unsigned int count)
|
||||
{
|
||||
if (dest == NULL || src == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (count > dest_size || count > src_size) {
|
||||
pr_err("%s: src_size = %d, dest_size = %d, count = %d\n",
|
||||
__func__, src_size, dest_size, count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy((void *)dest, (const void *)src, count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int syna_tcm_realloc_mem(struct syna_tcm_hcd *tcm_hcd,
|
||||
struct syna_tcm_buffer *buffer, unsigned int size)
|
||||
{
|
||||
int retval;
|
||||
unsigned char *temp;
|
||||
|
||||
if (size > buffer->buf_size) {
|
||||
temp = buffer->buf;
|
||||
|
||||
buffer->buf = kmalloc(size, GFP_KERNEL);
|
||||
if (!(buffer->buf)) {
|
||||
dev_err(tcm_hcd->pdev->dev.parent,
|
||||
"%s: Failed to allocate memory\n",
|
||||
__func__);
|
||||
kfree(temp);
|
||||
buffer->buf_size = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
retval = secure_memcpy(buffer->buf,
|
||||
size,
|
||||
temp,
|
||||
buffer->buf_size,
|
||||
buffer->buf_size);
|
||||
if (retval < 0) {
|
||||
dev_err(tcm_hcd->pdev->dev.parent,
|
||||
"%s: Failed to copy data\n",
|
||||
__func__);
|
||||
kfree(temp);
|
||||
kfree(buffer->buf);
|
||||
buffer->buf_size = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
kfree(temp);
|
||||
buffer->buf_size = size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int syna_tcm_alloc_mem(struct syna_tcm_hcd *tcm_hcd,
|
||||
struct syna_tcm_buffer *buffer, unsigned int size)
|
||||
{
|
||||
if (size > buffer->buf_size) {
|
||||
kfree(buffer->buf);
|
||||
buffer->buf = kmalloc(size, GFP_KERNEL);
|
||||
if (!(buffer->buf)) {
|
||||
dev_err(tcm_hcd->pdev->dev.parent,
|
||||
"%s: Failed to allocate memory\n",
|
||||
__func__);
|
||||
dev_err(tcm_hcd->pdev->dev.parent,
|
||||
"%s: Allocation size = %d\n",
|
||||
__func__, size);
|
||||
buffer->buf_size = 0;
|
||||
buffer->data_length = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
buffer->buf_size = size;
|
||||
}
|
||||
|
||||
memset(buffer->buf, 0x00, buffer->buf_size);
|
||||
buffer->data_length = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned int le2_to_uint(const unsigned char *src)
|
||||
{
|
||||
return (unsigned int)src[0] +
|
||||
(unsigned int)src[1] * 0x100;
|
||||
}
|
||||
|
||||
static inline unsigned int le4_to_uint(const unsigned char *src)
|
||||
{
|
||||
return (unsigned int)src[0] +
|
||||
(unsigned int)src[1] * 0x100 +
|
||||
(unsigned int)src[2] * 0x10000 +
|
||||
(unsigned int)src[3] * 0x1000000;
|
||||
}
|
||||
|
||||
static inline unsigned int ceil_div(unsigned int dividend, unsigned divisor)
|
||||
{
|
||||
return (dividend + divisor - 1) / divisor;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,708 @@
|
|||
/*
|
||||
* Synaptics TCM touchscreen driver
|
||||
*
|
||||
* Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2017-2018 Scott Lin <scott.lin@tw.synaptics.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
|
||||
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
|
||||
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
|
||||
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
|
||||
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
|
||||
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
|
||||
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
|
||||
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
|
||||
* DOLLARS.
|
||||
*/
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include "synaptics_tcm_core.h"
|
||||
|
||||
#define CHAR_DEVICE_NAME "tcm"
|
||||
|
||||
#define CONCURRENT true
|
||||
|
||||
#define DEVICE_IOC_MAGIC 's'
|
||||
#define DEVICE_IOC_RESET _IO(DEVICE_IOC_MAGIC, 0) /* 0x00007300 */
|
||||
#define DEVICE_IOC_IRQ _IOW(DEVICE_IOC_MAGIC, 1, int) /* 0x40047301 */
|
||||
#define DEVICE_IOC_RAW _IOW(DEVICE_IOC_MAGIC, 2, int) /* 0x40047302 */
|
||||
#define DEVICE_IOC_CONCURRENT _IOW(DEVICE_IOC_MAGIC, 3, int) /* 0x40047303 */
|
||||
|
||||
struct device_hcd {
|
||||
dev_t dev_num;
|
||||
bool raw_mode;
|
||||
bool concurrent;
|
||||
unsigned int ref_count;
|
||||
struct cdev char_dev;
|
||||
struct class *class;
|
||||
struct device *device;
|
||||
struct syna_tcm_buffer out;
|
||||
struct syna_tcm_buffer resp;
|
||||
struct syna_tcm_buffer report;
|
||||
struct syna_tcm_hcd *tcm_hcd;
|
||||
};
|
||||
|
||||
DECLARE_COMPLETION(device_remove_complete);
|
||||
|
||||
static struct device_hcd *device_hcd;
|
||||
|
||||
static int rmidev_major_num;
|
||||
|
||||
static void device_capture_touch_report(unsigned int count)
|
||||
{
|
||||
int retval;
|
||||
unsigned char id;
|
||||
unsigned int idx;
|
||||
unsigned int size;
|
||||
unsigned char *data;
|
||||
struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd;
|
||||
static bool report;
|
||||
static unsigned int offset;
|
||||
static unsigned int remaining_size;
|
||||
|
||||
if (count < 2)
|
||||
return;
|
||||
|
||||
data = &device_hcd->resp.buf[0];
|
||||
|
||||
if (data[0] != MESSAGE_MARKER)
|
||||
return;
|
||||
|
||||
id = data[1];
|
||||
|
||||
size = 0;
|
||||
|
||||
LOCK_BUFFER(device_hcd->report);
|
||||
|
||||
switch (id) {
|
||||
case REPORT_TOUCH:
|
||||
if (count >= 4) {
|
||||
remaining_size = le2_to_uint(&data[2]);
|
||||
} else {
|
||||
report = false;
|
||||
goto exit;
|
||||
}
|
||||
retval = syna_tcm_alloc_mem(tcm_hcd,
|
||||
&device_hcd->report,
|
||||
remaining_size);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for device_hcd->report.buf\n");
|
||||
report = false;
|
||||
goto exit;
|
||||
}
|
||||
idx = 4;
|
||||
size = count - idx;
|
||||
offset = 0;
|
||||
report = true;
|
||||
break;
|
||||
case STATUS_CONTINUED_READ:
|
||||
if (report == false)
|
||||
goto exit;
|
||||
if (count >= 2) {
|
||||
idx = 2;
|
||||
size = count - idx;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (size) {
|
||||
size = MIN(size, remaining_size);
|
||||
retval = secure_memcpy(&device_hcd->report.buf[offset],
|
||||
device_hcd->report.buf_size - offset,
|
||||
&data[idx],
|
||||
count - idx,
|
||||
size);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy touch report data\n");
|
||||
report = false;
|
||||
goto exit;
|
||||
} else {
|
||||
offset += size;
|
||||
remaining_size -= size;
|
||||
device_hcd->report.data_length += size;
|
||||
}
|
||||
}
|
||||
|
||||
if (remaining_size)
|
||||
goto exit;
|
||||
|
||||
LOCK_BUFFER(tcm_hcd->report.buffer);
|
||||
|
||||
tcm_hcd->report.buffer.buf = device_hcd->report.buf;
|
||||
tcm_hcd->report.buffer.buf_size = device_hcd->report.buf_size;
|
||||
tcm_hcd->report.buffer.data_length = device_hcd->report.data_length;
|
||||
|
||||
tcm_hcd->report_touch();
|
||||
|
||||
UNLOCK_BUFFER(tcm_hcd->report.buffer);
|
||||
|
||||
report = false;
|
||||
|
||||
exit:
|
||||
UNLOCK_BUFFER(device_hcd->report);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int device_capture_touch_report_config(unsigned int count)
|
||||
{
|
||||
int retval;
|
||||
unsigned int size;
|
||||
unsigned int buf_size;
|
||||
unsigned char *data;
|
||||
struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd;
|
||||
|
||||
if (device_hcd->raw_mode) {
|
||||
if (count < 3) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Invalid write data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size = le2_to_uint(&device_hcd->out.buf[1]);
|
||||
|
||||
if (count - 3 < size) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Incomplete write data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
data = &device_hcd->out.buf[3];
|
||||
buf_size = device_hcd->out.buf_size - 3;
|
||||
} else {
|
||||
size = count - 1;
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
data = &device_hcd->out.buf[1];
|
||||
buf_size = device_hcd->out.buf_size - 1;
|
||||
}
|
||||
|
||||
LOCK_BUFFER(tcm_hcd->config);
|
||||
|
||||
retval = syna_tcm_alloc_mem(tcm_hcd,
|
||||
&tcm_hcd->config,
|
||||
size);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for tcm_hcd->config.buf\n");
|
||||
UNLOCK_BUFFER(tcm_hcd->config);
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = secure_memcpy(tcm_hcd->config.buf,
|
||||
tcm_hcd->config.buf_size,
|
||||
data,
|
||||
buf_size,
|
||||
size);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy touch report config data\n");
|
||||
UNLOCK_BUFFER(tcm_hcd->config);
|
||||
return retval;
|
||||
}
|
||||
|
||||
tcm_hcd->config.data_length = size;
|
||||
|
||||
UNLOCK_BUFFER(tcm_hcd->config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_UNLOCKED_IOCTL
|
||||
static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
#else
|
||||
static int device_ioctl(struct inode *inp, struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
#endif
|
||||
{
|
||||
int retval;
|
||||
struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
retval = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case DEVICE_IOC_RESET:
|
||||
retval = tcm_hcd->reset(tcm_hcd, false, true);
|
||||
break;
|
||||
case DEVICE_IOC_IRQ:
|
||||
if (arg == 0)
|
||||
retval = tcm_hcd->enable_irq(tcm_hcd, false, false);
|
||||
else if (arg == 1)
|
||||
retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL);
|
||||
break;
|
||||
case DEVICE_IOC_RAW:
|
||||
if (arg == 0)
|
||||
device_hcd->raw_mode = false;
|
||||
else if (arg == 1)
|
||||
device_hcd->raw_mode = true;
|
||||
break;
|
||||
case DEVICE_IOC_CONCURRENT:
|
||||
if (arg == 0)
|
||||
device_hcd->concurrent = false;
|
||||
else if (arg == 1)
|
||||
device_hcd->concurrent = true;
|
||||
break;
|
||||
default:
|
||||
retval = -ENOTTY;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static loff_t device_llseek(struct file *filp, loff_t off, int whence)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t device_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *f_pos)
|
||||
{
|
||||
int retval;
|
||||
struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd;
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
LOCK_BUFFER(device_hcd->resp);
|
||||
|
||||
if (device_hcd->raw_mode) {
|
||||
retval = syna_tcm_alloc_mem(tcm_hcd,
|
||||
&device_hcd->resp,
|
||||
count);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for device_hcd->resp.buf\n");
|
||||
UNLOCK_BUFFER(device_hcd->resp);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
retval = tcm_hcd->read_message(tcm_hcd,
|
||||
device_hcd->resp.buf,
|
||||
count);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to read message\n");
|
||||
UNLOCK_BUFFER(device_hcd->resp);
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
if (count != device_hcd->resp.data_length) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Invalid length information\n");
|
||||
UNLOCK_BUFFER(device_hcd->resp);
|
||||
retval = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (copy_to_user(buf, device_hcd->resp.buf, count)) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy data to user space\n");
|
||||
UNLOCK_BUFFER(device_hcd->resp);
|
||||
retval = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!device_hcd->concurrent)
|
||||
goto skip_concurrent;
|
||||
|
||||
if (tcm_hcd->report_touch == NULL) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Unable to report touch\n");
|
||||
device_hcd->concurrent = false;
|
||||
}
|
||||
|
||||
if (device_hcd->raw_mode)
|
||||
device_capture_touch_report(count);
|
||||
|
||||
skip_concurrent:
|
||||
UNLOCK_BUFFER(device_hcd->resp);
|
||||
|
||||
retval = count;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t device_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *f_pos)
|
||||
{
|
||||
int retval;
|
||||
struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd;
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
LOCK_BUFFER(device_hcd->out);
|
||||
|
||||
retval = syna_tcm_alloc_mem(tcm_hcd,
|
||||
&device_hcd->out,
|
||||
count == 1 ? count + 1 : count);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for device_hcd->out.buf\n");
|
||||
UNLOCK_BUFFER(device_hcd->out);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (copy_from_user(device_hcd->out.buf, buf, count)) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy data from user space\n");
|
||||
UNLOCK_BUFFER(device_hcd->out);
|
||||
retval = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
LOCK_BUFFER(device_hcd->resp);
|
||||
|
||||
if (device_hcd->raw_mode) {
|
||||
retval = tcm_hcd->write_message(tcm_hcd,
|
||||
device_hcd->out.buf[0],
|
||||
&device_hcd->out.buf[1],
|
||||
count - 1,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
} else {
|
||||
mutex_lock(&tcm_hcd->reset_mutex);
|
||||
retval = tcm_hcd->write_message(tcm_hcd,
|
||||
device_hcd->out.buf[0],
|
||||
&device_hcd->out.buf[1],
|
||||
count - 1,
|
||||
&device_hcd->resp.buf,
|
||||
&device_hcd->resp.buf_size,
|
||||
&device_hcd->resp.data_length,
|
||||
NULL,
|
||||
0);
|
||||
mutex_unlock(&tcm_hcd->reset_mutex);
|
||||
}
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to write command 0x%02x\n",
|
||||
device_hcd->out.buf[0]);
|
||||
UNLOCK_BUFFER(device_hcd->resp);
|
||||
UNLOCK_BUFFER(device_hcd->out);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (count && device_hcd->out.buf[0] == CMD_SET_TOUCH_REPORT_CONFIG) {
|
||||
retval = device_capture_touch_report_config(count);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to capture touch report config\n");
|
||||
}
|
||||
}
|
||||
|
||||
UNLOCK_BUFFER(device_hcd->out);
|
||||
|
||||
if (device_hcd->raw_mode)
|
||||
retval = count;
|
||||
else
|
||||
retval = device_hcd->resp.data_length;
|
||||
|
||||
UNLOCK_BUFFER(device_hcd->resp);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int device_open(struct inode *inp, struct file *filp)
|
||||
{
|
||||
int retval;
|
||||
struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
if (device_hcd->ref_count < 1) {
|
||||
device_hcd->ref_count++;
|
||||
retval = 0;
|
||||
} else {
|
||||
retval = -EACCES;
|
||||
}
|
||||
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int device_release(struct inode *inp, struct file *filp)
|
||||
{
|
||||
struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
if (device_hcd->ref_count)
|
||||
device_hcd->ref_count--;
|
||||
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *device_devnode(struct device *dev, umode_t *mode)
|
||||
{
|
||||
if (!mode)
|
||||
return NULL;
|
||||
|
||||
*mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
|
||||
return kasprintf(GFP_KERNEL, "%s/%s", PLATFORM_DRIVER_NAME,
|
||||
dev_name(dev));
|
||||
}
|
||||
|
||||
static int device_create_class(void)
|
||||
{
|
||||
struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd;
|
||||
|
||||
if (device_hcd->class != NULL)
|
||||
return 0;
|
||||
|
||||
device_hcd->class = class_create(THIS_MODULE, PLATFORM_DRIVER_NAME);
|
||||
|
||||
if (IS_ERR(device_hcd->class)) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to create class\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
device_hcd->class->devnode = device_devnode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations device_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef HAVE_UNLOCKED_IOCTL
|
||||
.unlocked_ioctl = device_ioctl,
|
||||
#ifdef HAVE_COMPAT_IOCTL
|
||||
.compat_ioctl = device_ioctl,
|
||||
#endif
|
||||
#else
|
||||
.ioctl = device_ioctl,
|
||||
#endif
|
||||
.llseek = device_llseek,
|
||||
.read = device_read,
|
||||
.write = device_write,
|
||||
.open = device_open,
|
||||
.release = device_release,
|
||||
};
|
||||
|
||||
static int device_init(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
int retval;
|
||||
dev_t dev_num;
|
||||
const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
||||
|
||||
device_hcd = kzalloc(sizeof(*device_hcd), GFP_KERNEL);
|
||||
if (!device_hcd) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for device_hcd\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
device_hcd->tcm_hcd = tcm_hcd;
|
||||
|
||||
device_hcd->concurrent = CONCURRENT;
|
||||
|
||||
INIT_BUFFER(device_hcd->out, false);
|
||||
INIT_BUFFER(device_hcd->resp, false);
|
||||
INIT_BUFFER(device_hcd->report, false);
|
||||
|
||||
if (rmidev_major_num) {
|
||||
dev_num = MKDEV(rmidev_major_num, 0);
|
||||
retval = register_chrdev_region(dev_num, 1,
|
||||
PLATFORM_DRIVER_NAME);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to register char device\n");
|
||||
goto err_register_chrdev_region;
|
||||
}
|
||||
} else {
|
||||
retval = alloc_chrdev_region(&dev_num, 0, 1,
|
||||
PLATFORM_DRIVER_NAME);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate char device\n");
|
||||
goto err_alloc_chrdev_region;
|
||||
}
|
||||
|
||||
rmidev_major_num = MAJOR(dev_num);
|
||||
}
|
||||
|
||||
device_hcd->dev_num = dev_num;
|
||||
|
||||
cdev_init(&device_hcd->char_dev, &device_fops);
|
||||
|
||||
retval = cdev_add(&device_hcd->char_dev, dev_num, 1);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to add char device\n");
|
||||
goto err_add_chardev;
|
||||
}
|
||||
|
||||
retval = device_create_class();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to create class\n");
|
||||
goto err_create_class;
|
||||
}
|
||||
|
||||
device_hcd->device = device_create(device_hcd->class, NULL,
|
||||
device_hcd->dev_num, NULL, CHAR_DEVICE_NAME"%d",
|
||||
MINOR(device_hcd->dev_num));
|
||||
if (IS_ERR(device_hcd->device)) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to create device\n");
|
||||
retval = -ENODEV;
|
||||
goto err_create_device;
|
||||
}
|
||||
|
||||
if (bdata->irq_gpio >= 0) {
|
||||
retval = gpio_export(bdata->irq_gpio, false);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to export GPIO\n");
|
||||
} else {
|
||||
retval = gpio_export_link(&tcm_hcd->pdev->dev,
|
||||
"attn", bdata->irq_gpio);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to export GPIO link\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_create_device:
|
||||
class_destroy(device_hcd->class);
|
||||
|
||||
err_create_class:
|
||||
cdev_del(&device_hcd->char_dev);
|
||||
|
||||
err_add_chardev:
|
||||
unregister_chrdev_region(dev_num, 1);
|
||||
|
||||
err_alloc_chrdev_region:
|
||||
err_register_chrdev_region:
|
||||
RELEASE_BUFFER(device_hcd->report);
|
||||
RELEASE_BUFFER(device_hcd->resp);
|
||||
RELEASE_BUFFER(device_hcd->out);
|
||||
|
||||
kfree(device_hcd);
|
||||
device_hcd = NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int device_remove(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
if (!device_hcd)
|
||||
goto exit;
|
||||
|
||||
device_destroy(device_hcd->class, device_hcd->dev_num);
|
||||
|
||||
class_destroy(device_hcd->class);
|
||||
|
||||
cdev_del(&device_hcd->char_dev);
|
||||
|
||||
unregister_chrdev_region(device_hcd->dev_num, 1);
|
||||
|
||||
RELEASE_BUFFER(device_hcd->report);
|
||||
RELEASE_BUFFER(device_hcd->resp);
|
||||
RELEASE_BUFFER(device_hcd->out);
|
||||
|
||||
kfree(device_hcd);
|
||||
device_hcd = NULL;
|
||||
|
||||
exit:
|
||||
complete(&device_remove_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_reset(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (!device_hcd) {
|
||||
retval = device_init(tcm_hcd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct syna_tcm_module_cb device_module = {
|
||||
.type = TCM_DEVICE,
|
||||
.init = device_init,
|
||||
.remove = device_remove,
|
||||
.syncbox = NULL,
|
||||
.asyncbox = NULL,
|
||||
.reset = device_reset,
|
||||
.suspend = NULL,
|
||||
.resume = NULL,
|
||||
.early_suspend = NULL,
|
||||
};
|
||||
|
||||
static int __init device_module_init(void)
|
||||
{
|
||||
return syna_tcm_add_module(&device_module, true);
|
||||
}
|
||||
|
||||
static void __exit device_module_exit(void)
|
||||
{
|
||||
syna_tcm_add_module(&device_module, false);
|
||||
|
||||
wait_for_completion(&device_remove_complete);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(device_module_init);
|
||||
module_exit(device_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Synaptics, Inc.");
|
||||
MODULE_DESCRIPTION("Synaptics TCM Device Module");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -0,0 +1,567 @@
|
|||
/*
|
||||
* Synaptics TCM touchscreen driver
|
||||
*
|
||||
* Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2017-2018 Scott Lin <scott.lin@tw.synaptics.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
|
||||
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
|
||||
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
|
||||
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
|
||||
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
|
||||
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
|
||||
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
|
||||
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
|
||||
* DOLLARS.
|
||||
*/
|
||||
|
||||
#include "synaptics_tcm_core.h"
|
||||
|
||||
#define SYSFS_DIR_NAME "diagnostics"
|
||||
|
||||
enum pingpong_state {
|
||||
PING = 0,
|
||||
PONG = 1,
|
||||
};
|
||||
|
||||
struct diag_hcd {
|
||||
pid_t pid;
|
||||
unsigned char report_type;
|
||||
enum pingpong_state state;
|
||||
struct kobject *sysfs_dir;
|
||||
struct siginfo sigio;
|
||||
struct task_struct *task;
|
||||
struct syna_tcm_buffer ping;
|
||||
struct syna_tcm_buffer pong;
|
||||
struct syna_tcm_hcd *tcm_hcd;
|
||||
};
|
||||
|
||||
DECLARE_COMPLETION(diag_remove_complete);
|
||||
|
||||
static struct diag_hcd *diag_hcd;
|
||||
|
||||
STORE_PROTOTYPE(diag, pid)
|
||||
SHOW_PROTOTYPE(diag, size)
|
||||
STORE_PROTOTYPE(diag, type)
|
||||
SHOW_PROTOTYPE(diag, rows)
|
||||
SHOW_PROTOTYPE(diag, cols)
|
||||
SHOW_PROTOTYPE(diag, hybrid)
|
||||
SHOW_PROTOTYPE(diag, buttons)
|
||||
|
||||
static struct device_attribute *attrs[] = {
|
||||
ATTRIFY(pid),
|
||||
ATTRIFY(size),
|
||||
ATTRIFY(type),
|
||||
ATTRIFY(rows),
|
||||
ATTRIFY(cols),
|
||||
ATTRIFY(hybrid),
|
||||
ATTRIFY(buttons),
|
||||
};
|
||||
|
||||
static ssize_t diag_sysfs_data_show(struct file *data_file,
|
||||
struct kobject *kobj, struct bin_attribute *attributes,
|
||||
char *buf, loff_t pos, size_t count);
|
||||
|
||||
static struct bin_attribute bin_attr = {
|
||||
.attr = {
|
||||
.name = "data",
|
||||
.mode = S_IRUGO,
|
||||
},
|
||||
.size = 0,
|
||||
.read = diag_sysfs_data_show,
|
||||
};
|
||||
|
||||
static ssize_t diag_sysfs_pid_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
int retval;
|
||||
unsigned int input;
|
||||
struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
|
||||
|
||||
if (sscanf(buf, "%u", &input) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
diag_hcd->pid = input;
|
||||
|
||||
if (diag_hcd->pid) {
|
||||
diag_hcd->task = pid_task(find_vpid(diag_hcd->pid),
|
||||
PIDTYPE_PID);
|
||||
if (!diag_hcd->task) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to locate task\n");
|
||||
retval = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
retval = count;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t diag_sysfs_size_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int retval;
|
||||
struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
if (diag_hcd->state == PING) {
|
||||
LOCK_BUFFER(diag_hcd->ping);
|
||||
|
||||
retval = snprintf(buf, PAGE_SIZE,
|
||||
"%u\n",
|
||||
diag_hcd->ping.data_length);
|
||||
|
||||
UNLOCK_BUFFER(diag_hcd->ping);
|
||||
} else {
|
||||
LOCK_BUFFER(diag_hcd->pong);
|
||||
|
||||
retval = snprintf(buf, PAGE_SIZE,
|
||||
"%u\n",
|
||||
diag_hcd->pong.data_length);
|
||||
|
||||
UNLOCK_BUFFER(diag_hcd->pong);
|
||||
}
|
||||
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t diag_sysfs_type_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned int input;
|
||||
struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
|
||||
|
||||
if (sscanf(buf, "%u", &input) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
diag_hcd->report_type = (unsigned char)input;
|
||||
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t diag_sysfs_rows_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int retval;
|
||||
unsigned int rows;
|
||||
struct syna_tcm_app_info *app_info;
|
||||
struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
if (tcm_hcd->id_info.mode != MODE_APPLICATION ||
|
||||
tcm_hcd->app_status != APP_STATUS_OK) {
|
||||
retval = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
app_info = &tcm_hcd->app_info;
|
||||
rows = le2_to_uint(app_info->num_of_image_rows);
|
||||
|
||||
retval = snprintf(buf, PAGE_SIZE, "%u\n", rows);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t diag_sysfs_cols_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int retval;
|
||||
unsigned int cols;
|
||||
struct syna_tcm_app_info *app_info;
|
||||
struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
if (tcm_hcd->id_info.mode != MODE_APPLICATION ||
|
||||
tcm_hcd->app_status != APP_STATUS_OK) {
|
||||
retval = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
app_info = &tcm_hcd->app_info;
|
||||
cols = le2_to_uint(app_info->num_of_image_cols);
|
||||
|
||||
retval = snprintf(buf, PAGE_SIZE, "%u\n", cols);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t diag_sysfs_hybrid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int retval;
|
||||
unsigned int hybrid;
|
||||
struct syna_tcm_app_info *app_info;
|
||||
struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
if (tcm_hcd->id_info.mode != MODE_APPLICATION ||
|
||||
tcm_hcd->app_status != APP_STATUS_OK) {
|
||||
retval = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
app_info = &tcm_hcd->app_info;
|
||||
hybrid = le2_to_uint(app_info->has_hybrid_data);
|
||||
|
||||
retval = snprintf(buf, PAGE_SIZE, "%u\n", hybrid);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t diag_sysfs_buttons_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int retval;
|
||||
unsigned int buttons;
|
||||
struct syna_tcm_app_info *app_info;
|
||||
struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
if (tcm_hcd->id_info.mode != MODE_APPLICATION ||
|
||||
tcm_hcd->app_status != APP_STATUS_OK) {
|
||||
retval = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
app_info = &tcm_hcd->app_info;
|
||||
buttons = le2_to_uint(app_info->num_of_buttons);
|
||||
|
||||
retval = snprintf(buf, PAGE_SIZE, "%u\n", buttons);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t diag_sysfs_data_show(struct file *data_file,
|
||||
struct kobject *kobj, struct bin_attribute *attributes,
|
||||
char *buf, loff_t pos, size_t count)
|
||||
{
|
||||
int retval;
|
||||
unsigned int readlen;
|
||||
struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
retval = 0;
|
||||
|
||||
if (diag_hcd->state == PING) {
|
||||
LOCK_BUFFER(diag_hcd->ping);
|
||||
|
||||
if (diag_hcd->ping.data_length == 0) {
|
||||
readlen = 0;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
readlen = MIN(count, diag_hcd->ping.data_length - pos);
|
||||
|
||||
if (diag_hcd->ping.data_length) {
|
||||
retval = secure_memcpy(buf,
|
||||
count,
|
||||
&diag_hcd->ping.buf[pos],
|
||||
diag_hcd->ping.buf_size - pos,
|
||||
readlen);
|
||||
}
|
||||
|
||||
UNLOCK_BUFFER(diag_hcd->ping);
|
||||
} else {
|
||||
LOCK_BUFFER(diag_hcd->pong);
|
||||
|
||||
if (diag_hcd->pong.data_length == 0) {
|
||||
readlen = 0;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
readlen = MIN(count, diag_hcd->pong.data_length - pos);
|
||||
|
||||
if (diag_hcd->pong.data_length) {
|
||||
retval = secure_memcpy(buf,
|
||||
count,
|
||||
&diag_hcd->pong.buf[pos],
|
||||
diag_hcd->pong.buf_size - pos,
|
||||
readlen);
|
||||
}
|
||||
|
||||
UNLOCK_BUFFER(diag_hcd->pong);
|
||||
}
|
||||
|
||||
exit:
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy report data\n");
|
||||
} else {
|
||||
retval = readlen;
|
||||
}
|
||||
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void diag_report(void)
|
||||
{
|
||||
int retval;
|
||||
static enum pingpong_state state = PING;
|
||||
struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
|
||||
|
||||
if (state == PING) {
|
||||
LOCK_BUFFER(diag_hcd->ping);
|
||||
|
||||
retval = syna_tcm_alloc_mem(tcm_hcd,
|
||||
&diag_hcd->ping,
|
||||
tcm_hcd->report.buffer.data_length);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for diag_hcd->ping.buf\n");
|
||||
UNLOCK_BUFFER(diag_hcd->ping);
|
||||
return;
|
||||
}
|
||||
|
||||
retval = secure_memcpy(diag_hcd->ping.buf,
|
||||
diag_hcd->ping.buf_size,
|
||||
tcm_hcd->report.buffer.buf,
|
||||
tcm_hcd->report.buffer.buf_size,
|
||||
tcm_hcd->report.buffer.data_length);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy report data\n");
|
||||
UNLOCK_BUFFER(diag_hcd->ping);
|
||||
return;
|
||||
}
|
||||
|
||||
diag_hcd->ping.data_length = tcm_hcd->report.buffer.data_length;
|
||||
|
||||
UNLOCK_BUFFER(diag_hcd->ping);
|
||||
|
||||
diag_hcd->state = state;
|
||||
state = PONG;
|
||||
} else {
|
||||
LOCK_BUFFER(diag_hcd->pong);
|
||||
|
||||
retval = syna_tcm_alloc_mem(tcm_hcd,
|
||||
&diag_hcd->pong,
|
||||
tcm_hcd->report.buffer.data_length);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for diag_hcd->pong.buf\n");
|
||||
UNLOCK_BUFFER(diag_hcd->pong);
|
||||
return;
|
||||
}
|
||||
|
||||
retval = secure_memcpy(diag_hcd->pong.buf,
|
||||
diag_hcd->pong.buf_size,
|
||||
tcm_hcd->report.buffer.buf,
|
||||
tcm_hcd->report.buffer.buf_size,
|
||||
tcm_hcd->report.buffer.data_length);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy report data\n");
|
||||
UNLOCK_BUFFER(diag_hcd->pong);
|
||||
return;
|
||||
}
|
||||
|
||||
diag_hcd->pong.data_length = tcm_hcd->report.buffer.data_length;
|
||||
|
||||
UNLOCK_BUFFER(diag_hcd->pong);
|
||||
|
||||
diag_hcd->state = state;
|
||||
state = PING;
|
||||
}
|
||||
|
||||
if (diag_hcd->pid)
|
||||
send_sig_info(SIGIO, &diag_hcd->sigio, diag_hcd->task);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int diag_init(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
int retval;
|
||||
int idx;
|
||||
|
||||
diag_hcd = kzalloc(sizeof(*diag_hcd), GFP_KERNEL);
|
||||
if (!diag_hcd) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for diag_hcd\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
diag_hcd->tcm_hcd = tcm_hcd;
|
||||
diag_hcd->state = PING;
|
||||
|
||||
INIT_BUFFER(diag_hcd->ping, false);
|
||||
INIT_BUFFER(diag_hcd->pong, false);
|
||||
|
||||
memset(&diag_hcd->sigio, 0x00, sizeof(diag_hcd->sigio));
|
||||
diag_hcd->sigio.si_signo = SIGIO;
|
||||
diag_hcd->sigio.si_code = SI_USER;
|
||||
|
||||
diag_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME,
|
||||
tcm_hcd->sysfs_dir);
|
||||
if (!diag_hcd->sysfs_dir) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to create sysfs directory\n");
|
||||
retval = -EINVAL;
|
||||
goto err_sysfs_create_dir;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) {
|
||||
retval = sysfs_create_file(diag_hcd->sysfs_dir,
|
||||
&(*attrs[idx]).attr);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to create sysfs file\n");
|
||||
goto err_sysfs_create_file;
|
||||
}
|
||||
}
|
||||
|
||||
retval = sysfs_create_bin_file(diag_hcd->sysfs_dir, &bin_attr);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to create sysfs bin file\n");
|
||||
goto err_sysfs_create_bin_file;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_sysfs_create_bin_file:
|
||||
err_sysfs_create_file:
|
||||
for (idx--; idx >= 0; idx--)
|
||||
sysfs_remove_file(diag_hcd->sysfs_dir, &(*attrs[idx]).attr);
|
||||
|
||||
kobject_put(diag_hcd->sysfs_dir);
|
||||
|
||||
err_sysfs_create_dir:
|
||||
RELEASE_BUFFER(diag_hcd->pong);
|
||||
RELEASE_BUFFER(diag_hcd->ping);
|
||||
|
||||
kfree(diag_hcd);
|
||||
diag_hcd = NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int diag_remove(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
int idx;
|
||||
|
||||
if (!diag_hcd)
|
||||
goto exit;
|
||||
|
||||
sysfs_remove_bin_file(diag_hcd->sysfs_dir, &bin_attr);
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(attrs); idx++)
|
||||
sysfs_remove_file(diag_hcd->sysfs_dir, &(*attrs[idx]).attr);
|
||||
|
||||
kobject_put(diag_hcd->sysfs_dir);
|
||||
|
||||
RELEASE_BUFFER(diag_hcd->pong);
|
||||
RELEASE_BUFFER(diag_hcd->ping);
|
||||
|
||||
kfree(diag_hcd);
|
||||
diag_hcd = NULL;
|
||||
|
||||
exit:
|
||||
complete(&diag_remove_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diag_syncbox(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
if (!diag_hcd)
|
||||
return 0;
|
||||
|
||||
if (tcm_hcd->report.id == diag_hcd->report_type)
|
||||
diag_report();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diag_reset(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (!diag_hcd) {
|
||||
retval = diag_init(tcm_hcd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct syna_tcm_module_cb diag_module = {
|
||||
.type = TCM_DIAGNOSTICS,
|
||||
.init = diag_init,
|
||||
.remove = diag_remove,
|
||||
.syncbox = diag_syncbox,
|
||||
.asyncbox = NULL,
|
||||
.reset = diag_reset,
|
||||
.suspend = NULL,
|
||||
.resume = NULL,
|
||||
.early_suspend = NULL,
|
||||
};
|
||||
|
||||
static int __init diag_module_init(void)
|
||||
{
|
||||
return syna_tcm_add_module(&diag_module, true);
|
||||
}
|
||||
|
||||
static void __exit diag_module_exit(void)
|
||||
{
|
||||
syna_tcm_add_module(&diag_module, false);
|
||||
|
||||
wait_for_completion(&diag_remove_complete);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(diag_module_init);
|
||||
module_exit(diag_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Synaptics, Inc.");
|
||||
MODULE_DESCRIPTION("Synaptics TCM Diagnostics Module");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
510
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_i2c.c
Normal file
510
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_i2c.c
Normal file
|
|
@ -0,0 +1,510 @@
|
|||
/*
|
||||
* Synaptics TCM touchscreen driver
|
||||
*
|
||||
* Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2017-2018 Scott Lin <scott.lin@tw.synaptics.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
|
||||
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
|
||||
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
|
||||
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
|
||||
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
|
||||
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
|
||||
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
|
||||
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
|
||||
* DOLLARS.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include "synaptics_tcm_core.h"
|
||||
|
||||
#define XFER_ATTEMPTS 10
|
||||
|
||||
static unsigned char *buf;
|
||||
|
||||
static unsigned int buf_size;
|
||||
|
||||
static struct syna_tcm_bus_io bus_io;
|
||||
|
||||
static struct syna_tcm_hw_interface hw_if;
|
||||
|
||||
static struct platform_device *syna_tcm_i2c_device;
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int parse_dt(struct device *dev, struct syna_tcm_board_data *bdata)
|
||||
{
|
||||
int retval;
|
||||
u32 value;
|
||||
struct property *prop;
|
||||
struct device_node *np = dev->of_node;
|
||||
const char *name;
|
||||
|
||||
prop = of_find_property(np, "synaptics,irq-gpio", NULL);
|
||||
if (prop && prop->length) {
|
||||
bdata->irq_gpio = of_get_named_gpio_flags(np,
|
||||
"synaptics,irq-gpio", 0,
|
||||
(enum of_gpio_flags *)&bdata->irq_flags);
|
||||
} else {
|
||||
bdata->irq_gpio = -1;
|
||||
}
|
||||
|
||||
retval = of_property_read_u32(np, "synaptics,irq-on-state", &value);
|
||||
if (retval < 0)
|
||||
bdata->irq_on_state = 0;
|
||||
else
|
||||
bdata->irq_on_state = value;
|
||||
|
||||
retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name);
|
||||
if (retval < 0)
|
||||
bdata->pwr_reg_name = NULL;
|
||||
else
|
||||
bdata->pwr_reg_name = name;
|
||||
|
||||
retval = of_property_read_string(np, "synaptics,bus-reg-name", &name);
|
||||
if (retval < 0)
|
||||
bdata->bus_reg_name = NULL;
|
||||
else
|
||||
bdata->bus_reg_name = name;
|
||||
|
||||
prop = of_find_property(np, "synaptics,power-gpio", NULL);
|
||||
if (prop && prop->length) {
|
||||
bdata->power_gpio = of_get_named_gpio_flags(np,
|
||||
"synaptics,power-gpio", 0, NULL);
|
||||
} else {
|
||||
bdata->power_gpio = -1;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,power-on-state", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,power-on-state",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Failed to read synaptics,power-on-state property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->power_on_state = value;
|
||||
}
|
||||
} else {
|
||||
bdata->power_on_state = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,power-delay-ms", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,power-delay-ms",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Failed to read synaptics,power-delay-ms property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->power_delay_ms = value;
|
||||
}
|
||||
} else {
|
||||
bdata->power_delay_ms = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,reset-gpio", NULL);
|
||||
if (prop && prop->length) {
|
||||
bdata->reset_gpio = of_get_named_gpio_flags(np,
|
||||
"synaptics,reset-gpio", 0, NULL);
|
||||
} else {
|
||||
bdata->reset_gpio = -1;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,reset-on-state", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,reset-on-state",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Failed to read synaptics,reset-on-state property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->reset_on_state = value;
|
||||
}
|
||||
} else {
|
||||
bdata->reset_on_state = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,reset-active-ms", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,reset-active-ms",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Failed to read synaptics,reset-active-ms property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->reset_active_ms = value;
|
||||
}
|
||||
} else {
|
||||
bdata->reset_active_ms = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,reset-delay-ms", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,reset-delay-ms",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Unable to read synaptics,reset-delay-ms property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->reset_delay_ms = value;
|
||||
}
|
||||
} else {
|
||||
bdata->reset_delay_ms = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,x-flip", NULL);
|
||||
bdata->x_flip = prop > 0 ? true : false;
|
||||
|
||||
prop = of_find_property(np, "synaptics,y-flip", NULL);
|
||||
bdata->y_flip = prop > 0 ? true : false;
|
||||
|
||||
prop = of_find_property(np, "synaptics,swap-axes", NULL);
|
||||
bdata->swap_axes = prop > 0 ? true : false;
|
||||
|
||||
prop = of_find_property(np, "synaptics,ubl-i2c-addr", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,ubl-i2c-addr",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Unable to read synaptics,ubl-i2c-addr property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->ubl_i2c_addr = value;
|
||||
}
|
||||
} else {
|
||||
bdata->ubl_i2c_addr = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int syna_tcm_i2c_alloc_mem(struct syna_tcm_hcd *tcm_hcd,
|
||||
unsigned int size)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent);
|
||||
|
||||
if (size > buf_size) {
|
||||
if (buf_size)
|
||||
kfree(buf);
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
LOGE(&i2c->dev,
|
||||
"Failed to allocate memory for buf\n");
|
||||
buf_size = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf_size = size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int syna_tcm_i2c_rmi_read(struct syna_tcm_hcd *tcm_hcd,
|
||||
unsigned short addr, unsigned char *data, unsigned int length)
|
||||
{
|
||||
int retval;
|
||||
unsigned char address;
|
||||
unsigned int attempt;
|
||||
struct i2c_msg msg[2];
|
||||
struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent);
|
||||
const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
||||
|
||||
mutex_lock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
address = (unsigned char)addr;
|
||||
|
||||
msg[0].addr = bdata->ubl_i2c_addr;
|
||||
msg[0].flags = 0;
|
||||
msg[0].len = 1;
|
||||
msg[0].buf = &address;
|
||||
|
||||
msg[1].addr = bdata->ubl_i2c_addr;
|
||||
msg[1].flags = I2C_M_RD;
|
||||
msg[1].len = length;
|
||||
msg[1].buf = data;
|
||||
|
||||
for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) {
|
||||
if (i2c_transfer(i2c->adapter, msg, 2) == 2) {
|
||||
retval = length;
|
||||
goto exit;
|
||||
}
|
||||
LOGE(&i2c->dev,
|
||||
"Transfer attempt %d failed\n",
|
||||
attempt + 1);
|
||||
|
||||
if (attempt + 1 == XFER_ATTEMPTS) {
|
||||
retval = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int syna_tcm_i2c_rmi_write(struct syna_tcm_hcd *tcm_hcd,
|
||||
unsigned short addr, unsigned char *data, unsigned int length)
|
||||
{
|
||||
int retval;
|
||||
unsigned int attempt;
|
||||
unsigned int byte_count;
|
||||
struct i2c_msg msg;
|
||||
struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent);
|
||||
const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
||||
|
||||
mutex_lock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
byte_count = length + 1;
|
||||
|
||||
retval = syna_tcm_i2c_alloc_mem(tcm_hcd, byte_count);
|
||||
if (retval < 0) {
|
||||
LOGE(&i2c->dev,
|
||||
"Failed to allocate memory\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
buf[0] = (unsigned char)addr;
|
||||
retval = secure_memcpy(&buf[1],
|
||||
buf_size - 1,
|
||||
data,
|
||||
length,
|
||||
length);
|
||||
if (retval < 0) {
|
||||
LOGE(&i2c->dev,
|
||||
"Failed to copy write data\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
msg.addr = bdata->ubl_i2c_addr;
|
||||
msg.flags = 0;
|
||||
msg.len = byte_count;
|
||||
msg.buf = buf;
|
||||
|
||||
for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) {
|
||||
if (i2c_transfer(i2c->adapter, &msg, 1) == 1) {
|
||||
retval = length;
|
||||
goto exit;
|
||||
}
|
||||
LOGE(&i2c->dev,
|
||||
"Transfer attempt %d failed\n",
|
||||
attempt + 1);
|
||||
|
||||
if (attempt + 1 == XFER_ATTEMPTS) {
|
||||
retval = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int syna_tcm_i2c_read(struct syna_tcm_hcd *tcm_hcd, unsigned char *data,
|
||||
unsigned int length)
|
||||
{
|
||||
int retval;
|
||||
unsigned int attempt;
|
||||
struct i2c_msg msg;
|
||||
struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent);
|
||||
|
||||
mutex_lock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
msg.addr = i2c->addr;
|
||||
msg.flags = I2C_M_RD;
|
||||
msg.len = length;
|
||||
msg.buf = data;
|
||||
|
||||
for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) {
|
||||
if (i2c_transfer(i2c->adapter, &msg, 1) == 1) {
|
||||
retval = length;
|
||||
goto exit;
|
||||
}
|
||||
LOGE(&i2c->dev,
|
||||
"Transfer attempt %d failed\n",
|
||||
attempt + 1);
|
||||
|
||||
if (attempt + 1 == XFER_ATTEMPTS) {
|
||||
retval = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int syna_tcm_i2c_write(struct syna_tcm_hcd *tcm_hcd, unsigned char *data,
|
||||
unsigned int length)
|
||||
{
|
||||
int retval;
|
||||
unsigned int attempt;
|
||||
struct i2c_msg msg;
|
||||
struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent);
|
||||
|
||||
mutex_lock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
msg.addr = i2c->addr;
|
||||
msg.flags = 0;
|
||||
msg.len = length;
|
||||
msg.buf = data;
|
||||
|
||||
for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) {
|
||||
if (i2c_transfer(i2c->adapter, &msg, 1) == 1) {
|
||||
retval = length;
|
||||
goto exit;
|
||||
}
|
||||
LOGE(&i2c->dev,
|
||||
"Transfer attempt %d failed\n",
|
||||
attempt + 1);
|
||||
|
||||
if (attempt + 1 == XFER_ATTEMPTS) {
|
||||
retval = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int syna_tcm_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *dev_id)
|
||||
{
|
||||
int retval;
|
||||
|
||||
syna_tcm_i2c_device = platform_device_alloc(PLATFORM_DRIVER_NAME, 0);
|
||||
if (!syna_tcm_i2c_device) {
|
||||
LOGE(&i2c->dev,
|
||||
"Failed to allocate platform device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
hw_if.bdata = devm_kzalloc(&i2c->dev, sizeof(*hw_if.bdata), GFP_KERNEL);
|
||||
if (!hw_if.bdata) {
|
||||
LOGE(&i2c->dev,
|
||||
"Failed to allocate memory for board data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
parse_dt(&i2c->dev, hw_if.bdata);
|
||||
#else
|
||||
hw_if.bdata = i2c->dev.platform_data;
|
||||
#endif
|
||||
|
||||
bus_io.type = BUS_I2C;
|
||||
bus_io.read = syna_tcm_i2c_read;
|
||||
bus_io.write = syna_tcm_i2c_write;
|
||||
bus_io.rmi_read = syna_tcm_i2c_rmi_read;
|
||||
bus_io.rmi_write = syna_tcm_i2c_rmi_write;
|
||||
|
||||
hw_if.bus_io = &bus_io;
|
||||
|
||||
syna_tcm_i2c_device->dev.parent = &i2c->dev;
|
||||
syna_tcm_i2c_device->dev.platform_data = &hw_if;
|
||||
|
||||
retval = platform_device_add(syna_tcm_i2c_device);
|
||||
if (retval < 0) {
|
||||
LOGE(&i2c->dev,
|
||||
"Failed to add platform device\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int syna_tcm_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
syna_tcm_i2c_device->dev.platform_data = NULL;
|
||||
|
||||
platform_device_unregister(syna_tcm_i2c_device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id syna_tcm_id_table[] = {
|
||||
{I2C_MODULE_NAME, 0},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, syna_tcm_id_table);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id syna_tcm_of_match_table[] = {
|
||||
{
|
||||
.compatible = "synaptics,tcm-i2c",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, syna_tcm_of_match_table);
|
||||
#else
|
||||
#define syna_tcm_of_match_table NULL
|
||||
#endif
|
||||
|
||||
static struct i2c_driver syna_tcm_i2c_driver = {
|
||||
.driver = {
|
||||
.name = I2C_MODULE_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = syna_tcm_of_match_table,
|
||||
},
|
||||
.probe = syna_tcm_i2c_probe,
|
||||
.remove = syna_tcm_i2c_remove,
|
||||
.id_table = syna_tcm_id_table,
|
||||
};
|
||||
|
||||
int syna_tcm_bus_init(void)
|
||||
{
|
||||
return i2c_add_driver(&syna_tcm_i2c_driver);
|
||||
}
|
||||
EXPORT_SYMBOL(syna_tcm_bus_init);
|
||||
|
||||
void syna_tcm_bus_exit(void)
|
||||
{
|
||||
kfree(buf);
|
||||
|
||||
i2c_del_driver(&syna_tcm_i2c_driver);
|
||||
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(syna_tcm_bus_exit);
|
||||
|
||||
MODULE_AUTHOR("Synaptics, Inc.");
|
||||
MODULE_DESCRIPTION("Synaptics TCM I2C Bus Module");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -0,0 +1,900 @@
|
|||
/*
|
||||
* Synaptics TCM touchscreen driver
|
||||
*
|
||||
* Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2017-2018 Scott Lin <scott.lin@tw.synaptics.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
|
||||
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
|
||||
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
|
||||
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
|
||||
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
|
||||
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
|
||||
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
|
||||
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
|
||||
* DOLLARS.
|
||||
*/
|
||||
|
||||
#include "synaptics_tcm_core.h"
|
||||
|
||||
#define SET_UP_RECOVERY_MODE true
|
||||
|
||||
#define ENABLE_SYSFS_INTERFACE true
|
||||
|
||||
#define SYSFS_DIR_NAME "recovery"
|
||||
|
||||
#define IHEX_BUF_SIZE (2048 * 1024)
|
||||
|
||||
#define DATA_BUF_SIZE (512 * 1024)
|
||||
|
||||
#define IHEX_RECORD_SIZE 14
|
||||
|
||||
#define PDT_START_ADDR 0x00e9
|
||||
|
||||
#define UBL_FN_NUMBER 0x35
|
||||
|
||||
#define F35_CHUNK_SIZE 16
|
||||
|
||||
#define F35_CHUNK_SIZE_WORDS 8
|
||||
|
||||
#define F35_ERASE_ALL_WAIT_MS 5000
|
||||
|
||||
#define F35_ERASE_ALL_POLL_MS 100
|
||||
|
||||
#define F35_DATA5_OFFSET 5
|
||||
|
||||
#define F35_CTRL3_OFFSET 18
|
||||
|
||||
#define F35_RESET_COMMAND 16
|
||||
|
||||
#define F35_ERASE_ALL_COMMAND 3
|
||||
|
||||
#define F35_WRITE_CHUNK_COMMAND 2
|
||||
|
||||
#define F35_READ_FLASH_STATUS_COMMAND 1
|
||||
|
||||
struct rmi_pdt_entry {
|
||||
unsigned char query_base_addr;
|
||||
unsigned char command_base_addr;
|
||||
unsigned char control_base_addr;
|
||||
unsigned char data_base_addr;
|
||||
unsigned char intr_src_count:3;
|
||||
unsigned char reserved_1:2;
|
||||
unsigned char fn_version:2;
|
||||
unsigned char reserved_2:1;
|
||||
unsigned char fn_number;
|
||||
} __packed;
|
||||
|
||||
struct rmi_addr {
|
||||
unsigned short query_base;
|
||||
unsigned short command_base;
|
||||
unsigned short control_base;
|
||||
unsigned short data_base;
|
||||
};
|
||||
|
||||
struct recovery_hcd {
|
||||
bool set_up_recovery_mode;
|
||||
unsigned char chunk_buf[F35_CHUNK_SIZE + 3];
|
||||
unsigned char out_buf[3];
|
||||
unsigned char *ihex_buf;
|
||||
unsigned char *data_buf;
|
||||
unsigned int ihex_size;
|
||||
unsigned int ihex_records;
|
||||
unsigned int data_entries;
|
||||
struct kobject *sysfs_dir;
|
||||
struct rmi_addr f35_addr;
|
||||
struct syna_tcm_hcd *tcm_hcd;
|
||||
};
|
||||
|
||||
DECLARE_COMPLETION(recovery_remove_complete);
|
||||
|
||||
static struct recovery_hcd *recovery_hcd;
|
||||
|
||||
static int recovery_do_recovery(void);
|
||||
|
||||
STORE_PROTOTYPE(recovery, recovery)
|
||||
|
||||
static struct device_attribute *attrs[] = {
|
||||
ATTRIFY(recovery),
|
||||
};
|
||||
|
||||
static ssize_t recovery_sysfs_ihex_store(struct file *data_file,
|
||||
struct kobject *kobj, struct bin_attribute *attributes,
|
||||
char *buf, loff_t pos, size_t count);
|
||||
|
||||
static struct bin_attribute bin_attr = {
|
||||
.attr = {
|
||||
.name = "ihex",
|
||||
.mode = (S_IWUSR | S_IWGRP),
|
||||
},
|
||||
.size = 0,
|
||||
.write = recovery_sysfs_ihex_store,
|
||||
};
|
||||
|
||||
static ssize_t recovery_sysfs_recovery_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
int retval;
|
||||
unsigned int input;
|
||||
struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
||||
|
||||
if (sscanf(buf, "%u", &input) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (input == 1)
|
||||
recovery_hcd->set_up_recovery_mode = true;
|
||||
else if (input == 2)
|
||||
recovery_hcd->set_up_recovery_mode = false;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
if (recovery_hcd->ihex_size == 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to get ihex data\n");
|
||||
retval = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (recovery_hcd->ihex_size % IHEX_RECORD_SIZE) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Invalid ihex data\n");
|
||||
retval = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
recovery_hcd->ihex_records = recovery_hcd->ihex_size / IHEX_RECORD_SIZE;
|
||||
|
||||
retval = recovery_do_recovery();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to do recovery\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
retval = count;
|
||||
|
||||
exit:
|
||||
recovery_hcd->set_up_recovery_mode = SET_UP_RECOVERY_MODE;
|
||||
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t recovery_sysfs_ihex_store(struct file *data_file,
|
||||
struct kobject *kobj, struct bin_attribute *attributes,
|
||||
char *buf, loff_t pos, size_t count)
|
||||
{
|
||||
int retval;
|
||||
struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
||||
|
||||
mutex_lock(&tcm_hcd->extif_mutex);
|
||||
|
||||
retval = secure_memcpy(&recovery_hcd->ihex_buf[pos],
|
||||
IHEX_BUF_SIZE - pos,
|
||||
buf,
|
||||
count,
|
||||
count);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy ihex data\n");
|
||||
recovery_hcd->ihex_size = 0;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
recovery_hcd->ihex_size = pos + count;
|
||||
|
||||
retval = count;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->extif_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int recovery_device_reset(void)
|
||||
{
|
||||
int retval;
|
||||
unsigned char command;
|
||||
struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
||||
const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
||||
|
||||
command = F35_RESET_COMMAND;
|
||||
|
||||
retval = syna_tcm_rmi_write(tcm_hcd,
|
||||
recovery_hcd->f35_addr.control_base + F35_CTRL3_OFFSET,
|
||||
&command,
|
||||
sizeof(command));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to write F$35 command\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
msleep(bdata->reset_delay_ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recovery_add_data_entry(unsigned char data)
|
||||
{
|
||||
struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
||||
|
||||
if (recovery_hcd->data_entries >= DATA_BUF_SIZE) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Reached data buffer size limit\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
recovery_hcd->data_buf[recovery_hcd->data_entries++] = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recovery_add_padding(unsigned int *words)
|
||||
{
|
||||
int retval;
|
||||
unsigned int padding;
|
||||
struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
||||
|
||||
padding = (F35_CHUNK_SIZE_WORDS - *words % F35_CHUNK_SIZE_WORDS);
|
||||
padding %= F35_CHUNK_SIZE_WORDS;
|
||||
|
||||
while (padding) {
|
||||
retval = recovery_add_data_entry(0xff);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to add data entry\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = recovery_add_data_entry(0xff);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to add data entry\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
(*words)++;
|
||||
padding--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recovery_parse_ihex(void)
|
||||
{
|
||||
int retval;
|
||||
unsigned char colon;
|
||||
unsigned char *buf;
|
||||
unsigned int addr;
|
||||
unsigned int type;
|
||||
unsigned int addrl;
|
||||
unsigned int addrh;
|
||||
unsigned int data0;
|
||||
unsigned int data1;
|
||||
unsigned int count;
|
||||
unsigned int words;
|
||||
unsigned int offset;
|
||||
unsigned int record;
|
||||
struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
||||
|
||||
words = 0;
|
||||
|
||||
offset = 0;
|
||||
|
||||
buf = recovery_hcd->ihex_buf;
|
||||
|
||||
recovery_hcd->data_entries = 0;
|
||||
|
||||
for (record = 0; record < recovery_hcd->ihex_records; record++) {
|
||||
buf[(record + 1) * IHEX_RECORD_SIZE - 1] = 0x00;
|
||||
retval = sscanf(&buf[record * IHEX_RECORD_SIZE],
|
||||
"%c%02x%02x%02x%02x%02x%02x",
|
||||
&colon,
|
||||
&count,
|
||||
&addrh,
|
||||
&addrl,
|
||||
&type,
|
||||
&data0,
|
||||
&data1);
|
||||
if (retval != 7) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to read ihex record\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (type == 0x00) {
|
||||
if ((words % F35_CHUNK_SIZE_WORDS) == 0) {
|
||||
addr = (addrh << 8) + addrl;
|
||||
addr += offset;
|
||||
addr >>= 4;
|
||||
|
||||
retval = recovery_add_data_entry(addr);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to add data entry\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = recovery_add_data_entry(addr >> 8);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to add data entry\n");
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
retval = recovery_add_data_entry(data0);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to add data entry\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = recovery_add_data_entry(data1);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to add data entry\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
words++;
|
||||
} else if (type == 0x02) {
|
||||
retval = recovery_add_padding(&words);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to add padding\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
offset = (data0 << 8) + data1;
|
||||
offset <<= 4;
|
||||
}
|
||||
}
|
||||
|
||||
retval = recovery_add_padding(&words);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to add padding\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recovery_check_status(void)
|
||||
{
|
||||
int retval;
|
||||
unsigned char status;
|
||||
struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
||||
|
||||
retval = syna_tcm_rmi_read(tcm_hcd,
|
||||
recovery_hcd->f35_addr.data_base,
|
||||
&status,
|
||||
sizeof(status));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to read status\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
status = status & 0x1f;
|
||||
|
||||
if (status != 0x00) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Recovery mode status = 0x%02x\n",
|
||||
status);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recovery_write_flash(void)
|
||||
{
|
||||
int retval;
|
||||
unsigned char *data_ptr;
|
||||
unsigned int chunk_buf_size;
|
||||
unsigned int chunk_data_size;
|
||||
unsigned int entries_written;
|
||||
unsigned int entries_to_write;
|
||||
struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
||||
|
||||
entries_written = 0;
|
||||
|
||||
data_ptr = recovery_hcd->data_buf;
|
||||
|
||||
chunk_buf_size = sizeof(recovery_hcd->chunk_buf);
|
||||
|
||||
chunk_data_size = chunk_buf_size - 1;
|
||||
|
||||
recovery_hcd->chunk_buf[chunk_buf_size - 1] = F35_WRITE_CHUNK_COMMAND;
|
||||
|
||||
while (entries_written < recovery_hcd->data_entries) {
|
||||
entries_to_write = F35_CHUNK_SIZE + 2;
|
||||
|
||||
retval = secure_memcpy(recovery_hcd->chunk_buf,
|
||||
chunk_buf_size - 1,
|
||||
data_ptr,
|
||||
recovery_hcd->data_entries - entries_written,
|
||||
entries_to_write);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy chunk data\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = syna_tcm_rmi_write(tcm_hcd,
|
||||
recovery_hcd->f35_addr.control_base,
|
||||
recovery_hcd->chunk_buf,
|
||||
chunk_buf_size);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to write chunk data\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
data_ptr += entries_to_write;
|
||||
entries_written += entries_to_write;
|
||||
}
|
||||
|
||||
retval = recovery_check_status();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to get no error recovery mode status\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recovery_poll_erase_completion(void)
|
||||
{
|
||||
int retval;
|
||||
unsigned char status;
|
||||
unsigned char command;
|
||||
unsigned char data_base;
|
||||
unsigned int timeout;
|
||||
struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
||||
|
||||
timeout = F35_ERASE_ALL_WAIT_MS;
|
||||
|
||||
data_base = recovery_hcd->f35_addr.data_base;
|
||||
|
||||
do {
|
||||
command = F35_READ_FLASH_STATUS_COMMAND;
|
||||
|
||||
retval = syna_tcm_rmi_write(tcm_hcd,
|
||||
recovery_hcd->f35_addr.command_base,
|
||||
&command,
|
||||
sizeof(command));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to write F$35 command\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
do {
|
||||
retval = syna_tcm_rmi_read(tcm_hcd,
|
||||
recovery_hcd->f35_addr.command_base,
|
||||
&command,
|
||||
sizeof(command));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to read command status\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (command == 0x00)
|
||||
break;
|
||||
|
||||
if (timeout == 0)
|
||||
break;
|
||||
|
||||
msleep(F35_ERASE_ALL_POLL_MS);
|
||||
timeout -= F35_ERASE_ALL_POLL_MS;
|
||||
} while (true);
|
||||
|
||||
if (command != 0 && timeout == 0) {
|
||||
retval = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
retval = syna_tcm_rmi_read(tcm_hcd,
|
||||
data_base + F35_DATA5_OFFSET,
|
||||
&status,
|
||||
sizeof(status));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to read flash status\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
if ((status & 0x01) == 0x00)
|
||||
break;
|
||||
|
||||
if (timeout == 0) {
|
||||
retval = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
msleep(F35_ERASE_ALL_POLL_MS);
|
||||
timeout -= F35_ERASE_ALL_POLL_MS;
|
||||
} while (true);
|
||||
|
||||
retval = 0;
|
||||
|
||||
exit:
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to get erase completion\n");
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int recovery_erase_flash(void)
|
||||
{
|
||||
int retval;
|
||||
unsigned char command;
|
||||
struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
||||
|
||||
command = F35_ERASE_ALL_COMMAND;
|
||||
|
||||
retval = syna_tcm_rmi_write(tcm_hcd,
|
||||
recovery_hcd->f35_addr.control_base + F35_CTRL3_OFFSET,
|
||||
&command,
|
||||
sizeof(command));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to write F$35 command\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (recovery_hcd->f35_addr.command_base) {
|
||||
retval = recovery_poll_erase_completion();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to wait for erase completion\n");
|
||||
return retval;
|
||||
}
|
||||
} else {
|
||||
msleep(F35_ERASE_ALL_WAIT_MS);
|
||||
}
|
||||
|
||||
retval = recovery_check_status();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to get no error recovery mode status\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recovery_set_up_recovery_mode(void)
|
||||
{
|
||||
int retval;
|
||||
struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
||||
const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
||||
|
||||
retval = tcm_hcd->identify(tcm_hcd, true);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to do identification\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (tcm_hcd->id_info.mode == MODE_APPLICATION) {
|
||||
retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to enter bootloader mode\n");
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
retval = tcm_hcd->write_message(tcm_hcd,
|
||||
recovery_hcd->out_buf[0],
|
||||
&recovery_hcd->out_buf[1],
|
||||
2,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to write command %s\n",
|
||||
STR(CMD_REBOOT_TO_ROM_BOOTLOADER));
|
||||
return retval;
|
||||
}
|
||||
|
||||
msleep(bdata->reset_delay_ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recovery_do_recovery(void)
|
||||
{
|
||||
int retval;
|
||||
struct rmi_pdt_entry p_entry;
|
||||
struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd;
|
||||
|
||||
retval = recovery_parse_ihex();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to parse ihex data\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (recovery_hcd->set_up_recovery_mode) {
|
||||
retval = recovery_set_up_recovery_mode();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to set up recovery mode\n");
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
tcm_hcd->update_watchdog(tcm_hcd, false);
|
||||
|
||||
retval = syna_tcm_rmi_read(tcm_hcd,
|
||||
PDT_START_ADDR,
|
||||
(unsigned char *)&p_entry,
|
||||
sizeof(p_entry));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to read PDT entry\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (p_entry.fn_number != UBL_FN_NUMBER) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to find F$35\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
recovery_hcd->f35_addr.query_base = p_entry.query_base_addr;
|
||||
recovery_hcd->f35_addr.command_base = p_entry.command_base_addr;
|
||||
recovery_hcd->f35_addr.control_base = p_entry.control_base_addr;
|
||||
recovery_hcd->f35_addr.data_base = p_entry.data_base_addr;
|
||||
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"Start of recovery\n");
|
||||
|
||||
retval = recovery_erase_flash();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to erase flash\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"Flash erased\n");
|
||||
|
||||
retval = recovery_write_flash();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to write to flash\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"Flash written\n");
|
||||
|
||||
retval = recovery_device_reset();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to do reset\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"End of recovery\n");
|
||||
|
||||
if (recovery_hcd->set_up_recovery_mode)
|
||||
return 0;
|
||||
|
||||
tcm_hcd->update_watchdog(tcm_hcd, true);
|
||||
|
||||
retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to enable interrupt\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = tcm_hcd->identify(tcm_hcd, true);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to do identification\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (tcm_hcd->id_info.mode != MODE_APPLICATION) {
|
||||
retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to run application firmware\n");
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recovery_init(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
int retval;
|
||||
int idx;
|
||||
|
||||
recovery_hcd = kzalloc(sizeof(*recovery_hcd), GFP_KERNEL);
|
||||
if (!recovery_hcd) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for recovery_hcd\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
recovery_hcd->ihex_buf = kzalloc(IHEX_BUF_SIZE, GFP_KERNEL);
|
||||
if (!recovery_hcd->ihex_buf) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for recovery_hcd->ihex_buf\n");
|
||||
goto err_allocate_ihex_buf;
|
||||
}
|
||||
|
||||
recovery_hcd->data_buf = kzalloc(DATA_BUF_SIZE, GFP_KERNEL);
|
||||
if (!recovery_hcd->data_buf) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for recovery_hcd->data_buf\n");
|
||||
goto err_allocate_data_buf;
|
||||
}
|
||||
|
||||
recovery_hcd->tcm_hcd = tcm_hcd;
|
||||
|
||||
recovery_hcd->set_up_recovery_mode = SET_UP_RECOVERY_MODE;
|
||||
|
||||
recovery_hcd->out_buf[0] = CMD_REBOOT_TO_ROM_BOOTLOADER;
|
||||
recovery_hcd->out_buf[1] = 0;
|
||||
recovery_hcd->out_buf[2] = 0;
|
||||
|
||||
if (ENABLE_SYSFS_INTERFACE == false)
|
||||
return 0;
|
||||
|
||||
recovery_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME,
|
||||
tcm_hcd->sysfs_dir);
|
||||
if (!recovery_hcd->sysfs_dir) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to create sysfs directory\n");
|
||||
retval = -EINVAL;
|
||||
goto err_sysfs_create_dir;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) {
|
||||
retval = sysfs_create_file(recovery_hcd->sysfs_dir,
|
||||
&(*attrs[idx]).attr);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to create sysfs file\n");
|
||||
goto err_sysfs_create_file;
|
||||
}
|
||||
}
|
||||
|
||||
retval = sysfs_create_bin_file(recovery_hcd->sysfs_dir, &bin_attr);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to create sysfs bin file\n");
|
||||
goto err_sysfs_create_bin_file;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_sysfs_create_bin_file:
|
||||
err_sysfs_create_file:
|
||||
for (idx--; idx >= 0; idx--)
|
||||
sysfs_remove_file(recovery_hcd->sysfs_dir, &(*attrs[idx]).attr);
|
||||
|
||||
kobject_put(recovery_hcd->sysfs_dir);
|
||||
|
||||
err_sysfs_create_dir:
|
||||
kfree(recovery_hcd->data_buf);
|
||||
err_allocate_data_buf:
|
||||
kfree(recovery_hcd->ihex_buf);
|
||||
err_allocate_ihex_buf:
|
||||
kfree(recovery_hcd);
|
||||
recovery_hcd = NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int recovery_remove(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
int idx;
|
||||
|
||||
if (!recovery_hcd)
|
||||
goto exit;
|
||||
|
||||
if (ENABLE_SYSFS_INTERFACE == true) {
|
||||
sysfs_remove_bin_file(recovery_hcd->sysfs_dir, &bin_attr);
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) {
|
||||
sysfs_remove_file(recovery_hcd->sysfs_dir,
|
||||
&(*attrs[idx]).attr);
|
||||
}
|
||||
|
||||
kobject_put(recovery_hcd->sysfs_dir);
|
||||
}
|
||||
|
||||
kfree(recovery_hcd->data_buf);
|
||||
kfree(recovery_hcd->ihex_buf);
|
||||
kfree(recovery_hcd);
|
||||
recovery_hcd = NULL;
|
||||
|
||||
exit:
|
||||
complete(&recovery_remove_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recovery_reset(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (!recovery_hcd) {
|
||||
retval = recovery_init(tcm_hcd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct syna_tcm_module_cb recovery_module = {
|
||||
.type = TCM_RECOVERY,
|
||||
.init = recovery_init,
|
||||
.remove = recovery_remove,
|
||||
.syncbox = NULL,
|
||||
.asyncbox = NULL,
|
||||
.reset = recovery_reset,
|
||||
.suspend = NULL,
|
||||
.resume = NULL,
|
||||
.early_suspend = NULL,
|
||||
};
|
||||
|
||||
static int __init recovery_module_init(void)
|
||||
{
|
||||
return syna_tcm_add_module(&recovery_module, true);
|
||||
}
|
||||
|
||||
static void __exit recovery_module_exit(void)
|
||||
{
|
||||
syna_tcm_add_module(&recovery_module, false);
|
||||
|
||||
wait_for_completion(&recovery_remove_complete);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(recovery_module_init);
|
||||
module_exit(recovery_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Synaptics, Inc.");
|
||||
MODULE_DESCRIPTION("Synaptics TCM Recovery Module");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
2189
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_reflash.c
Normal file
2189
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_reflash.c
Normal file
File diff suppressed because it is too large
Load diff
684
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_spi.c
Normal file
684
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_spi.c
Normal file
|
|
@ -0,0 +1,684 @@
|
|||
/*
|
||||
* Synaptics TCM touchscreen driver
|
||||
*
|
||||
* Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2017-2018 Scott Lin <scott.lin@tw.synaptics.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
|
||||
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
|
||||
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
|
||||
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
|
||||
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
|
||||
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
|
||||
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
|
||||
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
|
||||
* DOLLARS.
|
||||
*/
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include "synaptics_tcm_core.h"
|
||||
|
||||
static unsigned char *buf;
|
||||
|
||||
static unsigned int buf_size;
|
||||
|
||||
static struct spi_transfer *xfer;
|
||||
|
||||
static struct syna_tcm_bus_io bus_io;
|
||||
|
||||
static struct syna_tcm_hw_interface hw_if;
|
||||
|
||||
static struct platform_device *syna_tcm_spi_device;
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int parse_dt(struct device *dev, struct syna_tcm_board_data *bdata)
|
||||
{
|
||||
int retval;
|
||||
u32 value;
|
||||
struct property *prop;
|
||||
struct device_node *np = dev->of_node;
|
||||
const char *name;
|
||||
|
||||
prop = of_find_property(np, "synaptics,irq-gpio", NULL);
|
||||
if (prop && prop->length) {
|
||||
bdata->irq_gpio = of_get_named_gpio_flags(np,
|
||||
"synaptics,irq-gpio", 0,
|
||||
(enum of_gpio_flags *)&bdata->irq_flags);
|
||||
} else {
|
||||
bdata->irq_gpio = -1;
|
||||
}
|
||||
|
||||
retval = of_property_read_u32(np, "synaptics,irq-on-state", &value);
|
||||
if (retval < 0)
|
||||
bdata->irq_on_state = 0;
|
||||
else
|
||||
bdata->irq_on_state = value;
|
||||
|
||||
retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name);
|
||||
if (retval < 0)
|
||||
bdata->pwr_reg_name = NULL;
|
||||
else
|
||||
bdata->pwr_reg_name = name;
|
||||
|
||||
retval = of_property_read_string(np, "synaptics,bus-reg-name", &name);
|
||||
if (retval < 0)
|
||||
bdata->bus_reg_name = NULL;
|
||||
else
|
||||
bdata->bus_reg_name = name;
|
||||
|
||||
prop = of_find_property(np, "synaptics,power-gpio", NULL);
|
||||
if (prop && prop->length) {
|
||||
bdata->power_gpio = of_get_named_gpio_flags(np,
|
||||
"synaptics,power-gpio", 0, NULL);
|
||||
} else {
|
||||
bdata->power_gpio = -1;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,power-on-state", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,power-on-state",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Failed to read synaptics,power-on-state property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->power_on_state = value;
|
||||
}
|
||||
} else {
|
||||
bdata->power_on_state = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,power-delay-ms", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,power-delay-ms",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Failed to read synaptics,power-delay-ms property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->power_delay_ms = value;
|
||||
}
|
||||
} else {
|
||||
bdata->power_delay_ms = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,reset-gpio", NULL);
|
||||
if (prop && prop->length) {
|
||||
bdata->reset_gpio = of_get_named_gpio_flags(np,
|
||||
"synaptics,reset-gpio", 0, NULL);
|
||||
} else {
|
||||
bdata->reset_gpio = -1;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,reset-on-state", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,reset-on-state",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Failed to read synaptics,reset-on-state property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->reset_on_state = value;
|
||||
}
|
||||
} else {
|
||||
bdata->reset_on_state = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,reset-active-ms", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,reset-active-ms",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Failed to read synaptics,reset-active-ms property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->reset_active_ms = value;
|
||||
}
|
||||
} else {
|
||||
bdata->reset_active_ms = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,reset-delay-ms", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,reset-delay-ms",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Unable to read synaptics,reset-delay-ms property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->reset_delay_ms = value;
|
||||
}
|
||||
} else {
|
||||
bdata->reset_delay_ms = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,x-flip", NULL);
|
||||
bdata->x_flip = prop > 0 ? true : false;
|
||||
|
||||
prop = of_find_property(np, "synaptics,y-flip", NULL);
|
||||
bdata->y_flip = prop > 0 ? true : false;
|
||||
|
||||
prop = of_find_property(np, "synaptics,swap-axes", NULL);
|
||||
bdata->swap_axes = prop > 0 ? true : false;
|
||||
|
||||
prop = of_find_property(np, "synaptics,byte-delay-us", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,byte-delay-us",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Unable to read synaptics,byte-delay-us property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->byte_delay_us = value;
|
||||
}
|
||||
} else {
|
||||
bdata->byte_delay_us = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,block-delay-us", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,block-delay-us",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Unable to read synaptics,block-delay-us property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->block_delay_us = value;
|
||||
}
|
||||
} else {
|
||||
bdata->block_delay_us = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,spi-mode", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,spi-mode",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Unable to read synaptics,spi-mode property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->spi_mode = value;
|
||||
}
|
||||
} else {
|
||||
bdata->spi_mode = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,ubl-max-freq", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,ubl-max-freq",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Unable to read synaptics,ubl-max-freq property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->ubl_max_freq = value;
|
||||
}
|
||||
} else {
|
||||
bdata->ubl_max_freq = 0;
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "synaptics,ubl-byte-delay-us", NULL);
|
||||
if (prop && prop->length) {
|
||||
retval = of_property_read_u32(np, "synaptics,ubl-byte-delay-us",
|
||||
&value);
|
||||
if (retval < 0) {
|
||||
LOGE(dev,
|
||||
"Unable to read synaptics,ubl-byte-delay-us property\n");
|
||||
return retval;
|
||||
} else {
|
||||
bdata->ubl_byte_delay_us = value;
|
||||
}
|
||||
} else {
|
||||
bdata->ubl_byte_delay_us = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int syna_tcm_spi_alloc_mem(struct syna_tcm_hcd *tcm_hcd,
|
||||
unsigned int count, unsigned int size)
|
||||
{
|
||||
static unsigned int xfer_count;
|
||||
struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent);
|
||||
|
||||
if (count > xfer_count) {
|
||||
kfree(xfer);
|
||||
xfer = kcalloc(count, sizeof(*xfer), GFP_KERNEL);
|
||||
if (!xfer) {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to allocate memory for xfer\n");
|
||||
xfer_count = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
xfer_count = count;
|
||||
} else {
|
||||
memset(xfer, 0, count * sizeof(*xfer));
|
||||
}
|
||||
|
||||
if (size > buf_size) {
|
||||
if (buf_size)
|
||||
kfree(buf);
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to allocate memory for buf\n");
|
||||
buf_size = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
buf_size = size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int syna_tcm_spi_rmi_read(struct syna_tcm_hcd *tcm_hcd,
|
||||
unsigned short addr, unsigned char *data, unsigned int length)
|
||||
{
|
||||
int retval;
|
||||
unsigned int idx;
|
||||
unsigned int mode;
|
||||
unsigned int byte_count;
|
||||
struct spi_message msg;
|
||||
struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent);
|
||||
const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
||||
|
||||
mutex_lock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
byte_count = length + 2;
|
||||
|
||||
if (bdata->ubl_byte_delay_us == 0)
|
||||
retval = syna_tcm_spi_alloc_mem(tcm_hcd, 2, 2);
|
||||
else
|
||||
retval = syna_tcm_spi_alloc_mem(tcm_hcd, byte_count, 2);
|
||||
if (retval < 0) {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to allocate memory\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
buf[0] = (unsigned char)(addr >> 8) | 0x80;
|
||||
buf[1] = (unsigned char)addr;
|
||||
|
||||
if (bdata->ubl_byte_delay_us == 0) {
|
||||
xfer[0].len = 2;
|
||||
xfer[0].tx_buf = buf;
|
||||
xfer[0].speed_hz = bdata->ubl_max_freq;
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
xfer[1].len = length;
|
||||
xfer[1].rx_buf = data;
|
||||
if (bdata->block_delay_us)
|
||||
xfer[1].delay_usecs = bdata->block_delay_us;
|
||||
xfer[1].speed_hz = bdata->ubl_max_freq;
|
||||
spi_message_add_tail(&xfer[1], &msg);
|
||||
} else {
|
||||
for (idx = 0; idx < byte_count; idx++) {
|
||||
xfer[idx].len = 1;
|
||||
if (idx < 2)
|
||||
xfer[idx].tx_buf = &buf[idx];
|
||||
else
|
||||
xfer[idx].rx_buf = &data[idx - 2];
|
||||
xfer[idx].delay_usecs = bdata->ubl_byte_delay_us;
|
||||
if (bdata->block_delay_us && (idx == byte_count - 1))
|
||||
xfer[idx].delay_usecs = bdata->block_delay_us;
|
||||
xfer[idx].speed_hz = bdata->ubl_max_freq;
|
||||
spi_message_add_tail(&xfer[idx], &msg);
|
||||
}
|
||||
}
|
||||
|
||||
mode = spi->mode;
|
||||
spi->mode = SPI_MODE_3;
|
||||
|
||||
retval = spi_sync(spi, &msg);
|
||||
if (retval == 0) {
|
||||
retval = length;
|
||||
} else {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to complete SPI transfer, error = %d\n",
|
||||
retval);
|
||||
}
|
||||
|
||||
spi->mode = mode;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int syna_tcm_spi_rmi_write(struct syna_tcm_hcd *tcm_hcd,
|
||||
unsigned short addr, unsigned char *data, unsigned int length)
|
||||
{
|
||||
int retval;
|
||||
unsigned int mode;
|
||||
unsigned int byte_count;
|
||||
struct spi_message msg;
|
||||
struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent);
|
||||
const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
||||
|
||||
mutex_lock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
byte_count = length + 2;
|
||||
|
||||
retval = syna_tcm_spi_alloc_mem(tcm_hcd, 1, byte_count);
|
||||
if (retval < 0) {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to allocate memory\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
buf[0] = (unsigned char)(addr >> 8) & ~0x80;
|
||||
buf[1] = (unsigned char)addr;
|
||||
retval = secure_memcpy(&buf[2],
|
||||
buf_size - 2,
|
||||
data,
|
||||
length,
|
||||
length);
|
||||
if (retval < 0) {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to copy write data\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
xfer[0].len = byte_count;
|
||||
xfer[0].tx_buf = buf;
|
||||
if (bdata->block_delay_us)
|
||||
xfer[0].delay_usecs = bdata->block_delay_us;
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
|
||||
mode = spi->mode;
|
||||
spi->mode = SPI_MODE_3;
|
||||
|
||||
retval = spi_sync(spi, &msg);
|
||||
if (retval == 0) {
|
||||
retval = length;
|
||||
} else {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to complete SPI transfer, error = %d\n",
|
||||
retval);
|
||||
}
|
||||
|
||||
spi->mode = mode;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int syna_tcm_spi_read(struct syna_tcm_hcd *tcm_hcd, unsigned char *data,
|
||||
unsigned int length)
|
||||
{
|
||||
int retval;
|
||||
unsigned int idx;
|
||||
struct spi_message msg;
|
||||
struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent);
|
||||
const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
||||
|
||||
mutex_lock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
if (bdata->byte_delay_us == 0)
|
||||
retval = syna_tcm_spi_alloc_mem(tcm_hcd, 1, length);
|
||||
else
|
||||
retval = syna_tcm_spi_alloc_mem(tcm_hcd, length, 1);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (bdata->byte_delay_us == 0) {
|
||||
memset(buf, 0xff, length);
|
||||
xfer[0].len = length;
|
||||
xfer[0].tx_buf = buf;
|
||||
xfer[0].rx_buf = data;
|
||||
if (bdata->block_delay_us)
|
||||
xfer[0].delay_usecs = bdata->block_delay_us;
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
} else {
|
||||
buf[0] = 0xff;
|
||||
for (idx = 0; idx < length; idx++) {
|
||||
xfer[idx].len = 1;
|
||||
xfer[idx].tx_buf = buf;
|
||||
xfer[idx].rx_buf = &data[idx];
|
||||
xfer[idx].delay_usecs = bdata->byte_delay_us;
|
||||
if (bdata->block_delay_us && (idx == length - 1))
|
||||
xfer[idx].delay_usecs = bdata->block_delay_us;
|
||||
spi_message_add_tail(&xfer[idx], &msg);
|
||||
}
|
||||
}
|
||||
|
||||
retval = spi_sync(spi, &msg);
|
||||
if (retval == 0) {
|
||||
retval = length;
|
||||
} else {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to complete SPI transfer, error = %d\n",
|
||||
retval);
|
||||
}
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int syna_tcm_spi_write(struct syna_tcm_hcd *tcm_hcd, unsigned char *data,
|
||||
unsigned int length)
|
||||
{
|
||||
int retval;
|
||||
unsigned int idx;
|
||||
struct spi_message msg;
|
||||
struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent);
|
||||
const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata;
|
||||
|
||||
mutex_lock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
if (bdata->byte_delay_us == 0)
|
||||
retval = syna_tcm_spi_alloc_mem(tcm_hcd, 1, 0);
|
||||
else
|
||||
retval = syna_tcm_spi_alloc_mem(tcm_hcd, length, 0);
|
||||
if (retval < 0) {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to allocate memory\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (bdata->byte_delay_us == 0) {
|
||||
xfer[0].len = length;
|
||||
xfer[0].tx_buf = data;
|
||||
if (bdata->block_delay_us)
|
||||
xfer[0].delay_usecs = bdata->block_delay_us;
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
} else {
|
||||
for (idx = 0; idx < length; idx++) {
|
||||
xfer[idx].len = 1;
|
||||
xfer[idx].tx_buf = &data[idx];
|
||||
xfer[idx].delay_usecs = bdata->byte_delay_us;
|
||||
if (bdata->block_delay_us && (idx == length - 1))
|
||||
xfer[idx].delay_usecs = bdata->block_delay_us;
|
||||
spi_message_add_tail(&xfer[idx], &msg);
|
||||
}
|
||||
}
|
||||
|
||||
retval = spi_sync(spi, &msg);
|
||||
if (retval == 0) {
|
||||
retval = length;
|
||||
} else {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to complete SPI transfer, error = %d\n",
|
||||
retval);
|
||||
}
|
||||
|
||||
exit:
|
||||
mutex_unlock(&tcm_hcd->io_ctrl_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int syna_tcm_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
|
||||
LOGE(&spi->dev,
|
||||
"Full duplex not supported by host\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
syna_tcm_spi_device = platform_device_alloc(PLATFORM_DRIVER_NAME, 0);
|
||||
if (!syna_tcm_spi_device) {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to allocate platform device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
hw_if.bdata = devm_kzalloc(&spi->dev, sizeof(*hw_if.bdata), GFP_KERNEL);
|
||||
if (!hw_if.bdata) {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to allocate memory for board data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
parse_dt(&spi->dev, hw_if.bdata);
|
||||
#else
|
||||
hw_if.bdata = spi->dev.platform_data;
|
||||
#endif
|
||||
|
||||
switch (hw_if.bdata->spi_mode) {
|
||||
case 0:
|
||||
spi->mode = SPI_MODE_0;
|
||||
break;
|
||||
case 1:
|
||||
spi->mode = SPI_MODE_1;
|
||||
break;
|
||||
case 2:
|
||||
spi->mode = SPI_MODE_2;
|
||||
break;
|
||||
case 3:
|
||||
spi->mode = SPI_MODE_3;
|
||||
break;
|
||||
}
|
||||
|
||||
bus_io.type = BUS_SPI;
|
||||
bus_io.read = syna_tcm_spi_read;
|
||||
bus_io.write = syna_tcm_spi_write;
|
||||
bus_io.rmi_read = syna_tcm_spi_rmi_read;
|
||||
bus_io.rmi_write = syna_tcm_spi_rmi_write;
|
||||
|
||||
hw_if.bus_io = &bus_io;
|
||||
|
||||
spi->bits_per_word = 8;
|
||||
|
||||
retval = spi_setup(spi);
|
||||
if (retval < 0) {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to set up SPI protocol driver\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
syna_tcm_spi_device->dev.parent = &spi->dev;
|
||||
syna_tcm_spi_device->dev.platform_data = &hw_if;
|
||||
|
||||
retval = platform_device_add(syna_tcm_spi_device);
|
||||
if (retval < 0) {
|
||||
LOGE(&spi->dev,
|
||||
"Failed to add platform device\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int syna_tcm_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
syna_tcm_spi_device->dev.platform_data = NULL;
|
||||
|
||||
platform_device_unregister(syna_tcm_spi_device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id syna_tcm_id_table[] = {
|
||||
{SPI_MODULE_NAME, 0},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, syna_tcm_id_table);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id syna_tcm_of_match_table[] = {
|
||||
{
|
||||
.compatible = "synaptics,tcm-spi",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, syna_tcm_of_match_table);
|
||||
#else
|
||||
#define syna_tcm_of_match_table NULL
|
||||
#endif
|
||||
|
||||
static struct spi_driver syna_tcm_spi_driver = {
|
||||
.driver = {
|
||||
.name = SPI_MODULE_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = syna_tcm_of_match_table,
|
||||
},
|
||||
.probe = syna_tcm_spi_probe,
|
||||
.remove = syna_tcm_spi_remove,
|
||||
.id_table = syna_tcm_id_table,
|
||||
};
|
||||
|
||||
int syna_tcm_bus_init(void)
|
||||
{
|
||||
return spi_register_driver(&syna_tcm_spi_driver);
|
||||
}
|
||||
EXPORT_SYMBOL(syna_tcm_bus_init);
|
||||
|
||||
void syna_tcm_bus_exit(void)
|
||||
{
|
||||
kfree(buf);
|
||||
|
||||
kfree(xfer);
|
||||
|
||||
spi_unregister_driver(&syna_tcm_spi_driver);
|
||||
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(syna_tcm_bus_exit);
|
||||
|
||||
MODULE_AUTHOR("Synaptics, Inc.");
|
||||
MODULE_DESCRIPTION("Synaptics TCM SPI Bus Module");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
1958
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_testing.c
Normal file
1958
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_testing.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* Synaptics TCM touchscreen driver
|
||||
*
|
||||
* Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2017-2018 Scott Lin <scott.lin@tw.synaptics.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
|
||||
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
|
||||
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
|
||||
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
|
||||
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
|
||||
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
|
||||
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
|
||||
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
|
||||
* DOLLARS.
|
||||
*/
|
||||
|
||||
#ifndef _SYNAPTICS_TCM_TESTING_H_
|
||||
#define _SYNAPTICS_TCM_TESTING_H_
|
||||
|
||||
static const unsigned short drt_hi_limits[32][32] = {
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
{1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700},
|
||||
};
|
||||
|
||||
static const unsigned short drt_lo_limits[32][32] = {
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
{300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300},
|
||||
};
|
||||
|
||||
static const unsigned short noise_limits[32][32] = {
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
{28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28},
|
||||
};
|
||||
|
||||
static const short pt11_hi_limits[32][32] = {
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||||
};
|
||||
|
||||
static const short pt11_lo_limits[32][32] = {
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
{-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8},
|
||||
};
|
||||
|
||||
static const short pt12_limits[32][32] = {
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
{-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10},
|
||||
};
|
||||
|
||||
static const short pt13_limits[32][32] = {
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
{-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100},
|
||||
};
|
||||
|
||||
static const unsigned char lockdown_limits[] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
};
|
||||
|
||||
static const unsigned char reset_open_limit = 0x13;
|
||||
|
||||
#endif
|
||||
1271
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_touch.c
Normal file
1271
drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_touch.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,975 @@
|
|||
/*
|
||||
* Synaptics TCM touchscreen driver
|
||||
*
|
||||
* Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2017-2018 Scott Lin <scott.lin@tw.synaptics.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
|
||||
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
|
||||
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
|
||||
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
|
||||
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
|
||||
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
|
||||
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
|
||||
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
|
||||
* DOLLARS.
|
||||
*/
|
||||
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/firmware.h>
|
||||
#include "synaptics_tcm_core.h"
|
||||
|
||||
#define FW_IMAGE_NAME "synaptics/hdl_firmware.img"
|
||||
|
||||
#define BOOT_CONFIG_ID "BOOT_CONFIG"
|
||||
|
||||
#define F35_APP_CODE_ID "F35_APP_CODE"
|
||||
|
||||
#define APP_CONFIG_ID "APP_CONFIG"
|
||||
|
||||
#define DISP_CONFIG_ID "DISPLAY"
|
||||
|
||||
#define IMAGE_FILE_MAGIC_VALUE 0x4818472b
|
||||
|
||||
#define FLASH_AREA_MAGIC_VALUE 0x7c05e516
|
||||
|
||||
#define PDT_START_ADDR 0x00e9
|
||||
|
||||
#define PDT_END_ADDR 0x00ee
|
||||
|
||||
#define UBL_FN_NUMBER 0x35
|
||||
|
||||
#define F35_CTRL3_OFFSET 18
|
||||
|
||||
#define F35_CTRL7_OFFSET 22
|
||||
|
||||
#define F35_WRITE_FW_TO_PMEM_COMMAND 4
|
||||
|
||||
#define DOWNLOAD_RETRY_COUNT 10
|
||||
|
||||
enum f35_error_code {
|
||||
SUCCESS = 0,
|
||||
UNKNOWN_FLASH_PRESENT,
|
||||
MAGIC_NUMBER_NOT_PRESENT,
|
||||
INVALID_BLOCK_NUMBER,
|
||||
BLOCK_NOT_ERASED,
|
||||
NO_FLASH_PRESENT,
|
||||
CHECKSUM_FAILURE,
|
||||
WRITE_FAILURE,
|
||||
INVALID_COMMAND,
|
||||
IN_DEBUG_MODE,
|
||||
INVALID_HEADER,
|
||||
REQUESTING_FIRMWARE,
|
||||
INVALID_CONFIGURATION,
|
||||
DISABLE_BLOCK_PROTECT_FAILURE,
|
||||
};
|
||||
|
||||
enum config_download {
|
||||
HDL_INVALID = 0,
|
||||
HDL_TOUCH_CONFIG_TO_PMEM,
|
||||
HDL_DISPLAY_CONFIG_TO_PMEM,
|
||||
HDL_DISPLAY_CONFIG_TO_RAM,
|
||||
};
|
||||
|
||||
struct area_descriptor {
|
||||
unsigned char magic_value[4];
|
||||
unsigned char id_string[16];
|
||||
unsigned char flags[4];
|
||||
unsigned char flash_addr_words[4];
|
||||
unsigned char length[4];
|
||||
unsigned char checksum[4];
|
||||
};
|
||||
|
||||
struct block_data {
|
||||
const unsigned char *data;
|
||||
unsigned int size;
|
||||
unsigned int flash_addr;
|
||||
};
|
||||
|
||||
struct image_info {
|
||||
unsigned int packrat_number;
|
||||
struct block_data boot_config;
|
||||
struct block_data app_firmware;
|
||||
struct block_data app_config;
|
||||
struct block_data disp_config;
|
||||
};
|
||||
|
||||
struct image_header {
|
||||
unsigned char magic_value[4];
|
||||
unsigned char num_of_areas[4];
|
||||
};
|
||||
|
||||
struct rmi_f35_query {
|
||||
unsigned char version:4;
|
||||
unsigned char has_debug_mode:1;
|
||||
unsigned char has_data5:1;
|
||||
unsigned char has_query1:1;
|
||||
unsigned char has_query2:1;
|
||||
unsigned char chunk_size;
|
||||
unsigned char has_ctrl7:1;
|
||||
unsigned char has_host_download:1;
|
||||
unsigned char has_spi_master:1;
|
||||
unsigned char advanced_recovery_mode:1;
|
||||
unsigned char reserved:4;
|
||||
} __packed;
|
||||
|
||||
struct rmi_f35_data {
|
||||
unsigned char error_code:5;
|
||||
unsigned char recovery_mode_forced:1;
|
||||
unsigned char nvm_programmed:1;
|
||||
unsigned char in_recovery:1;
|
||||
} __packed;
|
||||
|
||||
struct rmi_pdt_entry {
|
||||
unsigned char query_base_addr;
|
||||
unsigned char command_base_addr;
|
||||
unsigned char control_base_addr;
|
||||
unsigned char data_base_addr;
|
||||
unsigned char intr_src_count:3;
|
||||
unsigned char reserved_1:2;
|
||||
unsigned char fn_version:2;
|
||||
unsigned char reserved_2:1;
|
||||
unsigned char fn_number;
|
||||
} __packed;
|
||||
|
||||
struct rmi_addr {
|
||||
unsigned short query_base;
|
||||
unsigned short command_base;
|
||||
unsigned short control_base;
|
||||
unsigned short data_base;
|
||||
};
|
||||
|
||||
struct firmware_status {
|
||||
unsigned short invalid_static_config:1;
|
||||
unsigned short need_disp_config:1;
|
||||
unsigned short need_app_config:1;
|
||||
unsigned short reserved:13;
|
||||
} __packed;
|
||||
|
||||
struct zeroflash_hcd {
|
||||
bool has_hdl;
|
||||
bool f35_ready;
|
||||
const unsigned char *image;
|
||||
unsigned char *buf;
|
||||
const struct firmware *fw_entry;
|
||||
struct work_struct config_work;
|
||||
struct work_struct firmware_work;
|
||||
struct workqueue_struct *workqueue;
|
||||
struct rmi_addr f35_addr;
|
||||
struct image_info image_info;
|
||||
struct firmware_status fw_status;
|
||||
struct syna_tcm_buffer out;
|
||||
struct syna_tcm_buffer resp;
|
||||
struct syna_tcm_hcd *tcm_hcd;
|
||||
};
|
||||
|
||||
DECLARE_COMPLETION(zeroflash_remove_complete);
|
||||
|
||||
static struct zeroflash_hcd *zeroflash_hcd;
|
||||
|
||||
static int zeroflash_check_uboot(void)
|
||||
{
|
||||
int retval;
|
||||
unsigned char fn_number;
|
||||
struct rmi_f35_query query;
|
||||
struct rmi_pdt_entry p_entry;
|
||||
struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
||||
|
||||
retval = syna_tcm_rmi_read(tcm_hcd,
|
||||
PDT_END_ADDR,
|
||||
&fn_number,
|
||||
sizeof(fn_number));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to read RMI function number\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
LOGD(tcm_hcd->pdev->dev.parent,
|
||||
"Found F$%02x\n",
|
||||
fn_number);
|
||||
|
||||
if (fn_number != UBL_FN_NUMBER) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to find F$35\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (zeroflash_hcd->f35_ready)
|
||||
return 0;
|
||||
|
||||
retval = syna_tcm_rmi_read(tcm_hcd,
|
||||
PDT_START_ADDR,
|
||||
(unsigned char *)&p_entry,
|
||||
sizeof(p_entry));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to read PDT entry\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
zeroflash_hcd->f35_addr.query_base = p_entry.query_base_addr;
|
||||
zeroflash_hcd->f35_addr.command_base = p_entry.command_base_addr;
|
||||
zeroflash_hcd->f35_addr.control_base = p_entry.control_base_addr;
|
||||
zeroflash_hcd->f35_addr.data_base = p_entry.data_base_addr;
|
||||
|
||||
retval = syna_tcm_rmi_read(tcm_hcd,
|
||||
zeroflash_hcd->f35_addr.query_base,
|
||||
(unsigned char *)&query,
|
||||
sizeof(query));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to read F$35 query\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
zeroflash_hcd->f35_ready = true;
|
||||
|
||||
if (query.has_query2 && query.has_ctrl7 && query.has_host_download) {
|
||||
zeroflash_hcd->has_hdl = true;
|
||||
} else {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Host download not supported\n");
|
||||
zeroflash_hcd->has_hdl = false;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zeroflash_parse_fw_image(void)
|
||||
{
|
||||
unsigned int idx;
|
||||
unsigned int addr;
|
||||
unsigned int offset;
|
||||
unsigned int length;
|
||||
unsigned int checksum;
|
||||
unsigned int flash_addr;
|
||||
unsigned int magic_value;
|
||||
unsigned int num_of_areas;
|
||||
struct image_header *header;
|
||||
struct image_info *image_info;
|
||||
struct area_descriptor *descriptor;
|
||||
struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
||||
const unsigned char *image;
|
||||
const unsigned char *content;
|
||||
|
||||
image = zeroflash_hcd->image;
|
||||
image_info = &zeroflash_hcd->image_info;
|
||||
header = (struct image_header *)image;
|
||||
|
||||
magic_value = le4_to_uint(header->magic_value);
|
||||
if (magic_value != IMAGE_FILE_MAGIC_VALUE) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Invalid image file magic value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(image_info, 0x00, sizeof(*image_info));
|
||||
|
||||
offset = sizeof(*header);
|
||||
num_of_areas = le4_to_uint(header->num_of_areas);
|
||||
|
||||
for (idx = 0; idx < num_of_areas; idx++) {
|
||||
addr = le4_to_uint(image + offset);
|
||||
descriptor = (struct area_descriptor *)(image + addr);
|
||||
offset += 4;
|
||||
|
||||
magic_value = le4_to_uint(descriptor->magic_value);
|
||||
if (magic_value != FLASH_AREA_MAGIC_VALUE)
|
||||
continue;
|
||||
|
||||
length = le4_to_uint(descriptor->length);
|
||||
content = (unsigned char *)descriptor + sizeof(*descriptor);
|
||||
flash_addr = le4_to_uint(descriptor->flash_addr_words) * 2;
|
||||
checksum = le4_to_uint(descriptor->checksum);
|
||||
|
||||
if (0 == strncmp((char *)descriptor->id_string,
|
||||
BOOT_CONFIG_ID,
|
||||
strlen(BOOT_CONFIG_ID))) {
|
||||
if (checksum != (crc32(~0, content, length) ^ ~0)) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Boot config checksum error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
image_info->boot_config.size = length;
|
||||
image_info->boot_config.data = content;
|
||||
image_info->boot_config.flash_addr = flash_addr;
|
||||
LOGD(tcm_hcd->pdev->dev.parent,
|
||||
"Boot config size = %d\n",
|
||||
length);
|
||||
LOGD(tcm_hcd->pdev->dev.parent,
|
||||
"Boot config flash address = 0x%08x\n",
|
||||
flash_addr);
|
||||
} else if (0 == strncmp((char *)descriptor->id_string,
|
||||
F35_APP_CODE_ID,
|
||||
strlen(F35_APP_CODE_ID))) {
|
||||
if (checksum != (crc32(~0, content, length) ^ ~0)) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Application firmware checksum error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
image_info->app_firmware.size = length;
|
||||
image_info->app_firmware.data = content;
|
||||
image_info->app_firmware.flash_addr = flash_addr;
|
||||
LOGD(tcm_hcd->pdev->dev.parent,
|
||||
"Application firmware size = %d\n",
|
||||
length);
|
||||
LOGD(tcm_hcd->pdev->dev.parent,
|
||||
"Application firmware flash address = 0x%08x\n",
|
||||
flash_addr);
|
||||
} else if (0 == strncmp((char *)descriptor->id_string,
|
||||
APP_CONFIG_ID,
|
||||
strlen(APP_CONFIG_ID))) {
|
||||
if (checksum != (crc32(~0, content, length) ^ ~0)) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Application config checksum error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
image_info->app_config.size = length;
|
||||
image_info->app_config.data = content;
|
||||
image_info->app_config.flash_addr = flash_addr;
|
||||
image_info->packrat_number = le4_to_uint(&content[14]);
|
||||
LOGD(tcm_hcd->pdev->dev.parent,
|
||||
"Application config size = %d\n",
|
||||
length);
|
||||
LOGD(tcm_hcd->pdev->dev.parent,
|
||||
"Application config flash address = 0x%08x\n",
|
||||
flash_addr);
|
||||
} else if (0 == strncmp((char *)descriptor->id_string,
|
||||
DISP_CONFIG_ID,
|
||||
strlen(DISP_CONFIG_ID))) {
|
||||
if (checksum != (crc32(~0, content, length) ^ ~0)) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Display config checksum error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
image_info->disp_config.size = length;
|
||||
image_info->disp_config.data = content;
|
||||
image_info->disp_config.flash_addr = flash_addr;
|
||||
LOGD(tcm_hcd->pdev->dev.parent,
|
||||
"Display config size = %d\n",
|
||||
length);
|
||||
LOGD(tcm_hcd->pdev->dev.parent,
|
||||
"Display config flash address = 0x%08x\n",
|
||||
flash_addr);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zeroflash_get_fw_image(void)
|
||||
{
|
||||
int retval;
|
||||
struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
||||
|
||||
if (zeroflash_hcd->fw_entry != NULL)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
retval = request_firmware(&zeroflash_hcd->fw_entry,
|
||||
FW_IMAGE_NAME,
|
||||
tcm_hcd->pdev->dev.parent);
|
||||
if (retval < 0) {
|
||||
LOGD(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to request %s\n",
|
||||
FW_IMAGE_NAME);
|
||||
msleep(100);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
LOGD(tcm_hcd->pdev->dev.parent,
|
||||
"Firmware image size = %d\n",
|
||||
(unsigned int)zeroflash_hcd->fw_entry->size);
|
||||
|
||||
zeroflash_hcd->image = zeroflash_hcd->fw_entry->data;
|
||||
|
||||
retval = zeroflash_parse_fw_image();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to parse firmware image\n");
|
||||
release_firmware(zeroflash_hcd->fw_entry);
|
||||
zeroflash_hcd->fw_entry = NULL;
|
||||
zeroflash_hcd->image = NULL;
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void zeroflash_download_config(void)
|
||||
{
|
||||
struct firmware_status *fw_status;
|
||||
struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
||||
|
||||
fw_status = &zeroflash_hcd->fw_status;
|
||||
|
||||
if (!fw_status->need_app_config && !fw_status->need_disp_config) {
|
||||
if (atomic_read(&tcm_hcd->helper.task) == HELP_NONE) {
|
||||
atomic_set(&tcm_hcd->helper.task,
|
||||
HELP_SEND_RESET_NOTIFICATION);
|
||||
queue_work(tcm_hcd->helper.workqueue,
|
||||
&tcm_hcd->helper.work);
|
||||
}
|
||||
atomic_set(&tcm_hcd->host_downloading, 0);
|
||||
wake_up_interruptible(&tcm_hcd->hdl_wq);
|
||||
return;
|
||||
}
|
||||
|
||||
queue_work(zeroflash_hcd->workqueue, &zeroflash_hcd->config_work);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void zeroflash_download_firmware(void)
|
||||
{
|
||||
queue_work(zeroflash_hcd->workqueue, &zeroflash_hcd->firmware_work);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int zeroflash_download_disp_config(void)
|
||||
{
|
||||
int retval;
|
||||
unsigned char response_code;
|
||||
struct image_info *image_info;
|
||||
struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
||||
static unsigned int retry_count;
|
||||
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"Downloading display config\n");
|
||||
|
||||
image_info = &zeroflash_hcd->image_info;
|
||||
|
||||
if (image_info->disp_config.size == 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"No display config in image file\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOCK_BUFFER(zeroflash_hcd->out);
|
||||
|
||||
retval = syna_tcm_alloc_mem(tcm_hcd,
|
||||
&zeroflash_hcd->out,
|
||||
image_info->disp_config.size + 2);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for zeroflash_hcd->out.buf\n");
|
||||
goto unlock_out;
|
||||
}
|
||||
|
||||
zeroflash_hcd->out.buf[0] = 1;
|
||||
zeroflash_hcd->out.buf[1] = HDL_DISPLAY_CONFIG_TO_RAM;
|
||||
|
||||
retval = secure_memcpy(&zeroflash_hcd->out.buf[2],
|
||||
zeroflash_hcd->out.buf_size - 2,
|
||||
image_info->disp_config.data,
|
||||
image_info->disp_config.size,
|
||||
image_info->disp_config.size);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy display config data\n");
|
||||
goto unlock_out;
|
||||
}
|
||||
|
||||
zeroflash_hcd->out.data_length = image_info->disp_config.size + 2;
|
||||
|
||||
LOCK_BUFFER(zeroflash_hcd->resp);
|
||||
|
||||
retval = tcm_hcd->write_message(tcm_hcd,
|
||||
CMD_DOWNLOAD_CONFIG,
|
||||
zeroflash_hcd->out.buf,
|
||||
zeroflash_hcd->out.data_length,
|
||||
&zeroflash_hcd->resp.buf,
|
||||
&zeroflash_hcd->resp.buf_size,
|
||||
&zeroflash_hcd->resp.data_length,
|
||||
&response_code,
|
||||
0);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to write command %s\n",
|
||||
STR(CMD_DOWNLOAD_CONFIG));
|
||||
if (response_code != STATUS_ERROR)
|
||||
goto unlock_resp;
|
||||
retry_count++;
|
||||
if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT)
|
||||
goto unlock_resp;
|
||||
} else {
|
||||
retry_count = 0;
|
||||
}
|
||||
|
||||
retval = secure_memcpy((unsigned char *)&zeroflash_hcd->fw_status,
|
||||
sizeof(zeroflash_hcd->fw_status),
|
||||
zeroflash_hcd->resp.buf,
|
||||
zeroflash_hcd->resp.buf_size,
|
||||
sizeof(zeroflash_hcd->fw_status));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy firmware status\n");
|
||||
goto unlock_resp;
|
||||
}
|
||||
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"Display config downloaded\n");
|
||||
|
||||
retval = 0;
|
||||
|
||||
unlock_resp:
|
||||
UNLOCK_BUFFER(zeroflash_hcd->resp);
|
||||
|
||||
unlock_out:
|
||||
UNLOCK_BUFFER(zeroflash_hcd->out);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int zeroflash_download_app_config(void)
|
||||
{
|
||||
int retval;
|
||||
unsigned char response_code;
|
||||
struct image_info *image_info;
|
||||
struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
||||
static unsigned int retry_count;
|
||||
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"Downloading application config\n");
|
||||
|
||||
image_info = &zeroflash_hcd->image_info;
|
||||
|
||||
if (image_info->app_config.size == 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"No application config in image file\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOCK_BUFFER(zeroflash_hcd->out);
|
||||
|
||||
retval = syna_tcm_alloc_mem(tcm_hcd,
|
||||
&zeroflash_hcd->out,
|
||||
image_info->app_config.size + 2);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for zeroflash_hcd->out.buf\n");
|
||||
goto unlock_out;
|
||||
}
|
||||
|
||||
zeroflash_hcd->out.buf[0] = 1;
|
||||
zeroflash_hcd->out.buf[1] = HDL_TOUCH_CONFIG_TO_PMEM;
|
||||
|
||||
retval = secure_memcpy(&zeroflash_hcd->out.buf[2],
|
||||
zeroflash_hcd->out.buf_size - 2,
|
||||
image_info->app_config.data,
|
||||
image_info->app_config.size,
|
||||
image_info->app_config.size);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy application config data\n");
|
||||
goto unlock_out;
|
||||
}
|
||||
|
||||
zeroflash_hcd->out.data_length = image_info->app_config.size + 2;
|
||||
|
||||
LOCK_BUFFER(zeroflash_hcd->resp);
|
||||
|
||||
retval = tcm_hcd->write_message(tcm_hcd,
|
||||
CMD_DOWNLOAD_CONFIG,
|
||||
zeroflash_hcd->out.buf,
|
||||
zeroflash_hcd->out.data_length,
|
||||
&zeroflash_hcd->resp.buf,
|
||||
&zeroflash_hcd->resp.buf_size,
|
||||
&zeroflash_hcd->resp.data_length,
|
||||
&response_code,
|
||||
0);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to write command %s\n",
|
||||
STR(CMD_DOWNLOAD_CONFIG));
|
||||
if (response_code != STATUS_ERROR)
|
||||
goto unlock_resp;
|
||||
retry_count++;
|
||||
if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT)
|
||||
goto unlock_resp;
|
||||
} else {
|
||||
retry_count = 0;
|
||||
}
|
||||
|
||||
retval = secure_memcpy((unsigned char *)&zeroflash_hcd->fw_status,
|
||||
sizeof(zeroflash_hcd->fw_status),
|
||||
zeroflash_hcd->resp.buf,
|
||||
zeroflash_hcd->resp.buf_size,
|
||||
sizeof(zeroflash_hcd->fw_status));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy firmware status\n");
|
||||
goto unlock_resp;
|
||||
}
|
||||
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"Application config downloaded\n");
|
||||
|
||||
retval = 0;
|
||||
|
||||
unlock_resp:
|
||||
UNLOCK_BUFFER(zeroflash_hcd->resp);
|
||||
|
||||
unlock_out:
|
||||
UNLOCK_BUFFER(zeroflash_hcd->out);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void zeroflash_download_config_work(struct work_struct *work)
|
||||
{
|
||||
int retval;
|
||||
struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
||||
|
||||
retval = zeroflash_get_fw_image();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to get firmware image\n");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"Start of config download\n");
|
||||
|
||||
if (zeroflash_hcd->fw_status.need_app_config) {
|
||||
retval = zeroflash_download_app_config();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to download application config\n");
|
||||
return;
|
||||
}
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (zeroflash_hcd->fw_status.need_disp_config) {
|
||||
retval = zeroflash_download_disp_config();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to download display config\n");
|
||||
return;
|
||||
}
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"End of config download\n");
|
||||
|
||||
zeroflash_download_config();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int zeroflash_download_app_fw(void)
|
||||
{
|
||||
int retval;
|
||||
unsigned char command;
|
||||
struct image_info *image_info;
|
||||
struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
||||
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"Downloading application firmware\n");
|
||||
|
||||
image_info = &zeroflash_hcd->image_info;
|
||||
|
||||
if (image_info->app_firmware.size == 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"No application firmware in image file\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOCK_BUFFER(zeroflash_hcd->out);
|
||||
|
||||
retval = syna_tcm_alloc_mem(tcm_hcd,
|
||||
&zeroflash_hcd->out,
|
||||
image_info->app_firmware.size);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for zeroflash_hcd->out.buf\n");
|
||||
UNLOCK_BUFFER(zeroflash_hcd->out);
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = secure_memcpy(zeroflash_hcd->out.buf,
|
||||
zeroflash_hcd->out.buf_size,
|
||||
image_info->app_firmware.data,
|
||||
image_info->app_firmware.size,
|
||||
image_info->app_firmware.size);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy application firmware data\n");
|
||||
UNLOCK_BUFFER(zeroflash_hcd->out);
|
||||
return retval;
|
||||
}
|
||||
|
||||
zeroflash_hcd->out.data_length = image_info->app_firmware.size;
|
||||
|
||||
command = F35_WRITE_FW_TO_PMEM_COMMAND;
|
||||
|
||||
retval = syna_tcm_rmi_write(tcm_hcd,
|
||||
zeroflash_hcd->f35_addr.control_base + F35_CTRL3_OFFSET,
|
||||
&command,
|
||||
sizeof(command));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to write F$35 command\n");
|
||||
UNLOCK_BUFFER(zeroflash_hcd->out);
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = syna_tcm_rmi_write(tcm_hcd,
|
||||
zeroflash_hcd->f35_addr.control_base + F35_CTRL7_OFFSET,
|
||||
zeroflash_hcd->out.buf,
|
||||
zeroflash_hcd->out.data_length);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to write application firmware data\n");
|
||||
UNLOCK_BUFFER(zeroflash_hcd->out);
|
||||
return retval;
|
||||
}
|
||||
|
||||
UNLOCK_BUFFER(zeroflash_hcd->out);
|
||||
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"Application firmware downloaded\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void zeroflash_download_firmware_work(struct work_struct *work)
|
||||
{
|
||||
int retval;
|
||||
struct rmi_f35_data data;
|
||||
struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd;
|
||||
static unsigned int retry_count;
|
||||
|
||||
retval = zeroflash_check_uboot();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Microbootloader support unavailable\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
atomic_set(&tcm_hcd->host_downloading, 1);
|
||||
|
||||
retval = syna_tcm_rmi_read(tcm_hcd,
|
||||
zeroflash_hcd->f35_addr.data_base,
|
||||
(unsigned char *)&data,
|
||||
sizeof(data));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to read F$35 data\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (data.error_code != REQUESTING_FIRMWARE) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Microbootloader error code = 0x%02x\n",
|
||||
data.error_code);
|
||||
if (data.error_code != CHECKSUM_FAILURE) {
|
||||
retval = -EIO;
|
||||
goto exit;
|
||||
} else {
|
||||
retry_count++;
|
||||
}
|
||||
} else {
|
||||
retry_count = 0;
|
||||
}
|
||||
|
||||
retval = zeroflash_get_fw_image();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to get firmware image\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"Start of firmware download\n");
|
||||
|
||||
retval = zeroflash_download_app_fw();
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to download application firmware\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
LOGN(tcm_hcd->pdev->dev.parent,
|
||||
"End of firmware download\n");
|
||||
|
||||
exit:
|
||||
if (retval < 0)
|
||||
retry_count++;
|
||||
|
||||
if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT) {
|
||||
retval = tcm_hcd->enable_irq(tcm_hcd, false, true);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to disable interrupt\n");
|
||||
}
|
||||
} else {
|
||||
retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to enable interrupt\n");
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int zeroflash_init(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
zeroflash_hcd = kzalloc(sizeof(*zeroflash_hcd), GFP_KERNEL);
|
||||
if (!zeroflash_hcd) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to allocate memory for zeroflash_hcd\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
zeroflash_hcd->tcm_hcd = tcm_hcd;
|
||||
|
||||
INIT_BUFFER(zeroflash_hcd->out, false);
|
||||
INIT_BUFFER(zeroflash_hcd->resp, false);
|
||||
|
||||
zeroflash_hcd->workqueue =
|
||||
create_singlethread_workqueue("syna_tcm_zeroflash");
|
||||
INIT_WORK(&zeroflash_hcd->config_work,
|
||||
zeroflash_download_config_work);
|
||||
INIT_WORK(&zeroflash_hcd->firmware_work,
|
||||
zeroflash_download_firmware_work);
|
||||
|
||||
if (tcm_hcd->init_okay == false &&
|
||||
tcm_hcd->hw_if->bus_io->type == BUS_SPI)
|
||||
zeroflash_download_firmware();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zeroflash_remove(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
if (!zeroflash_hcd)
|
||||
goto exit;
|
||||
|
||||
if (zeroflash_hcd->fw_entry)
|
||||
release_firmware(zeroflash_hcd->fw_entry);
|
||||
|
||||
cancel_work_sync(&zeroflash_hcd->config_work);
|
||||
cancel_work_sync(&zeroflash_hcd->firmware_work);
|
||||
flush_workqueue(zeroflash_hcd->workqueue);
|
||||
destroy_workqueue(zeroflash_hcd->workqueue);
|
||||
|
||||
RELEASE_BUFFER(zeroflash_hcd->resp);
|
||||
RELEASE_BUFFER(zeroflash_hcd->out);
|
||||
|
||||
kfree(zeroflash_hcd);
|
||||
zeroflash_hcd = NULL;
|
||||
|
||||
exit:
|
||||
complete(&zeroflash_remove_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zeroflash_syncbox(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
int retval;
|
||||
unsigned char *fw_status;
|
||||
|
||||
if (!zeroflash_hcd)
|
||||
return 0;
|
||||
|
||||
switch (tcm_hcd->report.id) {
|
||||
case REPORT_STATUS:
|
||||
fw_status = (unsigned char *)&zeroflash_hcd->fw_status;
|
||||
retval = secure_memcpy(fw_status,
|
||||
sizeof(zeroflash_hcd->fw_status),
|
||||
tcm_hcd->report.buffer.buf,
|
||||
tcm_hcd->report.buffer.buf_size,
|
||||
sizeof(zeroflash_hcd->fw_status));
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to copy firmware status\n");
|
||||
return retval;
|
||||
}
|
||||
zeroflash_download_config();
|
||||
break;
|
||||
case REPORT_HDL:
|
||||
retval = tcm_hcd->enable_irq(tcm_hcd, false, true);
|
||||
if (retval < 0) {
|
||||
LOGE(tcm_hcd->pdev->dev.parent,
|
||||
"Failed to disable interrupt\n");
|
||||
return retval;
|
||||
}
|
||||
zeroflash_download_firmware();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zeroflash_reset(struct syna_tcm_hcd *tcm_hcd)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (!zeroflash_hcd) {
|
||||
retval = zeroflash_init(tcm_hcd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct syna_tcm_module_cb zeroflash_module = {
|
||||
.type = TCM_ZEROFLASH,
|
||||
.init = zeroflash_init,
|
||||
.remove = zeroflash_remove,
|
||||
.syncbox = zeroflash_syncbox,
|
||||
.asyncbox = NULL,
|
||||
.reset = zeroflash_reset,
|
||||
.suspend = NULL,
|
||||
.resume = NULL,
|
||||
.early_suspend = NULL,
|
||||
};
|
||||
|
||||
static int __init zeroflash_module_init(void)
|
||||
{
|
||||
return syna_tcm_add_module(&zeroflash_module, true);
|
||||
}
|
||||
|
||||
static void __exit zeroflash_module_exit(void)
|
||||
{
|
||||
syna_tcm_add_module(&zeroflash_module, false);
|
||||
|
||||
wait_for_completion(&zeroflash_remove_complete);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(zeroflash_module_init);
|
||||
module_exit(zeroflash_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Synaptics, Inc.");
|
||||
MODULE_DESCRIPTION("Synaptics TCM Zeroflash Module");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
63
include/linux/input/synaptics_tcm.h
Normal file
63
include/linux/input/synaptics_tcm.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Synaptics TCM touchscreen driver
|
||||
*
|
||||
* Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2017-2018 Scott Lin <scott.lin@tw.synaptics.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
|
||||
* EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
|
||||
* AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
|
||||
* IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
|
||||
* WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
|
||||
* AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
|
||||
* NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
|
||||
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
|
||||
* DOLLARS.
|
||||
*/
|
||||
|
||||
#ifndef _SYNAPTICS_TCM_H_
|
||||
#define _SYNAPTICS_TCM_H_
|
||||
|
||||
#define I2C_MODULE_NAME "synaptics_tcm_i2c"
|
||||
#define SPI_MODULE_NAME "synaptics_tcm_spi"
|
||||
|
||||
struct syna_tcm_board_data {
|
||||
bool x_flip;
|
||||
bool y_flip;
|
||||
bool swap_axes;
|
||||
int irq_gpio;
|
||||
int irq_on_state;
|
||||
int power_gpio;
|
||||
int power_on_state;
|
||||
int reset_gpio;
|
||||
int reset_on_state;
|
||||
unsigned int spi_mode;
|
||||
unsigned int power_delay_ms;
|
||||
unsigned int reset_delay_ms;
|
||||
unsigned int reset_active_ms;
|
||||
unsigned int byte_delay_us;
|
||||
unsigned int block_delay_us;
|
||||
unsigned int ubl_i2c_addr;
|
||||
unsigned int ubl_max_freq;
|
||||
unsigned int ubl_byte_delay_us;
|
||||
unsigned long irq_flags;
|
||||
const char *pwr_reg_name;
|
||||
const char *bus_reg_name;
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Reference in a new issue