162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Atmel maXTouch Touchscreen driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010 Samsung Electronics Co.Ltd
662306a36Sopenharmony_ci * Copyright (C) 2011-2014 Atmel Corporation
762306a36Sopenharmony_ci * Copyright (C) 2012 Google, Inc.
862306a36Sopenharmony_ci * Copyright (C) 2016 Zodiac Inflight Innovations
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Author: Joonyoung Shim <jy0922.shim@samsung.com>
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/acpi.h>
1462306a36Sopenharmony_ci#include <linux/dmi.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/init.h>
1762306a36Sopenharmony_ci#include <linux/completion.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/firmware.h>
2062306a36Sopenharmony_ci#include <linux/i2c.h>
2162306a36Sopenharmony_ci#include <linux/input/mt.h>
2262306a36Sopenharmony_ci#include <linux/interrupt.h>
2362306a36Sopenharmony_ci#include <linux/irq.h>
2462306a36Sopenharmony_ci#include <linux/of.h>
2562306a36Sopenharmony_ci#include <linux/property.h>
2662306a36Sopenharmony_ci#include <linux/slab.h>
2762306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2862306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2962306a36Sopenharmony_ci#include <asm/unaligned.h>
3062306a36Sopenharmony_ci#include <media/v4l2-device.h>
3162306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
3262306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h>
3362306a36Sopenharmony_ci#include <media/videobuf2-vmalloc.h>
3462306a36Sopenharmony_ci#include <dt-bindings/input/atmel-maxtouch.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* Firmware files */
3762306a36Sopenharmony_ci#define MXT_FW_NAME		"maxtouch.fw"
3862306a36Sopenharmony_ci#define MXT_CFG_NAME		"maxtouch.cfg"
3962306a36Sopenharmony_ci#define MXT_CFG_MAGIC		"OBP_RAW V1"
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* Registers */
4262306a36Sopenharmony_ci#define MXT_OBJECT_START	0x07
4362306a36Sopenharmony_ci#define MXT_OBJECT_SIZE		6
4462306a36Sopenharmony_ci#define MXT_INFO_CHECKSUM_SIZE	3
4562306a36Sopenharmony_ci#define MXT_MAX_BLOCK_WRITE	256
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Object types */
4862306a36Sopenharmony_ci#define MXT_DEBUG_DIAGNOSTIC_T37	37
4962306a36Sopenharmony_ci#define MXT_GEN_MESSAGE_T5		5
5062306a36Sopenharmony_ci#define MXT_GEN_COMMAND_T6		6
5162306a36Sopenharmony_ci#define MXT_GEN_POWER_T7		7
5262306a36Sopenharmony_ci#define MXT_GEN_ACQUIRE_T8		8
5362306a36Sopenharmony_ci#define MXT_GEN_DATASOURCE_T53		53
5462306a36Sopenharmony_ci#define MXT_TOUCH_MULTI_T9		9
5562306a36Sopenharmony_ci#define MXT_TOUCH_KEYARRAY_T15		15
5662306a36Sopenharmony_ci#define MXT_TOUCH_PROXIMITY_T23		23
5762306a36Sopenharmony_ci#define MXT_TOUCH_PROXKEY_T52		52
5862306a36Sopenharmony_ci#define MXT_TOUCH_PTC_KEYS_T97		97
5962306a36Sopenharmony_ci#define MXT_PROCI_GRIPFACE_T20		20
6062306a36Sopenharmony_ci#define MXT_PROCG_NOISE_T22		22
6162306a36Sopenharmony_ci#define MXT_PROCI_ONETOUCH_T24		24
6262306a36Sopenharmony_ci#define MXT_PROCI_TWOTOUCH_T27		27
6362306a36Sopenharmony_ci#define MXT_PROCI_GRIP_T40		40
6462306a36Sopenharmony_ci#define MXT_PROCI_PALM_T41		41
6562306a36Sopenharmony_ci#define MXT_PROCI_TOUCHSUPPRESSION_T42	42
6662306a36Sopenharmony_ci#define MXT_PROCI_STYLUS_T47		47
6762306a36Sopenharmony_ci#define MXT_PROCG_NOISESUPPRESSION_T48	48
6862306a36Sopenharmony_ci#define MXT_SPT_COMMSCONFIG_T18		18
6962306a36Sopenharmony_ci#define MXT_SPT_GPIOPWM_T19		19
7062306a36Sopenharmony_ci#define MXT_SPT_SELFTEST_T25		25
7162306a36Sopenharmony_ci#define MXT_SPT_CTECONFIG_T28		28
7262306a36Sopenharmony_ci#define MXT_SPT_USERDATA_T38		38
7362306a36Sopenharmony_ci#define MXT_SPT_DIGITIZER_T43		43
7462306a36Sopenharmony_ci#define MXT_SPT_MESSAGECOUNT_T44	44
7562306a36Sopenharmony_ci#define MXT_SPT_CTECONFIG_T46		46
7662306a36Sopenharmony_ci#define MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71 71
7762306a36Sopenharmony_ci#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/* MXT_GEN_MESSAGE_T5 object */
8062306a36Sopenharmony_ci#define MXT_RPTID_NOMSG		0xff
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* MXT_GEN_COMMAND_T6 field */
8362306a36Sopenharmony_ci#define MXT_COMMAND_RESET	0
8462306a36Sopenharmony_ci#define MXT_COMMAND_BACKUPNV	1
8562306a36Sopenharmony_ci#define MXT_COMMAND_CALIBRATE	2
8662306a36Sopenharmony_ci#define MXT_COMMAND_REPORTALL	3
8762306a36Sopenharmony_ci#define MXT_COMMAND_DIAGNOSTIC	5
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/* Define for T6 status byte */
9062306a36Sopenharmony_ci#define MXT_T6_STATUS_RESET	BIT(7)
9162306a36Sopenharmony_ci#define MXT_T6_STATUS_OFL	BIT(6)
9262306a36Sopenharmony_ci#define MXT_T6_STATUS_SIGERR	BIT(5)
9362306a36Sopenharmony_ci#define MXT_T6_STATUS_CAL	BIT(4)
9462306a36Sopenharmony_ci#define MXT_T6_STATUS_CFGERR	BIT(3)
9562306a36Sopenharmony_ci#define MXT_T6_STATUS_COMSERR	BIT(2)
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* MXT_GEN_POWER_T7 field */
9862306a36Sopenharmony_cistruct t7_config {
9962306a36Sopenharmony_ci	u8 idle;
10062306a36Sopenharmony_ci	u8 active;
10162306a36Sopenharmony_ci} __packed;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define MXT_POWER_CFG_RUN		0
10462306a36Sopenharmony_ci#define MXT_POWER_CFG_DEEPSLEEP		1
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/* MXT_TOUCH_MULTI_T9 field */
10762306a36Sopenharmony_ci#define MXT_T9_CTRL		0
10862306a36Sopenharmony_ci#define MXT_T9_XSIZE		3
10962306a36Sopenharmony_ci#define MXT_T9_YSIZE		4
11062306a36Sopenharmony_ci#define MXT_T9_ORIENT		9
11162306a36Sopenharmony_ci#define MXT_T9_RANGE		18
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/* MXT_TOUCH_MULTI_T9 status */
11462306a36Sopenharmony_ci#define MXT_T9_UNGRIP		BIT(0)
11562306a36Sopenharmony_ci#define MXT_T9_SUPPRESS		BIT(1)
11662306a36Sopenharmony_ci#define MXT_T9_AMP		BIT(2)
11762306a36Sopenharmony_ci#define MXT_T9_VECTOR		BIT(3)
11862306a36Sopenharmony_ci#define MXT_T9_MOVE		BIT(4)
11962306a36Sopenharmony_ci#define MXT_T9_RELEASE		BIT(5)
12062306a36Sopenharmony_ci#define MXT_T9_PRESS		BIT(6)
12162306a36Sopenharmony_ci#define MXT_T9_DETECT		BIT(7)
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistruct t9_range {
12462306a36Sopenharmony_ci	__le16 x;
12562306a36Sopenharmony_ci	__le16 y;
12662306a36Sopenharmony_ci} __packed;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/* MXT_TOUCH_MULTI_T9 orient */
12962306a36Sopenharmony_ci#define MXT_T9_ORIENT_SWITCH	BIT(0)
13062306a36Sopenharmony_ci#define MXT_T9_ORIENT_INVERTX	BIT(1)
13162306a36Sopenharmony_ci#define MXT_T9_ORIENT_INVERTY	BIT(2)
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/* MXT_SPT_COMMSCONFIG_T18 */
13462306a36Sopenharmony_ci#define MXT_COMMS_CTRL		0
13562306a36Sopenharmony_ci#define MXT_COMMS_CMD		1
13662306a36Sopenharmony_ci#define MXT_COMMS_RETRIGEN	BIT(6)
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/* MXT_DEBUG_DIAGNOSTIC_T37 */
13962306a36Sopenharmony_ci#define MXT_DIAGNOSTIC_PAGEUP	0x01
14062306a36Sopenharmony_ci#define MXT_DIAGNOSTIC_DELTAS	0x10
14162306a36Sopenharmony_ci#define MXT_DIAGNOSTIC_REFS	0x11
14262306a36Sopenharmony_ci#define MXT_DIAGNOSTIC_SIZE	128
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci#define MXT_FAMILY_1386			160
14562306a36Sopenharmony_ci#define MXT1386_COLUMNS			3
14662306a36Sopenharmony_ci#define MXT1386_PAGES_PER_COLUMN	8
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistruct t37_debug {
14962306a36Sopenharmony_ci#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37
15062306a36Sopenharmony_ci	u8 mode;
15162306a36Sopenharmony_ci	u8 page;
15262306a36Sopenharmony_ci	u8 data[MXT_DIAGNOSTIC_SIZE];
15362306a36Sopenharmony_ci#endif
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/* Define for MXT_GEN_COMMAND_T6 */
15762306a36Sopenharmony_ci#define MXT_BOOT_VALUE		0xa5
15862306a36Sopenharmony_ci#define MXT_RESET_VALUE		0x01
15962306a36Sopenharmony_ci#define MXT_BACKUP_VALUE	0x55
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/* T100 Multiple Touch Touchscreen */
16262306a36Sopenharmony_ci#define MXT_T100_CTRL		0
16362306a36Sopenharmony_ci#define MXT_T100_CFG1		1
16462306a36Sopenharmony_ci#define MXT_T100_TCHAUX		3
16562306a36Sopenharmony_ci#define MXT_T100_XSIZE		9
16662306a36Sopenharmony_ci#define MXT_T100_XRANGE		13
16762306a36Sopenharmony_ci#define MXT_T100_YSIZE		20
16862306a36Sopenharmony_ci#define MXT_T100_YRANGE		24
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci#define MXT_T100_CFG_SWITCHXY	BIT(5)
17162306a36Sopenharmony_ci#define MXT_T100_CFG_INVERTY	BIT(6)
17262306a36Sopenharmony_ci#define MXT_T100_CFG_INVERTX	BIT(7)
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci#define MXT_T100_TCHAUX_VECT	BIT(0)
17562306a36Sopenharmony_ci#define MXT_T100_TCHAUX_AMPL	BIT(1)
17662306a36Sopenharmony_ci#define MXT_T100_TCHAUX_AREA	BIT(2)
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci#define MXT_T100_DETECT		BIT(7)
17962306a36Sopenharmony_ci#define MXT_T100_TYPE_MASK	0x70
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cienum t100_type {
18262306a36Sopenharmony_ci	MXT_T100_TYPE_FINGER		= 1,
18362306a36Sopenharmony_ci	MXT_T100_TYPE_PASSIVE_STYLUS	= 2,
18462306a36Sopenharmony_ci	MXT_T100_TYPE_HOVERING_FINGER	= 4,
18562306a36Sopenharmony_ci	MXT_T100_TYPE_GLOVE		= 5,
18662306a36Sopenharmony_ci	MXT_T100_TYPE_LARGE_TOUCH	= 6,
18762306a36Sopenharmony_ci};
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci#define MXT_DISTANCE_ACTIVE_TOUCH	0
19062306a36Sopenharmony_ci#define MXT_DISTANCE_HOVERING		1
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci#define MXT_TOUCH_MAJOR_DEFAULT		1
19362306a36Sopenharmony_ci#define MXT_PRESSURE_DEFAULT		1
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/* Delay times */
19662306a36Sopenharmony_ci#define MXT_BACKUP_TIME		50	/* msec */
19762306a36Sopenharmony_ci#define MXT_RESET_GPIO_TIME	20	/* msec */
19862306a36Sopenharmony_ci#define MXT_RESET_INVALID_CHG	100	/* msec */
19962306a36Sopenharmony_ci#define MXT_RESET_TIME		200	/* msec */
20062306a36Sopenharmony_ci#define MXT_RESET_TIMEOUT	3000	/* msec */
20162306a36Sopenharmony_ci#define MXT_CRC_TIMEOUT		1000	/* msec */
20262306a36Sopenharmony_ci#define MXT_FW_RESET_TIME	3000	/* msec */
20362306a36Sopenharmony_ci#define MXT_FW_CHG_TIMEOUT	300	/* msec */
20462306a36Sopenharmony_ci#define MXT_WAKEUP_TIME		25	/* msec */
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/* Command to unlock bootloader */
20762306a36Sopenharmony_ci#define MXT_UNLOCK_CMD_MSB	0xaa
20862306a36Sopenharmony_ci#define MXT_UNLOCK_CMD_LSB	0xdc
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci/* Bootloader mode status */
21162306a36Sopenharmony_ci#define MXT_WAITING_BOOTLOAD_CMD	0xc0	/* valid 7 6 bit only */
21262306a36Sopenharmony_ci#define MXT_WAITING_FRAME_DATA	0x80	/* valid 7 6 bit only */
21362306a36Sopenharmony_ci#define MXT_FRAME_CRC_CHECK	0x02
21462306a36Sopenharmony_ci#define MXT_FRAME_CRC_FAIL	0x03
21562306a36Sopenharmony_ci#define MXT_FRAME_CRC_PASS	0x04
21662306a36Sopenharmony_ci#define MXT_APP_CRC_FAIL	0x40	/* valid 7 8 bit only */
21762306a36Sopenharmony_ci#define MXT_BOOT_STATUS_MASK	0x3f
21862306a36Sopenharmony_ci#define MXT_BOOT_EXTENDED_ID	BIT(5)
21962306a36Sopenharmony_ci#define MXT_BOOT_ID_MASK	0x1f
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci/* Touchscreen absolute values */
22262306a36Sopenharmony_ci#define MXT_MAX_AREA		0xff
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci#define MXT_PIXELS_PER_MM	20
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistruct mxt_info {
22762306a36Sopenharmony_ci	u8 family_id;
22862306a36Sopenharmony_ci	u8 variant_id;
22962306a36Sopenharmony_ci	u8 version;
23062306a36Sopenharmony_ci	u8 build;
23162306a36Sopenharmony_ci	u8 matrix_xsize;
23262306a36Sopenharmony_ci	u8 matrix_ysize;
23362306a36Sopenharmony_ci	u8 object_num;
23462306a36Sopenharmony_ci};
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistruct mxt_object {
23762306a36Sopenharmony_ci	u8 type;
23862306a36Sopenharmony_ci	u16 start_address;
23962306a36Sopenharmony_ci	u8 size_minus_one;
24062306a36Sopenharmony_ci	u8 instances_minus_one;
24162306a36Sopenharmony_ci	u8 num_report_ids;
24262306a36Sopenharmony_ci} __packed;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistruct mxt_dbg {
24562306a36Sopenharmony_ci	u16 t37_address;
24662306a36Sopenharmony_ci	u16 diag_cmd_address;
24762306a36Sopenharmony_ci	struct t37_debug *t37_buf;
24862306a36Sopenharmony_ci	unsigned int t37_pages;
24962306a36Sopenharmony_ci	unsigned int t37_nodes;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	struct v4l2_device v4l2;
25262306a36Sopenharmony_ci	struct v4l2_pix_format format;
25362306a36Sopenharmony_ci	struct video_device vdev;
25462306a36Sopenharmony_ci	struct vb2_queue queue;
25562306a36Sopenharmony_ci	struct mutex lock;
25662306a36Sopenharmony_ci	int input;
25762306a36Sopenharmony_ci};
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cienum v4l_dbg_inputs {
26062306a36Sopenharmony_ci	MXT_V4L_INPUT_DELTAS,
26162306a36Sopenharmony_ci	MXT_V4L_INPUT_REFS,
26262306a36Sopenharmony_ci	MXT_V4L_INPUT_MAX,
26362306a36Sopenharmony_ci};
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cienum mxt_suspend_mode {
26662306a36Sopenharmony_ci	MXT_SUSPEND_DEEP_SLEEP	= 0,
26762306a36Sopenharmony_ci	MXT_SUSPEND_T9_CTRL	= 1,
26862306a36Sopenharmony_ci};
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci/* Config update context */
27162306a36Sopenharmony_cistruct mxt_cfg {
27262306a36Sopenharmony_ci	u8 *raw;
27362306a36Sopenharmony_ci	size_t raw_size;
27462306a36Sopenharmony_ci	off_t raw_pos;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	u8 *mem;
27762306a36Sopenharmony_ci	size_t mem_size;
27862306a36Sopenharmony_ci	int start_ofs;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	struct mxt_info info;
28162306a36Sopenharmony_ci};
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/* Each client has this additional data */
28462306a36Sopenharmony_cistruct mxt_data {
28562306a36Sopenharmony_ci	struct i2c_client *client;
28662306a36Sopenharmony_ci	struct input_dev *input_dev;
28762306a36Sopenharmony_ci	char phys[64];		/* device physical location */
28862306a36Sopenharmony_ci	struct mxt_object *object_table;
28962306a36Sopenharmony_ci	struct mxt_info *info;
29062306a36Sopenharmony_ci	void *raw_info_block;
29162306a36Sopenharmony_ci	unsigned int irq;
29262306a36Sopenharmony_ci	unsigned int max_x;
29362306a36Sopenharmony_ci	unsigned int max_y;
29462306a36Sopenharmony_ci	bool invertx;
29562306a36Sopenharmony_ci	bool inverty;
29662306a36Sopenharmony_ci	bool xy_switch;
29762306a36Sopenharmony_ci	u8 xsize;
29862306a36Sopenharmony_ci	u8 ysize;
29962306a36Sopenharmony_ci	bool in_bootloader;
30062306a36Sopenharmony_ci	u16 mem_size;
30162306a36Sopenharmony_ci	u8 t100_aux_ampl;
30262306a36Sopenharmony_ci	u8 t100_aux_area;
30362306a36Sopenharmony_ci	u8 t100_aux_vect;
30462306a36Sopenharmony_ci	u8 max_reportid;
30562306a36Sopenharmony_ci	u32 config_crc;
30662306a36Sopenharmony_ci	u32 info_crc;
30762306a36Sopenharmony_ci	u8 bootloader_addr;
30862306a36Sopenharmony_ci	u8 *msg_buf;
30962306a36Sopenharmony_ci	u8 t6_status;
31062306a36Sopenharmony_ci	bool update_input;
31162306a36Sopenharmony_ci	u8 last_message_count;
31262306a36Sopenharmony_ci	u8 num_touchids;
31362306a36Sopenharmony_ci	u8 multitouch;
31462306a36Sopenharmony_ci	struct t7_config t7_cfg;
31562306a36Sopenharmony_ci	struct mxt_dbg dbg;
31662306a36Sopenharmony_ci	struct regulator_bulk_data regulators[2];
31762306a36Sopenharmony_ci	struct gpio_desc *reset_gpio;
31862306a36Sopenharmony_ci	struct gpio_desc *wake_gpio;
31962306a36Sopenharmony_ci	bool use_retrigen_workaround;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/* Cached parameters from object table */
32262306a36Sopenharmony_ci	u16 T5_address;
32362306a36Sopenharmony_ci	u8 T5_msg_size;
32462306a36Sopenharmony_ci	u8 T6_reportid;
32562306a36Sopenharmony_ci	u16 T6_address;
32662306a36Sopenharmony_ci	u16 T7_address;
32762306a36Sopenharmony_ci	u16 T71_address;
32862306a36Sopenharmony_ci	u8 T9_reportid_min;
32962306a36Sopenharmony_ci	u8 T9_reportid_max;
33062306a36Sopenharmony_ci	u8 T15_reportid_min;
33162306a36Sopenharmony_ci	u8 T15_reportid_max;
33262306a36Sopenharmony_ci	u16 T18_address;
33362306a36Sopenharmony_ci	u8 T19_reportid;
33462306a36Sopenharmony_ci	u16 T44_address;
33562306a36Sopenharmony_ci	u8 T97_reportid_min;
33662306a36Sopenharmony_ci	u8 T97_reportid_max;
33762306a36Sopenharmony_ci	u8 T100_reportid_min;
33862306a36Sopenharmony_ci	u8 T100_reportid_max;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* for fw update in bootloader */
34162306a36Sopenharmony_ci	struct completion bl_completion;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* for reset handling */
34462306a36Sopenharmony_ci	struct completion reset_completion;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/* for config update handling */
34762306a36Sopenharmony_ci	struct completion crc_completion;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	u32 *t19_keymap;
35062306a36Sopenharmony_ci	unsigned int t19_num_keys;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	u32 *t15_keymap;
35362306a36Sopenharmony_ci	unsigned int t15_num_keys;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	enum mxt_suspend_mode suspend_mode;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	u32 wakeup_method;
35862306a36Sopenharmony_ci};
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistruct mxt_vb2_buffer {
36162306a36Sopenharmony_ci	struct vb2_buffer	vb;
36262306a36Sopenharmony_ci	struct list_head	list;
36362306a36Sopenharmony_ci};
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic size_t mxt_obj_size(const struct mxt_object *obj)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	return obj->size_minus_one + 1;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic size_t mxt_obj_instances(const struct mxt_object *obj)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	return obj->instances_minus_one + 1;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic bool mxt_object_readable(unsigned int type)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	switch (type) {
37862306a36Sopenharmony_ci	case MXT_GEN_COMMAND_T6:
37962306a36Sopenharmony_ci	case MXT_GEN_POWER_T7:
38062306a36Sopenharmony_ci	case MXT_GEN_ACQUIRE_T8:
38162306a36Sopenharmony_ci	case MXT_GEN_DATASOURCE_T53:
38262306a36Sopenharmony_ci	case MXT_TOUCH_MULTI_T9:
38362306a36Sopenharmony_ci	case MXT_TOUCH_KEYARRAY_T15:
38462306a36Sopenharmony_ci	case MXT_TOUCH_PROXIMITY_T23:
38562306a36Sopenharmony_ci	case MXT_TOUCH_PROXKEY_T52:
38662306a36Sopenharmony_ci	case MXT_TOUCH_PTC_KEYS_T97:
38762306a36Sopenharmony_ci	case MXT_TOUCH_MULTITOUCHSCREEN_T100:
38862306a36Sopenharmony_ci	case MXT_PROCI_GRIPFACE_T20:
38962306a36Sopenharmony_ci	case MXT_PROCG_NOISE_T22:
39062306a36Sopenharmony_ci	case MXT_PROCI_ONETOUCH_T24:
39162306a36Sopenharmony_ci	case MXT_PROCI_TWOTOUCH_T27:
39262306a36Sopenharmony_ci	case MXT_PROCI_GRIP_T40:
39362306a36Sopenharmony_ci	case MXT_PROCI_PALM_T41:
39462306a36Sopenharmony_ci	case MXT_PROCI_TOUCHSUPPRESSION_T42:
39562306a36Sopenharmony_ci	case MXT_PROCI_STYLUS_T47:
39662306a36Sopenharmony_ci	case MXT_PROCG_NOISESUPPRESSION_T48:
39762306a36Sopenharmony_ci	case MXT_SPT_COMMSCONFIG_T18:
39862306a36Sopenharmony_ci	case MXT_SPT_GPIOPWM_T19:
39962306a36Sopenharmony_ci	case MXT_SPT_SELFTEST_T25:
40062306a36Sopenharmony_ci	case MXT_SPT_CTECONFIG_T28:
40162306a36Sopenharmony_ci	case MXT_SPT_USERDATA_T38:
40262306a36Sopenharmony_ci	case MXT_SPT_DIGITIZER_T43:
40362306a36Sopenharmony_ci	case MXT_SPT_CTECONFIG_T46:
40462306a36Sopenharmony_ci	case MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71:
40562306a36Sopenharmony_ci		return true;
40662306a36Sopenharmony_ci	default:
40762306a36Sopenharmony_ci		return false;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic void mxt_dump_message(struct mxt_data *data, u8 *message)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	dev_dbg(&data->client->dev, "message: %*ph\n",
41462306a36Sopenharmony_ci		data->T5_msg_size, message);
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic int mxt_wait_for_completion(struct mxt_data *data,
41862306a36Sopenharmony_ci				   struct completion *comp,
41962306a36Sopenharmony_ci				   unsigned int timeout_ms)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
42262306a36Sopenharmony_ci	unsigned long timeout = msecs_to_jiffies(timeout_ms);
42362306a36Sopenharmony_ci	long ret;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	ret = wait_for_completion_interruptible_timeout(comp, timeout);
42662306a36Sopenharmony_ci	if (ret < 0) {
42762306a36Sopenharmony_ci		return ret;
42862306a36Sopenharmony_ci	} else if (ret == 0) {
42962306a36Sopenharmony_ci		dev_err(dev, "Wait for completion timed out.\n");
43062306a36Sopenharmony_ci		return -ETIMEDOUT;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci	return 0;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic int mxt_bootloader_read(struct mxt_data *data,
43662306a36Sopenharmony_ci			       u8 *val, unsigned int count)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	int ret;
43962306a36Sopenharmony_ci	struct i2c_msg msg;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	msg.addr = data->bootloader_addr;
44262306a36Sopenharmony_ci	msg.flags = data->client->flags & I2C_M_TEN;
44362306a36Sopenharmony_ci	msg.flags |= I2C_M_RD;
44462306a36Sopenharmony_ci	msg.len = count;
44562306a36Sopenharmony_ci	msg.buf = val;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	ret = i2c_transfer(data->client->adapter, &msg, 1);
44862306a36Sopenharmony_ci	if (ret == 1) {
44962306a36Sopenharmony_ci		ret = 0;
45062306a36Sopenharmony_ci	} else {
45162306a36Sopenharmony_ci		ret = ret < 0 ? ret : -EIO;
45262306a36Sopenharmony_ci		dev_err(&data->client->dev, "%s: i2c recv failed (%d)\n",
45362306a36Sopenharmony_ci			__func__, ret);
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return ret;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic int mxt_bootloader_write(struct mxt_data *data,
46062306a36Sopenharmony_ci				const u8 * const val, unsigned int count)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	int ret;
46362306a36Sopenharmony_ci	struct i2c_msg msg;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	msg.addr = data->bootloader_addr;
46662306a36Sopenharmony_ci	msg.flags = data->client->flags & I2C_M_TEN;
46762306a36Sopenharmony_ci	msg.len = count;
46862306a36Sopenharmony_ci	msg.buf = (u8 *)val;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	ret = i2c_transfer(data->client->adapter, &msg, 1);
47162306a36Sopenharmony_ci	if (ret == 1) {
47262306a36Sopenharmony_ci		ret = 0;
47362306a36Sopenharmony_ci	} else {
47462306a36Sopenharmony_ci		ret = ret < 0 ? ret : -EIO;
47562306a36Sopenharmony_ci		dev_err(&data->client->dev, "%s: i2c send failed (%d)\n",
47662306a36Sopenharmony_ci			__func__, ret);
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	return ret;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	u8 appmode = data->client->addr;
48562306a36Sopenharmony_ci	u8 bootloader;
48662306a36Sopenharmony_ci	u8 family_id = data->info ? data->info->family_id : 0;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	switch (appmode) {
48962306a36Sopenharmony_ci	case 0x4a:
49062306a36Sopenharmony_ci	case 0x4b:
49162306a36Sopenharmony_ci		/* Chips after 1664S use different scheme */
49262306a36Sopenharmony_ci		if (retry || family_id >= 0xa2) {
49362306a36Sopenharmony_ci			bootloader = appmode - 0x24;
49462306a36Sopenharmony_ci			break;
49562306a36Sopenharmony_ci		}
49662306a36Sopenharmony_ci		fallthrough;	/* for normal case */
49762306a36Sopenharmony_ci	case 0x4c:
49862306a36Sopenharmony_ci	case 0x4d:
49962306a36Sopenharmony_ci	case 0x5a:
50062306a36Sopenharmony_ci	case 0x5b:
50162306a36Sopenharmony_ci		bootloader = appmode - 0x26;
50262306a36Sopenharmony_ci		break;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	default:
50562306a36Sopenharmony_ci		dev_err(&data->client->dev,
50662306a36Sopenharmony_ci			"Appmode i2c address 0x%02x not found\n",
50762306a36Sopenharmony_ci			appmode);
50862306a36Sopenharmony_ci		return -EINVAL;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	data->bootloader_addr = bootloader;
51262306a36Sopenharmony_ci	return 0;
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic int mxt_probe_bootloader(struct mxt_data *data, bool alt_address)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
51862306a36Sopenharmony_ci	int error;
51962306a36Sopenharmony_ci	u8 val;
52062306a36Sopenharmony_ci	bool crc_failure;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	error = mxt_lookup_bootloader_address(data, alt_address);
52362306a36Sopenharmony_ci	if (error)
52462306a36Sopenharmony_ci		return error;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	error = mxt_bootloader_read(data, &val, 1);
52762306a36Sopenharmony_ci	if (error)
52862306a36Sopenharmony_ci		return error;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* Check app crc fail mode */
53162306a36Sopenharmony_ci	crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	dev_err(dev, "Detected bootloader, status:%02X%s\n",
53462306a36Sopenharmony_ci			val, crc_failure ? ", APP_CRC_FAIL" : "");
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return 0;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
54262306a36Sopenharmony_ci	u8 buf[3];
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (val & MXT_BOOT_EXTENDED_ID) {
54562306a36Sopenharmony_ci		if (mxt_bootloader_read(data, &buf[0], 3) != 0) {
54662306a36Sopenharmony_ci			dev_err(dev, "%s: i2c failure\n", __func__);
54762306a36Sopenharmony_ci			return val;
54862306a36Sopenharmony_ci		}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		dev_dbg(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		return buf[0];
55362306a36Sopenharmony_ci	} else {
55462306a36Sopenharmony_ci		dev_dbg(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		return val;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic int mxt_check_bootloader(struct mxt_data *data, unsigned int state,
56162306a36Sopenharmony_ci				bool wait)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
56462306a36Sopenharmony_ci	u8 val;
56562306a36Sopenharmony_ci	int ret;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cirecheck:
56862306a36Sopenharmony_ci	if (wait) {
56962306a36Sopenharmony_ci		/*
57062306a36Sopenharmony_ci		 * In application update mode, the interrupt
57162306a36Sopenharmony_ci		 * line signals state transitions. We must wait for the
57262306a36Sopenharmony_ci		 * CHG assertion before reading the status byte.
57362306a36Sopenharmony_ci		 * Once the status byte has been read, the line is deasserted.
57462306a36Sopenharmony_ci		 */
57562306a36Sopenharmony_ci		ret = mxt_wait_for_completion(data, &data->bl_completion,
57662306a36Sopenharmony_ci					      MXT_FW_CHG_TIMEOUT);
57762306a36Sopenharmony_ci		if (ret) {
57862306a36Sopenharmony_ci			/*
57962306a36Sopenharmony_ci			 * TODO: handle -ERESTARTSYS better by terminating
58062306a36Sopenharmony_ci			 * fw update process before returning to userspace
58162306a36Sopenharmony_ci			 * by writing length 0x000 to device (iff we are in
58262306a36Sopenharmony_ci			 * WAITING_FRAME_DATA state).
58362306a36Sopenharmony_ci			 */
58462306a36Sopenharmony_ci			dev_err(dev, "Update wait error %d\n", ret);
58562306a36Sopenharmony_ci			return ret;
58662306a36Sopenharmony_ci		}
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	ret = mxt_bootloader_read(data, &val, 1);
59062306a36Sopenharmony_ci	if (ret)
59162306a36Sopenharmony_ci		return ret;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (state == MXT_WAITING_BOOTLOAD_CMD)
59462306a36Sopenharmony_ci		val = mxt_get_bootloader_version(data, val);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	switch (state) {
59762306a36Sopenharmony_ci	case MXT_WAITING_BOOTLOAD_CMD:
59862306a36Sopenharmony_ci	case MXT_WAITING_FRAME_DATA:
59962306a36Sopenharmony_ci	case MXT_APP_CRC_FAIL:
60062306a36Sopenharmony_ci		val &= ~MXT_BOOT_STATUS_MASK;
60162306a36Sopenharmony_ci		break;
60262306a36Sopenharmony_ci	case MXT_FRAME_CRC_PASS:
60362306a36Sopenharmony_ci		if (val == MXT_FRAME_CRC_CHECK) {
60462306a36Sopenharmony_ci			goto recheck;
60562306a36Sopenharmony_ci		} else if (val == MXT_FRAME_CRC_FAIL) {
60662306a36Sopenharmony_ci			dev_err(dev, "Bootloader CRC fail\n");
60762306a36Sopenharmony_ci			return -EINVAL;
60862306a36Sopenharmony_ci		}
60962306a36Sopenharmony_ci		break;
61062306a36Sopenharmony_ci	default:
61162306a36Sopenharmony_ci		return -EINVAL;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (val != state) {
61562306a36Sopenharmony_ci		dev_err(dev, "Invalid bootloader state %02X != %02X\n",
61662306a36Sopenharmony_ci			val, state);
61762306a36Sopenharmony_ci		return -EINVAL;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	return 0;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	u8 buf[2];
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	if (unlock) {
62862306a36Sopenharmony_ci		buf[0] = MXT_UNLOCK_CMD_LSB;
62962306a36Sopenharmony_ci		buf[1] = MXT_UNLOCK_CMD_MSB;
63062306a36Sopenharmony_ci	} else {
63162306a36Sopenharmony_ci		buf[0] = 0x01;
63262306a36Sopenharmony_ci		buf[1] = 0x01;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return mxt_bootloader_write(data, buf, sizeof(buf));
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic bool mxt_wakeup_toggle(struct i2c_client *client,
63962306a36Sopenharmony_ci			      bool wake_up, bool in_i2c)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct mxt_data *data = i2c_get_clientdata(client);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	switch (data->wakeup_method) {
64462306a36Sopenharmony_ci	case ATMEL_MXT_WAKEUP_I2C_SCL:
64562306a36Sopenharmony_ci		if (!in_i2c)
64662306a36Sopenharmony_ci			return false;
64762306a36Sopenharmony_ci		break;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	case ATMEL_MXT_WAKEUP_GPIO:
65062306a36Sopenharmony_ci		if (in_i2c)
65162306a36Sopenharmony_ci			return false;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		gpiod_set_value(data->wake_gpio, wake_up);
65462306a36Sopenharmony_ci		break;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	default:
65762306a36Sopenharmony_ci		return false;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (wake_up) {
66162306a36Sopenharmony_ci		dev_dbg(&client->dev, "waking up controller\n");
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		msleep(MXT_WAKEUP_TIME);
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	return true;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic int __mxt_read_reg(struct i2c_client *client,
67062306a36Sopenharmony_ci			       u16 reg, u16 len, void *val)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	struct i2c_msg xfer[2];
67362306a36Sopenharmony_ci	bool retried = false;
67462306a36Sopenharmony_ci	u8 buf[2];
67562306a36Sopenharmony_ci	int ret;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	buf[0] = reg & 0xff;
67862306a36Sopenharmony_ci	buf[1] = (reg >> 8) & 0xff;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	/* Write register */
68162306a36Sopenharmony_ci	xfer[0].addr = client->addr;
68262306a36Sopenharmony_ci	xfer[0].flags = 0;
68362306a36Sopenharmony_ci	xfer[0].len = 2;
68462306a36Sopenharmony_ci	xfer[0].buf = buf;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	/* Read data */
68762306a36Sopenharmony_ci	xfer[1].addr = client->addr;
68862306a36Sopenharmony_ci	xfer[1].flags = I2C_M_RD;
68962306a36Sopenharmony_ci	xfer[1].len = len;
69062306a36Sopenharmony_ci	xfer[1].buf = val;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ciretry:
69362306a36Sopenharmony_ci	ret = i2c_transfer(client->adapter, xfer, 2);
69462306a36Sopenharmony_ci	if (ret == 2) {
69562306a36Sopenharmony_ci		ret = 0;
69662306a36Sopenharmony_ci	} else if (!retried && mxt_wakeup_toggle(client, true, true)) {
69762306a36Sopenharmony_ci		retried = true;
69862306a36Sopenharmony_ci		goto retry;
69962306a36Sopenharmony_ci	} else {
70062306a36Sopenharmony_ci		if (ret >= 0)
70162306a36Sopenharmony_ci			ret = -EIO;
70262306a36Sopenharmony_ci		dev_err(&client->dev, "%s: i2c transfer failed (%d)\n",
70362306a36Sopenharmony_ci			__func__, ret);
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	return ret;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_cistatic int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
71062306a36Sopenharmony_ci			   const void *val)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	bool retried = false;
71362306a36Sopenharmony_ci	u8 *buf;
71462306a36Sopenharmony_ci	size_t count;
71562306a36Sopenharmony_ci	int ret;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	count = len + 2;
71862306a36Sopenharmony_ci	buf = kmalloc(count, GFP_KERNEL);
71962306a36Sopenharmony_ci	if (!buf)
72062306a36Sopenharmony_ci		return -ENOMEM;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	buf[0] = reg & 0xff;
72362306a36Sopenharmony_ci	buf[1] = (reg >> 8) & 0xff;
72462306a36Sopenharmony_ci	memcpy(&buf[2], val, len);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ciretry:
72762306a36Sopenharmony_ci	ret = i2c_master_send(client, buf, count);
72862306a36Sopenharmony_ci	if (ret == count) {
72962306a36Sopenharmony_ci		ret = 0;
73062306a36Sopenharmony_ci	} else if (!retried && mxt_wakeup_toggle(client, true, true)) {
73162306a36Sopenharmony_ci		retried = true;
73262306a36Sopenharmony_ci		goto retry;
73362306a36Sopenharmony_ci	} else {
73462306a36Sopenharmony_ci		if (ret >= 0)
73562306a36Sopenharmony_ci			ret = -EIO;
73662306a36Sopenharmony_ci		dev_err(&client->dev, "%s: i2c send failed (%d)\n",
73762306a36Sopenharmony_ci			__func__, ret);
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	kfree(buf);
74162306a36Sopenharmony_ci	return ret;
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cistatic int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	return __mxt_write_reg(client, reg, 1, &val);
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic struct mxt_object *
75062306a36Sopenharmony_cimxt_get_object(struct mxt_data *data, u8 type)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	struct mxt_object *object;
75362306a36Sopenharmony_ci	int i;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	for (i = 0; i < data->info->object_num; i++) {
75662306a36Sopenharmony_ci		object = data->object_table + i;
75762306a36Sopenharmony_ci		if (object->type == type)
75862306a36Sopenharmony_ci			return object;
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	dev_warn(&data->client->dev, "Invalid object type T%u\n", type);
76262306a36Sopenharmony_ci	return NULL;
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
76862306a36Sopenharmony_ci	u8 status = msg[1];
76962306a36Sopenharmony_ci	u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (crc != data->config_crc) {
77262306a36Sopenharmony_ci		data->config_crc = crc;
77362306a36Sopenharmony_ci		dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc);
77462306a36Sopenharmony_ci	}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	complete(&data->crc_completion);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	/* Detect reset */
77962306a36Sopenharmony_ci	if (status & MXT_T6_STATUS_RESET)
78062306a36Sopenharmony_ci		complete(&data->reset_completion);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	/* Output debug if status has changed */
78362306a36Sopenharmony_ci	if (status != data->t6_status)
78462306a36Sopenharmony_ci		dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n",
78562306a36Sopenharmony_ci			status,
78662306a36Sopenharmony_ci			status == 0 ? " OK" : "",
78762306a36Sopenharmony_ci			status & MXT_T6_STATUS_RESET ? " RESET" : "",
78862306a36Sopenharmony_ci			status & MXT_T6_STATUS_OFL ? " OFL" : "",
78962306a36Sopenharmony_ci			status & MXT_T6_STATUS_SIGERR ? " SIGERR" : "",
79062306a36Sopenharmony_ci			status & MXT_T6_STATUS_CAL ? " CAL" : "",
79162306a36Sopenharmony_ci			status & MXT_T6_STATUS_CFGERR ? " CFGERR" : "",
79262306a36Sopenharmony_ci			status & MXT_T6_STATUS_COMSERR ? " COMSERR" : "");
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/* Save current status */
79562306a36Sopenharmony_ci	data->t6_status = status;
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic int mxt_write_object(struct mxt_data *data,
79962306a36Sopenharmony_ci				 u8 type, u8 offset, u8 val)
80062306a36Sopenharmony_ci{
80162306a36Sopenharmony_ci	struct mxt_object *object;
80262306a36Sopenharmony_ci	u16 reg;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	object = mxt_get_object(data, type);
80562306a36Sopenharmony_ci	if (!object || offset >= mxt_obj_size(object))
80662306a36Sopenharmony_ci		return -EINVAL;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	reg = object->start_address;
80962306a36Sopenharmony_ci	return mxt_write_reg(data->client, reg + offset, val);
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic void mxt_input_button(struct mxt_data *data, u8 *message)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	struct input_dev *input = data->input_dev;
81562306a36Sopenharmony_ci	int i;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	for (i = 0; i < data->t19_num_keys; i++) {
81862306a36Sopenharmony_ci		if (data->t19_keymap[i] == KEY_RESERVED)
81962306a36Sopenharmony_ci			continue;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci		/* Active-low switch */
82262306a36Sopenharmony_ci		input_report_key(input, data->t19_keymap[i],
82362306a36Sopenharmony_ci				 !(message[1] & BIT(i)));
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_cistatic void mxt_input_sync(struct mxt_data *data)
82862306a36Sopenharmony_ci{
82962306a36Sopenharmony_ci	input_mt_report_pointer_emulation(data->input_dev,
83062306a36Sopenharmony_ci					  data->t19_num_keys);
83162306a36Sopenharmony_ci	input_sync(data->input_dev);
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cistatic void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
83762306a36Sopenharmony_ci	struct input_dev *input_dev = data->input_dev;
83862306a36Sopenharmony_ci	int id;
83962306a36Sopenharmony_ci	u8 status;
84062306a36Sopenharmony_ci	int x;
84162306a36Sopenharmony_ci	int y;
84262306a36Sopenharmony_ci	int area;
84362306a36Sopenharmony_ci	int amplitude;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	id = message[0] - data->T9_reportid_min;
84662306a36Sopenharmony_ci	status = message[1];
84762306a36Sopenharmony_ci	x = (message[2] << 4) | ((message[4] >> 4) & 0xf);
84862306a36Sopenharmony_ci	y = (message[3] << 4) | ((message[4] & 0xf));
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	/* Handle 10/12 bit switching */
85162306a36Sopenharmony_ci	if (data->max_x < 1024)
85262306a36Sopenharmony_ci		x >>= 2;
85362306a36Sopenharmony_ci	if (data->max_y < 1024)
85462306a36Sopenharmony_ci		y >>= 2;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	area = message[5];
85762306a36Sopenharmony_ci	amplitude = message[6];
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	dev_dbg(dev,
86062306a36Sopenharmony_ci		"[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n",
86162306a36Sopenharmony_ci		id,
86262306a36Sopenharmony_ci		(status & MXT_T9_DETECT) ? 'D' : '.',
86362306a36Sopenharmony_ci		(status & MXT_T9_PRESS) ? 'P' : '.',
86462306a36Sopenharmony_ci		(status & MXT_T9_RELEASE) ? 'R' : '.',
86562306a36Sopenharmony_ci		(status & MXT_T9_MOVE) ? 'M' : '.',
86662306a36Sopenharmony_ci		(status & MXT_T9_VECTOR) ? 'V' : '.',
86762306a36Sopenharmony_ci		(status & MXT_T9_AMP) ? 'A' : '.',
86862306a36Sopenharmony_ci		(status & MXT_T9_SUPPRESS) ? 'S' : '.',
86962306a36Sopenharmony_ci		(status & MXT_T9_UNGRIP) ? 'U' : '.',
87062306a36Sopenharmony_ci		x, y, area, amplitude);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	input_mt_slot(input_dev, id);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (status & MXT_T9_DETECT) {
87562306a36Sopenharmony_ci		/*
87662306a36Sopenharmony_ci		 * Multiple bits may be set if the host is slow to read
87762306a36Sopenharmony_ci		 * the status messages, indicating all the events that
87862306a36Sopenharmony_ci		 * have happened.
87962306a36Sopenharmony_ci		 */
88062306a36Sopenharmony_ci		if (status & MXT_T9_RELEASE) {
88162306a36Sopenharmony_ci			input_mt_report_slot_inactive(input_dev);
88262306a36Sopenharmony_ci			mxt_input_sync(data);
88362306a36Sopenharmony_ci		}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci		/* if active, pressure must be non-zero */
88662306a36Sopenharmony_ci		if (!amplitude)
88762306a36Sopenharmony_ci			amplitude = MXT_PRESSURE_DEFAULT;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci		/* Touch active */
89062306a36Sopenharmony_ci		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1);
89162306a36Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_POSITION_X, x);
89262306a36Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
89362306a36Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude);
89462306a36Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area);
89562306a36Sopenharmony_ci	} else {
89662306a36Sopenharmony_ci		/* Touch no longer active, close out slot */
89762306a36Sopenharmony_ci		input_mt_report_slot_inactive(input_dev);
89862306a36Sopenharmony_ci	}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	data->update_input = true;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic void mxt_proc_t15_messages(struct mxt_data *data, u8 *message)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct input_dev *input_dev = data->input_dev;
90662306a36Sopenharmony_ci	unsigned long keystates = get_unaligned_le32(&message[2]);
90762306a36Sopenharmony_ci	int key;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	for (key = 0; key < data->t15_num_keys; key++)
91062306a36Sopenharmony_ci		input_report_key(input_dev, data->t15_keymap[key],
91162306a36Sopenharmony_ci				 keystates & BIT(key));
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	data->update_input = true;
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cistatic void mxt_proc_t97_messages(struct mxt_data *data, u8 *message)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	mxt_proc_t15_messages(data, message);
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_cistatic void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
92462306a36Sopenharmony_ci	struct input_dev *input_dev = data->input_dev;
92562306a36Sopenharmony_ci	int id;
92662306a36Sopenharmony_ci	u8 status;
92762306a36Sopenharmony_ci	u8 type = 0;
92862306a36Sopenharmony_ci	u16 x;
92962306a36Sopenharmony_ci	u16 y;
93062306a36Sopenharmony_ci	int distance = 0;
93162306a36Sopenharmony_ci	int tool = 0;
93262306a36Sopenharmony_ci	u8 major = 0;
93362306a36Sopenharmony_ci	u8 pressure = 0;
93462306a36Sopenharmony_ci	u8 orientation = 0;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	id = message[0] - data->T100_reportid_min - 2;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	/* ignore SCRSTATUS events */
93962306a36Sopenharmony_ci	if (id < 0)
94062306a36Sopenharmony_ci		return;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	status = message[1];
94362306a36Sopenharmony_ci	x = get_unaligned_le16(&message[2]);
94462306a36Sopenharmony_ci	y = get_unaligned_le16(&message[4]);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	if (status & MXT_T100_DETECT) {
94762306a36Sopenharmony_ci		type = (status & MXT_T100_TYPE_MASK) >> 4;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci		switch (type) {
95062306a36Sopenharmony_ci		case MXT_T100_TYPE_HOVERING_FINGER:
95162306a36Sopenharmony_ci			tool = MT_TOOL_FINGER;
95262306a36Sopenharmony_ci			distance = MXT_DISTANCE_HOVERING;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci			if (data->t100_aux_vect)
95562306a36Sopenharmony_ci				orientation = message[data->t100_aux_vect];
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci			break;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci		case MXT_T100_TYPE_FINGER:
96062306a36Sopenharmony_ci		case MXT_T100_TYPE_GLOVE:
96162306a36Sopenharmony_ci			tool = MT_TOOL_FINGER;
96262306a36Sopenharmony_ci			distance = MXT_DISTANCE_ACTIVE_TOUCH;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci			if (data->t100_aux_area)
96562306a36Sopenharmony_ci				major = message[data->t100_aux_area];
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci			if (data->t100_aux_ampl)
96862306a36Sopenharmony_ci				pressure = message[data->t100_aux_ampl];
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci			if (data->t100_aux_vect)
97162306a36Sopenharmony_ci				orientation = message[data->t100_aux_vect];
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci			break;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci		case MXT_T100_TYPE_PASSIVE_STYLUS:
97662306a36Sopenharmony_ci			tool = MT_TOOL_PEN;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci			/*
97962306a36Sopenharmony_ci			 * Passive stylus is reported with size zero so
98062306a36Sopenharmony_ci			 * hardcode.
98162306a36Sopenharmony_ci			 */
98262306a36Sopenharmony_ci			major = MXT_TOUCH_MAJOR_DEFAULT;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci			if (data->t100_aux_ampl)
98562306a36Sopenharmony_ci				pressure = message[data->t100_aux_ampl];
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci			break;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci		case MXT_T100_TYPE_LARGE_TOUCH:
99062306a36Sopenharmony_ci			/* Ignore suppressed touch */
99162306a36Sopenharmony_ci			break;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci		default:
99462306a36Sopenharmony_ci			dev_dbg(dev, "Unexpected T100 type\n");
99562306a36Sopenharmony_ci			return;
99662306a36Sopenharmony_ci		}
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	/*
100062306a36Sopenharmony_ci	 * Values reported should be non-zero if tool is touching the
100162306a36Sopenharmony_ci	 * device
100262306a36Sopenharmony_ci	 */
100362306a36Sopenharmony_ci	if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER)
100462306a36Sopenharmony_ci		pressure = MXT_PRESSURE_DEFAULT;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	input_mt_slot(input_dev, id);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	if (status & MXT_T100_DETECT) {
100962306a36Sopenharmony_ci		dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n",
101062306a36Sopenharmony_ci			id, type, x, y, major, pressure, orientation);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci		input_mt_report_slot_state(input_dev, tool, 1);
101362306a36Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_POSITION_X, x);
101462306a36Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
101562306a36Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major);
101662306a36Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
101762306a36Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_DISTANCE, distance);
101862306a36Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation);
101962306a36Sopenharmony_ci	} else {
102062306a36Sopenharmony_ci		dev_dbg(dev, "[%u] release\n", id);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci		/* close out slot */
102362306a36Sopenharmony_ci		input_mt_report_slot_inactive(input_dev);
102462306a36Sopenharmony_ci	}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	data->update_input = true;
102762306a36Sopenharmony_ci}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_cistatic int mxt_proc_message(struct mxt_data *data, u8 *message)
103062306a36Sopenharmony_ci{
103162306a36Sopenharmony_ci	u8 report_id = message[0];
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	if (report_id == MXT_RPTID_NOMSG)
103462306a36Sopenharmony_ci		return 0;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	if (report_id == data->T6_reportid) {
103762306a36Sopenharmony_ci		mxt_proc_t6_messages(data, message);
103862306a36Sopenharmony_ci	} else if (!data->input_dev) {
103962306a36Sopenharmony_ci		/*
104062306a36Sopenharmony_ci		 * Do not report events if input device
104162306a36Sopenharmony_ci		 * is not yet registered.
104262306a36Sopenharmony_ci		 */
104362306a36Sopenharmony_ci		mxt_dump_message(data, message);
104462306a36Sopenharmony_ci	} else if (report_id >= data->T9_reportid_min &&
104562306a36Sopenharmony_ci		   report_id <= data->T9_reportid_max) {
104662306a36Sopenharmony_ci		mxt_proc_t9_message(data, message);
104762306a36Sopenharmony_ci	} else if (report_id >= data->T15_reportid_min &&
104862306a36Sopenharmony_ci		   report_id <= data->T15_reportid_max) {
104962306a36Sopenharmony_ci		mxt_proc_t15_messages(data, message);
105062306a36Sopenharmony_ci	} else if (report_id >= data->T97_reportid_min &&
105162306a36Sopenharmony_ci		   report_id <= data->T97_reportid_max) {
105262306a36Sopenharmony_ci		mxt_proc_t97_messages(data, message);
105362306a36Sopenharmony_ci	} else if (report_id >= data->T100_reportid_min &&
105462306a36Sopenharmony_ci		   report_id <= data->T100_reportid_max) {
105562306a36Sopenharmony_ci		mxt_proc_t100_message(data, message);
105662306a36Sopenharmony_ci	} else if (report_id == data->T19_reportid) {
105762306a36Sopenharmony_ci		mxt_input_button(data, message);
105862306a36Sopenharmony_ci		data->update_input = true;
105962306a36Sopenharmony_ci	} else {
106062306a36Sopenharmony_ci		mxt_dump_message(data, message);
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	return 1;
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_cistatic int mxt_read_and_process_messages(struct mxt_data *data, u8 count)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
106962306a36Sopenharmony_ci	int ret;
107062306a36Sopenharmony_ci	int i;
107162306a36Sopenharmony_ci	u8 num_valid = 0;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	/* Safety check for msg_buf */
107462306a36Sopenharmony_ci	if (count > data->max_reportid)
107562306a36Sopenharmony_ci		return -EINVAL;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	/* Process remaining messages if necessary */
107862306a36Sopenharmony_ci	ret = __mxt_read_reg(data->client, data->T5_address,
107962306a36Sopenharmony_ci				data->T5_msg_size * count, data->msg_buf);
108062306a36Sopenharmony_ci	if (ret) {
108162306a36Sopenharmony_ci		dev_err(dev, "Failed to read %u messages (%d)\n", count, ret);
108262306a36Sopenharmony_ci		return ret;
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	for (i = 0;  i < count; i++) {
108662306a36Sopenharmony_ci		ret = mxt_proc_message(data,
108762306a36Sopenharmony_ci			data->msg_buf + data->T5_msg_size * i);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci		if (ret == 1)
109062306a36Sopenharmony_ci			num_valid++;
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	/* return number of messages read */
109462306a36Sopenharmony_ci	return num_valid;
109562306a36Sopenharmony_ci}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cistatic irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
110062306a36Sopenharmony_ci	int ret;
110162306a36Sopenharmony_ci	u8 count, num_left;
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	/* Read T44 and T5 together */
110462306a36Sopenharmony_ci	ret = __mxt_read_reg(data->client, data->T44_address,
110562306a36Sopenharmony_ci		data->T5_msg_size + 1, data->msg_buf);
110662306a36Sopenharmony_ci	if (ret) {
110762306a36Sopenharmony_ci		dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret);
110862306a36Sopenharmony_ci		return IRQ_NONE;
110962306a36Sopenharmony_ci	}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	count = data->msg_buf[0];
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	/*
111462306a36Sopenharmony_ci	 * This condition may be caused by the CHG line being configured in
111562306a36Sopenharmony_ci	 * Mode 0. It results in unnecessary I2C operations but it is benign.
111662306a36Sopenharmony_ci	 */
111762306a36Sopenharmony_ci	if (count == 0)
111862306a36Sopenharmony_ci		return IRQ_NONE;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	if (count > data->max_reportid) {
112162306a36Sopenharmony_ci		dev_warn(dev, "T44 count %d exceeded max report id\n", count);
112262306a36Sopenharmony_ci		count = data->max_reportid;
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	/* Process first message */
112662306a36Sopenharmony_ci	ret = mxt_proc_message(data, data->msg_buf + 1);
112762306a36Sopenharmony_ci	if (ret < 0) {
112862306a36Sopenharmony_ci		dev_warn(dev, "Unexpected invalid message\n");
112962306a36Sopenharmony_ci		return IRQ_NONE;
113062306a36Sopenharmony_ci	}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	num_left = count - 1;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	/* Process remaining messages if necessary */
113562306a36Sopenharmony_ci	if (num_left) {
113662306a36Sopenharmony_ci		ret = mxt_read_and_process_messages(data, num_left);
113762306a36Sopenharmony_ci		if (ret < 0)
113862306a36Sopenharmony_ci			goto end;
113962306a36Sopenharmony_ci		else if (ret != num_left)
114062306a36Sopenharmony_ci			dev_warn(dev, "Unexpected invalid message\n");
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ciend:
114462306a36Sopenharmony_ci	if (data->update_input) {
114562306a36Sopenharmony_ci		mxt_input_sync(data);
114662306a36Sopenharmony_ci		data->update_input = false;
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	return IRQ_HANDLED;
115062306a36Sopenharmony_ci}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_cistatic int mxt_process_messages_until_invalid(struct mxt_data *data)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
115562306a36Sopenharmony_ci	int count, read;
115662306a36Sopenharmony_ci	u8 tries = 2;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	count = data->max_reportid;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	/* Read messages until we force an invalid */
116162306a36Sopenharmony_ci	do {
116262306a36Sopenharmony_ci		read = mxt_read_and_process_messages(data, count);
116362306a36Sopenharmony_ci		if (read < count)
116462306a36Sopenharmony_ci			return 0;
116562306a36Sopenharmony_ci	} while (--tries);
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	if (data->update_input) {
116862306a36Sopenharmony_ci		mxt_input_sync(data);
116962306a36Sopenharmony_ci		data->update_input = false;
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	dev_err(dev, "CHG pin isn't cleared\n");
117362306a36Sopenharmony_ci	return -EBUSY;
117462306a36Sopenharmony_ci}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_cistatic irqreturn_t mxt_process_messages(struct mxt_data *data)
117762306a36Sopenharmony_ci{
117862306a36Sopenharmony_ci	int total_handled, num_handled;
117962306a36Sopenharmony_ci	u8 count = data->last_message_count;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	if (count < 1 || count > data->max_reportid)
118262306a36Sopenharmony_ci		count = 1;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	/* include final invalid message */
118562306a36Sopenharmony_ci	total_handled = mxt_read_and_process_messages(data, count + 1);
118662306a36Sopenharmony_ci	if (total_handled < 0)
118762306a36Sopenharmony_ci		return IRQ_NONE;
118862306a36Sopenharmony_ci	/* if there were invalid messages, then we are done */
118962306a36Sopenharmony_ci	else if (total_handled <= count)
119062306a36Sopenharmony_ci		goto update_count;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	/* keep reading two msgs until one is invalid or reportid limit */
119362306a36Sopenharmony_ci	do {
119462306a36Sopenharmony_ci		num_handled = mxt_read_and_process_messages(data, 2);
119562306a36Sopenharmony_ci		if (num_handled < 0)
119662306a36Sopenharmony_ci			return IRQ_NONE;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci		total_handled += num_handled;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci		if (num_handled < 2)
120162306a36Sopenharmony_ci			break;
120262306a36Sopenharmony_ci	} while (total_handled < data->num_touchids);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ciupdate_count:
120562306a36Sopenharmony_ci	data->last_message_count = total_handled;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	if (data->update_input) {
120862306a36Sopenharmony_ci		mxt_input_sync(data);
120962306a36Sopenharmony_ci		data->update_input = false;
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	return IRQ_HANDLED;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_cistatic irqreturn_t mxt_interrupt(int irq, void *dev_id)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	struct mxt_data *data = dev_id;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	if (data->in_bootloader) {
122062306a36Sopenharmony_ci		/* bootloader state transition completion */
122162306a36Sopenharmony_ci		complete(&data->bl_completion);
122262306a36Sopenharmony_ci		return IRQ_HANDLED;
122362306a36Sopenharmony_ci	}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	if (!data->object_table)
122662306a36Sopenharmony_ci		return IRQ_HANDLED;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if (data->T44_address) {
122962306a36Sopenharmony_ci		return mxt_process_messages_t44(data);
123062306a36Sopenharmony_ci	} else {
123162306a36Sopenharmony_ci		return mxt_process_messages(data);
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic int mxt_t6_command(struct mxt_data *data, u16 cmd_offset,
123662306a36Sopenharmony_ci			  u8 value, bool wait)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	u16 reg;
123962306a36Sopenharmony_ci	u8 command_register;
124062306a36Sopenharmony_ci	int timeout_counter = 0;
124162306a36Sopenharmony_ci	int ret;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	reg = data->T6_address + cmd_offset;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	ret = mxt_write_reg(data->client, reg, value);
124662306a36Sopenharmony_ci	if (ret)
124762306a36Sopenharmony_ci		return ret;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	if (!wait)
125062306a36Sopenharmony_ci		return 0;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	do {
125362306a36Sopenharmony_ci		msleep(20);
125462306a36Sopenharmony_ci		ret = __mxt_read_reg(data->client, reg, 1, &command_register);
125562306a36Sopenharmony_ci		if (ret)
125662306a36Sopenharmony_ci			return ret;
125762306a36Sopenharmony_ci	} while (command_register != 0 && timeout_counter++ <= 100);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	if (timeout_counter > 100) {
126062306a36Sopenharmony_ci		dev_err(&data->client->dev, "Command failed!\n");
126162306a36Sopenharmony_ci		return -EIO;
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	return 0;
126562306a36Sopenharmony_ci}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_cistatic int mxt_acquire_irq(struct mxt_data *data)
126862306a36Sopenharmony_ci{
126962306a36Sopenharmony_ci	int error;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	enable_irq(data->irq);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	if (data->use_retrigen_workaround) {
127462306a36Sopenharmony_ci		error = mxt_process_messages_until_invalid(data);
127562306a36Sopenharmony_ci		if (error)
127662306a36Sopenharmony_ci			return error;
127762306a36Sopenharmony_ci	}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	return 0;
128062306a36Sopenharmony_ci}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_cistatic int mxt_soft_reset(struct mxt_data *data)
128362306a36Sopenharmony_ci{
128462306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
128562306a36Sopenharmony_ci	int ret = 0;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	dev_info(dev, "Resetting device\n");
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	disable_irq(data->irq);
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	reinit_completion(&data->reset_completion);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false);
129462306a36Sopenharmony_ci	if (ret)
129562306a36Sopenharmony_ci		return ret;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	/* Ignore CHG line for 100ms after reset */
129862306a36Sopenharmony_ci	msleep(MXT_RESET_INVALID_CHG);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	mxt_acquire_irq(data);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	ret = mxt_wait_for_completion(data, &data->reset_completion,
130362306a36Sopenharmony_ci				      MXT_RESET_TIMEOUT);
130462306a36Sopenharmony_ci	if (ret)
130562306a36Sopenharmony_ci		return ret;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	return 0;
130862306a36Sopenharmony_ci}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_cistatic void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	/*
131362306a36Sopenharmony_ci	 * On failure, CRC is set to 0 and config will always be
131462306a36Sopenharmony_ci	 * downloaded.
131562306a36Sopenharmony_ci	 */
131662306a36Sopenharmony_ci	data->config_crc = 0;
131762306a36Sopenharmony_ci	reinit_completion(&data->crc_completion);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	mxt_t6_command(data, cmd, value, true);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	/*
132262306a36Sopenharmony_ci	 * Wait for crc message. On failure, CRC is set to 0 and config will
132362306a36Sopenharmony_ci	 * always be downloaded.
132462306a36Sopenharmony_ci	 */
132562306a36Sopenharmony_ci	mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT);
132662306a36Sopenharmony_ci}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_cistatic void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte)
132962306a36Sopenharmony_ci{
133062306a36Sopenharmony_ci	static const unsigned int crcpoly = 0x80001B;
133162306a36Sopenharmony_ci	u32 result;
133262306a36Sopenharmony_ci	u32 data_word;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	data_word = (secondbyte << 8) | firstbyte;
133562306a36Sopenharmony_ci	result = ((*crc << 1) ^ data_word);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	if (result & 0x1000000)
133862306a36Sopenharmony_ci		result ^= crcpoly;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	*crc = result;
134162306a36Sopenharmony_ci}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_cistatic u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	u32 crc = 0;
134662306a36Sopenharmony_ci	u8 *ptr = base + start_off;
134762306a36Sopenharmony_ci	u8 *last_val = base + end_off - 1;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	if (end_off < start_off)
135062306a36Sopenharmony_ci		return -EINVAL;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	while (ptr < last_val) {
135362306a36Sopenharmony_ci		mxt_calc_crc24(&crc, *ptr, *(ptr + 1));
135462306a36Sopenharmony_ci		ptr += 2;
135562306a36Sopenharmony_ci	}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	/* if len is odd, fill the last byte with 0 */
135862306a36Sopenharmony_ci	if (ptr == last_val)
135962306a36Sopenharmony_ci		mxt_calc_crc24(&crc, *ptr, 0);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	/* Mask to 24-bit */
136262306a36Sopenharmony_ci	crc &= 0x00FFFFFF;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	return crc;
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistatic int mxt_check_retrigen(struct mxt_data *data)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	struct i2c_client *client = data->client;
137062306a36Sopenharmony_ci	int error;
137162306a36Sopenharmony_ci	int val;
137262306a36Sopenharmony_ci	struct irq_data *irqd;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	data->use_retrigen_workaround = false;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	irqd = irq_get_irq_data(data->irq);
137762306a36Sopenharmony_ci	if (!irqd)
137862306a36Sopenharmony_ci		return -EINVAL;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	if (irqd_is_level_type(irqd))
138162306a36Sopenharmony_ci		return 0;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	if (data->T18_address) {
138462306a36Sopenharmony_ci		error = __mxt_read_reg(client,
138562306a36Sopenharmony_ci				       data->T18_address + MXT_COMMS_CTRL,
138662306a36Sopenharmony_ci				       1, &val);
138762306a36Sopenharmony_ci		if (error)
138862306a36Sopenharmony_ci			return error;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci		if (val & MXT_COMMS_RETRIGEN)
139162306a36Sopenharmony_ci			return 0;
139262306a36Sopenharmony_ci	}
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	dev_warn(&client->dev, "Enabling RETRIGEN workaround\n");
139562306a36Sopenharmony_ci	data->use_retrigen_workaround = true;
139662306a36Sopenharmony_ci	return 0;
139762306a36Sopenharmony_ci}
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_cistatic int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg)
140062306a36Sopenharmony_ci{
140162306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
140262306a36Sopenharmony_ci	struct mxt_object *object;
140362306a36Sopenharmony_ci	unsigned int type, instance, size, byte_offset;
140462306a36Sopenharmony_ci	int offset;
140562306a36Sopenharmony_ci	int ret;
140662306a36Sopenharmony_ci	int i;
140762306a36Sopenharmony_ci	u16 reg;
140862306a36Sopenharmony_ci	u8 val;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	while (cfg->raw_pos < cfg->raw_size) {
141162306a36Sopenharmony_ci		/* Read type, instance, length */
141262306a36Sopenharmony_ci		ret = sscanf(cfg->raw + cfg->raw_pos, "%x %x %x%n",
141362306a36Sopenharmony_ci			     &type, &instance, &size, &offset);
141462306a36Sopenharmony_ci		if (ret == 0) {
141562306a36Sopenharmony_ci			/* EOF */
141662306a36Sopenharmony_ci			break;
141762306a36Sopenharmony_ci		} else if (ret != 3) {
141862306a36Sopenharmony_ci			dev_err(dev, "Bad format: failed to parse object\n");
141962306a36Sopenharmony_ci			return -EINVAL;
142062306a36Sopenharmony_ci		}
142162306a36Sopenharmony_ci		cfg->raw_pos += offset;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci		object = mxt_get_object(data, type);
142462306a36Sopenharmony_ci		if (!object) {
142562306a36Sopenharmony_ci			/* Skip object */
142662306a36Sopenharmony_ci			for (i = 0; i < size; i++) {
142762306a36Sopenharmony_ci				ret = sscanf(cfg->raw + cfg->raw_pos, "%hhx%n",
142862306a36Sopenharmony_ci					     &val, &offset);
142962306a36Sopenharmony_ci				if (ret != 1) {
143062306a36Sopenharmony_ci					dev_err(dev, "Bad format in T%d at %d\n",
143162306a36Sopenharmony_ci						type, i);
143262306a36Sopenharmony_ci					return -EINVAL;
143362306a36Sopenharmony_ci				}
143462306a36Sopenharmony_ci				cfg->raw_pos += offset;
143562306a36Sopenharmony_ci			}
143662306a36Sopenharmony_ci			continue;
143762306a36Sopenharmony_ci		}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci		if (size > mxt_obj_size(object)) {
144062306a36Sopenharmony_ci			/*
144162306a36Sopenharmony_ci			 * Either we are in fallback mode due to wrong
144262306a36Sopenharmony_ci			 * config or config from a later fw version,
144362306a36Sopenharmony_ci			 * or the file is corrupt or hand-edited.
144462306a36Sopenharmony_ci			 */
144562306a36Sopenharmony_ci			dev_warn(dev, "Discarding %zu byte(s) in T%u\n",
144662306a36Sopenharmony_ci				 size - mxt_obj_size(object), type);
144762306a36Sopenharmony_ci		} else if (mxt_obj_size(object) > size) {
144862306a36Sopenharmony_ci			/*
144962306a36Sopenharmony_ci			 * If firmware is upgraded, new bytes may be added to
145062306a36Sopenharmony_ci			 * end of objects. It is generally forward compatible
145162306a36Sopenharmony_ci			 * to zero these bytes - previous behaviour will be
145262306a36Sopenharmony_ci			 * retained. However this does invalidate the CRC and
145362306a36Sopenharmony_ci			 * will force fallback mode until the configuration is
145462306a36Sopenharmony_ci			 * updated. We warn here but do nothing else - the
145562306a36Sopenharmony_ci			 * malloc has zeroed the entire configuration.
145662306a36Sopenharmony_ci			 */
145762306a36Sopenharmony_ci			dev_warn(dev, "Zeroing %zu byte(s) in T%d\n",
145862306a36Sopenharmony_ci				 mxt_obj_size(object) - size, type);
145962306a36Sopenharmony_ci		}
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci		if (instance >= mxt_obj_instances(object)) {
146262306a36Sopenharmony_ci			dev_err(dev, "Object instances exceeded!\n");
146362306a36Sopenharmony_ci			return -EINVAL;
146462306a36Sopenharmony_ci		}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci		reg = object->start_address + mxt_obj_size(object) * instance;
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci		for (i = 0; i < size; i++) {
146962306a36Sopenharmony_ci			ret = sscanf(cfg->raw + cfg->raw_pos, "%hhx%n",
147062306a36Sopenharmony_ci				     &val,
147162306a36Sopenharmony_ci				     &offset);
147262306a36Sopenharmony_ci			if (ret != 1) {
147362306a36Sopenharmony_ci				dev_err(dev, "Bad format in T%d at %d\n",
147462306a36Sopenharmony_ci					type, i);
147562306a36Sopenharmony_ci				return -EINVAL;
147662306a36Sopenharmony_ci			}
147762306a36Sopenharmony_ci			cfg->raw_pos += offset;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci			if (i > mxt_obj_size(object))
148062306a36Sopenharmony_ci				continue;
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci			byte_offset = reg + i - cfg->start_ofs;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci			if (byte_offset >= 0 && byte_offset < cfg->mem_size) {
148562306a36Sopenharmony_ci				*(cfg->mem + byte_offset) = val;
148662306a36Sopenharmony_ci			} else {
148762306a36Sopenharmony_ci				dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n",
148862306a36Sopenharmony_ci					reg, object->type, byte_offset);
148962306a36Sopenharmony_ci				return -EINVAL;
149062306a36Sopenharmony_ci			}
149162306a36Sopenharmony_ci		}
149262306a36Sopenharmony_ci	}
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	return 0;
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_cistatic int mxt_upload_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg)
149862306a36Sopenharmony_ci{
149962306a36Sopenharmony_ci	unsigned int byte_offset = 0;
150062306a36Sopenharmony_ci	int error;
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	/* Write configuration as blocks */
150362306a36Sopenharmony_ci	while (byte_offset < cfg->mem_size) {
150462306a36Sopenharmony_ci		unsigned int size = cfg->mem_size - byte_offset;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci		if (size > MXT_MAX_BLOCK_WRITE)
150762306a36Sopenharmony_ci			size = MXT_MAX_BLOCK_WRITE;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci		error = __mxt_write_reg(data->client,
151062306a36Sopenharmony_ci					cfg->start_ofs + byte_offset,
151162306a36Sopenharmony_ci					size, cfg->mem + byte_offset);
151262306a36Sopenharmony_ci		if (error) {
151362306a36Sopenharmony_ci			dev_err(&data->client->dev,
151462306a36Sopenharmony_ci				"Config write error, ret=%d\n", error);
151562306a36Sopenharmony_ci			return error;
151662306a36Sopenharmony_ci		}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci		byte_offset += size;
151962306a36Sopenharmony_ci	}
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	return 0;
152262306a36Sopenharmony_ci}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_cistatic int mxt_init_t7_power_cfg(struct mxt_data *data);
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci/*
152762306a36Sopenharmony_ci * mxt_update_cfg - download configuration to chip
152862306a36Sopenharmony_ci *
152962306a36Sopenharmony_ci * Atmel Raw Config File Format
153062306a36Sopenharmony_ci *
153162306a36Sopenharmony_ci * The first four lines of the raw config file contain:
153262306a36Sopenharmony_ci *  1) Version
153362306a36Sopenharmony_ci *  2) Chip ID Information (first 7 bytes of device memory)
153462306a36Sopenharmony_ci *  3) Chip Information Block 24-bit CRC Checksum
153562306a36Sopenharmony_ci *  4) Chip Configuration 24-bit CRC Checksum
153662306a36Sopenharmony_ci *
153762306a36Sopenharmony_ci * The rest of the file consists of one line per object instance:
153862306a36Sopenharmony_ci *   <TYPE> <INSTANCE> <SIZE> <CONTENTS>
153962306a36Sopenharmony_ci *
154062306a36Sopenharmony_ci *   <TYPE> - 2-byte object type as hex
154162306a36Sopenharmony_ci *   <INSTANCE> - 2-byte object instance number as hex
154262306a36Sopenharmony_ci *   <SIZE> - 2-byte object size as hex
154362306a36Sopenharmony_ci *   <CONTENTS> - array of <SIZE> 1-byte hex values
154462306a36Sopenharmony_ci */
154562306a36Sopenharmony_cistatic int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw)
154662306a36Sopenharmony_ci{
154762306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
154862306a36Sopenharmony_ci	struct mxt_cfg cfg;
154962306a36Sopenharmony_ci	int ret;
155062306a36Sopenharmony_ci	int offset;
155162306a36Sopenharmony_ci	int i;
155262306a36Sopenharmony_ci	u32 info_crc, config_crc, calculated_crc;
155362306a36Sopenharmony_ci	u16 crc_start = 0;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	/* Make zero terminated copy of the OBP_RAW file */
155662306a36Sopenharmony_ci	cfg.raw = kmemdup_nul(fw->data, fw->size, GFP_KERNEL);
155762306a36Sopenharmony_ci	if (!cfg.raw)
155862306a36Sopenharmony_ci		return -ENOMEM;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	cfg.raw_size = fw->size;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	if (strncmp(cfg.raw, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) {
156562306a36Sopenharmony_ci		dev_err(dev, "Unrecognised config file\n");
156662306a36Sopenharmony_ci		ret = -EINVAL;
156762306a36Sopenharmony_ci		goto release_raw;
156862306a36Sopenharmony_ci	}
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	cfg.raw_pos = strlen(MXT_CFG_MAGIC);
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	/* Load information block and check */
157362306a36Sopenharmony_ci	for (i = 0; i < sizeof(struct mxt_info); i++) {
157462306a36Sopenharmony_ci		ret = sscanf(cfg.raw + cfg.raw_pos, "%hhx%n",
157562306a36Sopenharmony_ci			     (unsigned char *)&cfg.info + i,
157662306a36Sopenharmony_ci			     &offset);
157762306a36Sopenharmony_ci		if (ret != 1) {
157862306a36Sopenharmony_ci			dev_err(dev, "Bad format\n");
157962306a36Sopenharmony_ci			ret = -EINVAL;
158062306a36Sopenharmony_ci			goto release_raw;
158162306a36Sopenharmony_ci		}
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci		cfg.raw_pos += offset;
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	if (cfg.info.family_id != data->info->family_id) {
158762306a36Sopenharmony_ci		dev_err(dev, "Family ID mismatch!\n");
158862306a36Sopenharmony_ci		ret = -EINVAL;
158962306a36Sopenharmony_ci		goto release_raw;
159062306a36Sopenharmony_ci	}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	if (cfg.info.variant_id != data->info->variant_id) {
159362306a36Sopenharmony_ci		dev_err(dev, "Variant ID mismatch!\n");
159462306a36Sopenharmony_ci		ret = -EINVAL;
159562306a36Sopenharmony_ci		goto release_raw;
159662306a36Sopenharmony_ci	}
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	/* Read CRCs */
159962306a36Sopenharmony_ci	ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &info_crc, &offset);
160062306a36Sopenharmony_ci	if (ret != 1) {
160162306a36Sopenharmony_ci		dev_err(dev, "Bad format: failed to parse Info CRC\n");
160262306a36Sopenharmony_ci		ret = -EINVAL;
160362306a36Sopenharmony_ci		goto release_raw;
160462306a36Sopenharmony_ci	}
160562306a36Sopenharmony_ci	cfg.raw_pos += offset;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &config_crc, &offset);
160862306a36Sopenharmony_ci	if (ret != 1) {
160962306a36Sopenharmony_ci		dev_err(dev, "Bad format: failed to parse Config CRC\n");
161062306a36Sopenharmony_ci		ret = -EINVAL;
161162306a36Sopenharmony_ci		goto release_raw;
161262306a36Sopenharmony_ci	}
161362306a36Sopenharmony_ci	cfg.raw_pos += offset;
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	/*
161662306a36Sopenharmony_ci	 * The Info Block CRC is calculated over mxt_info and the object
161762306a36Sopenharmony_ci	 * table. If it does not match then we are trying to load the
161862306a36Sopenharmony_ci	 * configuration from a different chip or firmware version, so
161962306a36Sopenharmony_ci	 * the configuration CRC is invalid anyway.
162062306a36Sopenharmony_ci	 */
162162306a36Sopenharmony_ci	if (info_crc == data->info_crc) {
162262306a36Sopenharmony_ci		if (config_crc == 0 || data->config_crc == 0) {
162362306a36Sopenharmony_ci			dev_info(dev, "CRC zero, attempting to apply config\n");
162462306a36Sopenharmony_ci		} else if (config_crc == data->config_crc) {
162562306a36Sopenharmony_ci			dev_dbg(dev, "Config CRC 0x%06X: OK\n",
162662306a36Sopenharmony_ci				 data->config_crc);
162762306a36Sopenharmony_ci			ret = 0;
162862306a36Sopenharmony_ci			goto release_raw;
162962306a36Sopenharmony_ci		} else {
163062306a36Sopenharmony_ci			dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
163162306a36Sopenharmony_ci				 data->config_crc, config_crc);
163262306a36Sopenharmony_ci		}
163362306a36Sopenharmony_ci	} else {
163462306a36Sopenharmony_ci		dev_warn(dev,
163562306a36Sopenharmony_ci			 "Warning: Info CRC error - device=0x%06X file=0x%06X\n",
163662306a36Sopenharmony_ci			 data->info_crc, info_crc);
163762306a36Sopenharmony_ci	}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	/* Malloc memory to store configuration */
164062306a36Sopenharmony_ci	cfg.start_ofs = MXT_OBJECT_START +
164162306a36Sopenharmony_ci			data->info->object_num * sizeof(struct mxt_object) +
164262306a36Sopenharmony_ci			MXT_INFO_CHECKSUM_SIZE;
164362306a36Sopenharmony_ci	cfg.mem_size = data->mem_size - cfg.start_ofs;
164462306a36Sopenharmony_ci	cfg.mem = kzalloc(cfg.mem_size, GFP_KERNEL);
164562306a36Sopenharmony_ci	if (!cfg.mem) {
164662306a36Sopenharmony_ci		ret = -ENOMEM;
164762306a36Sopenharmony_ci		goto release_raw;
164862306a36Sopenharmony_ci	}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	ret = mxt_prepare_cfg_mem(data, &cfg);
165162306a36Sopenharmony_ci	if (ret)
165262306a36Sopenharmony_ci		goto release_mem;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	/* Calculate crc of the received configs (not the raw config file) */
165562306a36Sopenharmony_ci	if (data->T71_address)
165662306a36Sopenharmony_ci		crc_start = data->T71_address;
165762306a36Sopenharmony_ci	else if (data->T7_address)
165862306a36Sopenharmony_ci		crc_start = data->T7_address;
165962306a36Sopenharmony_ci	else
166062306a36Sopenharmony_ci		dev_warn(dev, "Could not find CRC start\n");
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	if (crc_start > cfg.start_ofs) {
166362306a36Sopenharmony_ci		calculated_crc = mxt_calculate_crc(cfg.mem,
166462306a36Sopenharmony_ci						   crc_start - cfg.start_ofs,
166562306a36Sopenharmony_ci						   cfg.mem_size);
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci		if (config_crc > 0 && config_crc != calculated_crc)
166862306a36Sopenharmony_ci			dev_warn(dev, "Config CRC in file inconsistent, calculated=%06X, file=%06X\n",
166962306a36Sopenharmony_ci				 calculated_crc, config_crc);
167062306a36Sopenharmony_ci	}
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	ret = mxt_upload_cfg_mem(data, &cfg);
167362306a36Sopenharmony_ci	if (ret)
167462306a36Sopenharmony_ci		goto release_mem;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	ret = mxt_check_retrigen(data);
167962306a36Sopenharmony_ci	if (ret)
168062306a36Sopenharmony_ci		goto release_mem;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	ret = mxt_soft_reset(data);
168362306a36Sopenharmony_ci	if (ret)
168462306a36Sopenharmony_ci		goto release_mem;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	dev_info(dev, "Config successfully updated\n");
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	/* T7 config may have changed */
168962306a36Sopenharmony_ci	mxt_init_t7_power_cfg(data);
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_cirelease_mem:
169262306a36Sopenharmony_ci	kfree(cfg.mem);
169362306a36Sopenharmony_cirelease_raw:
169462306a36Sopenharmony_ci	kfree(cfg.raw);
169562306a36Sopenharmony_ci	return ret;
169662306a36Sopenharmony_ci}
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_cistatic void mxt_free_input_device(struct mxt_data *data)
169962306a36Sopenharmony_ci{
170062306a36Sopenharmony_ci	if (data->input_dev) {
170162306a36Sopenharmony_ci		input_unregister_device(data->input_dev);
170262306a36Sopenharmony_ci		data->input_dev = NULL;
170362306a36Sopenharmony_ci	}
170462306a36Sopenharmony_ci}
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_cistatic void mxt_free_object_table(struct mxt_data *data)
170762306a36Sopenharmony_ci{
170862306a36Sopenharmony_ci#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37
170962306a36Sopenharmony_ci	video_unregister_device(&data->dbg.vdev);
171062306a36Sopenharmony_ci	v4l2_device_unregister(&data->dbg.v4l2);
171162306a36Sopenharmony_ci#endif
171262306a36Sopenharmony_ci	data->object_table = NULL;
171362306a36Sopenharmony_ci	data->info = NULL;
171462306a36Sopenharmony_ci	kfree(data->raw_info_block);
171562306a36Sopenharmony_ci	data->raw_info_block = NULL;
171662306a36Sopenharmony_ci	kfree(data->msg_buf);
171762306a36Sopenharmony_ci	data->msg_buf = NULL;
171862306a36Sopenharmony_ci	data->T5_address = 0;
171962306a36Sopenharmony_ci	data->T5_msg_size = 0;
172062306a36Sopenharmony_ci	data->T6_reportid = 0;
172162306a36Sopenharmony_ci	data->T7_address = 0;
172262306a36Sopenharmony_ci	data->T71_address = 0;
172362306a36Sopenharmony_ci	data->T9_reportid_min = 0;
172462306a36Sopenharmony_ci	data->T9_reportid_max = 0;
172562306a36Sopenharmony_ci	data->T15_reportid_min = 0;
172662306a36Sopenharmony_ci	data->T15_reportid_max = 0;
172762306a36Sopenharmony_ci	data->T18_address = 0;
172862306a36Sopenharmony_ci	data->T19_reportid = 0;
172962306a36Sopenharmony_ci	data->T44_address = 0;
173062306a36Sopenharmony_ci	data->T97_reportid_min = 0;
173162306a36Sopenharmony_ci	data->T97_reportid_max = 0;
173262306a36Sopenharmony_ci	data->T100_reportid_min = 0;
173362306a36Sopenharmony_ci	data->T100_reportid_max = 0;
173462306a36Sopenharmony_ci	data->max_reportid = 0;
173562306a36Sopenharmony_ci}
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_cistatic int mxt_parse_object_table(struct mxt_data *data,
173862306a36Sopenharmony_ci				  struct mxt_object *object_table)
173962306a36Sopenharmony_ci{
174062306a36Sopenharmony_ci	struct i2c_client *client = data->client;
174162306a36Sopenharmony_ci	int i;
174262306a36Sopenharmony_ci	u8 reportid;
174362306a36Sopenharmony_ci	u16 end_address;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	/* Valid Report IDs start counting from 1 */
174662306a36Sopenharmony_ci	reportid = 1;
174762306a36Sopenharmony_ci	data->mem_size = 0;
174862306a36Sopenharmony_ci	for (i = 0; i < data->info->object_num; i++) {
174962306a36Sopenharmony_ci		struct mxt_object *object = object_table + i;
175062306a36Sopenharmony_ci		u8 min_id, max_id;
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci		le16_to_cpus(&object->start_address);
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci		if (object->num_report_ids) {
175562306a36Sopenharmony_ci			min_id = reportid;
175662306a36Sopenharmony_ci			reportid += object->num_report_ids *
175762306a36Sopenharmony_ci					mxt_obj_instances(object);
175862306a36Sopenharmony_ci			max_id = reportid - 1;
175962306a36Sopenharmony_ci		} else {
176062306a36Sopenharmony_ci			min_id = 0;
176162306a36Sopenharmony_ci			max_id = 0;
176262306a36Sopenharmony_ci		}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci		dev_dbg(&data->client->dev,
176562306a36Sopenharmony_ci			"T%u Start:%u Size:%zu Instances:%zu Report IDs:%u-%u\n",
176662306a36Sopenharmony_ci			object->type, object->start_address,
176762306a36Sopenharmony_ci			mxt_obj_size(object), mxt_obj_instances(object),
176862306a36Sopenharmony_ci			min_id, max_id);
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci		switch (object->type) {
177162306a36Sopenharmony_ci		case MXT_GEN_MESSAGE_T5:
177262306a36Sopenharmony_ci			if (data->info->family_id == 0x80 &&
177362306a36Sopenharmony_ci			    data->info->version < 0x20) {
177462306a36Sopenharmony_ci				/*
177562306a36Sopenharmony_ci				 * On mXT224 firmware versions prior to V2.0
177662306a36Sopenharmony_ci				 * read and discard unused CRC byte otherwise
177762306a36Sopenharmony_ci				 * DMA reads are misaligned.
177862306a36Sopenharmony_ci				 */
177962306a36Sopenharmony_ci				data->T5_msg_size = mxt_obj_size(object);
178062306a36Sopenharmony_ci			} else {
178162306a36Sopenharmony_ci				/* CRC not enabled, so skip last byte */
178262306a36Sopenharmony_ci				data->T5_msg_size = mxt_obj_size(object) - 1;
178362306a36Sopenharmony_ci			}
178462306a36Sopenharmony_ci			data->T5_address = object->start_address;
178562306a36Sopenharmony_ci			break;
178662306a36Sopenharmony_ci		case MXT_GEN_COMMAND_T6:
178762306a36Sopenharmony_ci			data->T6_reportid = min_id;
178862306a36Sopenharmony_ci			data->T6_address = object->start_address;
178962306a36Sopenharmony_ci			break;
179062306a36Sopenharmony_ci		case MXT_GEN_POWER_T7:
179162306a36Sopenharmony_ci			data->T7_address = object->start_address;
179262306a36Sopenharmony_ci			break;
179362306a36Sopenharmony_ci		case MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71:
179462306a36Sopenharmony_ci			data->T71_address = object->start_address;
179562306a36Sopenharmony_ci			break;
179662306a36Sopenharmony_ci		case MXT_TOUCH_MULTI_T9:
179762306a36Sopenharmony_ci			data->multitouch = MXT_TOUCH_MULTI_T9;
179862306a36Sopenharmony_ci			/* Only handle messages from first T9 instance */
179962306a36Sopenharmony_ci			data->T9_reportid_min = min_id;
180062306a36Sopenharmony_ci			data->T9_reportid_max = min_id +
180162306a36Sopenharmony_ci						object->num_report_ids - 1;
180262306a36Sopenharmony_ci			data->num_touchids = object->num_report_ids;
180362306a36Sopenharmony_ci			break;
180462306a36Sopenharmony_ci		case MXT_TOUCH_KEYARRAY_T15:
180562306a36Sopenharmony_ci			data->T15_reportid_min = min_id;
180662306a36Sopenharmony_ci			data->T15_reportid_max = max_id;
180762306a36Sopenharmony_ci			break;
180862306a36Sopenharmony_ci		case MXT_SPT_COMMSCONFIG_T18:
180962306a36Sopenharmony_ci			data->T18_address = object->start_address;
181062306a36Sopenharmony_ci			break;
181162306a36Sopenharmony_ci		case MXT_SPT_MESSAGECOUNT_T44:
181262306a36Sopenharmony_ci			data->T44_address = object->start_address;
181362306a36Sopenharmony_ci			break;
181462306a36Sopenharmony_ci		case MXT_SPT_GPIOPWM_T19:
181562306a36Sopenharmony_ci			data->T19_reportid = min_id;
181662306a36Sopenharmony_ci			break;
181762306a36Sopenharmony_ci		case MXT_TOUCH_PTC_KEYS_T97:
181862306a36Sopenharmony_ci			data->T97_reportid_min = min_id;
181962306a36Sopenharmony_ci			data->T97_reportid_max = max_id;
182062306a36Sopenharmony_ci			break;
182162306a36Sopenharmony_ci		case MXT_TOUCH_MULTITOUCHSCREEN_T100:
182262306a36Sopenharmony_ci			data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100;
182362306a36Sopenharmony_ci			data->T100_reportid_min = min_id;
182462306a36Sopenharmony_ci			data->T100_reportid_max = max_id;
182562306a36Sopenharmony_ci			/* first two report IDs reserved */
182662306a36Sopenharmony_ci			data->num_touchids = object->num_report_ids - 2;
182762306a36Sopenharmony_ci			break;
182862306a36Sopenharmony_ci		}
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci		end_address = object->start_address
183162306a36Sopenharmony_ci			+ mxt_obj_size(object) * mxt_obj_instances(object) - 1;
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci		if (end_address >= data->mem_size)
183462306a36Sopenharmony_ci			data->mem_size = end_address + 1;
183562306a36Sopenharmony_ci	}
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	/* Store maximum reportid */
183862306a36Sopenharmony_ci	data->max_reportid = reportid;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	/* If T44 exists, T5 position has to be directly after */
184162306a36Sopenharmony_ci	if (data->T44_address && (data->T5_address != data->T44_address + 1)) {
184262306a36Sopenharmony_ci		dev_err(&client->dev, "Invalid T44 position\n");
184362306a36Sopenharmony_ci		return -EINVAL;
184462306a36Sopenharmony_ci	}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	data->msg_buf = kcalloc(data->max_reportid,
184762306a36Sopenharmony_ci				data->T5_msg_size, GFP_KERNEL);
184862306a36Sopenharmony_ci	if (!data->msg_buf)
184962306a36Sopenharmony_ci		return -ENOMEM;
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	return 0;
185262306a36Sopenharmony_ci}
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_cistatic int mxt_read_info_block(struct mxt_data *data)
185562306a36Sopenharmony_ci{
185662306a36Sopenharmony_ci	struct i2c_client *client = data->client;
185762306a36Sopenharmony_ci	int error;
185862306a36Sopenharmony_ci	size_t size;
185962306a36Sopenharmony_ci	void *id_buf, *buf;
186062306a36Sopenharmony_ci	uint8_t num_objects;
186162306a36Sopenharmony_ci	u32 calculated_crc;
186262306a36Sopenharmony_ci	u8 *crc_ptr;
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	/* If info block already allocated, free it */
186562306a36Sopenharmony_ci	if (data->raw_info_block)
186662306a36Sopenharmony_ci		mxt_free_object_table(data);
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	/* Read 7-byte ID information block starting at address 0 */
186962306a36Sopenharmony_ci	size = sizeof(struct mxt_info);
187062306a36Sopenharmony_ci	id_buf = kzalloc(size, GFP_KERNEL);
187162306a36Sopenharmony_ci	if (!id_buf)
187262306a36Sopenharmony_ci		return -ENOMEM;
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	error = __mxt_read_reg(client, 0, size, id_buf);
187562306a36Sopenharmony_ci	if (error)
187662306a36Sopenharmony_ci		goto err_free_mem;
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	/* Resize buffer to give space for rest of info block */
187962306a36Sopenharmony_ci	num_objects = ((struct mxt_info *)id_buf)->object_num;
188062306a36Sopenharmony_ci	size += (num_objects * sizeof(struct mxt_object))
188162306a36Sopenharmony_ci		+ MXT_INFO_CHECKSUM_SIZE;
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	buf = krealloc(id_buf, size, GFP_KERNEL);
188462306a36Sopenharmony_ci	if (!buf) {
188562306a36Sopenharmony_ci		error = -ENOMEM;
188662306a36Sopenharmony_ci		goto err_free_mem;
188762306a36Sopenharmony_ci	}
188862306a36Sopenharmony_ci	id_buf = buf;
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	/* Read rest of info block */
189162306a36Sopenharmony_ci	error = __mxt_read_reg(client, MXT_OBJECT_START,
189262306a36Sopenharmony_ci			       size - MXT_OBJECT_START,
189362306a36Sopenharmony_ci			       id_buf + MXT_OBJECT_START);
189462306a36Sopenharmony_ci	if (error)
189562306a36Sopenharmony_ci		goto err_free_mem;
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	/* Extract & calculate checksum */
189862306a36Sopenharmony_ci	crc_ptr = id_buf + size - MXT_INFO_CHECKSUM_SIZE;
189962306a36Sopenharmony_ci	data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16);
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	calculated_crc = mxt_calculate_crc(id_buf, 0,
190262306a36Sopenharmony_ci					   size - MXT_INFO_CHECKSUM_SIZE);
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci	/*
190562306a36Sopenharmony_ci	 * CRC mismatch can be caused by data corruption due to I2C comms
190662306a36Sopenharmony_ci	 * issue or else device is not using Object Based Protocol (eg i2c-hid)
190762306a36Sopenharmony_ci	 */
190862306a36Sopenharmony_ci	if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) {
190962306a36Sopenharmony_ci		dev_err(&client->dev,
191062306a36Sopenharmony_ci			"Info Block CRC error calculated=0x%06X read=0x%06X\n",
191162306a36Sopenharmony_ci			calculated_crc, data->info_crc);
191262306a36Sopenharmony_ci		error = -EIO;
191362306a36Sopenharmony_ci		goto err_free_mem;
191462306a36Sopenharmony_ci	}
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	data->raw_info_block = id_buf;
191762306a36Sopenharmony_ci	data->info = (struct mxt_info *)id_buf;
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	dev_info(&client->dev,
192062306a36Sopenharmony_ci		 "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
192162306a36Sopenharmony_ci		 data->info->family_id, data->info->variant_id,
192262306a36Sopenharmony_ci		 data->info->version >> 4, data->info->version & 0xf,
192362306a36Sopenharmony_ci		 data->info->build, data->info->object_num);
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci	/* Parse object table information */
192662306a36Sopenharmony_ci	error = mxt_parse_object_table(data, id_buf + MXT_OBJECT_START);
192762306a36Sopenharmony_ci	if (error) {
192862306a36Sopenharmony_ci		dev_err(&client->dev, "Error %d parsing object table\n", error);
192962306a36Sopenharmony_ci		mxt_free_object_table(data);
193062306a36Sopenharmony_ci		return error;
193162306a36Sopenharmony_ci	}
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	data->object_table = (struct mxt_object *)(id_buf + MXT_OBJECT_START);
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	return 0;
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_cierr_free_mem:
193862306a36Sopenharmony_ci	kfree(id_buf);
193962306a36Sopenharmony_ci	return error;
194062306a36Sopenharmony_ci}
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_cistatic int mxt_read_t9_resolution(struct mxt_data *data)
194362306a36Sopenharmony_ci{
194462306a36Sopenharmony_ci	struct i2c_client *client = data->client;
194562306a36Sopenharmony_ci	int error;
194662306a36Sopenharmony_ci	struct t9_range range;
194762306a36Sopenharmony_ci	unsigned char orient;
194862306a36Sopenharmony_ci	struct mxt_object *object;
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
195162306a36Sopenharmony_ci	if (!object)
195262306a36Sopenharmony_ci		return -EINVAL;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	error = __mxt_read_reg(client,
195562306a36Sopenharmony_ci			       object->start_address + MXT_T9_XSIZE,
195662306a36Sopenharmony_ci			       sizeof(data->xsize), &data->xsize);
195762306a36Sopenharmony_ci	if (error)
195862306a36Sopenharmony_ci		return error;
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci	error = __mxt_read_reg(client,
196162306a36Sopenharmony_ci			       object->start_address + MXT_T9_YSIZE,
196262306a36Sopenharmony_ci			       sizeof(data->ysize), &data->ysize);
196362306a36Sopenharmony_ci	if (error)
196462306a36Sopenharmony_ci		return error;
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	error = __mxt_read_reg(client,
196762306a36Sopenharmony_ci			       object->start_address + MXT_T9_RANGE,
196862306a36Sopenharmony_ci			       sizeof(range), &range);
196962306a36Sopenharmony_ci	if (error)
197062306a36Sopenharmony_ci		return error;
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	data->max_x = get_unaligned_le16(&range.x);
197362306a36Sopenharmony_ci	data->max_y = get_unaligned_le16(&range.y);
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	error =  __mxt_read_reg(client,
197662306a36Sopenharmony_ci				object->start_address + MXT_T9_ORIENT,
197762306a36Sopenharmony_ci				1, &orient);
197862306a36Sopenharmony_ci	if (error)
197962306a36Sopenharmony_ci		return error;
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	data->xy_switch = orient & MXT_T9_ORIENT_SWITCH;
198262306a36Sopenharmony_ci	data->invertx = orient & MXT_T9_ORIENT_INVERTX;
198362306a36Sopenharmony_ci	data->inverty = orient & MXT_T9_ORIENT_INVERTY;
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci	return 0;
198662306a36Sopenharmony_ci}
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_cistatic int mxt_read_t100_config(struct mxt_data *data)
198962306a36Sopenharmony_ci{
199062306a36Sopenharmony_ci	struct i2c_client *client = data->client;
199162306a36Sopenharmony_ci	int error;
199262306a36Sopenharmony_ci	struct mxt_object *object;
199362306a36Sopenharmony_ci	u16 range_x, range_y;
199462306a36Sopenharmony_ci	u8 cfg, tchaux;
199562306a36Sopenharmony_ci	u8 aux;
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100);
199862306a36Sopenharmony_ci	if (!object)
199962306a36Sopenharmony_ci		return -EINVAL;
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci	/* read touchscreen dimensions */
200262306a36Sopenharmony_ci	error = __mxt_read_reg(client,
200362306a36Sopenharmony_ci			       object->start_address + MXT_T100_XRANGE,
200462306a36Sopenharmony_ci			       sizeof(range_x), &range_x);
200562306a36Sopenharmony_ci	if (error)
200662306a36Sopenharmony_ci		return error;
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	data->max_x = get_unaligned_le16(&range_x);
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	error = __mxt_read_reg(client,
201162306a36Sopenharmony_ci			       object->start_address + MXT_T100_YRANGE,
201262306a36Sopenharmony_ci			       sizeof(range_y), &range_y);
201362306a36Sopenharmony_ci	if (error)
201462306a36Sopenharmony_ci		return error;
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	data->max_y = get_unaligned_le16(&range_y);
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	error = __mxt_read_reg(client,
201962306a36Sopenharmony_ci			       object->start_address + MXT_T100_XSIZE,
202062306a36Sopenharmony_ci			       sizeof(data->xsize), &data->xsize);
202162306a36Sopenharmony_ci	if (error)
202262306a36Sopenharmony_ci		return error;
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	error = __mxt_read_reg(client,
202562306a36Sopenharmony_ci			       object->start_address + MXT_T100_YSIZE,
202662306a36Sopenharmony_ci			       sizeof(data->ysize), &data->ysize);
202762306a36Sopenharmony_ci	if (error)
202862306a36Sopenharmony_ci		return error;
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci	/* read orientation config */
203162306a36Sopenharmony_ci	error =  __mxt_read_reg(client,
203262306a36Sopenharmony_ci				object->start_address + MXT_T100_CFG1,
203362306a36Sopenharmony_ci				1, &cfg);
203462306a36Sopenharmony_ci	if (error)
203562306a36Sopenharmony_ci		return error;
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci	data->xy_switch = cfg & MXT_T100_CFG_SWITCHXY;
203862306a36Sopenharmony_ci	data->invertx = cfg & MXT_T100_CFG_INVERTX;
203962306a36Sopenharmony_ci	data->inverty = cfg & MXT_T100_CFG_INVERTY;
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	/* allocate aux bytes */
204262306a36Sopenharmony_ci	error =  __mxt_read_reg(client,
204362306a36Sopenharmony_ci				object->start_address + MXT_T100_TCHAUX,
204462306a36Sopenharmony_ci				1, &tchaux);
204562306a36Sopenharmony_ci	if (error)
204662306a36Sopenharmony_ci		return error;
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	aux = 6;
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	if (tchaux & MXT_T100_TCHAUX_VECT)
205162306a36Sopenharmony_ci		data->t100_aux_vect = aux++;
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	if (tchaux & MXT_T100_TCHAUX_AMPL)
205462306a36Sopenharmony_ci		data->t100_aux_ampl = aux++;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	if (tchaux & MXT_T100_TCHAUX_AREA)
205762306a36Sopenharmony_ci		data->t100_aux_area = aux++;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	dev_dbg(&client->dev,
206062306a36Sopenharmony_ci		"T100 aux mappings vect:%u ampl:%u area:%u\n",
206162306a36Sopenharmony_ci		data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area);
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	return 0;
206462306a36Sopenharmony_ci}
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_cistatic int mxt_input_open(struct input_dev *dev);
206762306a36Sopenharmony_cistatic void mxt_input_close(struct input_dev *dev);
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_cistatic void mxt_set_up_as_touchpad(struct input_dev *input_dev,
207062306a36Sopenharmony_ci				   struct mxt_data *data)
207162306a36Sopenharmony_ci{
207262306a36Sopenharmony_ci	int i;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	input_dev->name = "Atmel maXTouch Touchpad";
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
207962306a36Sopenharmony_ci	input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
208062306a36Sopenharmony_ci	input_abs_set_res(input_dev, ABS_MT_POSITION_X,
208162306a36Sopenharmony_ci			  MXT_PIXELS_PER_MM);
208262306a36Sopenharmony_ci	input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
208362306a36Sopenharmony_ci			  MXT_PIXELS_PER_MM);
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	for (i = 0; i < data->t19_num_keys; i++)
208662306a36Sopenharmony_ci		if (data->t19_keymap[i] != KEY_RESERVED)
208762306a36Sopenharmony_ci			input_set_capability(input_dev, EV_KEY,
208862306a36Sopenharmony_ci					     data->t19_keymap[i]);
208962306a36Sopenharmony_ci}
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_cistatic int mxt_initialize_input_device(struct mxt_data *data)
209262306a36Sopenharmony_ci{
209362306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
209462306a36Sopenharmony_ci	struct input_dev *input_dev;
209562306a36Sopenharmony_ci	int error;
209662306a36Sopenharmony_ci	unsigned int num_mt_slots;
209762306a36Sopenharmony_ci	unsigned int mt_flags = 0;
209862306a36Sopenharmony_ci	int i;
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	switch (data->multitouch) {
210162306a36Sopenharmony_ci	case MXT_TOUCH_MULTI_T9:
210262306a36Sopenharmony_ci		num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
210362306a36Sopenharmony_ci		error = mxt_read_t9_resolution(data);
210462306a36Sopenharmony_ci		if (error)
210562306a36Sopenharmony_ci			dev_warn(dev, "Failed to initialize T9 resolution\n");
210662306a36Sopenharmony_ci		break;
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	case MXT_TOUCH_MULTITOUCHSCREEN_T100:
210962306a36Sopenharmony_ci		num_mt_slots = data->num_touchids;
211062306a36Sopenharmony_ci		error = mxt_read_t100_config(data);
211162306a36Sopenharmony_ci		if (error)
211262306a36Sopenharmony_ci			dev_warn(dev, "Failed to read T100 config\n");
211362306a36Sopenharmony_ci		break;
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci	default:
211662306a36Sopenharmony_ci		dev_err(dev, "Invalid multitouch object\n");
211762306a36Sopenharmony_ci		return -EINVAL;
211862306a36Sopenharmony_ci	}
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	/* Handle default values and orientation switch */
212162306a36Sopenharmony_ci	if (data->max_x == 0)
212262306a36Sopenharmony_ci		data->max_x = 1023;
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci	if (data->max_y == 0)
212562306a36Sopenharmony_ci		data->max_y = 1023;
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci	if (data->xy_switch)
212862306a36Sopenharmony_ci		swap(data->max_x, data->max_y);
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	dev_info(dev, "Touchscreen size X%uY%u\n", data->max_x, data->max_y);
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	/* Register input device */
213362306a36Sopenharmony_ci	input_dev = input_allocate_device();
213462306a36Sopenharmony_ci	if (!input_dev)
213562306a36Sopenharmony_ci		return -ENOMEM;
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	input_dev->name = "Atmel maXTouch Touchscreen";
213862306a36Sopenharmony_ci	input_dev->phys = data->phys;
213962306a36Sopenharmony_ci	input_dev->id.bustype = BUS_I2C;
214062306a36Sopenharmony_ci	input_dev->dev.parent = dev;
214162306a36Sopenharmony_ci	input_dev->open = mxt_input_open;
214262306a36Sopenharmony_ci	input_dev->close = mxt_input_close;
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci	input_dev->keycode = data->t15_keymap;
214562306a36Sopenharmony_ci	input_dev->keycodemax = data->t15_num_keys;
214662306a36Sopenharmony_ci	input_dev->keycodesize = sizeof(data->t15_keymap[0]);
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	/* For single touch */
215162306a36Sopenharmony_ci	input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0);
215262306a36Sopenharmony_ci	input_set_abs_params(input_dev, ABS_Y, 0, data->max_y, 0, 0);
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
215562306a36Sopenharmony_ci	    (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
215662306a36Sopenharmony_ci	     data->t100_aux_ampl)) {
215762306a36Sopenharmony_ci		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
215862306a36Sopenharmony_ci	}
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	/* If device has buttons we assume it is a touchpad */
216162306a36Sopenharmony_ci	if (data->t19_num_keys) {
216262306a36Sopenharmony_ci		mxt_set_up_as_touchpad(input_dev, data);
216362306a36Sopenharmony_ci		mt_flags |= INPUT_MT_POINTER;
216462306a36Sopenharmony_ci	} else {
216562306a36Sopenharmony_ci		mt_flags |= INPUT_MT_DIRECT;
216662306a36Sopenharmony_ci	}
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	/* For multi touch */
216962306a36Sopenharmony_ci	error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags);
217062306a36Sopenharmony_ci	if (error) {
217162306a36Sopenharmony_ci		dev_err(dev, "Error %d initialising slots\n", error);
217262306a36Sopenharmony_ci		goto err_free_mem;
217362306a36Sopenharmony_ci	}
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100) {
217662306a36Sopenharmony_ci		input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
217762306a36Sopenharmony_ci				     0, MT_TOOL_MAX, 0, 0);
217862306a36Sopenharmony_ci		input_set_abs_params(input_dev, ABS_MT_DISTANCE,
217962306a36Sopenharmony_ci				     MXT_DISTANCE_ACTIVE_TOUCH,
218062306a36Sopenharmony_ci				     MXT_DISTANCE_HOVERING,
218162306a36Sopenharmony_ci				     0, 0);
218262306a36Sopenharmony_ci	}
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
218562306a36Sopenharmony_ci			     0, data->max_x, 0, 0);
218662306a36Sopenharmony_ci	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
218762306a36Sopenharmony_ci			     0, data->max_y, 0, 0);
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci	if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
219062306a36Sopenharmony_ci	    (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
219162306a36Sopenharmony_ci	     data->t100_aux_area)) {
219262306a36Sopenharmony_ci		input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
219362306a36Sopenharmony_ci				     0, MXT_MAX_AREA, 0, 0);
219462306a36Sopenharmony_ci	}
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
219762306a36Sopenharmony_ci	    (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
219862306a36Sopenharmony_ci	     data->t100_aux_ampl)) {
219962306a36Sopenharmony_ci		input_set_abs_params(input_dev, ABS_MT_PRESSURE,
220062306a36Sopenharmony_ci				     0, 255, 0, 0);
220162306a36Sopenharmony_ci	}
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci	if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
220462306a36Sopenharmony_ci	    data->t100_aux_vect) {
220562306a36Sopenharmony_ci		input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
220662306a36Sopenharmony_ci				     0, 255, 0, 0);
220762306a36Sopenharmony_ci	}
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
221062306a36Sopenharmony_ci	    data->t100_aux_vect) {
221162306a36Sopenharmony_ci		input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
221262306a36Sopenharmony_ci				     0, 255, 0, 0);
221362306a36Sopenharmony_ci	}
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci	/* For T15 and T97 Key Array */
221662306a36Sopenharmony_ci	if (data->T15_reportid_min || data->T97_reportid_min) {
221762306a36Sopenharmony_ci		for (i = 0; i < data->t15_num_keys; i++)
221862306a36Sopenharmony_ci			input_set_capability(input_dev,
221962306a36Sopenharmony_ci					     EV_KEY, data->t15_keymap[i]);
222062306a36Sopenharmony_ci	}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci	input_set_drvdata(input_dev, data);
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	error = input_register_device(input_dev);
222562306a36Sopenharmony_ci	if (error) {
222662306a36Sopenharmony_ci		dev_err(dev, "Error %d registering input device\n", error);
222762306a36Sopenharmony_ci		goto err_free_mem;
222862306a36Sopenharmony_ci	}
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci	data->input_dev = input_dev;
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci	return 0;
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_cierr_free_mem:
223562306a36Sopenharmony_ci	input_free_device(input_dev);
223662306a36Sopenharmony_ci	return error;
223762306a36Sopenharmony_ci}
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_cistatic int mxt_configure_objects(struct mxt_data *data,
224062306a36Sopenharmony_ci				 const struct firmware *cfg);
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_cistatic void mxt_config_cb(const struct firmware *cfg, void *ctx)
224362306a36Sopenharmony_ci{
224462306a36Sopenharmony_ci	mxt_configure_objects(ctx, cfg);
224562306a36Sopenharmony_ci	release_firmware(cfg);
224662306a36Sopenharmony_ci}
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_cistatic int mxt_initialize(struct mxt_data *data)
224962306a36Sopenharmony_ci{
225062306a36Sopenharmony_ci	struct i2c_client *client = data->client;
225162306a36Sopenharmony_ci	int recovery_attempts = 0;
225262306a36Sopenharmony_ci	int error;
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	while (1) {
225562306a36Sopenharmony_ci		error = mxt_read_info_block(data);
225662306a36Sopenharmony_ci		if (!error)
225762306a36Sopenharmony_ci			break;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci		/* Check bootloader state */
226062306a36Sopenharmony_ci		error = mxt_probe_bootloader(data, false);
226162306a36Sopenharmony_ci		if (error) {
226262306a36Sopenharmony_ci			dev_info(&client->dev, "Trying alternate bootloader address\n");
226362306a36Sopenharmony_ci			error = mxt_probe_bootloader(data, true);
226462306a36Sopenharmony_ci			if (error) {
226562306a36Sopenharmony_ci				/* Chip is not in appmode or bootloader mode */
226662306a36Sopenharmony_ci				return error;
226762306a36Sopenharmony_ci			}
226862306a36Sopenharmony_ci		}
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci		/* OK, we are in bootloader, see if we can recover */
227162306a36Sopenharmony_ci		if (++recovery_attempts > 1) {
227262306a36Sopenharmony_ci			dev_err(&client->dev, "Could not recover from bootloader mode\n");
227362306a36Sopenharmony_ci			/*
227462306a36Sopenharmony_ci			 * We can reflash from this state, so do not
227562306a36Sopenharmony_ci			 * abort initialization.
227662306a36Sopenharmony_ci			 */
227762306a36Sopenharmony_ci			data->in_bootloader = true;
227862306a36Sopenharmony_ci			return 0;
227962306a36Sopenharmony_ci		}
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci		/* Attempt to exit bootloader into app mode */
228262306a36Sopenharmony_ci		mxt_send_bootloader_cmd(data, false);
228362306a36Sopenharmony_ci		msleep(MXT_FW_RESET_TIME);
228462306a36Sopenharmony_ci	}
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	error = mxt_check_retrigen(data);
228762306a36Sopenharmony_ci	if (error)
228862306a36Sopenharmony_ci		return error;
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci	error = mxt_acquire_irq(data);
229162306a36Sopenharmony_ci	if (error)
229262306a36Sopenharmony_ci		return error;
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME,
229562306a36Sopenharmony_ci					&client->dev, GFP_KERNEL, data,
229662306a36Sopenharmony_ci					mxt_config_cb);
229762306a36Sopenharmony_ci	if (error) {
229862306a36Sopenharmony_ci		dev_err(&client->dev, "Failed to invoke firmware loader: %d\n",
229962306a36Sopenharmony_ci			error);
230062306a36Sopenharmony_ci		return error;
230162306a36Sopenharmony_ci	}
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	return 0;
230462306a36Sopenharmony_ci}
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_cistatic int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep)
230762306a36Sopenharmony_ci{
230862306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
230962306a36Sopenharmony_ci	int error;
231062306a36Sopenharmony_ci	struct t7_config *new_config;
231162306a36Sopenharmony_ci	struct t7_config deepsleep = { .active = 0, .idle = 0 };
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci	if (sleep == MXT_POWER_CFG_DEEPSLEEP)
231462306a36Sopenharmony_ci		new_config = &deepsleep;
231562306a36Sopenharmony_ci	else
231662306a36Sopenharmony_ci		new_config = &data->t7_cfg;
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	error = __mxt_write_reg(data->client, data->T7_address,
231962306a36Sopenharmony_ci				sizeof(data->t7_cfg), new_config);
232062306a36Sopenharmony_ci	if (error)
232162306a36Sopenharmony_ci		return error;
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n",
232462306a36Sopenharmony_ci		new_config->active, new_config->idle);
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	return 0;
232762306a36Sopenharmony_ci}
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_cistatic int mxt_init_t7_power_cfg(struct mxt_data *data)
233062306a36Sopenharmony_ci{
233162306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
233262306a36Sopenharmony_ci	int error;
233362306a36Sopenharmony_ci	bool retry = false;
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_cirecheck:
233662306a36Sopenharmony_ci	error = __mxt_read_reg(data->client, data->T7_address,
233762306a36Sopenharmony_ci				sizeof(data->t7_cfg), &data->t7_cfg);
233862306a36Sopenharmony_ci	if (error)
233962306a36Sopenharmony_ci		return error;
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci	if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) {
234262306a36Sopenharmony_ci		if (!retry) {
234362306a36Sopenharmony_ci			dev_dbg(dev, "T7 cfg zero, resetting\n");
234462306a36Sopenharmony_ci			mxt_soft_reset(data);
234562306a36Sopenharmony_ci			retry = true;
234662306a36Sopenharmony_ci			goto recheck;
234762306a36Sopenharmony_ci		} else {
234862306a36Sopenharmony_ci			dev_dbg(dev, "T7 cfg zero after reset, overriding\n");
234962306a36Sopenharmony_ci			data->t7_cfg.active = 20;
235062306a36Sopenharmony_ci			data->t7_cfg.idle = 100;
235162306a36Sopenharmony_ci			return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
235262306a36Sopenharmony_ci		}
235362306a36Sopenharmony_ci	}
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci	dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n",
235662306a36Sopenharmony_ci		data->t7_cfg.active, data->t7_cfg.idle);
235762306a36Sopenharmony_ci	return 0;
235862306a36Sopenharmony_ci}
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_ci#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37
236162306a36Sopenharmony_cistatic const struct v4l2_file_operations mxt_video_fops = {
236262306a36Sopenharmony_ci	.owner = THIS_MODULE,
236362306a36Sopenharmony_ci	.open = v4l2_fh_open,
236462306a36Sopenharmony_ci	.release = vb2_fop_release,
236562306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
236662306a36Sopenharmony_ci	.read = vb2_fop_read,
236762306a36Sopenharmony_ci	.mmap = vb2_fop_mmap,
236862306a36Sopenharmony_ci	.poll = vb2_fop_poll,
236962306a36Sopenharmony_ci};
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_cistatic u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x,
237262306a36Sopenharmony_ci			       unsigned int y)
237362306a36Sopenharmony_ci{
237462306a36Sopenharmony_ci	struct mxt_info *info = data->info;
237562306a36Sopenharmony_ci	struct mxt_dbg *dbg = &data->dbg;
237662306a36Sopenharmony_ci	unsigned int ofs, page;
237762306a36Sopenharmony_ci	unsigned int col = 0;
237862306a36Sopenharmony_ci	unsigned int col_width;
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	if (info->family_id == MXT_FAMILY_1386) {
238162306a36Sopenharmony_ci		col_width = info->matrix_ysize / MXT1386_COLUMNS;
238262306a36Sopenharmony_ci		col = y / col_width;
238362306a36Sopenharmony_ci		y = y % col_width;
238462306a36Sopenharmony_ci	} else {
238562306a36Sopenharmony_ci		col_width = info->matrix_ysize;
238662306a36Sopenharmony_ci	}
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	ofs = (y + (x * col_width)) * sizeof(u16);
238962306a36Sopenharmony_ci	page = ofs / MXT_DIAGNOSTIC_SIZE;
239062306a36Sopenharmony_ci	ofs %= MXT_DIAGNOSTIC_SIZE;
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci	if (info->family_id == MXT_FAMILY_1386)
239362306a36Sopenharmony_ci		page += col * MXT1386_PAGES_PER_COLUMN;
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci	return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]);
239662306a36Sopenharmony_ci}
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_cistatic int mxt_convert_debug_pages(struct mxt_data *data, u16 *outbuf)
239962306a36Sopenharmony_ci{
240062306a36Sopenharmony_ci	struct mxt_dbg *dbg = &data->dbg;
240162306a36Sopenharmony_ci	unsigned int x = 0;
240262306a36Sopenharmony_ci	unsigned int y = 0;
240362306a36Sopenharmony_ci	unsigned int i, rx, ry;
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci	for (i = 0; i < dbg->t37_nodes; i++) {
240662306a36Sopenharmony_ci		/* Handle orientation */
240762306a36Sopenharmony_ci		rx = data->xy_switch ? y : x;
240862306a36Sopenharmony_ci		ry = data->xy_switch ? x : y;
240962306a36Sopenharmony_ci		rx = data->invertx ? (data->xsize - 1 - rx) : rx;
241062306a36Sopenharmony_ci		ry = data->inverty ? (data->ysize - 1 - ry) : ry;
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_ci		outbuf[i] = mxt_get_debug_value(data, rx, ry);
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci		/* Next value */
241562306a36Sopenharmony_ci		if (++x >= (data->xy_switch ? data->ysize : data->xsize)) {
241662306a36Sopenharmony_ci			x = 0;
241762306a36Sopenharmony_ci			y++;
241862306a36Sopenharmony_ci		}
241962306a36Sopenharmony_ci	}
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	return 0;
242262306a36Sopenharmony_ci}
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_cistatic int mxt_read_diagnostic_debug(struct mxt_data *data, u8 mode,
242562306a36Sopenharmony_ci				     u16 *outbuf)
242662306a36Sopenharmony_ci{
242762306a36Sopenharmony_ci	struct mxt_dbg *dbg = &data->dbg;
242862306a36Sopenharmony_ci	int retries = 0;
242962306a36Sopenharmony_ci	int page;
243062306a36Sopenharmony_ci	int ret;
243162306a36Sopenharmony_ci	u8 cmd = mode;
243262306a36Sopenharmony_ci	struct t37_debug *p;
243362306a36Sopenharmony_ci	u8 cmd_poll;
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	for (page = 0; page < dbg->t37_pages; page++) {
243662306a36Sopenharmony_ci		p = dbg->t37_buf + page;
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci		ret = mxt_write_reg(data->client, dbg->diag_cmd_address,
243962306a36Sopenharmony_ci				    cmd);
244062306a36Sopenharmony_ci		if (ret)
244162306a36Sopenharmony_ci			return ret;
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci		retries = 0;
244462306a36Sopenharmony_ci		msleep(20);
244562306a36Sopenharmony_ciwait_cmd:
244662306a36Sopenharmony_ci		/* Read back command byte */
244762306a36Sopenharmony_ci		ret = __mxt_read_reg(data->client, dbg->diag_cmd_address,
244862306a36Sopenharmony_ci				     sizeof(cmd_poll), &cmd_poll);
244962306a36Sopenharmony_ci		if (ret)
245062306a36Sopenharmony_ci			return ret;
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci		/* Field is cleared once the command has been processed */
245362306a36Sopenharmony_ci		if (cmd_poll) {
245462306a36Sopenharmony_ci			if (retries++ > 100)
245562306a36Sopenharmony_ci				return -EINVAL;
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci			msleep(20);
245862306a36Sopenharmony_ci			goto wait_cmd;
245962306a36Sopenharmony_ci		}
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_ci		/* Read T37 page */
246262306a36Sopenharmony_ci		ret = __mxt_read_reg(data->client, dbg->t37_address,
246362306a36Sopenharmony_ci				     sizeof(struct t37_debug), p);
246462306a36Sopenharmony_ci		if (ret)
246562306a36Sopenharmony_ci			return ret;
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ci		if (p->mode != mode || p->page != page) {
246862306a36Sopenharmony_ci			dev_err(&data->client->dev, "T37 page mismatch\n");
246962306a36Sopenharmony_ci			return -EINVAL;
247062306a36Sopenharmony_ci		}
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci		dev_dbg(&data->client->dev, "%s page:%d retries:%d\n",
247362306a36Sopenharmony_ci			__func__, page, retries);
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci		/* For remaining pages, write PAGEUP rather than mode */
247662306a36Sopenharmony_ci		cmd = MXT_DIAGNOSTIC_PAGEUP;
247762306a36Sopenharmony_ci	}
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	return mxt_convert_debug_pages(data, outbuf);
248062306a36Sopenharmony_ci}
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_cistatic int mxt_queue_setup(struct vb2_queue *q,
248362306a36Sopenharmony_ci		       unsigned int *nbuffers, unsigned int *nplanes,
248462306a36Sopenharmony_ci		       unsigned int sizes[], struct device *alloc_devs[])
248562306a36Sopenharmony_ci{
248662306a36Sopenharmony_ci	struct mxt_data *data = q->drv_priv;
248762306a36Sopenharmony_ci	size_t size = data->dbg.t37_nodes * sizeof(u16);
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci	if (*nplanes)
249062306a36Sopenharmony_ci		return sizes[0] < size ? -EINVAL : 0;
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci	*nplanes = 1;
249362306a36Sopenharmony_ci	sizes[0] = size;
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	return 0;
249662306a36Sopenharmony_ci}
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_cistatic void mxt_buffer_queue(struct vb2_buffer *vb)
249962306a36Sopenharmony_ci{
250062306a36Sopenharmony_ci	struct mxt_data *data = vb2_get_drv_priv(vb->vb2_queue);
250162306a36Sopenharmony_ci	u16 *ptr;
250262306a36Sopenharmony_ci	int ret;
250362306a36Sopenharmony_ci	u8 mode;
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci	ptr = vb2_plane_vaddr(vb, 0);
250662306a36Sopenharmony_ci	if (!ptr) {
250762306a36Sopenharmony_ci		dev_err(&data->client->dev, "Error acquiring frame ptr\n");
250862306a36Sopenharmony_ci		goto fault;
250962306a36Sopenharmony_ci	}
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci	switch (data->dbg.input) {
251262306a36Sopenharmony_ci	case MXT_V4L_INPUT_DELTAS:
251362306a36Sopenharmony_ci	default:
251462306a36Sopenharmony_ci		mode = MXT_DIAGNOSTIC_DELTAS;
251562306a36Sopenharmony_ci		break;
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci	case MXT_V4L_INPUT_REFS:
251862306a36Sopenharmony_ci		mode = MXT_DIAGNOSTIC_REFS;
251962306a36Sopenharmony_ci		break;
252062306a36Sopenharmony_ci	}
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	ret = mxt_read_diagnostic_debug(data, mode, ptr);
252362306a36Sopenharmony_ci	if (ret)
252462306a36Sopenharmony_ci		goto fault;
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	vb2_set_plane_payload(vb, 0, data->dbg.t37_nodes * sizeof(u16));
252762306a36Sopenharmony_ci	vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
252862306a36Sopenharmony_ci	return;
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_cifault:
253162306a36Sopenharmony_ci	vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
253262306a36Sopenharmony_ci}
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci/* V4L2 structures */
253562306a36Sopenharmony_cistatic const struct vb2_ops mxt_queue_ops = {
253662306a36Sopenharmony_ci	.queue_setup		= mxt_queue_setup,
253762306a36Sopenharmony_ci	.buf_queue		= mxt_buffer_queue,
253862306a36Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
253962306a36Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
254062306a36Sopenharmony_ci};
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_cistatic const struct vb2_queue mxt_queue = {
254362306a36Sopenharmony_ci	.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
254462306a36Sopenharmony_ci	.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ,
254562306a36Sopenharmony_ci	.buf_struct_size = sizeof(struct mxt_vb2_buffer),
254662306a36Sopenharmony_ci	.ops = &mxt_queue_ops,
254762306a36Sopenharmony_ci	.mem_ops = &vb2_vmalloc_memops,
254862306a36Sopenharmony_ci	.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC,
254962306a36Sopenharmony_ci	.min_buffers_needed = 1,
255062306a36Sopenharmony_ci};
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_cistatic int mxt_vidioc_querycap(struct file *file, void *priv,
255362306a36Sopenharmony_ci				 struct v4l2_capability *cap)
255462306a36Sopenharmony_ci{
255562306a36Sopenharmony_ci	struct mxt_data *data = video_drvdata(file);
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci	strscpy(cap->driver, "atmel_mxt_ts", sizeof(cap->driver));
255862306a36Sopenharmony_ci	strscpy(cap->card, "atmel_mxt_ts touch", sizeof(cap->card));
255962306a36Sopenharmony_ci	snprintf(cap->bus_info, sizeof(cap->bus_info),
256062306a36Sopenharmony_ci		 "I2C:%s", dev_name(&data->client->dev));
256162306a36Sopenharmony_ci	return 0;
256262306a36Sopenharmony_ci}
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_cistatic int mxt_vidioc_enum_input(struct file *file, void *priv,
256562306a36Sopenharmony_ci				   struct v4l2_input *i)
256662306a36Sopenharmony_ci{
256762306a36Sopenharmony_ci	if (i->index >= MXT_V4L_INPUT_MAX)
256862306a36Sopenharmony_ci		return -EINVAL;
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	i->type = V4L2_INPUT_TYPE_TOUCH;
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	switch (i->index) {
257362306a36Sopenharmony_ci	case MXT_V4L_INPUT_REFS:
257462306a36Sopenharmony_ci		strscpy(i->name, "Mutual Capacitance References",
257562306a36Sopenharmony_ci			sizeof(i->name));
257662306a36Sopenharmony_ci		break;
257762306a36Sopenharmony_ci	case MXT_V4L_INPUT_DELTAS:
257862306a36Sopenharmony_ci		strscpy(i->name, "Mutual Capacitance Deltas", sizeof(i->name));
257962306a36Sopenharmony_ci		break;
258062306a36Sopenharmony_ci	}
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	return 0;
258362306a36Sopenharmony_ci}
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_cistatic int mxt_set_input(struct mxt_data *data, unsigned int i)
258662306a36Sopenharmony_ci{
258762306a36Sopenharmony_ci	struct v4l2_pix_format *f = &data->dbg.format;
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	if (i >= MXT_V4L_INPUT_MAX)
259062306a36Sopenharmony_ci		return -EINVAL;
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci	if (i == MXT_V4L_INPUT_DELTAS)
259362306a36Sopenharmony_ci		f->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
259462306a36Sopenharmony_ci	else
259562306a36Sopenharmony_ci		f->pixelformat = V4L2_TCH_FMT_TU16;
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	f->width = data->xy_switch ? data->ysize : data->xsize;
259862306a36Sopenharmony_ci	f->height = data->xy_switch ? data->xsize : data->ysize;
259962306a36Sopenharmony_ci	f->field = V4L2_FIELD_NONE;
260062306a36Sopenharmony_ci	f->colorspace = V4L2_COLORSPACE_RAW;
260162306a36Sopenharmony_ci	f->bytesperline = f->width * sizeof(u16);
260262306a36Sopenharmony_ci	f->sizeimage = f->width * f->height * sizeof(u16);
260362306a36Sopenharmony_ci
260462306a36Sopenharmony_ci	data->dbg.input = i;
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ci	return 0;
260762306a36Sopenharmony_ci}
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_cistatic int mxt_vidioc_s_input(struct file *file, void *priv, unsigned int i)
261062306a36Sopenharmony_ci{
261162306a36Sopenharmony_ci	return mxt_set_input(video_drvdata(file), i);
261262306a36Sopenharmony_ci}
261362306a36Sopenharmony_ci
261462306a36Sopenharmony_cistatic int mxt_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
261562306a36Sopenharmony_ci{
261662306a36Sopenharmony_ci	struct mxt_data *data = video_drvdata(file);
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci	*i = data->dbg.input;
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci	return 0;
262162306a36Sopenharmony_ci}
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_cistatic int mxt_vidioc_fmt(struct file *file, void *priv, struct v4l2_format *f)
262462306a36Sopenharmony_ci{
262562306a36Sopenharmony_ci	struct mxt_data *data = video_drvdata(file);
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
262862306a36Sopenharmony_ci	f->fmt.pix = data->dbg.format;
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci	return 0;
263162306a36Sopenharmony_ci}
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_cistatic int mxt_vidioc_enum_fmt(struct file *file, void *priv,
263462306a36Sopenharmony_ci				 struct v4l2_fmtdesc *fmt)
263562306a36Sopenharmony_ci{
263662306a36Sopenharmony_ci	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
263762306a36Sopenharmony_ci		return -EINVAL;
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	switch (fmt->index) {
264062306a36Sopenharmony_ci	case 0:
264162306a36Sopenharmony_ci		fmt->pixelformat = V4L2_TCH_FMT_TU16;
264262306a36Sopenharmony_ci		break;
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci	case 1:
264562306a36Sopenharmony_ci		fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
264662306a36Sopenharmony_ci		break;
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci	default:
264962306a36Sopenharmony_ci		return -EINVAL;
265062306a36Sopenharmony_ci	}
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci	return 0;
265362306a36Sopenharmony_ci}
265462306a36Sopenharmony_ci
265562306a36Sopenharmony_cistatic int mxt_vidioc_g_parm(struct file *file, void *fh,
265662306a36Sopenharmony_ci			     struct v4l2_streamparm *a)
265762306a36Sopenharmony_ci{
265862306a36Sopenharmony_ci	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
265962306a36Sopenharmony_ci		return -EINVAL;
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ci	a->parm.capture.readbuffers = 1;
266262306a36Sopenharmony_ci	a->parm.capture.timeperframe.numerator = 1;
266362306a36Sopenharmony_ci	a->parm.capture.timeperframe.denominator = 10;
266462306a36Sopenharmony_ci	return 0;
266562306a36Sopenharmony_ci}
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops mxt_video_ioctl_ops = {
266862306a36Sopenharmony_ci	.vidioc_querycap        = mxt_vidioc_querycap,
266962306a36Sopenharmony_ci
267062306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap = mxt_vidioc_enum_fmt,
267162306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap   = mxt_vidioc_fmt,
267262306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap   = mxt_vidioc_fmt,
267362306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap	= mxt_vidioc_fmt,
267462306a36Sopenharmony_ci	.vidioc_g_parm		= mxt_vidioc_g_parm,
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci	.vidioc_enum_input      = mxt_vidioc_enum_input,
267762306a36Sopenharmony_ci	.vidioc_g_input         = mxt_vidioc_g_input,
267862306a36Sopenharmony_ci	.vidioc_s_input         = mxt_vidioc_s_input,
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci	.vidioc_reqbufs         = vb2_ioctl_reqbufs,
268162306a36Sopenharmony_ci	.vidioc_create_bufs     = vb2_ioctl_create_bufs,
268262306a36Sopenharmony_ci	.vidioc_querybuf        = vb2_ioctl_querybuf,
268362306a36Sopenharmony_ci	.vidioc_qbuf            = vb2_ioctl_qbuf,
268462306a36Sopenharmony_ci	.vidioc_dqbuf           = vb2_ioctl_dqbuf,
268562306a36Sopenharmony_ci	.vidioc_expbuf          = vb2_ioctl_expbuf,
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci	.vidioc_streamon        = vb2_ioctl_streamon,
268862306a36Sopenharmony_ci	.vidioc_streamoff       = vb2_ioctl_streamoff,
268962306a36Sopenharmony_ci};
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_cistatic const struct video_device mxt_video_device = {
269262306a36Sopenharmony_ci	.name = "Atmel maxTouch",
269362306a36Sopenharmony_ci	.fops = &mxt_video_fops,
269462306a36Sopenharmony_ci	.ioctl_ops = &mxt_video_ioctl_ops,
269562306a36Sopenharmony_ci	.release = video_device_release_empty,
269662306a36Sopenharmony_ci	.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH |
269762306a36Sopenharmony_ci		       V4L2_CAP_READWRITE | V4L2_CAP_STREAMING,
269862306a36Sopenharmony_ci};
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_cistatic void mxt_debug_init(struct mxt_data *data)
270162306a36Sopenharmony_ci{
270262306a36Sopenharmony_ci	struct mxt_info *info = data->info;
270362306a36Sopenharmony_ci	struct mxt_dbg *dbg = &data->dbg;
270462306a36Sopenharmony_ci	struct mxt_object *object;
270562306a36Sopenharmony_ci	int error;
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci	object = mxt_get_object(data, MXT_GEN_COMMAND_T6);
270862306a36Sopenharmony_ci	if (!object)
270962306a36Sopenharmony_ci		goto error;
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ci	dbg->diag_cmd_address = object->start_address + MXT_COMMAND_DIAGNOSTIC;
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_ci	object = mxt_get_object(data, MXT_DEBUG_DIAGNOSTIC_T37);
271462306a36Sopenharmony_ci	if (!object)
271562306a36Sopenharmony_ci		goto error;
271662306a36Sopenharmony_ci
271762306a36Sopenharmony_ci	if (mxt_obj_size(object) != sizeof(struct t37_debug)) {
271862306a36Sopenharmony_ci		dev_warn(&data->client->dev, "Bad T37 size");
271962306a36Sopenharmony_ci		goto error;
272062306a36Sopenharmony_ci	}
272162306a36Sopenharmony_ci
272262306a36Sopenharmony_ci	dbg->t37_address = object->start_address;
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci	/* Calculate size of data and allocate buffer */
272562306a36Sopenharmony_ci	dbg->t37_nodes = data->xsize * data->ysize;
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	if (info->family_id == MXT_FAMILY_1386)
272862306a36Sopenharmony_ci		dbg->t37_pages = MXT1386_COLUMNS * MXT1386_PAGES_PER_COLUMN;
272962306a36Sopenharmony_ci	else
273062306a36Sopenharmony_ci		dbg->t37_pages = DIV_ROUND_UP(data->xsize *
273162306a36Sopenharmony_ci					      info->matrix_ysize *
273262306a36Sopenharmony_ci					      sizeof(u16),
273362306a36Sopenharmony_ci					      sizeof(dbg->t37_buf->data));
273462306a36Sopenharmony_ci
273562306a36Sopenharmony_ci	dbg->t37_buf = devm_kmalloc_array(&data->client->dev, dbg->t37_pages,
273662306a36Sopenharmony_ci					  sizeof(struct t37_debug), GFP_KERNEL);
273762306a36Sopenharmony_ci	if (!dbg->t37_buf)
273862306a36Sopenharmony_ci		goto error;
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	/* init channel to zero */
274162306a36Sopenharmony_ci	mxt_set_input(data, 0);
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ci	/* register video device */
274462306a36Sopenharmony_ci	snprintf(dbg->v4l2.name, sizeof(dbg->v4l2.name), "%s", "atmel_mxt_ts");
274562306a36Sopenharmony_ci	error = v4l2_device_register(&data->client->dev, &dbg->v4l2);
274662306a36Sopenharmony_ci	if (error)
274762306a36Sopenharmony_ci		goto error;
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	/* initialize the queue */
275062306a36Sopenharmony_ci	mutex_init(&dbg->lock);
275162306a36Sopenharmony_ci	dbg->queue = mxt_queue;
275262306a36Sopenharmony_ci	dbg->queue.drv_priv = data;
275362306a36Sopenharmony_ci	dbg->queue.lock = &dbg->lock;
275462306a36Sopenharmony_ci	dbg->queue.dev = &data->client->dev;
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_ci	error = vb2_queue_init(&dbg->queue);
275762306a36Sopenharmony_ci	if (error)
275862306a36Sopenharmony_ci		goto error_unreg_v4l2;
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ci	dbg->vdev = mxt_video_device;
276162306a36Sopenharmony_ci	dbg->vdev.v4l2_dev = &dbg->v4l2;
276262306a36Sopenharmony_ci	dbg->vdev.lock = &dbg->lock;
276362306a36Sopenharmony_ci	dbg->vdev.vfl_dir = VFL_DIR_RX;
276462306a36Sopenharmony_ci	dbg->vdev.queue = &dbg->queue;
276562306a36Sopenharmony_ci	video_set_drvdata(&dbg->vdev, data);
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci	error = video_register_device(&dbg->vdev, VFL_TYPE_TOUCH, -1);
276862306a36Sopenharmony_ci	if (error)
276962306a36Sopenharmony_ci		goto error_unreg_v4l2;
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_ci	return;
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_cierror_unreg_v4l2:
277462306a36Sopenharmony_ci	v4l2_device_unregister(&dbg->v4l2);
277562306a36Sopenharmony_cierror:
277662306a36Sopenharmony_ci	dev_warn(&data->client->dev, "Error initializing T37\n");
277762306a36Sopenharmony_ci}
277862306a36Sopenharmony_ci#else
277962306a36Sopenharmony_cistatic void mxt_debug_init(struct mxt_data *data)
278062306a36Sopenharmony_ci{
278162306a36Sopenharmony_ci}
278262306a36Sopenharmony_ci#endif
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_cistatic int mxt_configure_objects(struct mxt_data *data,
278562306a36Sopenharmony_ci				 const struct firmware *cfg)
278662306a36Sopenharmony_ci{
278762306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
278862306a36Sopenharmony_ci	int error;
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	error = mxt_init_t7_power_cfg(data);
279162306a36Sopenharmony_ci	if (error) {
279262306a36Sopenharmony_ci		dev_err(dev, "Failed to initialize power cfg\n");
279362306a36Sopenharmony_ci		return error;
279462306a36Sopenharmony_ci	}
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci	if (cfg) {
279762306a36Sopenharmony_ci		error = mxt_update_cfg(data, cfg);
279862306a36Sopenharmony_ci		if (error)
279962306a36Sopenharmony_ci			dev_warn(dev, "Error %d updating config\n", error);
280062306a36Sopenharmony_ci	}
280162306a36Sopenharmony_ci
280262306a36Sopenharmony_ci	if (data->multitouch) {
280362306a36Sopenharmony_ci		error = mxt_initialize_input_device(data);
280462306a36Sopenharmony_ci		if (error)
280562306a36Sopenharmony_ci			return error;
280662306a36Sopenharmony_ci	} else {
280762306a36Sopenharmony_ci		dev_warn(dev, "No touch object detected\n");
280862306a36Sopenharmony_ci	}
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	mxt_debug_init(data);
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ci	return 0;
281362306a36Sopenharmony_ci}
281462306a36Sopenharmony_ci
281562306a36Sopenharmony_ci/* Firmware Version is returned as Major.Minor.Build */
281662306a36Sopenharmony_cistatic ssize_t mxt_fw_version_show(struct device *dev,
281762306a36Sopenharmony_ci				   struct device_attribute *attr, char *buf)
281862306a36Sopenharmony_ci{
281962306a36Sopenharmony_ci	struct mxt_data *data = dev_get_drvdata(dev);
282062306a36Sopenharmony_ci	struct mxt_info *info = data->info;
282162306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
282262306a36Sopenharmony_ci			 info->version >> 4, info->version & 0xf, info->build);
282362306a36Sopenharmony_ci}
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci/* Hardware Version is returned as FamilyID.VariantID */
282662306a36Sopenharmony_cistatic ssize_t mxt_hw_version_show(struct device *dev,
282762306a36Sopenharmony_ci				   struct device_attribute *attr, char *buf)
282862306a36Sopenharmony_ci{
282962306a36Sopenharmony_ci	struct mxt_data *data = dev_get_drvdata(dev);
283062306a36Sopenharmony_ci	struct mxt_info *info = data->info;
283162306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
283262306a36Sopenharmony_ci			 info->family_id, info->variant_id);
283362306a36Sopenharmony_ci}
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_cistatic ssize_t mxt_show_instance(char *buf, int count,
283662306a36Sopenharmony_ci				 struct mxt_object *object, int instance,
283762306a36Sopenharmony_ci				 const u8 *val)
283862306a36Sopenharmony_ci{
283962306a36Sopenharmony_ci	int i;
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	if (mxt_obj_instances(object) > 1)
284262306a36Sopenharmony_ci		count += scnprintf(buf + count, PAGE_SIZE - count,
284362306a36Sopenharmony_ci				   "Instance %u\n", instance);
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci	for (i = 0; i < mxt_obj_size(object); i++)
284662306a36Sopenharmony_ci		count += scnprintf(buf + count, PAGE_SIZE - count,
284762306a36Sopenharmony_ci				"\t[%2u]: %02x (%d)\n", i, val[i], val[i]);
284862306a36Sopenharmony_ci	count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_ci	return count;
285162306a36Sopenharmony_ci}
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_cistatic ssize_t mxt_object_show(struct device *dev,
285462306a36Sopenharmony_ci				    struct device_attribute *attr, char *buf)
285562306a36Sopenharmony_ci{
285662306a36Sopenharmony_ci	struct mxt_data *data = dev_get_drvdata(dev);
285762306a36Sopenharmony_ci	struct mxt_object *object;
285862306a36Sopenharmony_ci	int count = 0;
285962306a36Sopenharmony_ci	int i, j;
286062306a36Sopenharmony_ci	int error;
286162306a36Sopenharmony_ci	u8 *obuf;
286262306a36Sopenharmony_ci
286362306a36Sopenharmony_ci	/* Pre-allocate buffer large enough to hold max sized object. */
286462306a36Sopenharmony_ci	obuf = kmalloc(256, GFP_KERNEL);
286562306a36Sopenharmony_ci	if (!obuf)
286662306a36Sopenharmony_ci		return -ENOMEM;
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ci	error = 0;
286962306a36Sopenharmony_ci	for (i = 0; i < data->info->object_num; i++) {
287062306a36Sopenharmony_ci		object = data->object_table + i;
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_ci		if (!mxt_object_readable(object->type))
287362306a36Sopenharmony_ci			continue;
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci		count += scnprintf(buf + count, PAGE_SIZE - count,
287662306a36Sopenharmony_ci				"T%u:\n", object->type);
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_ci		for (j = 0; j < mxt_obj_instances(object); j++) {
287962306a36Sopenharmony_ci			u16 size = mxt_obj_size(object);
288062306a36Sopenharmony_ci			u16 addr = object->start_address + j * size;
288162306a36Sopenharmony_ci
288262306a36Sopenharmony_ci			error = __mxt_read_reg(data->client, addr, size, obuf);
288362306a36Sopenharmony_ci			if (error)
288462306a36Sopenharmony_ci				goto done;
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_ci			count = mxt_show_instance(buf, count, object, j, obuf);
288762306a36Sopenharmony_ci		}
288862306a36Sopenharmony_ci	}
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_cidone:
289162306a36Sopenharmony_ci	kfree(obuf);
289262306a36Sopenharmony_ci	return error ?: count;
289362306a36Sopenharmony_ci}
289462306a36Sopenharmony_ci
289562306a36Sopenharmony_cistatic int mxt_check_firmware_format(struct device *dev,
289662306a36Sopenharmony_ci				     const struct firmware *fw)
289762306a36Sopenharmony_ci{
289862306a36Sopenharmony_ci	unsigned int pos = 0;
289962306a36Sopenharmony_ci	char c;
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci	while (pos < fw->size) {
290262306a36Sopenharmony_ci		c = *(fw->data + pos);
290362306a36Sopenharmony_ci
290462306a36Sopenharmony_ci		if (c < '0' || (c > '9' && c < 'A') || c > 'F')
290562306a36Sopenharmony_ci			return 0;
290662306a36Sopenharmony_ci
290762306a36Sopenharmony_ci		pos++;
290862306a36Sopenharmony_ci	}
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	/*
291162306a36Sopenharmony_ci	 * To convert file try:
291262306a36Sopenharmony_ci	 * xxd -r -p mXTXXX__APP_VX-X-XX.enc > maxtouch.fw
291362306a36Sopenharmony_ci	 */
291462306a36Sopenharmony_ci	dev_err(dev, "Aborting: firmware file must be in binary format\n");
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_ci	return -EINVAL;
291762306a36Sopenharmony_ci}
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_cistatic int mxt_load_fw(struct device *dev, const char *fn)
292062306a36Sopenharmony_ci{
292162306a36Sopenharmony_ci	struct mxt_data *data = dev_get_drvdata(dev);
292262306a36Sopenharmony_ci	const struct firmware *fw = NULL;
292362306a36Sopenharmony_ci	unsigned int frame_size;
292462306a36Sopenharmony_ci	unsigned int pos = 0;
292562306a36Sopenharmony_ci	unsigned int retry = 0;
292662306a36Sopenharmony_ci	unsigned int frame = 0;
292762306a36Sopenharmony_ci	int ret;
292862306a36Sopenharmony_ci
292962306a36Sopenharmony_ci	ret = request_firmware(&fw, fn, dev);
293062306a36Sopenharmony_ci	if (ret) {
293162306a36Sopenharmony_ci		dev_err(dev, "Unable to open firmware %s\n", fn);
293262306a36Sopenharmony_ci		return ret;
293362306a36Sopenharmony_ci	}
293462306a36Sopenharmony_ci
293562306a36Sopenharmony_ci	/* Check for incorrect enc file */
293662306a36Sopenharmony_ci	ret = mxt_check_firmware_format(dev, fw);
293762306a36Sopenharmony_ci	if (ret)
293862306a36Sopenharmony_ci		goto release_firmware;
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	if (!data->in_bootloader) {
294162306a36Sopenharmony_ci		/* Change to the bootloader mode */
294262306a36Sopenharmony_ci		data->in_bootloader = true;
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci		ret = mxt_t6_command(data, MXT_COMMAND_RESET,
294562306a36Sopenharmony_ci				     MXT_BOOT_VALUE, false);
294662306a36Sopenharmony_ci		if (ret)
294762306a36Sopenharmony_ci			goto release_firmware;
294862306a36Sopenharmony_ci
294962306a36Sopenharmony_ci		msleep(MXT_RESET_TIME);
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci		/* Do not need to scan since we know family ID */
295262306a36Sopenharmony_ci		ret = mxt_lookup_bootloader_address(data, 0);
295362306a36Sopenharmony_ci		if (ret)
295462306a36Sopenharmony_ci			goto release_firmware;
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci		mxt_free_input_device(data);
295762306a36Sopenharmony_ci		mxt_free_object_table(data);
295862306a36Sopenharmony_ci	} else {
295962306a36Sopenharmony_ci		enable_irq(data->irq);
296062306a36Sopenharmony_ci	}
296162306a36Sopenharmony_ci
296262306a36Sopenharmony_ci	reinit_completion(&data->bl_completion);
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci	ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false);
296562306a36Sopenharmony_ci	if (ret) {
296662306a36Sopenharmony_ci		/* Bootloader may still be unlocked from previous attempt */
296762306a36Sopenharmony_ci		ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false);
296862306a36Sopenharmony_ci		if (ret)
296962306a36Sopenharmony_ci			goto disable_irq;
297062306a36Sopenharmony_ci	} else {
297162306a36Sopenharmony_ci		dev_info(dev, "Unlocking bootloader\n");
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_ci		/* Unlock bootloader */
297462306a36Sopenharmony_ci		ret = mxt_send_bootloader_cmd(data, true);
297562306a36Sopenharmony_ci		if (ret)
297662306a36Sopenharmony_ci			goto disable_irq;
297762306a36Sopenharmony_ci	}
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci	while (pos < fw->size) {
298062306a36Sopenharmony_ci		ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true);
298162306a36Sopenharmony_ci		if (ret)
298262306a36Sopenharmony_ci			goto disable_irq;
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci		frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci		/* Take account of CRC bytes */
298762306a36Sopenharmony_ci		frame_size += 2;
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci		/* Write one frame to device */
299062306a36Sopenharmony_ci		ret = mxt_bootloader_write(data, fw->data + pos, frame_size);
299162306a36Sopenharmony_ci		if (ret)
299262306a36Sopenharmony_ci			goto disable_irq;
299362306a36Sopenharmony_ci
299462306a36Sopenharmony_ci		ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true);
299562306a36Sopenharmony_ci		if (ret) {
299662306a36Sopenharmony_ci			retry++;
299762306a36Sopenharmony_ci
299862306a36Sopenharmony_ci			/* Back off by 20ms per retry */
299962306a36Sopenharmony_ci			msleep(retry * 20);
300062306a36Sopenharmony_ci
300162306a36Sopenharmony_ci			if (retry > 20) {
300262306a36Sopenharmony_ci				dev_err(dev, "Retry count exceeded\n");
300362306a36Sopenharmony_ci				goto disable_irq;
300462306a36Sopenharmony_ci			}
300562306a36Sopenharmony_ci		} else {
300662306a36Sopenharmony_ci			retry = 0;
300762306a36Sopenharmony_ci			pos += frame_size;
300862306a36Sopenharmony_ci			frame++;
300962306a36Sopenharmony_ci		}
301062306a36Sopenharmony_ci
301162306a36Sopenharmony_ci		if (frame % 50 == 0)
301262306a36Sopenharmony_ci			dev_dbg(dev, "Sent %d frames, %d/%zd bytes\n",
301362306a36Sopenharmony_ci				frame, pos, fw->size);
301462306a36Sopenharmony_ci	}
301562306a36Sopenharmony_ci
301662306a36Sopenharmony_ci	/* Wait for flash. */
301762306a36Sopenharmony_ci	ret = mxt_wait_for_completion(data, &data->bl_completion,
301862306a36Sopenharmony_ci				      MXT_FW_RESET_TIME);
301962306a36Sopenharmony_ci	if (ret)
302062306a36Sopenharmony_ci		goto disable_irq;
302162306a36Sopenharmony_ci
302262306a36Sopenharmony_ci	dev_dbg(dev, "Sent %d frames, %d bytes\n", frame, pos);
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci	/*
302562306a36Sopenharmony_ci	 * Wait for device to reset. Some bootloader versions do not assert
302662306a36Sopenharmony_ci	 * the CHG line after bootloading has finished, so ignore potential
302762306a36Sopenharmony_ci	 * errors.
302862306a36Sopenharmony_ci	 */
302962306a36Sopenharmony_ci	mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME);
303062306a36Sopenharmony_ci
303162306a36Sopenharmony_ci	data->in_bootloader = false;
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_cidisable_irq:
303462306a36Sopenharmony_ci	disable_irq(data->irq);
303562306a36Sopenharmony_cirelease_firmware:
303662306a36Sopenharmony_ci	release_firmware(fw);
303762306a36Sopenharmony_ci	return ret;
303862306a36Sopenharmony_ci}
303962306a36Sopenharmony_ci
304062306a36Sopenharmony_cistatic ssize_t mxt_update_fw_store(struct device *dev,
304162306a36Sopenharmony_ci					struct device_attribute *attr,
304262306a36Sopenharmony_ci					const char *buf, size_t count)
304362306a36Sopenharmony_ci{
304462306a36Sopenharmony_ci	struct mxt_data *data = dev_get_drvdata(dev);
304562306a36Sopenharmony_ci	int error;
304662306a36Sopenharmony_ci
304762306a36Sopenharmony_ci	error = mxt_load_fw(dev, MXT_FW_NAME);
304862306a36Sopenharmony_ci	if (error) {
304962306a36Sopenharmony_ci		dev_err(dev, "The firmware update failed(%d)\n", error);
305062306a36Sopenharmony_ci		count = error;
305162306a36Sopenharmony_ci	} else {
305262306a36Sopenharmony_ci		dev_info(dev, "The firmware update succeeded\n");
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci		error = mxt_initialize(data);
305562306a36Sopenharmony_ci		if (error)
305662306a36Sopenharmony_ci			return error;
305762306a36Sopenharmony_ci	}
305862306a36Sopenharmony_ci
305962306a36Sopenharmony_ci	return count;
306062306a36Sopenharmony_ci}
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_cistatic DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL);
306362306a36Sopenharmony_cistatic DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL);
306462306a36Sopenharmony_cistatic DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
306562306a36Sopenharmony_cistatic DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store);
306662306a36Sopenharmony_ci
306762306a36Sopenharmony_cistatic struct attribute *mxt_attrs[] = {
306862306a36Sopenharmony_ci	&dev_attr_fw_version.attr,
306962306a36Sopenharmony_ci	&dev_attr_hw_version.attr,
307062306a36Sopenharmony_ci	&dev_attr_object.attr,
307162306a36Sopenharmony_ci	&dev_attr_update_fw.attr,
307262306a36Sopenharmony_ci	NULL
307362306a36Sopenharmony_ci};
307462306a36Sopenharmony_ci
307562306a36Sopenharmony_cistatic const struct attribute_group mxt_attr_group = {
307662306a36Sopenharmony_ci	.attrs = mxt_attrs,
307762306a36Sopenharmony_ci};
307862306a36Sopenharmony_ci
307962306a36Sopenharmony_cistatic void mxt_start(struct mxt_data *data)
308062306a36Sopenharmony_ci{
308162306a36Sopenharmony_ci	mxt_wakeup_toggle(data->client, true, false);
308262306a36Sopenharmony_ci
308362306a36Sopenharmony_ci	switch (data->suspend_mode) {
308462306a36Sopenharmony_ci	case MXT_SUSPEND_T9_CTRL:
308562306a36Sopenharmony_ci		mxt_soft_reset(data);
308662306a36Sopenharmony_ci
308762306a36Sopenharmony_ci		/* Touch enable */
308862306a36Sopenharmony_ci		/* 0x83 = SCANEN | RPTEN | ENABLE */
308962306a36Sopenharmony_ci		mxt_write_object(data,
309062306a36Sopenharmony_ci				MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0x83);
309162306a36Sopenharmony_ci		break;
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci	case MXT_SUSPEND_DEEP_SLEEP:
309462306a36Sopenharmony_ci	default:
309562306a36Sopenharmony_ci		mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
309662306a36Sopenharmony_ci
309762306a36Sopenharmony_ci		/* Recalibrate since chip has been in deep sleep */
309862306a36Sopenharmony_ci		mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false);
309962306a36Sopenharmony_ci		break;
310062306a36Sopenharmony_ci	}
310162306a36Sopenharmony_ci}
310262306a36Sopenharmony_ci
310362306a36Sopenharmony_cistatic void mxt_stop(struct mxt_data *data)
310462306a36Sopenharmony_ci{
310562306a36Sopenharmony_ci	switch (data->suspend_mode) {
310662306a36Sopenharmony_ci	case MXT_SUSPEND_T9_CTRL:
310762306a36Sopenharmony_ci		/* Touch disable */
310862306a36Sopenharmony_ci		mxt_write_object(data,
310962306a36Sopenharmony_ci				MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0);
311062306a36Sopenharmony_ci		break;
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ci	case MXT_SUSPEND_DEEP_SLEEP:
311362306a36Sopenharmony_ci	default:
311462306a36Sopenharmony_ci		mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP);
311562306a36Sopenharmony_ci		break;
311662306a36Sopenharmony_ci	}
311762306a36Sopenharmony_ci
311862306a36Sopenharmony_ci	mxt_wakeup_toggle(data->client, false, false);
311962306a36Sopenharmony_ci}
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_cistatic int mxt_input_open(struct input_dev *dev)
312262306a36Sopenharmony_ci{
312362306a36Sopenharmony_ci	struct mxt_data *data = input_get_drvdata(dev);
312462306a36Sopenharmony_ci
312562306a36Sopenharmony_ci	mxt_start(data);
312662306a36Sopenharmony_ci
312762306a36Sopenharmony_ci	return 0;
312862306a36Sopenharmony_ci}
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_cistatic void mxt_input_close(struct input_dev *dev)
313162306a36Sopenharmony_ci{
313262306a36Sopenharmony_ci	struct mxt_data *data = input_get_drvdata(dev);
313362306a36Sopenharmony_ci
313462306a36Sopenharmony_ci	mxt_stop(data);
313562306a36Sopenharmony_ci}
313662306a36Sopenharmony_ci
313762306a36Sopenharmony_cistatic int mxt_parse_device_properties(struct mxt_data *data)
313862306a36Sopenharmony_ci{
313962306a36Sopenharmony_ci	static const char keymap_property[] = "linux,gpio-keymap";
314062306a36Sopenharmony_ci	static const char buttons_property[] = "linux,keycodes";
314162306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
314262306a36Sopenharmony_ci	u32 *keymap;
314362306a36Sopenharmony_ci	u32 *buttonmap;
314462306a36Sopenharmony_ci	int n_keys;
314562306a36Sopenharmony_ci	int error;
314662306a36Sopenharmony_ci
314762306a36Sopenharmony_ci	if (device_property_present(dev, keymap_property)) {
314862306a36Sopenharmony_ci		n_keys = device_property_count_u32(dev, keymap_property);
314962306a36Sopenharmony_ci		if (n_keys <= 0) {
315062306a36Sopenharmony_ci			error = n_keys < 0 ? n_keys : -EINVAL;
315162306a36Sopenharmony_ci			dev_err(dev, "invalid/malformed '%s' property: %d\n",
315262306a36Sopenharmony_ci				keymap_property, error);
315362306a36Sopenharmony_ci			return error;
315462306a36Sopenharmony_ci		}
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci		keymap = devm_kmalloc_array(dev, n_keys, sizeof(*keymap),
315762306a36Sopenharmony_ci					    GFP_KERNEL);
315862306a36Sopenharmony_ci		if (!keymap)
315962306a36Sopenharmony_ci			return -ENOMEM;
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci		error = device_property_read_u32_array(dev, keymap_property,
316262306a36Sopenharmony_ci						       keymap, n_keys);
316362306a36Sopenharmony_ci		if (error) {
316462306a36Sopenharmony_ci			dev_err(dev, "failed to parse '%s' property: %d\n",
316562306a36Sopenharmony_ci				keymap_property, error);
316662306a36Sopenharmony_ci			return error;
316762306a36Sopenharmony_ci		}
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci		data->t19_keymap = keymap;
317062306a36Sopenharmony_ci		data->t19_num_keys = n_keys;
317162306a36Sopenharmony_ci	}
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci	if (device_property_present(dev, buttons_property)) {
317462306a36Sopenharmony_ci		n_keys = device_property_count_u32(dev, buttons_property);
317562306a36Sopenharmony_ci		if (n_keys <= 0) {
317662306a36Sopenharmony_ci			error = n_keys < 0 ? n_keys : -EINVAL;
317762306a36Sopenharmony_ci			dev_err(dev, "invalid/malformed '%s' property: %d\n",
317862306a36Sopenharmony_ci				buttons_property, error);
317962306a36Sopenharmony_ci			return error;
318062306a36Sopenharmony_ci		}
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_ci		buttonmap = devm_kmalloc_array(dev, n_keys, sizeof(*buttonmap),
318362306a36Sopenharmony_ci					       GFP_KERNEL);
318462306a36Sopenharmony_ci		if (!buttonmap)
318562306a36Sopenharmony_ci			return -ENOMEM;
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci		error = device_property_read_u32_array(dev, buttons_property,
318862306a36Sopenharmony_ci						       buttonmap, n_keys);
318962306a36Sopenharmony_ci		if (error) {
319062306a36Sopenharmony_ci			dev_err(dev, "failed to parse '%s' property: %d\n",
319162306a36Sopenharmony_ci				buttons_property, error);
319262306a36Sopenharmony_ci			return error;
319362306a36Sopenharmony_ci		}
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_ci		data->t15_keymap = buttonmap;
319662306a36Sopenharmony_ci		data->t15_num_keys = n_keys;
319762306a36Sopenharmony_ci	}
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci	return 0;
320062306a36Sopenharmony_ci}
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_cistatic const struct dmi_system_id chromebook_T9_suspend_dmi[] = {
320362306a36Sopenharmony_ci	{
320462306a36Sopenharmony_ci		.matches = {
320562306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
320662306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
320762306a36Sopenharmony_ci		},
320862306a36Sopenharmony_ci	},
320962306a36Sopenharmony_ci	{
321062306a36Sopenharmony_ci		.matches = {
321162306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
321262306a36Sopenharmony_ci		},
321362306a36Sopenharmony_ci	},
321462306a36Sopenharmony_ci	{ }
321562306a36Sopenharmony_ci};
321662306a36Sopenharmony_ci
321762306a36Sopenharmony_cistatic int mxt_probe(struct i2c_client *client)
321862306a36Sopenharmony_ci{
321962306a36Sopenharmony_ci	struct mxt_data *data;
322062306a36Sopenharmony_ci	int error;
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci	/*
322362306a36Sopenharmony_ci	 * Ignore devices that do not have device properties attached to
322462306a36Sopenharmony_ci	 * them, as we need help determining whether we are dealing with
322562306a36Sopenharmony_ci	 * touch screen or touchpad.
322662306a36Sopenharmony_ci	 *
322762306a36Sopenharmony_ci	 * So far on x86 the only users of Atmel touch controllers are
322862306a36Sopenharmony_ci	 * Chromebooks, and chromeos_laptop driver will ensure that
322962306a36Sopenharmony_ci	 * necessary properties are provided (if firmware does not do that).
323062306a36Sopenharmony_ci	 */
323162306a36Sopenharmony_ci	if (!device_property_present(&client->dev, "compatible"))
323262306a36Sopenharmony_ci		return -ENXIO;
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_ci	/*
323562306a36Sopenharmony_ci	 * Ignore ACPI devices representing bootloader mode.
323662306a36Sopenharmony_ci	 *
323762306a36Sopenharmony_ci	 * This is a bit of a hack: Google Chromebook BIOS creates ACPI
323862306a36Sopenharmony_ci	 * devices for both application and bootloader modes, but we are
323962306a36Sopenharmony_ci	 * interested in application mode only (if device is in bootloader
324062306a36Sopenharmony_ci	 * mode we'll end up switching into application anyway). So far
324162306a36Sopenharmony_ci	 * application mode addresses were all above 0x40, so we'll use it
324262306a36Sopenharmony_ci	 * as a threshold.
324362306a36Sopenharmony_ci	 */
324462306a36Sopenharmony_ci	if (ACPI_COMPANION(&client->dev) && client->addr < 0x40)
324562306a36Sopenharmony_ci		return -ENXIO;
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci	data = devm_kzalloc(&client->dev, sizeof(struct mxt_data), GFP_KERNEL);
324862306a36Sopenharmony_ci	if (!data)
324962306a36Sopenharmony_ci		return -ENOMEM;
325062306a36Sopenharmony_ci
325162306a36Sopenharmony_ci	snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
325262306a36Sopenharmony_ci		 client->adapter->nr, client->addr);
325362306a36Sopenharmony_ci
325462306a36Sopenharmony_ci	data->client = client;
325562306a36Sopenharmony_ci	data->irq = client->irq;
325662306a36Sopenharmony_ci	i2c_set_clientdata(client, data);
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_ci	init_completion(&data->bl_completion);
325962306a36Sopenharmony_ci	init_completion(&data->reset_completion);
326062306a36Sopenharmony_ci	init_completion(&data->crc_completion);
326162306a36Sopenharmony_ci
326262306a36Sopenharmony_ci	data->suspend_mode = dmi_check_system(chromebook_T9_suspend_dmi) ?
326362306a36Sopenharmony_ci		MXT_SUSPEND_T9_CTRL : MXT_SUSPEND_DEEP_SLEEP;
326462306a36Sopenharmony_ci
326562306a36Sopenharmony_ci	error = mxt_parse_device_properties(data);
326662306a36Sopenharmony_ci	if (error)
326762306a36Sopenharmony_ci		return error;
326862306a36Sopenharmony_ci
326962306a36Sopenharmony_ci	/*
327062306a36Sopenharmony_ci	 * VDDA is the analog voltage supply 2.57..3.47 V
327162306a36Sopenharmony_ci	 * VDD  is the digital voltage supply 1.71..3.47 V
327262306a36Sopenharmony_ci	 */
327362306a36Sopenharmony_ci	data->regulators[0].supply = "vdda";
327462306a36Sopenharmony_ci	data->regulators[1].supply = "vdd";
327562306a36Sopenharmony_ci	error = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(data->regulators),
327662306a36Sopenharmony_ci					data->regulators);
327762306a36Sopenharmony_ci	if (error) {
327862306a36Sopenharmony_ci		if (error != -EPROBE_DEFER)
327962306a36Sopenharmony_ci			dev_err(&client->dev, "Failed to get regulators %d\n",
328062306a36Sopenharmony_ci				error);
328162306a36Sopenharmony_ci		return error;
328262306a36Sopenharmony_ci	}
328362306a36Sopenharmony_ci
328462306a36Sopenharmony_ci	/* Request the RESET line as asserted so we go into reset */
328562306a36Sopenharmony_ci	data->reset_gpio = devm_gpiod_get_optional(&client->dev,
328662306a36Sopenharmony_ci						   "reset", GPIOD_OUT_HIGH);
328762306a36Sopenharmony_ci	if (IS_ERR(data->reset_gpio)) {
328862306a36Sopenharmony_ci		error = PTR_ERR(data->reset_gpio);
328962306a36Sopenharmony_ci		dev_err(&client->dev, "Failed to get reset gpio: %d\n", error);
329062306a36Sopenharmony_ci		return error;
329162306a36Sopenharmony_ci	}
329262306a36Sopenharmony_ci
329362306a36Sopenharmony_ci	/* Request the WAKE line as asserted so we go out of sleep */
329462306a36Sopenharmony_ci	data->wake_gpio = devm_gpiod_get_optional(&client->dev,
329562306a36Sopenharmony_ci						  "wake", GPIOD_OUT_HIGH);
329662306a36Sopenharmony_ci	if (IS_ERR(data->wake_gpio)) {
329762306a36Sopenharmony_ci		error = PTR_ERR(data->wake_gpio);
329862306a36Sopenharmony_ci		dev_err(&client->dev, "Failed to get wake gpio: %d\n", error);
329962306a36Sopenharmony_ci		return error;
330062306a36Sopenharmony_ci	}
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci	error = devm_request_threaded_irq(&client->dev, client->irq,
330362306a36Sopenharmony_ci					  NULL, mxt_interrupt,
330462306a36Sopenharmony_ci					  IRQF_ONESHOT | IRQF_NO_AUTOEN,
330562306a36Sopenharmony_ci					  client->name, data);
330662306a36Sopenharmony_ci	if (error) {
330762306a36Sopenharmony_ci		dev_err(&client->dev, "Failed to register interrupt\n");
330862306a36Sopenharmony_ci		return error;
330962306a36Sopenharmony_ci	}
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ci	error = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
331262306a36Sopenharmony_ci				      data->regulators);
331362306a36Sopenharmony_ci	if (error) {
331462306a36Sopenharmony_ci		dev_err(&client->dev, "failed to enable regulators: %d\n",
331562306a36Sopenharmony_ci			error);
331662306a36Sopenharmony_ci		return error;
331762306a36Sopenharmony_ci	}
331862306a36Sopenharmony_ci	/*
331962306a36Sopenharmony_ci	 * The device takes 40ms to come up after power-on according
332062306a36Sopenharmony_ci	 * to the mXT224 datasheet, page 13.
332162306a36Sopenharmony_ci	 */
332262306a36Sopenharmony_ci	msleep(MXT_BACKUP_TIME);
332362306a36Sopenharmony_ci
332462306a36Sopenharmony_ci	if (data->reset_gpio) {
332562306a36Sopenharmony_ci		/* Wait a while and then de-assert the RESET GPIO line */
332662306a36Sopenharmony_ci		msleep(MXT_RESET_GPIO_TIME);
332762306a36Sopenharmony_ci		gpiod_set_value(data->reset_gpio, 0);
332862306a36Sopenharmony_ci		msleep(MXT_RESET_INVALID_CHG);
332962306a36Sopenharmony_ci	}
333062306a36Sopenharmony_ci
333162306a36Sopenharmony_ci	/*
333262306a36Sopenharmony_ci	 * Controllers like mXT1386 have a dedicated WAKE line that could be
333362306a36Sopenharmony_ci	 * connected to a GPIO or to I2C SCL pin, or permanently asserted low.
333462306a36Sopenharmony_ci	 *
333562306a36Sopenharmony_ci	 * This WAKE line is used for waking controller from a deep-sleep and
333662306a36Sopenharmony_ci	 * it needs to be asserted low for 25 milliseconds before I2C transfers
333762306a36Sopenharmony_ci	 * could be accepted by controller if it was in a deep-sleep mode.
333862306a36Sopenharmony_ci	 * Controller will go into sleep automatically after 2 seconds of
333962306a36Sopenharmony_ci	 * inactivity if WAKE line is deasserted and deep sleep is activated.
334062306a36Sopenharmony_ci	 *
334162306a36Sopenharmony_ci	 * If WAKE line is connected to I2C SCL pin, then the first I2C transfer
334262306a36Sopenharmony_ci	 * will get an instant NAK and transfer needs to be retried after 25ms.
334362306a36Sopenharmony_ci	 *
334462306a36Sopenharmony_ci	 * If WAKE line is connected to a GPIO line, the line must be asserted
334562306a36Sopenharmony_ci	 * 25ms before the host attempts to communicate with the controller.
334662306a36Sopenharmony_ci	 */
334762306a36Sopenharmony_ci	device_property_read_u32(&client->dev, "atmel,wakeup-method",
334862306a36Sopenharmony_ci				 &data->wakeup_method);
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci	error = mxt_initialize(data);
335162306a36Sopenharmony_ci	if (error)
335262306a36Sopenharmony_ci		goto err_disable_regulators;
335362306a36Sopenharmony_ci
335462306a36Sopenharmony_ci	error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
335562306a36Sopenharmony_ci	if (error) {
335662306a36Sopenharmony_ci		dev_err(&client->dev, "Failure %d creating sysfs group\n",
335762306a36Sopenharmony_ci			error);
335862306a36Sopenharmony_ci		goto err_free_object;
335962306a36Sopenharmony_ci	}
336062306a36Sopenharmony_ci
336162306a36Sopenharmony_ci	return 0;
336262306a36Sopenharmony_ci
336362306a36Sopenharmony_cierr_free_object:
336462306a36Sopenharmony_ci	mxt_free_input_device(data);
336562306a36Sopenharmony_ci	mxt_free_object_table(data);
336662306a36Sopenharmony_cierr_disable_regulators:
336762306a36Sopenharmony_ci	regulator_bulk_disable(ARRAY_SIZE(data->regulators),
336862306a36Sopenharmony_ci			       data->regulators);
336962306a36Sopenharmony_ci	return error;
337062306a36Sopenharmony_ci}
337162306a36Sopenharmony_ci
337262306a36Sopenharmony_cistatic void mxt_remove(struct i2c_client *client)
337362306a36Sopenharmony_ci{
337462306a36Sopenharmony_ci	struct mxt_data *data = i2c_get_clientdata(client);
337562306a36Sopenharmony_ci
337662306a36Sopenharmony_ci	disable_irq(data->irq);
337762306a36Sopenharmony_ci	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
337862306a36Sopenharmony_ci	mxt_free_input_device(data);
337962306a36Sopenharmony_ci	mxt_free_object_table(data);
338062306a36Sopenharmony_ci	regulator_bulk_disable(ARRAY_SIZE(data->regulators),
338162306a36Sopenharmony_ci			       data->regulators);
338262306a36Sopenharmony_ci}
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_cistatic int mxt_suspend(struct device *dev)
338562306a36Sopenharmony_ci{
338662306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
338762306a36Sopenharmony_ci	struct mxt_data *data = i2c_get_clientdata(client);
338862306a36Sopenharmony_ci	struct input_dev *input_dev = data->input_dev;
338962306a36Sopenharmony_ci
339062306a36Sopenharmony_ci	if (!input_dev)
339162306a36Sopenharmony_ci		return 0;
339262306a36Sopenharmony_ci
339362306a36Sopenharmony_ci	mutex_lock(&input_dev->mutex);
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci	if (input_device_enabled(input_dev))
339662306a36Sopenharmony_ci		mxt_stop(data);
339762306a36Sopenharmony_ci
339862306a36Sopenharmony_ci	mutex_unlock(&input_dev->mutex);
339962306a36Sopenharmony_ci
340062306a36Sopenharmony_ci	disable_irq(data->irq);
340162306a36Sopenharmony_ci
340262306a36Sopenharmony_ci	return 0;
340362306a36Sopenharmony_ci}
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_cistatic int mxt_resume(struct device *dev)
340662306a36Sopenharmony_ci{
340762306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
340862306a36Sopenharmony_ci	struct mxt_data *data = i2c_get_clientdata(client);
340962306a36Sopenharmony_ci	struct input_dev *input_dev = data->input_dev;
341062306a36Sopenharmony_ci
341162306a36Sopenharmony_ci	if (!input_dev)
341262306a36Sopenharmony_ci		return 0;
341362306a36Sopenharmony_ci
341462306a36Sopenharmony_ci	enable_irq(data->irq);
341562306a36Sopenharmony_ci
341662306a36Sopenharmony_ci	mutex_lock(&input_dev->mutex);
341762306a36Sopenharmony_ci
341862306a36Sopenharmony_ci	if (input_device_enabled(input_dev))
341962306a36Sopenharmony_ci		mxt_start(data);
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ci	mutex_unlock(&input_dev->mutex);
342262306a36Sopenharmony_ci
342362306a36Sopenharmony_ci	return 0;
342462306a36Sopenharmony_ci}
342562306a36Sopenharmony_ci
342662306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume);
342762306a36Sopenharmony_ci
342862306a36Sopenharmony_cistatic const struct of_device_id mxt_of_match[] = {
342962306a36Sopenharmony_ci	{ .compatible = "atmel,maxtouch", },
343062306a36Sopenharmony_ci	/* Compatibles listed below are deprecated */
343162306a36Sopenharmony_ci	{ .compatible = "atmel,qt602240_ts", },
343262306a36Sopenharmony_ci	{ .compatible = "atmel,atmel_mxt_ts", },
343362306a36Sopenharmony_ci	{ .compatible = "atmel,atmel_mxt_tp", },
343462306a36Sopenharmony_ci	{ .compatible = "atmel,mXT224", },
343562306a36Sopenharmony_ci	{},
343662306a36Sopenharmony_ci};
343762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxt_of_match);
343862306a36Sopenharmony_ci
343962306a36Sopenharmony_ci#ifdef CONFIG_ACPI
344062306a36Sopenharmony_cistatic const struct acpi_device_id mxt_acpi_id[] = {
344162306a36Sopenharmony_ci	{ "ATML0000", 0 },	/* Touchpad */
344262306a36Sopenharmony_ci	{ "ATML0001", 0 },	/* Touchscreen */
344362306a36Sopenharmony_ci	{ }
344462306a36Sopenharmony_ci};
344562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, mxt_acpi_id);
344662306a36Sopenharmony_ci#endif
344762306a36Sopenharmony_ci
344862306a36Sopenharmony_cistatic const struct i2c_device_id mxt_id[] = {
344962306a36Sopenharmony_ci	{ "qt602240_ts", 0 },
345062306a36Sopenharmony_ci	{ "atmel_mxt_ts", 0 },
345162306a36Sopenharmony_ci	{ "atmel_mxt_tp", 0 },
345262306a36Sopenharmony_ci	{ "maxtouch", 0 },
345362306a36Sopenharmony_ci	{ "mXT224", 0 },
345462306a36Sopenharmony_ci	{ }
345562306a36Sopenharmony_ci};
345662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mxt_id);
345762306a36Sopenharmony_ci
345862306a36Sopenharmony_cistatic struct i2c_driver mxt_driver = {
345962306a36Sopenharmony_ci	.driver = {
346062306a36Sopenharmony_ci		.name	= "atmel_mxt_ts",
346162306a36Sopenharmony_ci		.of_match_table = mxt_of_match,
346262306a36Sopenharmony_ci		.acpi_match_table = ACPI_PTR(mxt_acpi_id),
346362306a36Sopenharmony_ci		.pm	= pm_sleep_ptr(&mxt_pm_ops),
346462306a36Sopenharmony_ci	},
346562306a36Sopenharmony_ci	.probe		= mxt_probe,
346662306a36Sopenharmony_ci	.remove		= mxt_remove,
346762306a36Sopenharmony_ci	.id_table	= mxt_id,
346862306a36Sopenharmony_ci};
346962306a36Sopenharmony_ci
347062306a36Sopenharmony_cimodule_i2c_driver(mxt_driver);
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci/* Module information */
347362306a36Sopenharmony_ciMODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
347462306a36Sopenharmony_ciMODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver");
347562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3476