summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWendly Li <wendlyli@google.com>2022-09-26 07:32:45 +0000
committerWendly Li <wendlyli@google.com>2022-12-22 02:54:32 +0000
commit60af8f12336967a3684faca2c9dd06d03177da69 (patch)
treef9e7cf7f377fcc502ec5adbb7499d20ab0b32750
parente5b0ac0a985b17dc4865532b84daa92d1fc09798 (diff)
downloadcommon-60af8f12336967a3684faca2c9dd06d03177da69.tar.gz
touch/common: suppport aoc cannel mode for tbn
Bug: 177329320 Test: Wakeup gesture works properly Change-Id: I233c4c89a985b9279a0833c734c834a262eabe22 Signed-off-by: Wendly Li <wendlyli@google.com>
-rw-r--r--Kbuild1
-rw-r--r--Kconfig8
-rw-r--r--Makefile1
-rw-r--r--goog_touch_interface.c12
-rw-r--r--goog_touch_interface.h4
-rw-r--r--touch_bus_negotiator.c239
-rw-r--r--touch_bus_negotiator.h38
7 files changed, 267 insertions, 36 deletions
diff --git a/Kbuild b/Kbuild
index a42d6f2..62e5fa3 100644
--- a/Kbuild
+++ b/Kbuild
@@ -3,6 +3,7 @@
ccflags-y += -I$(srctree)/$(src)/include
ccflags-y += -I$(srctree)/../private/google-modules/display
ccflags-y += -I$(srctree)/../private/google-modules/display/include/uapi
+ccflags-y += -I$(srctree)/../private/google-modules/aoc
obj-$(CONFIG_TOUCHSCREEN_TBN) += touch_bus_negotiator.o
obj-$(CONFIG_TOUCHSCREEN_HEATMAP) += heatmap.o
diff --git a/Kconfig b/Kconfig
index 7636621..d121fa6 100644
--- a/Kconfig
+++ b/Kconfig
@@ -20,6 +20,14 @@ config TOUCHSCREEN_TBN
To compile this driver as a module, choose M here: the module will be
called touch_bus_negotiator.
+config TOUCHSCREEN_TBN_AOC_CHANNEL_MODE
+ bool "AOC channel mode for TBN"
+ depends on TOUCHSCREEN_TBN
+ help
+ Say Y here if you want to enable AOC channel mode for TBN.
+
+ If unsure, say N.
+
config TOUCHSCREEN_OFFLOAD
tristate "Touchscreen algorithm offload"
depends on (TOUCHSCREEN_FTS || TOUCHSCREEN_SEC_TS)
diff --git a/Makefile b/Makefile
index 3cc7e5c..36f7956 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,7 @@ KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
M ?= $(shell pwd)
EXTRA_CFLAGS += -DDYNAMIC_DEBUG_MODULE
+EXTRA_SYMBOLS += $(OUT_DIR)/../private/google-modules/aoc/Module.symvers
include $(KERNEL_SRC)/../private/google-modules/soc/gs/Makefile.include
diff --git a/goog_touch_interface.c b/goog_touch_interface.c
index dd0de7e..cd1f8fd 100644
--- a/goog_touch_interface.c
+++ b/goog_touch_interface.c
@@ -2904,7 +2904,8 @@ static void goog_pm_resume(struct gti_pm *pm)
pm_stay_awake(gti->dev);
if (gti->tbn_register_mask) {
- ret = tbn_request_bus(gti->tbn_register_mask);
+ gti->lptw_triggered = false;
+ ret = tbn_request_bus_with_result(gti->tbn_register_mask, &gti->lptw_triggered);
if (ret)
GOOG_ERR("tbn_request_bus failed, ret %d!\n", ret);
}
@@ -3177,6 +3178,15 @@ init_variable_report_rate_failed:
return 0;
}
+int goog_get_lptw_triggered(struct goog_touch_interface *gti)
+{
+ if (gti == NULL)
+ return -ENODEV;
+
+ return gti->lptw_triggered;
+}
+EXPORT_SYMBOL(goog_get_lptw_triggered);
+
static irqreturn_t gti_irq_handler(int irq, void *data)
{
irqreturn_t ret;
diff --git a/goog_touch_interface.h b/goog_touch_interface.h
index bb33686..35357b6 100644
--- a/goog_touch_interface.h
+++ b/goog_touch_interface.h
@@ -549,6 +549,7 @@ struct gti_pm {
* @ignore_palm_update: Ignore fw_palm status updates made on offload state change.
* @default_palm_enabled: the palm default setting.
* @wakeup_before_force_active_enabled: waking up the screen to force active.
+ * @lptw_triggered: LPTW is triggered or not.
* @wakeup_before_force_active_delay: the ms delay after waking up screen to force active.
* @offload_id: id that used by touch offload.
* @heatmap_buf: heatmap buffer that used by v4l2.
@@ -617,6 +618,7 @@ struct goog_touch_interface {
bool ignore_palm_update;
bool default_palm_enabled;
bool wakeup_before_force_active_enabled;
+ bool lptw_triggered;
unsigned int wakeup_before_force_active_delay;
union {
u8 offload_id_byte[4];
@@ -710,5 +712,7 @@ void goog_notify_fw_status_changed(struct goog_touch_interface *gti,
void gti_debug_hc_dump(struct goog_touch_interface *gti);
void gti_debug_input_dump(struct goog_touch_interface *gti);
+int goog_get_lptw_triggered(struct goog_touch_interface *gti);
+
#endif // _GOOG_TOUCH_INTERFACE_
diff --git a/touch_bus_negotiator.c b/touch_bus_negotiator.c
index cae8435..31d96b1 100644
--- a/touch_bus_negotiator.c
+++ b/touch_bus_negotiator.c
@@ -17,20 +17,26 @@
#include <linux/delay.h>
#include "touch_bus_negotiator.h"
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN_AOC_CHANNEL_MODE)
+#include <uapi/linux/sched/types.h>
+#include "aoc_tbn_service_dev.h"
+#endif
+
#define TBN_MODULE_NAME "touch_bus_negotiator"
+#define TBN_AOC_CHANNEL_THREAD_NAME "tbn_aoc_channel"
-static struct tbn_context *tbn_context = NULL;
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN_AOC_CHANNEL_MODE)
+static void handle_tbn_event_response(struct tbn_context *tbn,
+ struct TbnEventResponse *response);
+#endif
-enum tbn_operation {
- AP_RELEASE_BUS,
- AP_REQUEST_BUS,
-};
+static struct tbn_context *tbn_context;
static irqreturn_t tbn_aoc2ap_irq_thread(int irq, void *ptr)
{
struct tbn_context *tbn = ptr;
- dev_info(tbn_context->dev, "%s: bus_released:%d bus_requested:%d.\n", __func__,
+ dev_info(tbn->dev, "%s: bus_released:%d bus_requested:%d.\n", __func__,
completion_done(&tbn->bus_released), completion_done(&tbn->bus_requested));
if (completion_done(&tbn->bus_released) && completion_done(&tbn->bus_requested))
@@ -65,7 +71,108 @@ static irqreturn_t tbn_aoc2ap_irq_thread(int irq, void *ptr)
return IRQ_HANDLED;
}
-int tbn_handshaking(struct tbn_context *tbn, enum tbn_operation operation)
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN_AOC_CHANNEL_MODE)
+static int aoc_channel_kthread(void *data)
+{
+ struct tbn_context *tbn = data;
+ struct TbnEventResponse resp;
+ ssize_t len;
+
+ while (!kthread_should_stop()) {
+ if (!aoc_tbn_service_ready()) {
+ dev_warn(tbn->dev, "%s: AOC TBN service is not ready.\n",
+ __func__);
+ msleep(1000);
+ continue;
+ }
+
+ len = aoc_tbn_service_read(&resp, sizeof(resp));
+ if (len < 0) {
+ dev_err(tbn->dev, "%s: failed to read message, err: %d\n",
+ __func__, len);
+ msleep(1000);
+ continue;
+ }
+
+ if (kthread_should_stop()) {
+ break;
+ }
+
+ if (len == sizeof(resp)) {
+ handle_tbn_event_response(tbn, &resp);
+ }
+ }
+
+ return 0;
+}
+
+static void handle_tbn_event_response(struct tbn_context *tbn,
+ struct TbnEventResponse *response)
+{
+ mutex_lock(&tbn->event_lock);
+
+ if (response->id != tbn->event.id) {
+ dev_err(tbn->dev,
+ "%s: receive wrong response, id: %d, expected id: %d, "
+ "bus_released:%d bus_requested:%d.\n",
+ __func__, response->id, tbn->event.id,
+ completion_done(&tbn->bus_released),
+ completion_done(&tbn->bus_requested));
+ goto exit;
+ }
+
+ if (response->err != 0) {
+ dev_err(tbn->dev, "%s: send tbn event failed, err %d!\n",
+ __func__, response->err);
+ tbn->event_resp.err = response->err;
+ } else {
+ tbn->event_resp.lptw_triggered = response->lptw_triggered;
+ }
+
+ if (response->operation == TBN_OPERATION_AP_REQUEST_BUS) {
+ complete_all(&tbn->bus_requested);
+ } else if (response->operation == TBN_OPERATION_AP_RELEASE_BUS) {
+ complete_all(&tbn->bus_released);
+ } else {
+ dev_err(tbn->dev, "%s: response unknown operation, op: %d!\n",
+ __func__, response->operation);
+ }
+
+exit:
+ mutex_unlock(&tbn->event_lock);
+}
+
+static void send_tbn_event(struct tbn_context *tbn, enum TbnOperation operation)
+{
+ ssize_t len;
+ int retry = 3;
+
+ if (!aoc_tbn_service_ready()) {
+ dev_err(tbn_context->dev, "%s: AOC TBN service is not ready.\n",
+ __func__);
+ return;
+ }
+
+ mutex_lock(&tbn->event_lock);
+
+ tbn->event.operation = operation;
+ tbn->event.id++;
+
+ while (retry) {
+ len = aoc_tbn_service_write(&tbn->event, sizeof(tbn->event));
+ if (len == sizeof(tbn->event)) {
+ break;
+ }
+ dev_err(tbn_context->dev, "%s: failed to send TBN event, retry: %d.\n",
+ __func__, retry);
+ retry--;
+ }
+
+ mutex_unlock(&tbn->event_lock);
+}
+#endif
+
+int tbn_handshaking(struct tbn_context *tbn, enum TbnOperation operation)
{
struct completion *wait_for_completion;
enum tbn_bus_owner bus_owner;
@@ -74,21 +181,27 @@ int tbn_handshaking(struct tbn_context *tbn, enum tbn_operation operation)
const char *msg;
int ret = 0;
- if (!tbn || tbn->registered_mask == 0)
- return 0;
+ if (!tbn || tbn->registered_mask == 0) {
+ dev_err(tbn_context->dev, "%s: tbn is not ready to serve.\n", __func__);
+ return -EINVAL;
+ }
- if (operation == AP_REQUEST_BUS) {
+ if (operation == TBN_OPERATION_AP_REQUEST_BUS) {
wait_for_completion = &tbn->bus_requested;
bus_owner = TBN_BUS_OWNER_AP;
irq_type = IRQF_TRIGGER_FALLING;
timeout = TBN_REQUEST_BUS_TIMEOUT_MS;
msg = "request";
- } else {
+ } else if (operation == TBN_OPERATION_AP_RELEASE_BUS) {
wait_for_completion = &tbn->bus_released;
bus_owner = TBN_BUS_OWNER_AOC;
irq_type = IRQF_TRIGGER_RISING;
timeout = TBN_RELEASE_BUS_TIMEOUT_MS;
msg = "release";
+ } else {
+ dev_err(tbn_context->dev, "%s: request unknown operation, op: %d.\n",
+ __func__, operation);
+ return -EINVAL;
}
reinit_completion(wait_for_completion);
@@ -117,12 +230,34 @@ int tbn_handshaking(struct tbn_context *tbn, enum tbn_operation operation)
} else
dev_info(tbn->dev, "AP %s bus ... SUCCESS!\n", msg);
disable_irq_nosync(tbn->aoc2ap_irq);
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN_AOC_CHANNEL_MODE)
+ } else if (tbn->mode == TBN_MODE_AOC_CHANNEL) {
+ tbn->event_resp.lptw_triggered = false;
+ tbn->event_resp.err = 0;
+
+ send_tbn_event(tbn, operation);
+ if (wait_for_completion_timeout(wait_for_completion,
+ msecs_to_jiffies(timeout)) == 0) {
+ dev_err(tbn->dev, "AP %s bus ... timeout!\n", msg);
+ complete_all(wait_for_completion);
+ ret = -ETIMEDOUT;
+ } else {
+ if (tbn->event_resp.err == 0) {
+ dev_info(tbn->dev, "AP %s bus ... SUCCESS!\n", msg);
+ } else {
+ dev_info(tbn->dev, "AP %s bus ... failed!\n", msg);
+ ret = -EBUSY;
+ }
+ }
+#endif
+ } else {
+ ret = -EINVAL;
}
return ret;
}
-int tbn_request_bus(u32 dev_mask)
+int tbn_request_bus_with_result(u32 dev_mask, bool *lptw_triggered)
{
int ret = 0;
@@ -139,11 +274,13 @@ int tbn_request_bus(u32 dev_mask)
}
if (tbn_context->requested_dev_mask == 0) {
- ret = tbn_handshaking(tbn_context, AP_REQUEST_BUS);
+ ret = tbn_handshaking(tbn_context, TBN_OPERATION_AP_REQUEST_BUS);
+ if ((ret == 0) && (lptw_triggered != NULL))
+ *lptw_triggered = tbn_context->event_resp.lptw_triggered;
} else {
dev_dbg(tbn_context->dev,
- "%s: Bus already requested, requested_dev_mask %#x dev_mask %#x.\n",
- __func__, tbn_context->requested_dev_mask, dev_mask);
+ "%s: Bus already requested, requested_dev_mask %#x dev_mask %#x.\n",
+ __func__, tbn_context->requested_dev_mask, dev_mask);
}
tbn_context->requested_dev_mask |= dev_mask;
@@ -151,6 +288,12 @@ int tbn_request_bus(u32 dev_mask)
return ret;
}
+EXPORT_SYMBOL_GPL(tbn_request_bus_with_result);
+
+int tbn_request_bus(u32 dev_mask)
+{
+ return tbn_request_bus_with_result(dev_mask, NULL);
+}
EXPORT_SYMBOL_GPL(tbn_request_bus);
int tbn_release_bus(u32 dev_mask)
@@ -179,7 +322,7 @@ int tbn_release_bus(u32 dev_mask)
/* Release the bus when the last requested_dev_mask bit releases. */
if (tbn_context->requested_dev_mask == dev_mask) {
- ret = tbn_handshaking(tbn_context, AP_RELEASE_BUS);
+ ret = tbn_handshaking(tbn_context, TBN_OPERATION_AP_RELEASE_BUS);
} else {
dev_dbg(tbn_context->dev,
"%s: Bus is still in use, requested_dev_mask %#x dev_mask %#x.\n",
@@ -238,22 +381,30 @@ static int tbn_probe(struct platform_device *pdev)
struct tbn_context *tbn = NULL;
struct device_node *np = dev->of_node;
int err = 0;
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN_AOC_CHANNEL_MODE)
+ struct sched_param param = {
+ .sched_priority = 10,
+ };
+#endif
+
tbn = devm_kzalloc(dev, sizeof(struct tbn_context), GFP_KERNEL);
if (!tbn)
goto failed;
tbn->dev = dev;
+ tbn->event_resp.lptw_triggered = false;
tbn_context = tbn;
dev_set_drvdata(tbn->dev, tbn);
if (of_property_read_u32(np, "tbn,max_devices", &tbn->max_devices))
tbn->max_devices = 1;
- if (of_property_read_bool(np, "tbn,ap2aoc_gpio") &&
- of_property_read_bool(np, "tbn,aoc2ap_gpio")) {
- tbn->mode = TBN_MODE_GPIO;
+ err = of_property_read_u32(np, "tbn,mode", &tbn->mode);
+ if (err)
+ tbn->mode = TBN_MODE_DISABLED;
+ if (tbn->mode == TBN_MODE_GPIO) {
tbn->ap2aoc_gpio = of_get_named_gpio(np, "tbn,ap2aoc_gpio", 0);
if (gpio_is_valid(tbn->ap2aoc_gpio)) {
err = devm_gpio_request_one(tbn->dev, tbn->ap2aoc_gpio,
@@ -292,8 +443,30 @@ static int tbn_probe(struct platform_device *pdev)
__func__, tbn->aoc2ap_gpio);
goto failed;
}
+
+ dev_info(tbn->dev,
+ "%s: gpios(aoc2ap: %d ap2aoc: %d)\n",
+ __func__, tbn->aoc2ap_gpio, tbn->ap2aoc_gpio);
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN_AOC_CHANNEL_MODE)
+ } else if (tbn->mode == TBN_MODE_AOC_CHANNEL) {
+ mutex_init(&tbn->event_lock);
+
+ tbn->aoc_channel_task = kthread_run(&aoc_channel_kthread, tbn,
+ TBN_AOC_CHANNEL_THREAD_NAME);
+ if (IS_ERR(tbn->aoc_channel_task)) {
+ err = PTR_ERR(tbn->aoc_channel_task);
+ goto failed;
+ }
+
+ err = sched_setscheduler(tbn->aoc_channel_task, SCHED_FIFO, &param);
+ if (err != 0) {
+ goto failed;
+ }
+#endif
} else {
- tbn->mode = TBN_MODE_DISABLED;
+ dev_err(tbn->dev, "bus negotiator: invalid mode: %d\n", tbn->mode);
+ err = -EINVAL;
+ goto failed;
}
mutex_init(&tbn->dev_mask_mutex);
@@ -303,11 +476,7 @@ static int tbn_probe(struct platform_device *pdev)
complete_all(&tbn->bus_requested);
complete_all(&tbn->bus_released);
- dev_info(tbn->dev,
- "%s: gpios(aoc2ap: %d ap2aoc: %d), mode %d\n",
- __func__, tbn->aoc2ap_gpio, tbn->ap2aoc_gpio, tbn->mode);
-
- dev_dbg(tbn->dev, "bus negotiator initialized: %pK\n", tbn);
+ dev_info(tbn->dev, "bus negotiator initialized: %pK, mode: %d\n", tbn, tbn->mode);
failed:
return err;
@@ -315,14 +484,20 @@ failed:
static int tbn_remove(struct platform_device *pdev)
{
- struct tbn_context *tbn = dev_get_drvdata(&(pdev->dev));
-
- free_irq(tbn->aoc2ap_irq, tbn);
- if (gpio_is_valid(tbn->aoc2ap_gpio))
- gpio_free(tbn->aoc2ap_gpio);
- if (gpio_is_valid(tbn->aoc2ap_gpio))
- gpio_free(tbn->aoc2ap_gpio);
+ struct device *dev = &pdev->dev;
+ struct tbn_context *tbn = dev_get_drvdata(dev);
+ if (tbn->mode == TBN_MODE_GPIO) {
+ free_irq(tbn->aoc2ap_irq, tbn);
+ if (gpio_is_valid(tbn->aoc2ap_gpio))
+ gpio_free(tbn->aoc2ap_gpio);
+ if (gpio_is_valid(tbn->aoc2ap_gpio))
+ gpio_free(tbn->aoc2ap_gpio);
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN_AOC_CHANNEL_MODE)
+ } else if (tbn->mode == TBN_MODE_AOC_CHANNEL) {
+ kthread_stop(tbn->aoc_channel_task);
+#endif
+ }
return 0;
}
diff --git a/touch_bus_negotiator.h b/touch_bus_negotiator.h
index a4c157e..a7c971f 100644
--- a/touch_bus_negotiator.h
+++ b/touch_bus_negotiator.h
@@ -6,14 +6,19 @@
#include <linux/notifier.h>
#include <linux/mutex.h>
#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/kthread.h>
+
+#define TBN_DEVICE_NAME "tbn"
+#define TBN_CLASS_NAME "tbn"
#define TBN_REQUEST_BUS_TIMEOUT_MS 500
#define TBN_RELEASE_BUS_TIMEOUT_MS 500
enum tbn_mode {
- TBN_MODE_DISABLED,
+ TBN_MODE_DISABLED = 0,
TBN_MODE_GPIO,
- TBN_MODE_MAILBOX,
+ TBN_MODE_AOC_CHANNEL,
};
enum tbn_bus_owner {
@@ -21,22 +26,49 @@ enum tbn_bus_owner {
TBN_BUS_OWNER_AOC = 1,
};
+enum TbnOperation : __u32 {
+ TBN_OPERATION_IDLE = 0,
+ TBN_OPERATION_AP_RELEASE_BUS,
+ TBN_OPERATION_AP_REQUEST_BUS,
+};
+
+struct TbnEvent {
+ __u32 id;
+ enum TbnOperation operation;
+} __packed;
+
+struct TbnEventResponse {
+ __u32 id;
+ __s32 err;
+ enum TbnOperation operation;
+ bool lptw_triggered;
+} __packed;
+
struct tbn_context {
struct device *dev;
struct completion bus_requested;
struct completion bus_released;
struct mutex dev_mask_mutex;
- u8 mode;
+ u32 mode;
u32 max_devices;
u32 registered_mask;
u32 requested_dev_mask;
int aoc2ap_gpio;
int ap2aoc_gpio;
int aoc2ap_irq;
+ struct task_struct *aoc_channel_task;
+
+ /* event management */
+ struct TbnEventResponse event_resp;
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN_AOC_CHANNEL_MODE)
+ struct TbnEvent event;
+ struct mutex event_lock;
+#endif
};
int register_tbn(u32 *output);
void unregister_tbn(u32 *output);
+int tbn_request_bus_with_result(u32 dev_mask, bool *lptw_triggered);
int tbn_request_bus(u32 dev_mask);
int tbn_release_bus(u32 dev_mask);