[U-Boot,v2,4/6] ARM: tegra: nvec: add keyboard support
diff mbox

Message ID 1398561270-25091-5-git-send-email-danindrey@mail.ru
State Deferred
Delegated to: Tom Warren
Headers show

Commit Message

Andrey Danin April 27, 2014, 1:14 a.m. UTC
Signed-off-by: Andrey Danin <danindrey@mail.ru>
CC: Stephen Warren <swarren@nvidia.com>
CC: Marc Dietrich <marvin24@gmx.de>
CC: Julian Andres Klode <jak@jak-linux.org>
CC: ac100@lists.launchpad.net
---
 Changes for v2:
	- fixed incorrect keys handling in nvec-keyboard driver

 .../include/asm/arch-tegra/tegra_nvec_keyboard.h   |  304 ++++++++++++++++++++
 drivers/input/Makefile                             |    3 +
 drivers/input/tegra-nvec-kbc.c                     |  215 ++++++++++++++
 include/configs/tegra-common-post.h                |    2 +
 4 files changed, 524 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-tegra/tegra_nvec_keyboard.h
 create mode 100644 drivers/input/tegra-nvec-kbc.c

Comments

Stephen Warren April 28, 2014, 11:23 p.m. UTC | #1
On 04/26/2014 07:14 PM, Andrey Danin wrote:
> Signed-off-by: Andrey Danin <danindrey@mail.ru>
> CC: Stephen Warren <swarren@nvidia.com>
> CC: Marc Dietrich <marvin24@gmx.de>
> CC: Julian Andres Klode <jak@jak-linux.org>
> CC: ac100@lists.launchpad.net
> ---
>  Changes for v2:
> 	- fixed incorrect keys handling in nvec-keyboard driver

No patch description?

Something isn't quite right with the device registration here. If U-Boot
starts with stdin=serial, and then I "setenv stdin
serial,tegra-nvec-kbc", then I can't use the keyboard. However, if I
save the environment and reset, then nvec input works. Similarly, it
gets confused if I remove and re-add tegra-nvec-kbc to $stdin.

I've also observed quite a stuck keys, hangs, and failed requests:
ERROR: nvec failed to perform request
at .../drivers/i2c/tegra_nvec.c:166/nvec_do_request()

But when it works, it's nice:-)

Patch
diff mbox

