summaryrefslogtreecommitdiff
path: root/himax_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'himax_common.c')
-rw-r--r--himax_common.c334
1 files changed, 333 insertions, 1 deletions
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);