summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormasonwang <masonwang@google.com>2021-08-11 21:05:54 +0800
committermasonwang <masonwang@google.com>2021-08-13 20:39:21 +0800
commit8679f5c537224dfeee6917d4fda681fa1bc705cc (patch)
treeb6765028a39b62bc1a177c3ec1ef44762010e214
parent533a40307e2d8d1bcb6504b4c87d669459e49fb4 (diff)
downloadhimax_touch-8679f5c537224dfeee6917d4fda681fa1bc705cc.tar.gz
touch/himax: fix malfunction of suspend and resume.
To register to panel bridge to notify touch to suspend or resume when display is on or off. Bug: 196147257 Test: Verify pass by checking the log while display is on or off, please refer to test result in the b/196147257#comment2. Change-Id: Ic5db2be975a6df387422f0398d6d69aff0f52f9f
-rw-r--r--Makefile1
-rw-r--r--himax_common.c334
-rw-r--r--himax_common.h44
-rw-r--r--himax_debug.c2
-rw-r--r--himax_platform.c37
-rw-r--r--himax_platform.h1
6 files changed, 386 insertions, 33 deletions
diff --git a/Makefile b/Makefile
index 7726687..6dae80d 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,7 @@ KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_HIMAX_INCELL=y
KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_HIMAX_IC_HX83102=y
EXTRA_CFLAGS += -DDYNAMIC_DEBUG_MODULE
+EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_PANEL_BRIDGE
EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_TBN
EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HEATMAP
EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_OFFLOAD
diff --git a/himax_common.c b/himax_common.c
index e64826b..cb5c816 100644
--- a/himax_common.c
+++ b/himax_common.c
@@ -17,7 +17,9 @@
/*#include "himax_ic_core.h"*/
#include "himax_inspection.h"
#include "himax_modular.h"
-
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE)
+#include <samsung/exynos_drm_connector.h>
+#endif
#if defined(__HIMAX_MOD__)
int (*hx_msm_drm_register_client)(struct notifier_block *nb);
int (*hx_msm_drm_unregister_client)(struct notifier_block *nb);
@@ -215,6 +217,11 @@ static int gest_width, gest_height, gest_mid_x, gest_mid_y;
static int hx_gesture_coor[16];
#endif
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE)
+static int register_panel_bridge(struct himax_ts_data *ts);
+static void unregister_panel_bridge(struct drm_bridge *bridge);
+#endif
+
int g_ts_dbg;
EXPORT_SYMBOL(g_ts_dbg);
@@ -2800,8 +2807,194 @@ static int hx_chk_flash_sts(uint32_t size)
}
#endif
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE)
+static void himax_ts_aggregate_bus_state(struct himax_ts_data *ts)
+{
+ I("%s: bus_refmask = 0x%02X.\n", __func__, ts->bus_refmask);
+
+ /* Complete or cancel any outstanding transitions */
+ cancel_work_sync(&ts->suspend_work);
+ cancel_work_sync(&ts->resume_work);
+
+ if ((ts->bus_refmask == 0 &&
+ ts->power_status == HIMAX_TS_STATE_SUSPEND) ||
+ (ts->bus_refmask != 0 &&
+ ts->power_status != HIMAX_TS_STATE_SUSPEND))
+ return;
+
+ if (ts->bus_refmask == 0)
+ queue_work(ts->event_wq, &ts->suspend_work);
+ else
+ queue_work(ts->event_wq, &ts->resume_work);
+}
+
+int himax_ts_set_bus_ref(struct himax_ts_data *ts, u16 ref, bool enable)
+{
+ int result = 0;
+
+ mutex_lock(&ts->bus_mutex);
+
+ I("%s: bus_refmask = 0x%02X, enable = %d.\n", __func__, ref, enable);
+
+ if ((enable && (ts->bus_refmask & ref)) ||
+ (!enable && !(ts->bus_refmask & ref))) {
+ D("%s: reference is unexpectedly set: mask=0x%04X, ref=0x%04X, enable=%d\n",
+ __func__, ts->bus_refmask, ref, enable);
+ mutex_unlock(&ts->bus_mutex);
+ return -EINVAL;
+ }
+
+ if (enable) {
+ /* IRQs can only keep the bus active. IRQs received while the
+ * bus is transferred to AOC should be ignored.
+ */
+ if (ref == HIMAX_TS_BUS_REF_IRQ && ts->bus_refmask == 0)
+ result = -EAGAIN;
+ else
+ ts->bus_refmask |= ref;
+ } else
+ ts->bus_refmask &= ~ref;
+ himax_ts_aggregate_bus_state(ts);
+
+ mutex_unlock(&ts->bus_mutex);
+
+ /* When triggering a wake, wait up to one second to resume. SCREEN_ON
+ * and IRQ references do not need to wait.
+ */
+ if (enable &&
+ ref != HIMAX_TS_BUS_REF_SCREEN_ON && ref != HIMAX_TS_BUS_REF_IRQ) {
+ wait_for_completion_timeout(&ts->bus_resumed, HZ);
+ if (ts->power_status != HIMAX_TS_STATE_POWER_ON) {
+ I("%s: Failed to wake the touch bus.\n", __func__);
+ result = -ETIMEDOUT;
+ }
+ }
+
+ return result;
+}
+
+struct drm_connector *get_bridge_connector(struct drm_bridge *bridge)
+{
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(bridge->dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ if (connector->encoder == bridge->encoder)
+ break;
+ }
+ drm_connector_list_iter_end(&conn_iter);
+ return connector;
+}
+
+static bool bridge_is_lp_mode(struct drm_connector *connector)
+{
+ if (connector && connector->state) {
+ struct exynos_drm_connector_state *s =
+ to_exynos_connector_state(connector->state);
+ return s->exynos_mode.is_lp_mode;
+ }
+ return false;
+}
+
+static void panel_bridge_enable(struct drm_bridge *bridge)
+{
+ struct himax_ts_data *ts =
+ container_of(bridge, struct himax_ts_data, panel_bridge);
+
+ I("%s\n", __func__);
+ if (!ts->is_panel_lp_mode)
+ himax_ts_set_bus_ref(ts, HIMAX_TS_BUS_REF_SCREEN_ON, true);
+}
+
+static void panel_bridge_disable(struct drm_bridge *bridge)
+{
+ struct himax_ts_data *ts =
+ container_of(bridge, struct himax_ts_data, panel_bridge);
+
+ if (bridge->encoder && bridge->encoder->crtc) {
+ const struct drm_crtc_state *crtc_state = bridge->encoder->crtc->state;
+
+ if (drm_atomic_crtc_effectively_active(crtc_state))
+ return;
+ }
+
+ I("%s\n", __func__);
+ himax_ts_set_bus_ref(ts, HIMAX_TS_BUS_REF_SCREEN_ON, false);
+}
+
+static void panel_bridge_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode)
+{
+ struct himax_ts_data *ts =
+ container_of(bridge, struct himax_ts_data, panel_bridge);
+
+ if (!ts->connector || !ts->connector->state)
+ ts->connector = get_bridge_connector(bridge);
+
+ ts->is_panel_lp_mode = bridge_is_lp_mode(ts->connector);
+ himax_ts_set_bus_ref(ts, HIMAX_TS_BUS_REF_SCREEN_ON, !ts->is_panel_lp_mode);
+
+ if (adjusted_mode) {
+ int vrefresh = drm_mode_vrefresh(adjusted_mode);
+
+ if (ts->display_refresh_rate != vrefresh) {
+ I("%s: refresh rate(Hz) changed to %d from %d\n",
+ __func__, vrefresh, ts->display_refresh_rate);
+ ts->display_refresh_rate = vrefresh;
+ }
+ }
+}
+
+static const struct drm_bridge_funcs panel_bridge_funcs = {
+ .enable = panel_bridge_enable,
+ .disable = panel_bridge_disable,
+ .mode_set = panel_bridge_mode_set,
+};
+
+static int register_panel_bridge(struct himax_ts_data *ts)
+{
+ I("%s\n", __func__);
+#ifdef CONFIG_OF
+ ts->panel_bridge.of_node = ts->spi->dev.of_node;
+#endif
+ ts->panel_bridge.funcs = &panel_bridge_funcs;
+ drm_bridge_add(&ts->panel_bridge);
+
+ return 0;
+}
+
+static void unregister_panel_bridge(struct drm_bridge *bridge)
+{
+ struct drm_bridge *node;
+
+ I("%s\n", __func__);
+ drm_bridge_remove(bridge);
+
+ if (!bridge->dev) /* not attached */
+ return;
+
+ drm_modeset_lock(&bridge->dev->mode_config.connection_mutex, NULL);
+ list_for_each_entry(node, &bridge->encoder->bridge_chain, chain_node)
+ if (node == bridge) {
+ if (bridge->funcs->detach)
+ bridge->funcs->detach(bridge);
+ list_del(&bridge->chain_node);
+ break;
+ }
+ drm_modeset_unlock(&bridge->dev->mode_config.connection_mutex);
+ bridge->dev = NULL;
+}
+#endif
+
static void himax_boot_upgrade(struct work_struct *work)
{
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE)
+ struct himax_ts_data *ts = container_of(work, struct himax_ts_data,
+ work_boot_upgrade.work);
+ int ret = 0;
+#endif
#if defined(HX_BOOT_UPGRADE) || defined(HX_ZERO_FLASH)
int fw_sts = -1;
#endif
@@ -2863,6 +3056,12 @@ static void himax_boot_upgrade(struct work_struct *work)
#endif
END:
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE)
+ ret = register_panel_bridge(ts);
+ if (ret < 0) {
+ E("%s: register_panel_bridge failed. ret = 0x%08X\n", __func__, ret);
+ }
+#endif
ic_boot_done = 1;
himax_int_enable(1);
}
@@ -3007,6 +3206,89 @@ END:
return ret;
}
+int himax_ts_pinctrl_configure(struct himax_ts_data *ts, bool enable)
+{
+ struct pinctrl_state *state;
+
+ I("%s: %s\n", __func__, enable ? "ACTIVE" : "SUSPEND");
+
+ if (enable) {
+ state = pinctrl_lookup_state(ts->pdata->pinctrl, "on_state");
+ if (IS_ERR(ts->pdata->pinctrl))
+ E("%s: could not get active pinstate\n", __func__);
+ } else {
+ state = pinctrl_lookup_state(ts->pdata->pinctrl, "off_state");
+ if (IS_ERR(ts->pdata->pinctrl))
+ E("%s: could not get suspend pinstate\n", __func__);
+ }
+
+ if (!IS_ERR_OR_NULL(state))
+ return pinctrl_select_state(ts->pdata->pinctrl, state);
+
+ return 0;
+}
+
+static void himax_ts_suspend_work(struct work_struct *work)
+{
+ struct himax_ts_data *ts = container_of(work, struct himax_ts_data,
+ suspend_work);
+
+ I("%s\n", __func__);
+
+ mutex_lock(&ts->device_mutex);
+
+ reinit_completion(&ts->bus_resumed);
+
+ if (ts->power_status == HIMAX_TS_STATE_SUSPEND) {
+ E("%s: already suspended.\n", __func__);
+ mutex_unlock(&ts->device_mutex);
+ return;
+ }
+
+ himax_chip_common_suspend(ts);
+
+ ts->power_status = HIMAX_TS_STATE_SUSPEND;
+
+ himax_ts_pinctrl_configure(ts, false);
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN)
+ if (ts->tbn_register_mask)
+ tbn_release_bus(ts->tbn_register_mask);
+#endif
+ mutex_unlock(&ts->device_mutex);
+}
+
+static void himax_ts_resume_work(struct work_struct *work)
+{
+ struct himax_ts_data *ts = container_of(work, struct himax_ts_data,
+ resume_work);
+
+ I("%s\n", __func__);
+
+ mutex_lock(&ts->device_mutex);
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN)
+ if (ts->tbn_register_mask)
+ tbn_request_bus(ts->tbn_register_mask);
+#endif
+
+ if (ts->power_status == HIMAX_TS_STATE_POWER_ON) {
+ E("%s: already resumed.\n", __func__);
+ mutex_unlock(&ts->device_mutex);
+ return;
+ }
+
+ himax_ts_pinctrl_configure(ts, true);
+
+ himax_chip_common_resume(ts);
+
+ ts->power_status = HIMAX_TS_STATE_POWER_ON;
+
+ complete_all(&ts->bus_resumed);
+
+ mutex_unlock(&ts->device_mutex);
+}
+
int himax_chip_common_init(void)
{
int ret = 0;
@@ -3100,6 +3382,35 @@ int himax_chip_common_init(void)
#endif
g_core_fp.fp_touch_information();
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE)
+ ts->power_status = HIMAX_TS_STATE_POWER_ON;
+ ts->bus_refmask = HIMAX_TS_BUS_REF_SCREEN_ON;
+ mutex_init(&ts->bus_mutex);
+#endif
+ mutex_init(&ts->device_mutex);
+ INIT_WORK(&ts->suspend_work, himax_ts_suspend_work);
+ INIT_WORK(&ts->resume_work, himax_ts_resume_work);
+ ts->event_wq = alloc_workqueue("himax_ts-event-queue", WQ_UNBOUND |
+ WQ_HIGHPRI | WQ_CPU_INTENSIVE, 1);
+ if (!ts->event_wq) {
+ E("%s: Cannot create work thread\n", __func__);
+ ret = -ENOMEM;
+ goto err_alloc_workqueue;
+ }
+
+ init_completion(&ts->bus_resumed);
+ complete_all(&ts->bus_resumed);
+ init_completion(&ts->resume_done);
+ complete_all(&ts->resume_done);
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN)
+ if (register_tbn(&ts->tbn_register_mask)) {
+ ret = -ENODEV;
+ E("%s: Failed to register tbn context.\n", __func__);
+ goto err_init_tbn;
+ }
+ I("%s: tbn_register_mask = %#x.\n", __func__, ts->tbn_register_mask);
+#endif
spin_lock_init(&ts->irq_lock);
if (himax_ts_register_interrupt()) {
@@ -3235,6 +3546,14 @@ err_debug_init_failed:
#endif
himax_common_proc_deinit();
err_creat_proc_file_failed:
+ if (ts->event_wq)
+ destroy_workqueue(ts->event_wq);
+err_alloc_workqueue:
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN)
+ if (ts->tbn_register_mask)
+ unregister_tbn(&ts->tbn_register_mask);
+err_init_tbn:
+#endif
#if defined(HX_SMART_WAKEUP)
#if defined(KERNEL_VER_ABOVE_4_19)
wakeup_source_unregister(ts->ts_SMWP_wake_lock);
@@ -3274,6 +3593,8 @@ void himax_chip_common_deinit(void)
{
struct himax_ts_data *ts = private_ts;
+ himax_ts_pinctrl_configure(ts, false);
+
himax_ts_unregister_interrupt();
#if defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT)
@@ -3326,6 +3647,17 @@ else
cancel_delayed_work_sync(&ts->work_att);
destroy_workqueue(ts->himax_att_wq);
#endif
+ cancel_work_sync(&ts->suspend_work);
+ cancel_work_sync(&ts->resume_work);
+ destroy_workqueue(ts->event_wq);
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN)
+ if (ts->tbn_register_mask)
+ unregister_tbn(&ts->tbn_register_mask);
+#endif
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE)
+ unregister_panel_bridge(&ts->panel_bridge);
+#endif
input_free_device(ts->input_dev);
#if defined(HX_CONTAINER_SPEED_UP)
cancel_delayed_work_sync(&ts->ts_int_work);
diff --git a/himax_common.h b/himax_common.h
index 83c13d5..8790cef 100644
--- a/himax_common.h
+++ b/himax_common.h
@@ -39,6 +39,13 @@
#include <linux/kallsyms.h>
#include <linux/version.h>
#include <linux/time64.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_device.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_modes.h>
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN)
+#include <touch_bus_negotiator.h>
+#endif
#if defined(CONFIG_OF)
#include <linux/of_gpio.h>
@@ -108,6 +115,9 @@ extern struct drm_panel gNotifier_dummy_panel;
#elif defined(HX_CONFIG_DRM)
#include <linux/msm_drm_notify.h>
#endif
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE)
+#include <drm/drm_panel.h>
+#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))
@@ -331,6 +341,16 @@ enum cell_type {
#define HX_KEY_DF_RIGHT 275
#endif
+enum {
+ HIMAX_TS_BUS_REF_SCREEN_ON = 0x01,
+ HIMAX_TS_BUS_REF_IRQ = 0x02
+};
+
+enum TOUCH_POWER_MODE {
+ HIMAX_TS_STATE_POWER_ON = 0,
+ HIMAX_TS_STATE_SUSPEND,
+};
+
enum fix_touch_info {
FIX_HX_RX_NUM = 40,
FIX_HX_TX_NUM = 28,
@@ -521,6 +541,27 @@ struct himax_ts_data {
struct delayed_work work_att;
#endif
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE)
+ volatile int power_status;
+ u16 bus_refmask;
+ struct mutex bus_mutex;
+ struct drm_bridge panel_bridge;
+ struct drm_connector *connector;
+ bool is_panel_lp_mode;
+ int display_refresh_rate; /* Display rate in Hz */
+#endif
+
+ struct mutex device_mutex;
+ struct completion bus_resumed;
+ struct work_struct suspend_work;
+ struct work_struct resume_work;
+ struct workqueue_struct *event_wq;
+ struct completion resume_done;
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN)
+ u32 tbn_register_mask;
+#endif
+
struct workqueue_struct *dump_wq;
struct work_struct dump_work;
struct workqueue_struct *himax_boot_upgrade_wq;
@@ -557,7 +598,6 @@ struct himax_ts_data {
struct work_struct guest_info_work;
#endif
-
};
struct himax_debug {
@@ -625,8 +665,6 @@ extern bool ic_boot_done;
int himax_parse_dt(struct himax_ts_data *ts, struct himax_platform_data *pdata);
-int himax_ts_pinctrl_configure(struct himax_ts_data *ts, bool enable);
-
extern void himax_parse_dt_ic_info(struct himax_ts_data *ts,
struct himax_platform_data *pdata);
diff --git a/himax_debug.c b/himax_debug.c
index d0abb47..72df84c 100644
--- a/himax_debug.c
+++ b/himax_debug.c
@@ -2987,12 +2987,10 @@ CONTI:
/* Compare Touch Information */
g_core_fp.fp_tp_info_check();
goto ENDFUCTION;
-#if !defined(HX_ZERO_FLASH)
} else if (buf[0] == 'e' && buf[1] == 'c') {
/* Erase firmware */
g_core_fp.fp_sense_off(true);
g_core_fp.fp_chip_erase();
-#endif
} else {
/* others, do nothing */
debug_level_cmd = 0;
diff --git a/himax_platform.c b/himax_platform.c
index 7481657..14ed92e 100644
--- a/himax_platform.c
+++ b/himax_platform.c
@@ -692,9 +692,7 @@ err_regulator_not_on:
int himax_gpio_power_config(struct himax_platform_data *pdata)
{
int error = 0;
- /* struct i2c_client *client = private_ts->client; */
#if defined(HX_RST_PIN_FUNC)
-
if (pdata->gpio_reset >= 0) {
error = gpio_request_one(pdata->gpio_reset, GPIOF_OUT_INIT_HIGH, "himax-reset");
@@ -711,7 +709,6 @@ int himax_gpio_power_config(struct himax_platform_data *pdata)
goto err_gpio_reset_dir;
}
}
-
#endif
#if defined(HX_PON_PIN_SUPPORT)
@@ -889,8 +886,18 @@ static void himax_ts_isr_func(struct himax_ts_data *ts)
irqreturn_t himax_ts_thread(int irq, void *ptr)
{
+ struct himax_ts_data *ts = (struct himax_ts_data *)ptr;
+ if (himax_ts_set_bus_ref(ts, HIMAX_TS_BUS_REF_IRQ, true) < 0) {
+ /* Interrupt during bus suspend */
+ I("%s: Skipping stray interrupt since bus is suspended(power_status: %d)\n",
+ __func__, ts->power_status);
+ return IRQ_HANDLED;
+ }
+
himax_ts_isr_func((struct himax_ts_data *)ptr);
+ himax_ts_set_bus_ref(ts, HIMAX_TS_BUS_REF_IRQ, false);
+
return IRQ_HANDLED;
}
@@ -1178,28 +1185,6 @@ int drm_notifier_callback(struct notifier_block *self,
}
#endif
-int himax_ts_pinctrl_configure(struct himax_ts_data *ts, bool enable)
-{
- struct pinctrl_state *state;
-
- I("%s: %s\n", __func__, enable ? "ACTIVE" : "SUSPEND");
-
- if (enable) {
- state = pinctrl_lookup_state(ts->pdata->pinctrl, "on_state");
- if (IS_ERR(ts->pdata->pinctrl))
- E("%s: could not get active pinstate\n", __func__);
- } else {
- state = pinctrl_lookup_state(ts->pdata->pinctrl, "off_state");
- if (IS_ERR(ts->pdata->pinctrl))
- E("%s: could not get suspend pinstate\n", __func__);
- }
-
- if (!IS_ERR_OR_NULL(state))
- return pinctrl_select_state(ts->pdata->pinctrl, state);
-
- return 0;
-}
-
int himax_chip_common_probe(struct spi_device *spi)
{
struct himax_ts_data *ts;
@@ -1272,8 +1257,6 @@ int himax_chip_common_remove(struct spi_device *spi)
{
struct himax_ts_data *ts = spi_get_drvdata(spi);
- himax_ts_pinctrl_configure(ts, false);
-
if (g_hx_chip_inited)
himax_chip_common_deinit();
diff --git a/himax_platform.h b/himax_platform.h
index 8eb4c83..fa3b452 100644
--- a/himax_platform.h
+++ b/himax_platform.h
@@ -131,6 +131,7 @@ extern uint8_t himax_int_gpio_read(int pinnum);
extern int himax_gpio_power_config(struct himax_platform_data *pdata);
void himax_gpio_power_deconfig(struct himax_platform_data *pdata);
+extern int himax_ts_set_bus_ref(struct himax_ts_data *ts, u16 ref, bool enable);
#if defined(HX_CONFIG_FB)
extern int fb_notifier_callback(struct notifier_block *self,