diff --git a/arch/arm/include/asm/arch-tegra/tegra_nvec_keyboard.h b/arch/arm/include/asm/arch-tegra/tegra_nvec_keyboard.h
new file mode 100644
index 0000000..5f789f4
--- /dev/null
+++ b/arch/arm/include/asm/arch-tegra/tegra_nvec_keyboard.h
@@ -0,0 +1,304 @@ 
+/*
+ * (C) Copyright 2014
+ * Andrey Danin <danindrey@mail.ru>
+ * (C) Copyright 2009
+ * NVIDIA Corporation.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _TEGRA_NVEC_KEYBOARD_H_
+#define _TEGRA_NVEC_KEYBOARD_H_
+
+#include <linux/input.h>
+
+
+/* Keytables */
+
+static unsigned short code_tab_102us[] = {
+	/* 0x00 */
+	KEY_GRAVE,
+	KEY_ESC,
+	KEY_1,
+	KEY_2,
+	KEY_3,
+	KEY_4,
+	KEY_5,
+	KEY_6,
+	KEY_7,
+	KEY_8,
+	KEY_9,
+	KEY_0,
+	KEY_MINUS,
+	KEY_EQUAL,
+	KEY_BACKSPACE,
+	KEY_TAB,
+	/* 0x10 */
+	KEY_Q,
+	KEY_W,
+	KEY_E,
+	KEY_R,
+	KEY_T,
+	KEY_Y,
+	KEY_U,
+	KEY_I,
+	KEY_O,
+	KEY_P,
+	KEY_LEFTBRACE,
+	KEY_RIGHTBRACE,
+	KEY_ENTER,
+	KEY_LEFTCTRL,
+	KEY_A,
+	KEY_S,
+	/* 0x20 */
+	KEY_D,
+	KEY_F,
+	KEY_G,
+	KEY_H,
+	KEY_J,
+	KEY_K,
+	KEY_L,
+	KEY_SEMICOLON,
+	KEY_APOSTROPHE,
+	KEY_GRAVE,
+	KEY_LEFTSHIFT,
+	KEY_BACKSLASH,
+	KEY_Z,
+	KEY_X,
+	KEY_C,
+	KEY_V,
+	/* 0x30 */
+	KEY_B,
+	KEY_N,
+	KEY_M,
+	KEY_COMMA,
+	KEY_DOT,
+	KEY_SLASH,
+	KEY_RIGHTSHIFT,
+	KEY_KPASTERISK,
+	KEY_LEFTALT,
+	KEY_SPACE,
+	KEY_CAPSLOCK,
+	KEY_F1,
+	KEY_F2,
+	KEY_F3,
+	KEY_F4,
+	KEY_F5,
+	/* 0x40 */
+	KEY_F6,
+	KEY_F7,
+	KEY_F8,
+	KEY_F9,
+	KEY_F10,
+	KEY_FN,
+	/* VK_SCROLL */
+	0,
+	KEY_KP7,
+	KEY_KP8,
+	KEY_KP9,
+	KEY_KPMINUS,
+	KEY_KP4,
+	KEY_KP5,
+	KEY_KP6,
+	KEY_KPPLUS,
+	KEY_KP1,
+	/* 0x50 */
+	KEY_KP2,
+	KEY_KP3,
+	KEY_KP0,
+	KEY_KPDOT,
+	/* VK_SNAPSHOT */
+	0, /* KEY_MENU, */
+	KEY_POWER,
+	/* VK_OEM_102 */
+	KEY_102ND,
+	KEY_F11,
+	KEY_F12,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	/* 0x60 */
+	0,
+	0,
+	0,
+	0, /* KEY_SEARCH, */
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	/* 0x70 */
+	0,
+	0,
+	0,
+	KEY_KP5,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	KEY_KP9,
+};
+
+static unsigned short extcode_tab_us102[] = {
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	/* 0x10 */
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	/* VK_MEDIA_NEXT_TRACK */
+	0,
+	0,
+	0,
+	/* VK_RETURN */
+	0,
+	KEY_RIGHTCTRL,
+	0,
+	0,
+	/* 0x20 */
+	KEY_MUTE,
+	/* VK_LAUNCH_APP1 */
+	0,
+	/* VK_MEDIA_PLAY_PAUSE */
+	0,
+	0,
+	/* VK_MEDIA_STOP */
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	/* 0x30 */
+	KEY_VOLUMEUP,
+	0,
+	/* VK_BROWSER_HOME */
+	0,
+	0,
+	0,
+	/* VK_DIVIDE */
+	KEY_KPSLASH,
+	0,
+	/* VK_SNAPSHOT */
+	KEY_SYSRQ,
+	/* VK_RMENU */
+	KEY_RIGHTALT,
+	/* VK_OEM_NV_BACKLIGHT_UP */
+	0,
+	/* VK_OEM_NV_BACKLIGHT_DN */
+	0,
+	/* VK_OEM_NV_BACKLIGHT_AUTOTOGGLE */
+	0,
+	/* VK_OEM_NV_POWER_INFO */
+	0,
+	/* VK_OEM_NV_WIFI_TOGGLE */
+	0,
+	/* VK_OEM_NV_DISPLAY_SELECT */
+	0,
+	/* VK_OEM_NV_AIRPLANE_TOGGLE */
+	0,
+	/* 0x40 */
+	0,
+	KEY_LEFT,
+	0,
+	0,
+	0,
+	0,
+	0, /* KEY_CANCEL, */
+	KEY_HOME,
+	KEY_UP,
+	KEY_PAGEUP,
+	0,
+	KEY_LEFT,
+	0,
+	KEY_RIGHT,
+	0,
+	KEY_END,
+	/* 0x50 */
+	KEY_DOWN,
+	KEY_PAGEDOWN,
+	KEY_INSERT,
+	KEY_DELETE,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	KEY_LEFTMETA,
+	0,
+	KEY_ESC,
+	KEY_KPMINUS,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	/* VK_BROWSER_SEARCH */
+	0,
+	/* VK_BROWSER_FAVORITES */
+	0,
+	/* VK_BROWSER_REFRESH */
+	0,
+	/* VK_BROWSER_STOP */
+	0,
+	/* VK_BROWSER_FORWARD */
+	0,
+	/* VK_BROWSER_BACK */
+	0,
+	/* VK_LAUNCH_APP2 */
+	0,
+	/* VK_LAUNCH_MAIL */
+	0,
+	/* VK_LAUNCH_MEDIA_SELECT */
+	0,
+};
+
+static unsigned short *code_tabs[] = { code_tab_102us, extcode_tab_us102 };
+
+
+#endif /* _TEGRA_NVEC_KEYBOARD_H_ */
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index a8e9be2..38c576f 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -7,6 +7,9 @@ 
 
 obj-$(CONFIG_I8042_KBD) += i8042.o
 obj-$(CONFIG_TEGRA_KEYBOARD) += tegra-kbc.o
