input: keyboard: implement brain-kbd-i2c
This commit is contained in:
parent
f0d9cb1e56
commit
e2163ed803
|
@ -226,6 +226,14 @@
|
|||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c1_pins_a>;
|
||||
status = "okay";
|
||||
|
||||
keyboard_i2c: keyboard_i2c@28 {
|
||||
status = "disabled";
|
||||
compatible = "sharp,brain-kbd-i2c";
|
||||
reg = <0x28>;
|
||||
interrupt-parent = <&gpio4>;
|
||||
interrupts = <2 4>;
|
||||
};
|
||||
};
|
||||
|
||||
pwm: pwm@80064000 {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// Copyright 2012 Freescale Semiconductor, Inc.
|
||||
|
||||
/dts-v1/;
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include "imx28-brain.dtsi"
|
||||
|
||||
/ {
|
||||
|
@ -11,19 +12,106 @@
|
|||
};
|
||||
|
||||
&brainlcd {
|
||||
status = "okay";
|
||||
status = "okay";
|
||||
|
||||
sharp,lcd-width = <800>;
|
||||
sharp,lcd-height = <480>;
|
||||
sharp,lcd-width-mm = <112>;
|
||||
sharp,lcd-height-mm = <67>;
|
||||
sharp,lcd-width = <800>;
|
||||
sharp,lcd-height = <480>;
|
||||
sharp,lcd-width-mm = <112>;
|
||||
sharp,lcd-height-mm = <67>;
|
||||
|
||||
sharp,mac-transpose;
|
||||
sharp,mac-inversion;
|
||||
sharp,mac-bgr;
|
||||
sharp,mac-transpose;
|
||||
sharp,mac-inversion;
|
||||
sharp,mac-bgr;
|
||||
|
||||
sharp,en-gpios =
|
||||
<&gpio0 26 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio0 27 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio4 16 GPIO_ACTIVE_HIGH>;
|
||||
sharp,en-gpios =
|
||||
<&gpio0 26 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio0 27 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio4 16 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
||||
&keyboard_i2c {
|
||||
status = "okay";
|
||||
keymap =
|
||||
<0x01 KEY_POWER>, /* Power */
|
||||
<0x07 KEY_ESC>, /* Search */
|
||||
<0x0d KEY_TAB>, /* Kokugo */
|
||||
<0x11 KEY_PAGEUP>, /* Eiwa Waei */
|
||||
<0x15 KEY_PAGEDOWN>, /* My Dictionary */
|
||||
<0x25 KEY_INSERT>, /* History / Bookmark */
|
||||
<0x1d KEY_DELETE>, /* Marker test */
|
||||
/* <0x2b Memorization tool> */
|
||||
/* <0x24 Home> */
|
||||
<0x02 KEY_Q>, /* Q */
|
||||
<0x08 KEY_W>, /* W */
|
||||
<0x0e KEY_E>, /* E */
|
||||
<0x12 KEY_R>, /* R */
|
||||
<0x16 KEY_T>, /* T */
|
||||
<0x1e KEY_Y>, /* Y */
|
||||
<0x26 KEY_U>, /* U */
|
||||
<0x2c KEY_I>, /* I */
|
||||
<0x27 KEY_O>, /* O */
|
||||
<0x2d KEY_P>, /* P */
|
||||
<0x03 KEY_A>, /* A */
|
||||
<0x09 KEY_S>, /* S */
|
||||
<0x0f KEY_D>, /* D */
|
||||
<0x13 KEY_F>, /* F */
|
||||
<0x17 KEY_G>, /* G */
|
||||
<0x1f KEY_H>, /* H */
|
||||
<0x20 KEY_J>, /* J */
|
||||
<0x28 KEY_K>, /* K */
|
||||
<0x2e KEY_L>, /* L */
|
||||
<0x05 KEY_LEFTSHIFT>,
|
||||
<0x04 KEY_Z>, /* Z */
|
||||
<0x0a KEY_X>, /* X */
|
||||
<0x10 KEY_C>, /* C */
|
||||
<0x14 KEY_V>, /* V */
|
||||
<0x18 KEY_B>, /* B */
|
||||
<0x21 KEY_N>, /* N */
|
||||
<0x29 KEY_M>, /* M */
|
||||
<0x2f KEY_MINUS>, /* Minus */
|
||||
<0x31 KEY_BACKSPACE>, /* Backspace */
|
||||
<0x0b KEY_LEFTCTRL>, /* Page Up */
|
||||
/* <0x0c KEY_PAGEDOWN>, */ /* Page Down */
|
||||
<0x06 KEY_LEFTALT>, /* Switch characters */
|
||||
/* < 0x19, Symbols>, */
|
||||
<0x1b KEY_ESC>, /* Go Back */
|
||||
<0x1c KEY_SPACE>, /* Space */
|
||||
<0x23 KEY_ENTER>, /* Enter */
|
||||
<0x1a KEY_LEFT>, /* Left */
|
||||
<0x22 KEY_UP>, /* Up */
|
||||
<0x2a KEY_DOWN>, /* Down */
|
||||
<0x30 KEY_RIGHT>; /* Right */
|
||||
keymap-symbol =
|
||||
<0x01 KEY_POWER>, /* Power */
|
||||
<0x07 KEY_ESC>, /* Search */
|
||||
<0x0d KEY_TAB>, /* Kokugo */
|
||||
<0x11 KEY_PAGEUP>, /* Eiwa Waei */
|
||||
<0x15 KEY_PAGEDOWN>, /* My Dictionary */
|
||||
<0x25 KEY_INSERT>, /* History / Bookmark */
|
||||
<0x1d KEY_DELETE>, /* Marker test */
|
||||
<0x02 KEY_1>, /* Q */
|
||||
<0x08 KEY_2>, /* W */
|
||||
<0x0e KEY_3>, /* E */
|
||||
<0x12 KEY_4>, /* R */
|
||||
<0x16 KEY_5>, /* T */
|
||||
<0x1e KEY_6>, /* Y */
|
||||
<0x26 KEY_7>, /* U */
|
||||
<0x2c KEY_8>, /* I */
|
||||
<0x27 KEY_9>, /* O */
|
||||
<0x2d KEY_0>, /* P */
|
||||
<0x0f KEY_GRAVE>, /* D */
|
||||
<0x13 KEY_EQUAL>, /* F */
|
||||
<0x17 KEY_BACKSLASH>, /* G */
|
||||
<0x1f KEY_SEMICOLON>, /* H */
|
||||
<0x20 KEY_APOSTROPHE>, /* J */
|
||||
<0x28 KEY_LEFTBRACE>, /* K */
|
||||
<0x2e KEY_RIGHTBRACE>, /* L */
|
||||
<0x05 KEY_LEFTSHIFT>,
|
||||
<0x21 KEY_COMMA>, /* N */
|
||||
<0x29 KEY_DOT>, /* M */
|
||||
<0x2f KEY_SLASH>, /* Minus */
|
||||
<0x31 KEY_BACKSPACE>, /* Backspace */
|
||||
<0x0b KEY_LEFTCTRL>, /* Page Up */
|
||||
/* <0x0c KEY_PAGEDOWN>, */ /* Page Down */
|
||||
<0x06 KEY_LEFTALT>; /* Switch characters */
|
||||
};
|
||||
|
|
|
@ -64,12 +64,11 @@ CONFIG_USB_USBNET=y
|
|||
CONFIG_USB_NET_SMSC95XX=y
|
||||
# CONFIG_WLAN is not set
|
||||
CONFIG_INPUT_EVDEV=y
|
||||
# CONFIG_INPUT_KEYBOARD is not set
|
||||
CONFIG_KEYBOARD_BRAIN_I2C=y
|
||||
# CONFIG_INPUT_MOUSE is not set
|
||||
CONFIG_INPUT_TOUCHSCREEN=y
|
||||
CONFIG_TOUCHSCREEN_MXS_LRADC=y
|
||||
CONFIG_TOUCHSCREEN_TSC2007=m
|
||||
# CONFIG_SERIO is not set
|
||||
# CONFIG_LEGACY_PTYS is not set
|
||||
CONFIG_SERIAL_AMBA_PL011=y
|
||||
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
|
||||
|
|
|
@ -781,6 +781,16 @@ config KEYBOARD_BCM
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called bcm-keypad.
|
||||
|
||||
config KEYBOARD_BRAIN_I2C
|
||||
tristate "SHARP Brain keyboard (I2C)"
|
||||
depends on OF && I2C
|
||||
help
|
||||
Say Y here to enable the SHARP Brain keyboard driver
|
||||
which is accessible via I2C bus.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called brain-kbd-i2c.
|
||||
|
||||
config KEYBOARD_MTK_PMIC
|
||||
tristate "MediaTek PMIC keys support"
|
||||
depends on MFD_MT6397
|
||||
|
|
|
@ -14,6 +14,7 @@ obj-$(CONFIG_KEYBOARD_APPLESPI) += applespi.o
|
|||
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
|
||||
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_BRAIN_I2C) += brain-kbd-i2c.o
|
||||
obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o
|
||||
obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o
|
||||
|
|
|
@ -0,0 +1,321 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* SHARP Brain keyboard (I2C) driver
|
||||
*
|
||||
* Copyright 2021 Takumi Sueda
|
||||
*
|
||||
* Author: Takumi Sueda <puhitaku@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#define BRAIN_KBD_I2C_DEV_NAME "brain-kbd-i2c"
|
||||
|
||||
#define BK_CMD_KEYCODE 0x04
|
||||
|
||||
#define BK_IS_PRESSED(val) ((~val & 0x40) >> 6)
|
||||
|
||||
struct keymap_def {
|
||||
u8 brain_keycode;
|
||||
unsigned int kernel_keycode;
|
||||
};
|
||||
|
||||
struct bk_i2c_data {
|
||||
struct i2c_client *cli;
|
||||
struct input_dev *idev;
|
||||
struct keymap_def *km;
|
||||
struct keymap_def *km_symbol;
|
||||
int kmlen;
|
||||
int kmlen_symbol;
|
||||
|
||||
bool symbol;
|
||||
};
|
||||
|
||||
static bool detect_key(struct bk_i2c_data *kbd, u8 keycode)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ((keycode & 0x3f) == 0x19) {
|
||||
if (BK_IS_PRESSED(keycode)) {
|
||||
dev_dbg(&kbd->cli->dev, "symbol pressed!\n");
|
||||
kbd->symbol = true;
|
||||
} else {
|
||||
dev_dbg(&kbd->cli->dev, "symbol released!\n");
|
||||
kbd->symbol = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (kbd->symbol) {
|
||||
for (i = 0; i < kbd->kmlen_symbol; i++) {
|
||||
if ((keycode & 0x3f) ==
|
||||
kbd->km_symbol[i].brain_keycode) {
|
||||
dev_dbg(&kbd->cli->dev,
|
||||
"symbol: pressed %02x\n",
|
||||
kbd->km_symbol[i].brain_keycode);
|
||||
input_report_key(
|
||||
kbd->idev,
|
||||
kbd->km_symbol[i].kernel_keycode,
|
||||
BK_IS_PRESSED(keycode));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < kbd->kmlen; i++) {
|
||||
if ((keycode & 0x3f) == kbd->km[i].brain_keycode) {
|
||||
dev_dbg(&kbd->cli->dev,
|
||||
"normal: pressed %02x\n",
|
||||
kbd->km[i].brain_keycode);
|
||||
input_report_key(kbd->idev,
|
||||
kbd->km[i].kernel_keycode,
|
||||
BK_IS_PRESSED(keycode));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static irqreturn_t bk_i2c_irq_handler(int irq, void *devid)
|
||||
{
|
||||
struct bk_i2c_data *kbd = devid;
|
||||
s32 raw;
|
||||
u8 n, k1, k2, k3;
|
||||
|
||||
raw = i2c_smbus_read_word_swapped(kbd->cli, BK_CMD_KEYCODE);
|
||||
if (raw < 0) {
|
||||
dev_err(&kbd->cli->dev, "failed to read keycode: %d\n", raw);
|
||||
goto err;
|
||||
}
|
||||
|
||||
n = raw >> 8;
|
||||
k1 = raw & 0xff;
|
||||
|
||||
if (k1 == 0x00) {
|
||||
dev_dbg(&kbd->cli->dev,
|
||||
"interrupted but no key press was found\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (n == 1) {
|
||||
if (detect_key(kbd, k1)) {
|
||||
goto done;
|
||||
} else {
|
||||
dev_dbg(&kbd->cli->dev,
|
||||
"unknown key was pressed: %02x\n", (k1 & 0x3f));
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
raw = i2c_smbus_read_word_swapped(kbd->cli, BK_CMD_KEYCODE);
|
||||
if (raw < 0) {
|
||||
dev_err(&kbd->cli->dev, "failed to read 2nd/3rd keycode: %d\n",
|
||||
raw);
|
||||
goto err;
|
||||
}
|
||||
|
||||
k2 = (raw & 0xff00) >> 8;
|
||||
k3 = raw & 0xff;
|
||||
|
||||
detect_key(kbd, k2);
|
||||
|
||||
if (n == 3) {
|
||||
detect_key(kbd, k3);
|
||||
}
|
||||
|
||||
done:
|
||||
input_sync(kbd->idev);
|
||||
err:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bk_i2c_probe(struct i2c_client *cli, const struct i2c_device_id *id)
|
||||
{
|
||||
struct bk_i2c_data *kbd;
|
||||
int i, err, cells, len, offset;
|
||||
u32 brain_keycode, kernel_keycode;
|
||||
|
||||
if (!i2c_check_functionality(cli->adapter, I2C_FUNC_SMBUS_BYTE)) {
|
||||
dev_err(&cli->dev, "the I2C bus is not compatible\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
kbd = devm_kzalloc(&cli->dev, sizeof(*kbd), GFP_KERNEL);
|
||||
if (!kbd) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
cells = 2;
|
||||
if (!of_get_property(cli->dev.of_node, "keymap", &len)) {
|
||||
dev_err(&cli->dev, "DT node has no keymap\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
len /= sizeof(u32) * cells;
|
||||
kbd->kmlen = len;
|
||||
|
||||
kbd->km = devm_kzalloc(&cli->dev, sizeof(struct keymap_def) * len,
|
||||
GFP_KERNEL);
|
||||
if (!kbd->km) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
offset = i * cells;
|
||||
if (of_property_read_u32_index(cli->dev.of_node, "keymap",
|
||||
offset, &brain_keycode)) {
|
||||
dev_err(&cli->dev,
|
||||
"could not read DT property (brain keycode)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (of_property_read_u32_index(cli->dev.of_node, "keymap",
|
||||
offset + 1, &kernel_keycode)) {
|
||||
dev_err(&cli->dev,
|
||||
"could not read DT property (kernel keycode)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
kbd->km[i].brain_keycode = brain_keycode;
|
||||
kbd->km[i].kernel_keycode = kernel_keycode;
|
||||
dev_dbg(&cli->dev, "normal: brain: %02x, kernel: %02x",
|
||||
brain_keycode, kernel_keycode);
|
||||
}
|
||||
|
||||
if (!of_get_property(cli->dev.of_node, "keymap-symbol", &len)) {
|
||||
dev_err(&cli->dev, "DT node has no keymap (symbol)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
len /= sizeof(u32) * cells;
|
||||
kbd->kmlen_symbol = len;
|
||||
|
||||
kbd->km_symbol = devm_kzalloc(
|
||||
&cli->dev, sizeof(struct keymap_def) * len, GFP_KERNEL);
|
||||
if (!kbd->km_symbol) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
offset = i * cells;
|
||||
if (of_property_read_u32_index(cli->dev.of_node,
|
||||
"keymap-symbol", offset,
|
||||
&brain_keycode)) {
|
||||
dev_err(&cli->dev,
|
||||
"could not read DT property (brain keycode)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (of_property_read_u32_index(cli->dev.of_node,
|
||||
"keymap-symbol", offset + 1,
|
||||
&kernel_keycode)) {
|
||||
dev_err(&cli->dev,
|
||||
"could not read DT property (kernel keycode)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
kbd->km_symbol[i].brain_keycode = brain_keycode;
|
||||
kbd->km_symbol[i].kernel_keycode = kernel_keycode;
|
||||
dev_dbg(&cli->dev, "symbol: brain: %02x, kernel: %02x",
|
||||
brain_keycode, kernel_keycode);
|
||||
}
|
||||
|
||||
kbd->cli = cli;
|
||||
i2c_set_clientdata(cli, kbd);
|
||||
|
||||
kbd->idev = devm_input_allocate_device(&cli->dev);
|
||||
if (!kbd->idev) {
|
||||
dev_err(&cli->dev, "failed to allocate inpute device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
kbd->idev->name = BRAIN_KBD_I2C_DEV_NAME;
|
||||
kbd->idev->id.bustype = BUS_I2C;
|
||||
|
||||
__set_bit(EV_REP, kbd->idev->evbit); /* autorepeat */
|
||||
|
||||
for (i = 0; i < kbd->kmlen; i++) {
|
||||
input_set_capability(kbd->idev, EV_KEY,
|
||||
kbd->km[i].kernel_keycode);
|
||||
}
|
||||
|
||||
for (i = 0; i < kbd->kmlen_symbol; i++) {
|
||||
input_set_capability(kbd->idev, EV_KEY,
|
||||
kbd->km_symbol[i].kernel_keycode);
|
||||
}
|
||||
|
||||
err = input_register_device(kbd->idev);
|
||||
if (err) {
|
||||
dev_err(&cli->dev, "failed to register input device: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x00);
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x0a);
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x01);
|
||||
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x40);
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x41);
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x48);
|
||||
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x00);
|
||||
i2c_smbus_read_word_data(kbd->cli, 0x04);
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x01);
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x0a);
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x00);
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x0a);
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x01);
|
||||
i2c_smbus_write_byte_data(kbd->cli, 0x88, 0x08);
|
||||
i2c_smbus_write_byte_data(kbd->cli, 0x82, 0x05);
|
||||
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x00);
|
||||
i2c_smbus_read_byte_data(kbd->cli, 0x0a);
|
||||
|
||||
i2c_smbus_write_byte_data(kbd->cli, 0x82, 0x02);
|
||||
i2c_smbus_write_byte_data(kbd->cli, 0x82, 0x03);
|
||||
|
||||
msleep(500);
|
||||
|
||||
i2c_smbus_read_word_data(kbd->cli, 0x04);
|
||||
|
||||
err = devm_request_threaded_irq(&cli->dev, cli->irq, bk_i2c_irq_handler,
|
||||
NULL, IRQF_ONESHOT,
|
||||
BRAIN_KBD_I2C_DEV_NAME, kbd);
|
||||
if (err) {
|
||||
dev_err(&cli->dev, "failed to request irq: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bk_i2c_id_table[] = {
|
||||
{ BRAIN_KBD_I2C_DEV_NAME, 0 },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static const struct of_device_id bk_i2c_of_match[] = {
|
||||
{
|
||||
.compatible = "sharp,brain-kbd-i2c",
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct i2c_driver bk_i2c_driver = {
|
||||
.driver = {
|
||||
.name = BRAIN_KBD_I2C_DEV_NAME,
|
||||
.of_match_table = of_match_ptr(bk_i2c_of_match),
|
||||
},
|
||||
.probe = bk_i2c_probe,
|
||||
.id_table = bk_i2c_id_table,
|
||||
};
|
||||
module_i2c_driver(bk_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Takumi Sueda <puhitaku@gmail.com>");
|
||||
MODULE_DESCRIPTION("SHARP Brain keyboard driver");
|
||||
MODULE_LICENSE("GPL v2");
|
Loading…
Reference in New Issue