18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2020 Synaptics Incorporated
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/rmi.h>
88c2ecf20Sopenharmony_ci#include <linux/input.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include "rmi_driver.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define RMI_F3A_MAX_GPIO_COUNT		128
138c2ecf20Sopenharmony_ci#define RMI_F3A_MAX_REG_SIZE		DIV_ROUND_UP(RMI_F3A_MAX_GPIO_COUNT, 8)
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/* Defs for Query 0 */
168c2ecf20Sopenharmony_ci#define RMI_F3A_GPIO_COUNT		0x7F
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define RMI_F3A_DATA_REGS_MAX_SIZE	RMI_F3A_MAX_REG_SIZE
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define TRACKSTICK_RANGE_START		3
218c2ecf20Sopenharmony_ci#define TRACKSTICK_RANGE_END		6
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistruct f3a_data {
248c2ecf20Sopenharmony_ci	/* Query Data */
258c2ecf20Sopenharmony_ci	u8 gpio_count;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	u8 register_count;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	u8 data_regs[RMI_F3A_DATA_REGS_MAX_SIZE];
308c2ecf20Sopenharmony_ci	u16 *gpio_key_map;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	struct input_dev *input;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	struct rmi_function *f03;
358c2ecf20Sopenharmony_ci	bool trackstick_buttons;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void rmi_f3a_report_button(struct rmi_function *fn,
398c2ecf20Sopenharmony_ci				  struct f3a_data *f3a, unsigned int button)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	u16 key_code = f3a->gpio_key_map[button];
428c2ecf20Sopenharmony_ci	bool key_down = !(f3a->data_regs[0] & BIT(button));
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	if (f3a->trackstick_buttons &&
458c2ecf20Sopenharmony_ci		button >= TRACKSTICK_RANGE_START &&
468c2ecf20Sopenharmony_ci		button <= TRACKSTICK_RANGE_END) {
478c2ecf20Sopenharmony_ci		rmi_f03_overwrite_button(f3a->f03, key_code, key_down);
488c2ecf20Sopenharmony_ci	} else {
498c2ecf20Sopenharmony_ci		rmi_dbg(RMI_DEBUG_FN, &fn->dev,
508c2ecf20Sopenharmony_ci			"%s: call input report key (0x%04x) value (0x%02x)",
518c2ecf20Sopenharmony_ci			__func__, key_code, key_down);
528c2ecf20Sopenharmony_ci		input_report_key(f3a->input, key_code, key_down);
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic irqreturn_t rmi_f3a_attention(int irq, void *ctx)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	struct rmi_function *fn = ctx;
598c2ecf20Sopenharmony_ci	struct f3a_data *f3a = dev_get_drvdata(&fn->dev);
608c2ecf20Sopenharmony_ci	struct rmi_driver_data *drvdata = dev_get_drvdata(&fn->rmi_dev->dev);
618c2ecf20Sopenharmony_ci	int error;
628c2ecf20Sopenharmony_ci	int i;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (drvdata->attn_data.data) {
658c2ecf20Sopenharmony_ci		if (drvdata->attn_data.size < f3a->register_count) {
668c2ecf20Sopenharmony_ci			dev_warn(&fn->dev,
678c2ecf20Sopenharmony_ci				 "F3A interrupted, but data is missing\n");
688c2ecf20Sopenharmony_ci			return IRQ_HANDLED;
698c2ecf20Sopenharmony_ci		}
708c2ecf20Sopenharmony_ci		memcpy(f3a->data_regs, drvdata->attn_data.data,
718c2ecf20Sopenharmony_ci			f3a->register_count);
728c2ecf20Sopenharmony_ci		drvdata->attn_data.data += f3a->register_count;
738c2ecf20Sopenharmony_ci		drvdata->attn_data.size -= f3a->register_count;
748c2ecf20Sopenharmony_ci	} else {
758c2ecf20Sopenharmony_ci		error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr,
768c2ecf20Sopenharmony_ci					f3a->data_regs, f3a->register_count);
778c2ecf20Sopenharmony_ci		if (error) {
788c2ecf20Sopenharmony_ci			dev_err(&fn->dev,
798c2ecf20Sopenharmony_ci				"%s: Failed to read F3a data registers: %d\n",
808c2ecf20Sopenharmony_ci				__func__, error);
818c2ecf20Sopenharmony_ci			return IRQ_RETVAL(error);
828c2ecf20Sopenharmony_ci		}
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	for (i = 0; i < f3a->gpio_count; i++)
868c2ecf20Sopenharmony_ci		if (f3a->gpio_key_map[i] != KEY_RESERVED)
878c2ecf20Sopenharmony_ci			rmi_f3a_report_button(fn, f3a, i);
888c2ecf20Sopenharmony_ci	if (f3a->trackstick_buttons)
898c2ecf20Sopenharmony_ci		rmi_f03_commit_buttons(f3a->f03);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int rmi_f3a_config(struct rmi_function *fn)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct f3a_data *f3a = dev_get_drvdata(&fn->dev);
978c2ecf20Sopenharmony_ci	struct rmi_driver *drv = fn->rmi_dev->driver;
988c2ecf20Sopenharmony_ci	const struct rmi_device_platform_data *pdata =
998c2ecf20Sopenharmony_ci			rmi_get_platform_data(fn->rmi_dev);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (!f3a)
1028c2ecf20Sopenharmony_ci		return 0;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (pdata->gpio_data.trackstick_buttons) {
1058c2ecf20Sopenharmony_ci		/* Try [re-]establish link to F03. */
1068c2ecf20Sopenharmony_ci		f3a->f03 = rmi_find_function(fn->rmi_dev, 0x03);
1078c2ecf20Sopenharmony_ci		f3a->trackstick_buttons = f3a->f03 != NULL;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	drv->set_irq_bits(fn->rmi_dev, fn->irq_mask);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return 0;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic bool rmi_f3a_is_valid_button(int button, struct f3a_data *f3a,
1168c2ecf20Sopenharmony_ci					u8 *query1_regs, u8 *ctrl1_regs)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	/* gpio exist && direction input */
1198c2ecf20Sopenharmony_ci	return (query1_regs[0] & BIT(button)) && !(ctrl1_regs[0] & BIT(button));
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic int rmi_f3a_map_gpios(struct rmi_function *fn, struct f3a_data *f3a,
1238c2ecf20Sopenharmony_ci				u8 *query1_regs, u8 *ctrl1_regs)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	const struct rmi_device_platform_data *pdata =
1268c2ecf20Sopenharmony_ci			rmi_get_platform_data(fn->rmi_dev);
1278c2ecf20Sopenharmony_ci	struct input_dev *input = f3a->input;
1288c2ecf20Sopenharmony_ci	unsigned int button = BTN_LEFT;
1298c2ecf20Sopenharmony_ci	unsigned int trackstick_button = BTN_LEFT;
1308c2ecf20Sopenharmony_ci	bool button_mapped = false;
1318c2ecf20Sopenharmony_ci	int i;
1328c2ecf20Sopenharmony_ci	int button_count = min_t(u8, f3a->gpio_count, TRACKSTICK_RANGE_END);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	f3a->gpio_key_map = devm_kcalloc(&fn->dev,
1358c2ecf20Sopenharmony_ci						button_count,
1368c2ecf20Sopenharmony_ci						sizeof(f3a->gpio_key_map[0]),
1378c2ecf20Sopenharmony_ci						GFP_KERNEL);
1388c2ecf20Sopenharmony_ci	if (!f3a->gpio_key_map) {
1398c2ecf20Sopenharmony_ci		dev_err(&fn->dev, "Failed to allocate gpio map memory.\n");
1408c2ecf20Sopenharmony_ci		return -ENOMEM;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	for (i = 0; i < button_count; i++) {
1448c2ecf20Sopenharmony_ci		if (!rmi_f3a_is_valid_button(i, f3a, query1_regs, ctrl1_regs))
1458c2ecf20Sopenharmony_ci			continue;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci		if (pdata->gpio_data.trackstick_buttons &&
1488c2ecf20Sopenharmony_ci			i >= TRACKSTICK_RANGE_START &&
1498c2ecf20Sopenharmony_ci			i < TRACKSTICK_RANGE_END) {
1508c2ecf20Sopenharmony_ci			f3a->gpio_key_map[i] = trackstick_button++;
1518c2ecf20Sopenharmony_ci		} else if (!pdata->gpio_data.buttonpad || !button_mapped) {
1528c2ecf20Sopenharmony_ci			f3a->gpio_key_map[i] = button;
1538c2ecf20Sopenharmony_ci			input_set_capability(input, EV_KEY, button++);
1548c2ecf20Sopenharmony_ci			button_mapped = true;
1558c2ecf20Sopenharmony_ci		}
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci	input->keycode = f3a->gpio_key_map;
1588c2ecf20Sopenharmony_ci	input->keycodesize = sizeof(f3a->gpio_key_map[0]);
1598c2ecf20Sopenharmony_ci	input->keycodemax = f3a->gpio_count;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (pdata->gpio_data.buttonpad || (button - BTN_LEFT == 1))
1628c2ecf20Sopenharmony_ci		__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic int rmi_f3a_initialize(struct rmi_function *fn, struct f3a_data *f3a)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	u8 query1[RMI_F3A_MAX_REG_SIZE];
1708c2ecf20Sopenharmony_ci	u8 ctrl1[RMI_F3A_MAX_REG_SIZE];
1718c2ecf20Sopenharmony_ci	u8 buf;
1728c2ecf20Sopenharmony_ci	int error;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	error = rmi_read(fn->rmi_dev, fn->fd.query_base_addr, &buf);
1758c2ecf20Sopenharmony_ci	if (error < 0) {
1768c2ecf20Sopenharmony_ci		dev_err(&fn->dev, "Failed to read general info register: %d\n",
1778c2ecf20Sopenharmony_ci			error);
1788c2ecf20Sopenharmony_ci		return -ENODEV;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	f3a->gpio_count = buf & RMI_F3A_GPIO_COUNT;
1828c2ecf20Sopenharmony_ci	f3a->register_count = DIV_ROUND_UP(f3a->gpio_count, 8);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/* Query1 -> gpio exist */
1858c2ecf20Sopenharmony_ci	error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr + 1,
1868c2ecf20Sopenharmony_ci				query1, f3a->register_count);
1878c2ecf20Sopenharmony_ci	if (error) {
1888c2ecf20Sopenharmony_ci		dev_err(&fn->dev, "Failed to read query1 register\n");
1898c2ecf20Sopenharmony_ci		return error;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* Ctrl1 -> gpio direction */
1938c2ecf20Sopenharmony_ci	error = rmi_read_block(fn->rmi_dev, fn->fd.control_base_addr + 1,
1948c2ecf20Sopenharmony_ci				ctrl1, f3a->register_count);
1958c2ecf20Sopenharmony_ci	if (error) {
1968c2ecf20Sopenharmony_ci		dev_err(&fn->dev, "Failed to read control1 register\n");
1978c2ecf20Sopenharmony_ci		return error;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	error = rmi_f3a_map_gpios(fn, f3a, query1, ctrl1);
2018c2ecf20Sopenharmony_ci	if (error)
2028c2ecf20Sopenharmony_ci		return error;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return 0;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic int rmi_f3a_probe(struct rmi_function *fn)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct rmi_device *rmi_dev = fn->rmi_dev;
2108c2ecf20Sopenharmony_ci	struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
2118c2ecf20Sopenharmony_ci	struct f3a_data *f3a;
2128c2ecf20Sopenharmony_ci	int error;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (!drv_data->input) {
2158c2ecf20Sopenharmony_ci		dev_info(&fn->dev, "F3A: no input device found, ignoring\n");
2168c2ecf20Sopenharmony_ci		return -ENXIO;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	f3a = devm_kzalloc(&fn->dev, sizeof(*f3a), GFP_KERNEL);
2208c2ecf20Sopenharmony_ci	if (!f3a)
2218c2ecf20Sopenharmony_ci		return -ENOMEM;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	f3a->input = drv_data->input;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	error = rmi_f3a_initialize(fn, f3a);
2268c2ecf20Sopenharmony_ci	if (error)
2278c2ecf20Sopenharmony_ci		return error;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	dev_set_drvdata(&fn->dev, f3a);
2308c2ecf20Sopenharmony_ci	return 0;
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistruct rmi_function_handler rmi_f3a_handler = {
2348c2ecf20Sopenharmony_ci	.driver = {
2358c2ecf20Sopenharmony_ci		.name = "rmi4_f3a",
2368c2ecf20Sopenharmony_ci	},
2378c2ecf20Sopenharmony_ci	.func = 0x3a,
2388c2ecf20Sopenharmony_ci	.probe = rmi_f3a_probe,
2398c2ecf20Sopenharmony_ci	.config = rmi_f3a_config,
2408c2ecf20Sopenharmony_ci	.attention = rmi_f3a_attention,
2418c2ecf20Sopenharmony_ci};
242