+ifdef CONFIG_SYS_I2C_TEGRA_NVEC
+obj-$(CONFIG_TEGRA_NVEC_KEYBOARD) += tegra-nvec-kbc.o
+endif
 obj-$(CONFIG_CROS_EC_KEYB) += cros_ec_keyb.o
 ifdef CONFIG_PS2KBD
 obj-y += keyboard.o pc_keyb.o
diff --git a/drivers/input/tegra-nvec-kbc.c b/drivers/input/tegra-nvec-kbc.c
new file mode 100644
index 0000000..f5832b1
--- /dev/null
+++ b/drivers/input/tegra-nvec-kbc.c
@@ -0,0 +1,215 @@ 
+/*
+ *  (C) Copyright 2014
+ *  Andrey Danin <danindrey@mail.ru>
+ *  (C) Copyright 2011
+ *  NVIDIA Corporation <www.nvidia.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <input.h>
+#include <asm/arch-tegra/tegra_nvec.h>
+#include <asm/arch-tegra/tegra_nvec_keyboard.h>
+
+
+enum {
+	KBC_MAX_KPENT = 8,
+	KBC_REPEAT_RATE_MS	= 30,
+	KBC_REPEAT_DELAY_MS	= 240,
+};
+
+/* keyboard config/state */
+static struct keyb {
+	int registered;
+	struct input_config input;	/* The input layer */
+	int pressed_keys[KBC_MAX_KPENT];
+	int pressed_keys_cnt;
+} config;
+
+
+/* 0 - pressed, other - released */
+char keys[256];
+
+
+/* nvec commands */
+static char enable_kbd[] = { NVEC_KBD, ENABLE_KBD };
+static char reset_kbd[] = { NVEC_PS2, MOUSE_SEND_CMD, MOUSE_RESET, 3 };
+static char clear_leds[] = { NVEC_KBD, SET_LEDS, 0 };
+
+
+/**
+ * Check the tegra nvec keyboard, and send any keys that are pressed.
+ *
+ * This is called by input_tstc() and input_getc() when they need more
+ * characters
+ *
+ * @param input		Input configuration
+ * @return 1, to indicate that we have something to look at
+ */
+int tegra_nvec_kbc_check(struct input_config *input)
+{
+	int i;
+
+	nvec_read_events();
+
+	config.pressed_keys_cnt = 0;
+	/* TODO Optimization required */
+	for (i = 0; i < sizeof(keys); ++i) {
+		if (keys[i] == 0 && config.pressed_keys_cnt < KBC_MAX_KPENT) {
+			config.pressed_keys[config.pressed_keys_cnt] = i;
+			++config.pressed_keys_cnt;
+		}
+	}
+
+	input_send_keycodes(input,
+			    config.pressed_keys,
+			    config.pressed_keys_cnt);
+
+	return config.pressed_keys_cnt;
+}
+
+
+/**
+ * Test if keys are available to be read
+ *
+ * @return 0 if no keys available, 1 if keys are available
+ */
+static int kbd_tstc(void)
+{
+	/* Just get input to do this for us */
+	return input_tstc(&config.input);
+}
+
+
+/**
+ * Read a key
+ *
+ * TODO: U-Boot wants 0 for no key, but Ctrl-@ is a valid key...
+ *
+ * @return ASCII key code, or 0 if no key, or -1 if error
+ */
+static int kbd_getc(void)
+{
+	/* Just get input to do this for us */
+	return input_getc(&config.input);
+}
+
+
+int tegra_nvec_process_keyboard_msg(const unsigned char *msg)
+{
+	int code, state;
+	int event_type;
+	int _size;
+	int key;
+
+
+	event_type = nvec_msg_event_type(msg);
+	if (event_type != NVEC_KEYBOARD)
+		return -1;
+
+	_size = (msg[0] & (3 << 5)) >> 5;
+
+	if (_size == NVEC_VAR_SIZE) {
+		debug("Skip unsupported msg (size: %d)\n", _size);
+		return -1;
+	}
+
+	if (_size == NVEC_3BYTES)
+		msg++;
+
+	code = msg[1] & 0x7f;
+	state = msg[1] & 0x80;
+
+	key = code_tabs[_size][code];
+	keys[key] = state;
+
+	return 0;
+}
+
+
+int tegra_nvec_enable_kbd_events(void)
+{
+	if (nvec_do_request(reset_kbd, 4)) {
+		error("NVEC: failed to reset keyboard\n");
+		return -1;
+	}
+	if (nvec_do_request(clear_leds, 3)) {
+		error("NVEC: failed to clear leds\n");
+		return -1;
+	}
+	if (nvec_do_request(enable_kbd, 2)) {
+		error("NVEC: failed to enable keyboard\n");
+		return -1;
+	}
+
+	debug("NVEC: keyboard initialization finished\n");
+
+	return 0;
+}
+
+
+static int tegra_nvec_kbc_start(void)
+{
+	if (config.registered)
+		return 0;
+
+	struct nvec_periph nvec_keyboard;
+	memset(&nvec_keyboard, 0, sizeof(nvec_keyboard));
+	nvec_keyboard.start = tegra_nvec_enable_kbd_events;
+	nvec_keyboard.process_msg = tegra_nvec_process_keyboard_msg;
+
+	if (nvec_register_periph(NVEC_KEYBOARD, &nvec_keyboard)) {
+		error("NVEC: failed to register keyboard perephirial device");
+		return -1;
+	}
+
+	config.registered = 1;
+
+	return 0;
+}
+
+
+int drv_keyboard_init(void)
+{
+	struct stdio_dev dev;
+	int error;
+#ifdef CONFIG_CONSOLE_MUX
+	char *stdinname = getenv("stdin");
+#endif
+
+	memset(keys, 1, sizeof(keys));
+
+	config.registered = 0;
+
+	if (input_init(&config.input, 0)) {
+		printf("nvec kbc: cannot set up input\n");
+		return -1;
+	}
+	input_set_delays(&config.input, KBC_REPEAT_DELAY_MS,
+			 KBC_REPEAT_RATE_MS);
+	config.input.read_keys = tegra_nvec_kbc_check;
+
+	memset(&dev, '\0', sizeof(dev));
+	strcpy(dev.name, "tegra-nvec-kbc");
+	dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
+	dev.getc = kbd_getc;
+	dev.tstc = kbd_tstc;
+	dev.start = tegra_nvec_kbc_start;
+
+	/* Register the device. tegra_nvec_kbc_start() will be called soon */
+	error = input_stdio_register(&dev);
+	if (error) {
+		printf("nvec kbc: failed to register stdio device, %d\n",
+		       error);
+		return error;
+	}
+#ifdef CONFIG_CONSOLE_MUX
+	error = iomux_doenv(stdin, stdinname);
+	if (error) {
+		printf("nvec kbc: iomux_doenv failed, %d\n", error);
+		return error;
+	}
+#endif
+	return 0;
+}
diff --git a/include/configs/tegra-common-post.h b/include/configs/tegra-common-post.h
index e1a3bbc..efc0ef9 100644
--- a/include/configs/tegra-common-post.h
+++ b/include/configs/tegra-common-post.h
@@ -106,6 +106,8 @@ 
 
 #ifdef CONFIG_TEGRA_KEYBOARD
 #define STDIN_KBD_KBC ",tegra-kbc"
+#elif defined(CONFIG_TEGRA_NVEC_KEYBOARD)
+#define STDIN_KBD_KBC ",tegra-nvec-kbc"
 #else
 #define STDIN_KBD_KBC ""
 #endif