armbian_build/patch/kernel/archive/sunxi-5.16/patches.megous/input-kb151-Add-a-driver-for-the-KB151-keyboard.patch
The-going f80117f21c
Recycling of megous v5.16.4 patches (#3449)
* Recycling of megous v5.16.4 patches

The patches were sorted as far as possible. Some patches are renamed
according to their place of application. The length of the patch name
has been changed to improve readability using
the `git format-patch --filename-max-length=75` method.

The folder containing the patches will have the name `patches.name`
and the corresponding file `series.name` for the convenience
of processing and moving them upstream. But the control file remains
`series.conf`.


Signed-off-by: The-going <48602507+The-going@users.noreply.github.com>

* Add a series of armbian patches for 5.16

Signed-off-by: The-going <48602507+The-going@users.noreply.github.com>

* Remove patches whose fixes are already in the kernel

Signed-off-by: The-going <48602507+The-going@users.noreply.github.com>
2022-02-02 11:03:44 +01:00

314 lines
8.8 KiB
Diff

From f8cb5422b1b9146321c23876df3c0577bc31872e Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sat, 19 Jun 2021 18:36:05 -0500
Subject: [PATCH 315/446] input: kb151: Add a driver for the KB151 keyboard
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This keyboard is found in the official Pine64 PinePhone keyboard case.
It is connected over I2C and runs a libre firmware.
Signed-off-by: Samuel Holland <samuel@sholland.org>
Signed-off-by: Ondřej Jirman <megi@xff.cz>
---
drivers/input/keyboard/Kconfig | 10 ++
drivers/input/keyboard/Makefile | 1 +
drivers/input/keyboard/kb151.c | 251 ++++++++++++++++++++++++++++++++
3 files changed, 262 insertions(+)
create mode 100644 drivers/input/keyboard/kb151.c
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 0c607da9e..cdcbf8232 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -353,6 +353,16 @@ config KEYBOARD_HP7XX
To compile this driver as a module, choose M here: the
module will be called jornada720_kbd.
+config KEYBOARD_KB151
+ tristate "Pine64 KB151 Keyboard"
+ depends on I2C
+ select CRC8
+ select INPUT_MATRIXKMAP
+ help
+ Say Y here to enable support for the KB151 keyboard used in the
+ Pine64 PinePhone keyboard case. This driver supports the FLOSS
+ firmware available at https://megous.com/git/pinephone-keyboard/
+
config KEYBOARD_LM8323
tristate "LM8323 keypad chip"
depends on I2C
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index e3c8648f8..1a7010f5d 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o
obj-$(CONFIG_KEYBOARD_IMX_SC_KEY) += imx_sc_key.o
obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
+obj-$(CONFIG_KEYBOARD_KB151) += kb151.o
obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o
obj-$(CONFIG_KEYBOARD_LM8333) += lm8333.o
diff --git a/drivers/input/keyboard/kb151.c b/drivers/input/keyboard/kb151.c
new file mode 100644
index 000000000..bb6250efe
--- /dev/null
+++ b/drivers/input/keyboard/kb151.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
+
+#include <linux/crc8.h>
+#include <linux/i2c.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pm_wakeirq.h>
+
+#define KB151_CRC8_POLYNOMIAL 0x07
+
+#define KB151_DEVICE_ID_HI 0x00
+#define KB151_DEVICE_ID_HI_VALUE 0x4b
+#define KB151_DEVICE_ID_LO 0x01
+#define KB151_DEVICE_ID_LO_VALUE 0x42
+#define KB151_FW_REVISION 0x02
+#define KB151_FW_FEATURES 0x03
+#define KB151_MATRIX_SIZE 0x06
+#define KB151_SCAN_CRC 0x07
+#define KB151_SCAN_DATA 0x08
+#define KB151_SYS_CONFIG 0x20
+#define KB151_SYS_CONFIG_DISABLE_SCAN BIT(0)
+
+struct kb151 {
+ struct input_dev *input;
+ u8 crc_table[CRC8_TABLE_SIZE];
+ u8 row_shift;
+ u8 rows;
+ u8 cols;
+ u8 fn_state;
+ u8 buf_swap;
+ u8 buf[];
+};
+
+static void kb151_update(struct i2c_client *client)
+{
+ struct kb151 *kb151 = i2c_get_clientdata(client);
+ unsigned short *keymap = kb151->input->keycode;
+ struct device *dev = &client->dev;
+ size_t buf_len = kb151->cols + 1;
+ u8 *old_buf = kb151->buf;
+ u8 *new_buf = kb151->buf;
+ int col, crc, ret, row;
+
+ if (kb151->buf_swap)
+ old_buf += buf_len;
+ else
+ new_buf += buf_len;
+
+ ret = i2c_smbus_read_i2c_block_data(client, KB151_SCAN_CRC,
+ buf_len, new_buf);
+ if (ret != buf_len) {
+ dev_err(dev, "Failed to read scan data: %d\n", ret);
+ return;
+ }
+
+ dev_dbg(dev, "%02x | %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
+ new_buf[0], new_buf[1], new_buf[2], new_buf[3], new_buf[4], new_buf[5],
+ new_buf[6], new_buf[7], new_buf[8], new_buf[9], new_buf[10], new_buf[11],
+ new_buf[12]);
+ crc = crc8(kb151->crc_table, new_buf + 1, kb151->cols, CRC8_INIT_VALUE);
+ if (crc != new_buf[0]) {
+ dev_err(dev, "Bad scan data (%02x != %02x)\n",
+ crc, new_buf[0]);
+ return;
+ }
+
+ for (col = 0; col < kb151->cols; ++col) {
+ u8 old = *(++old_buf);
+ u8 new = *(++new_buf);
+ u8 changed = old ^ new;
+
+ for (row = 0; row < kb151->rows; ++row) {
+ u8 pressed = new & BIT(row);
+ u8 map_row = row + (kb151->fn_state ? kb151->rows : 0);
+ int code = MATRIX_SCAN_CODE(map_row, col, kb151->row_shift);
+
+ if (!(changed & BIT(row)))
+ continue;
+
+ dev_dbg(&client->dev, "row %u col %u %sed\n",
+ map_row, col, pressed ? "press" : "releas");
+ if (keymap[code] == KEY_FN) {
+ dev_dbg(&client->dev, "FN is now %s\n",
+ pressed ? "pressed" : "released");
+ kb151->fn_state = pressed;
+ } else
+ input_report_key(kb151->input, keymap[code], pressed);
+ }
+ }
+ input_sync(kb151->input);
+
+ kb151->buf_swap = !kb151->buf_swap;
+}
+
+static int kb151_open(struct input_dev *input)
+{
+ struct i2c_client *client = input_get_drvdata(input);
+ struct device *dev = &client->dev;
+ int ret, val;
+
+ ret = i2c_smbus_read_byte_data(client, KB151_SYS_CONFIG);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read config: %d\n", ret);
+ return ret;
+ }
+
+ val = ret & ~KB151_SYS_CONFIG_DISABLE_SCAN;
+ ret = i2c_smbus_write_byte_data(client, KB151_SYS_CONFIG, val);
+ if (ret) {
+ dev_err(dev, "Failed to write config: %d\n", ret);
+ return ret;
+ }
+
+ kb151_update(client);
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static void kb151_close(struct input_dev *input)
+{
+ struct i2c_client *client = input_get_drvdata(input);
+ struct device *dev = &client->dev;
+ int ret, val;
+
+ disable_irq(client->irq);
+
+ ret = i2c_smbus_read_byte_data(client, KB151_SYS_CONFIG);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read config: %d\n", ret);
+ return;
+ }
+
+ val = ret | KB151_SYS_CONFIG_DISABLE_SCAN;
+ ret = i2c_smbus_write_byte_data(client, KB151_SYS_CONFIG, val);
+ if (ret) {
+ dev_err(dev, "Failed to write config: %d\n", ret);
+ }
+}
+
+static irqreturn_t kb151_irq_thread(int irq, void *data)
+{
+ struct i2c_client *client = data;
+
+ kb151_update(client);
+
+ return IRQ_HANDLED;
+}
+
+static int kb151_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ u8 info[KB151_MATRIX_SIZE + 1];
+ unsigned int kb_rows, kb_cols;
+ unsigned int map_rows, map_cols;
+ struct kb151 *kb151;
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(client, 0, sizeof(info), info);
+ if (ret != sizeof(info))
+ return ret;
+
+ if (info[KB151_DEVICE_ID_HI] != KB151_DEVICE_ID_HI_VALUE ||
+ info[KB151_DEVICE_ID_LO] != KB151_DEVICE_ID_LO_VALUE)
+ return -ENODEV;
+
+ dev_info(dev, "Found KB151 with firmware %d.%d (features=%#x)\n",
+ info[KB151_FW_REVISION] >> 4,
+ info[KB151_FW_REVISION] & 0xf,
+ info[KB151_FW_FEATURES]);
+
+ ret = matrix_keypad_parse_properties(dev, &map_rows, &map_cols);
+ if (ret)
+ return ret;
+
+ kb_rows = info[KB151_MATRIX_SIZE] & 0xf;
+ kb_cols = info[KB151_MATRIX_SIZE] >> 4;
+ if (map_rows != 2 * kb_rows || map_cols != kb_cols) {
+ dev_err(dev, "Keyboard matrix is %ux%u, but key map is %ux%u\n",
+ kb_rows, kb_cols, map_rows, map_cols);
+ return -EINVAL;
+ }
+
+ /* Allocate two buffers, and include space for the CRC. */
+ kb151 = devm_kzalloc(dev, struct_size(kb151, buf, 2 * (kb_cols + 1)), GFP_KERNEL);
+ if (!kb151)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, kb151);
+
+ crc8_populate_msb(kb151->crc_table, KB151_CRC8_POLYNOMIAL);
+
+ kb151->row_shift = get_count_order(kb_cols);
+ kb151->rows = kb_rows;
+ kb151->cols = kb_cols;
+
+ kb151->input = devm_input_allocate_device(dev);
+ if (!kb151->input)
+ return -ENOMEM;
+
+ input_set_drvdata(kb151->input, client);
+
+ kb151->input->name = client->name;
+ kb151->input->phys = "kb151/input0";
+ kb151->input->id.bustype = BUS_I2C;
+ kb151->input->open = kb151_open;
+ kb151->input->close = kb151_close;
+
+ __set_bit(EV_REP, kb151->input->evbit);
+
+ ret = matrix_keypad_build_keymap(NULL, NULL, map_rows, map_cols,
+ NULL, kb151->input);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to build keymap\n");
+
+ ret = devm_request_threaded_irq(dev, client->irq,
+ NULL, kb151_irq_thread,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
+ client->name, client);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request IRQ\n");
+
+ ret = input_register_device(kb151->input);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register input\n");
+
+ return 0;
+}
+
+static const struct of_device_id kb151_of_match[] = {
+ { .compatible = "pine64,kb151" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, kb151_of_match);
+
+static struct i2c_driver kb151_driver = {
+ .probe_new = kb151_probe,
+ .driver = {
+ .name = "kb151",
+ .of_match_table = kb151_of_match,
+ },
+};
+module_i2c_driver(kb151_driver);
+
+MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
+MODULE_DESCRIPTION("Pine64 KB151 keyboard driver");
+MODULE_LICENSE("GPL");
--
2.31.1