diff options
Diffstat (limited to 'himax_common.c')
-rw-r--r-- | himax_common.c | 334 |
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); |