From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id B0CD7CA5FDD for ; Sat, 3 Oct 2026 14:28:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Transfer-Encoding: Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc: To:From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=CBNWHT4zmYLHY4YuP3DZLBFkghTYi5YIb06vr+tOSbs=; b=W8EuXxrXVdf/rBd+gIzRSpnb8g lSt6rMlgoEFzd1RUt2hcy36sib10N3xhPG7pd4wz5/a5I8N+9ur1a135HKiIdx7QEixED1gOrnIMK 9DqBqCt63aVB67aESyzTMT7T+I5RD01mEx8ag0ufgAwdTBAcyOoJTvhkeb4bynJj6gabxb8M3ENI5 SdYsJuZ9jfsN3AT3RpYoezrufgqN+/tM98FRrKXPJeZum1LOCQHCBu+WCt2y5edp+DMxBcuJgXPt4 hF7N7JIbadqCmlXp9HI42fFHdGDex7tpERIqXEmX1IfX5kcPO0LLJFQ3DTGNyQ3NHeIwBDhIp1Li9 R6Ffmxtw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1xD0ih-0000000DbqE-1sp6; Sat, 03 Oct 2026 14:28:11 +0000 Received: from mail.nozomi.space ([139.162.184.125]) by bombadil.infradead.org with esmtps (Exim 4.99.1 #2 (Red Hat Linux)) id 1xD0iW-0000000Dbae-3tLF; Sat, 03 Oct 2026 14:28:06 +0000 From: =?UTF-8?q?Micha=C5=82=20Kope=C4=87?= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nozomi.space; s=mail; t=1791037671; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=CBNWHT4zmYLHY4YuP3DZLBFkghTYi5YIb06vr+tOSbs=; b=CJ80KUgtCYXRZtHqLASY7bcpkjb7VIz2W7FOjKjOQ7dhCqXBePr8JoB6hpZAr4MblVuECe sMY6tI3xK2Vhex+qCTaOCbKoJwDp+KDKYUwAP1Z++8cmzD6SVp89TqwPn0Z0/IhWnrwq8U TaLh8sLQQUJZmqZEV9XL4K82dfsaqVp0YWU7Z+T3kdxWPc3+sSWdto2cEX2FzgZEl/dQqr R1kXa0bNjLtv1VclIpbhGZdN/D4Tr+HKUXvF1oY5/OIuKRmu2v8s7+RdjzgX+Puaf48nj5 9+fX2nbBo8BwdSLHmJuOp5sdLAhUu7P5qBytJI2S+LMav+D+MHiRWnjIha0xeQ== To: linux-input@vger.kernel.org Cc: devicetree@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Jiri Kosina , Benjamin Tissoires , Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Matthias Brugger , AngeloGioacchino Del Regno , Allen Lin , Tylor Yang , Felix Kaechele , Douglas Anderson , =?UTF-8?q?Micha=C5=82=20Kope=C4=87?= Subject: [PATCH v5 3/6] HID: himax: Add DRM panel follower support Date: Sat, 3 Oct 2026 16:27:38 +0200 Message-ID: <20261003142741.48634-4-michal@nozomi.space> In-Reply-To: <20261003142741.48634-1-michal@nozomi.space> References: <20261003142741.48634-1-michal@nozomi.space> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.9.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20261003_072801_271819_05EC6844 X-CRM114-Status: GOOD ( 26.49 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org From: Allen_Lin The touch function of the HX83102J is part of a TDDI controller and has to follow the power sequence of the panel. Register as a DRM panel follower, and suspend and resume the touch function when the panel is unprepared and prepared. Link: https://lore.kernel.org/all/20230727171750.633410-1-dianders@chromium.org Signed-off-by: Allen_Lin [michal@nozomi.space: unregister the panel follower on remove] Signed-off-by: Michał Kopeć --- drivers/hid/hid-himax.c | 295 +++++++++++++++++++++++++++++++++++++--- drivers/hid/hid-himax.h | 10 ++ 2 files changed, 289 insertions(+), 16 deletions(-) diff --git a/drivers/hid/hid-himax.c b/drivers/hid/hid-himax.c index 1cdb310dec642..653c92829ec7f 100644 --- a/drivers/hid/hid-himax.c +++ b/drivers/hid/hid-himax.c @@ -8,6 +8,7 @@ #include "hid-himax.h" static int himax_chip_init(struct himax_ts_data *ts); +static int himax_platform_init(struct himax_ts_data *ts); static void himax_ts_work(struct himax_ts_data *ts); /** @@ -1145,7 +1146,6 @@ static int himax_hid_probe(struct himax_ts_data *ts) hid = ts->hid; if (hid) { - dev_warn(ts->dev, "%s: hid device already exist!\n", __func__); hid_destroy_device(hid); hid = NULL; } @@ -1501,6 +1501,154 @@ static int himax_load_config(struct himax_ts_data *ts) return -EINVAL; } +/** + * himax_ap_notify_fw_suspend() - Notify the FW of AP suspend status + * @ts: Himax touch screen data + * @suspend: Suspend status, true for suspend, false for resume + * + * This function is used to notify the FW of AP suspend status. It will write + * the suspend status to the DSRAM and read the status back to check if the + * status is written successfully. If IC is powered off when suspend, this + * function will only be used when resume. + * + * Return: None + */ +static void himax_ap_notify_fw_suspend(struct himax_ts_data *ts, bool suspend) +{ + int ret; + u32 retry_cnt; + const u32 retry_limit = 10; + union himax_dword_data rdata, data; + + if (suspend) + data.dword = cpu_to_le32(HIMAX_DSRAM_DATA_AP_NOTIFY_FW_SUSPEND); + else + data.dword = cpu_to_le32(HIMAX_DSRAM_DATA_AP_NOTIFY_FW_RESUME); + + for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) { + ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_AP_NOTIFY_FW_SUSPEND, + data.byte, 4); + if (ret) { + dev_err(ts->dev, "%s: write suspend status failed!\n", __func__); + return; + } + usleep_range(1000, 1100); + ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_AP_NOTIFY_FW_SUSPEND, + rdata.byte, 4); + if (ret) { + dev_err(ts->dev, "%s: read suspend status failed!\n", __func__); + return; + } + + if (rdata.dword == data.dword) + break; + } +} + +/** + * himax_resume_proc() - Chip resume procedure of touch screen + * @ts: Himax touch screen data + * + * This function is used to resume the touch screen. It will call the + * himax_ap_notify_fw_suspend() to notify the FW of AP resume status. + * + * Return: None + */ +static void himax_resume_proc(struct himax_ts_data *ts) +{ + himax_ap_notify_fw_suspend(ts, false); +} + +/** + * himax_chip_suspend() - Suspend the touch screen + * @ts: Himax touch screen data + * + * This function is used to suspend the touch screen. It will disable the + * interrupt and set the reset pin to activate state. Remove the HID at + * the end, to prevent stuck finger when resume. + * + * Return: 0 on success, negative error code on failure + */ +static int himax_chip_suspend(struct himax_ts_data *ts) +{ + himax_int_enable(ts, false); + gpiod_set_value(ts->pdata.gpiod_rst, 1); + himax_power_set(ts, false); + himax_hid_remove(ts); + + return 0; +} + +/** + * himax_chip_resume() - Setup flags, I/O and resume + * @ts: Himax touch screen data + * + * This function is used to resume the touch screen. It will set the resume + * success flag to false, and disable reset pin. Then call the himax_resume_proc() + * to process detailed resume procedure. + * + * Return: 0 on success, negative error code on failure + */ +static int himax_chip_resume(struct himax_ts_data *ts) +{ + if (himax_power_set(ts, true)) + return -EIO; + gpiod_set_value(ts->pdata.gpiod_rst, 0); + himax_resume_proc(ts); + himax_hid_probe(ts); + himax_int_enable(ts, true); + + return 0; +} + +/** + * himax_suspend() - Suspend the touch screen + * @dev: Device structure + * + * Wrapper function for himax_chip_suspend() to be called by the PM or + * the DRM panel notifier. + * + * Return: 0 on success, negative error code on failure + */ +static int himax_suspend(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + if (!ts->initialized) { + dev_err(ts->dev, "%s: init not ready, skip!\n", __func__); + return -ECANCELED; + } + himax_chip_suspend(ts); + + return 0; +} + +/** + * himax_resume() - Resume the touch screen + * @dev: Device structure + * + * Wrapper function for himax_chip_resume() to be called by the PM or + * the DRM panel notifier. + * + * Return: 0 on success, negative error code on failure + */ +static int himax_resume(struct device *dev) +{ + int ret; + struct himax_ts_data *ts = dev_get_drvdata(dev); + + if (!ts->initialized) { + if (himax_chip_init(ts)) + return -ECANCELED; + } + + ret = himax_chip_resume(ts); + if (ret < 0) + dev_err(ts->dev, "%s: resume failed!\n", __func__); + + return ret; +} + /** * himax_chip_init() - Initialize the Himax touch screen * @ts: Himax touch screen data @@ -1531,6 +1679,130 @@ static int himax_chip_init(struct himax_ts_data *ts) return 0; } +/** + * __himax_initial_power_up() - Initial power up of the Himax touch screen + * @ts: Himax touch screen data + * + * This function is used to perform the initial power up sequence of the Himax + * touch screen for DRM panel notifier. + * + * Return: 0 on success, negative error code on failure + */ +static int __himax_initial_power_up(struct himax_ts_data *ts) +{ + int ret; + + ret = himax_platform_init(ts); + if (ret) { + dev_err(ts->dev, "%s: platform init failed\n", __func__); + return ret; + } + + ret = himax_chip_detect(ts); + if (ret) { + dev_err(ts->dev, "%s: IC detect failed\n", __func__); + return ret; + } + + ret = himax_chip_init(ts); + if (ret) { + dev_err(ts->dev, "%s: chip init failed\n", __func__); + return ret; + } + ts->probe_finish = true; + + return 0; +} + +/** + * himax_panel_prepared() - Panel prepared callback + * @follower: DRM panel follower + * + * This function is called when the panel is prepared. It will call the + * __himax_initial_power_up() when the probe is not finished which means + * the first time driver start. Otherwise, it will call the himax_resume() + * to performed resume process. + * + * Return: 0 on success, negative error code on failure + */ +static int himax_panel_prepared(struct drm_panel_follower *follower) +{ + struct himax_platform_data *pdata = + container_of(follower, struct himax_platform_data, panel_follower); + struct himax_ts_data *ts = container_of(pdata, struct himax_ts_data, pdata); + + if (!ts->probe_finish) + return __himax_initial_power_up(ts); + else + return himax_resume(ts->dev); +} + +/** + * himax_panel_unpreparing() - Panel unpreparing callback + * @follower: DRM panel follower + * + * This function is called when the panel is unpreparing. It will call the + * himax_suspend() to perform the suspend process. + * + * Return: 0 on success, negative error code on failure + */ +static int himax_panel_unpreparing(struct drm_panel_follower *follower) +{ + struct himax_platform_data *pdata = + container_of(follower, struct himax_platform_data, panel_follower); + struct himax_ts_data *ts = container_of(pdata, struct himax_ts_data, pdata); + + return himax_suspend(ts->dev); +} + +/* Panel follower function table */ +static const struct drm_panel_follower_funcs himax_panel_follower_funcs = { + .panel_prepared = himax_panel_prepared, + .panel_unpreparing = himax_panel_unpreparing, +}; + +/** + * himax_register_panel_follower() - Register the panel follower + * @ts: Himax touch screen data + * + * This function is used to register the panel follower. It will set the + * pdata.is_panel_follower to true and register the panel follower. + * + * Return: 0 on success, negative error code on failure + */ +static int himax_register_panel_follower(struct himax_ts_data *ts) +{ + struct device *dev = ts->dev; + + ts->pdata.is_panel_follower = true; + ts->pdata.panel_follower.funcs = &himax_panel_follower_funcs; + + if (device_can_wakeup(dev)) { + dev_warn(ts->dev, "Can't wakeup if following panel"); + device_set_wakeup_capable(dev, false); + } + + return drm_panel_add_follower(dev, &ts->pdata.panel_follower); +} + +/** + * himax_initial_power_up() - Initial power up of the Himax touch screen + * @ts: Himax touch screen data + * + * This function checks if the device is a panel follower and calls + * himax_register_panel_follower() if it is. Otherwise, it calls + * __himax_initial_power_up(). + * + * Return: 0 on success, negative error code on failure + */ +static int himax_initial_power_up(struct himax_ts_data *ts) +{ + if (drm_is_panel_follower(ts->dev)) + return himax_register_panel_follower(ts); + else + return __himax_initial_power_up(ts); +} + /** * himax_platform_deinit() - Deinitialize the platform related settings * @ts: Pointer to the himax_ts_data structure @@ -1660,25 +1932,13 @@ static int himax_spi_drv_probe(struct spi_device *spi) ts->initialized = false; ts->ic_boot_done = false; - ret = himax_platform_init(ts); - if (ret) { - dev_err(ts->dev, "%s: platform init failed\n", __func__); - return ret; - } - - ret = himax_chip_detect(ts); + ret = himax_initial_power_up(ts); if (ret) { - dev_err(ts->dev, "%s: IC detect failed\n", __func__); - return ret; + dev_err(ts->dev, "%s: initial power up failed\n", __func__); + return -ENODEV; } - ret = himax_chip_init(ts); - if (ret < 0) - return ret; - ts->probe_finish = true; - return ret; - himax_platform_deinit(ts); } /** @@ -1693,6 +1953,9 @@ static void himax_spi_drv_remove(struct spi_device *spi) { struct himax_ts_data *ts = spi_get_drvdata(spi); + if (ts->pdata.is_panel_follower) + drm_panel_remove_follower(&ts->pdata.panel_follower); + if (ts->probe_finish) { if (ts->ic_boot_done) { himax_int_enable(ts, false); diff --git a/drivers/hid/hid-himax.h b/drivers/hid/hid-himax.h index 1818474552a92..0ec43fc00a469 100644 --- a/drivers/hid/hid-himax.h +++ b/drivers/hid/hid-himax.h @@ -8,6 +8,7 @@ #ifndef __HID_HIMAX_83102J_H__ #define __HID_HIMAX_83102J_H__ +#include #include #include #include @@ -69,6 +70,11 @@ #define HIMAX_AHB_CMD_INCR4 0x10 #define HIMAX_AHB_CMD_INCR4_ADD_4_BYTE 0x01 #define HIMAX_AHB_CMD_LEAVE_SAFE_MODE 0x0000 +/* DSRAM flag addresses */ +#define HIMAX_DSRAM_ADDR_AP_NOTIFY_FW_SUSPEND 0x10007fd0 +/* dsram flag data */ +#define HIMAX_DSRAM_DATA_AP_NOTIFY_FW_SUSPEND 0xa55aa55a +#define HIMAX_DSRAM_DATA_AP_NOTIFY_FW_RESUME 0x00000000 /* hx83102j-specific register/dsram flags/data */ #define HIMAX_HX83102J_REG_ADDR_TCON_RST 0x80020004 /* hardware register addresses */ @@ -219,13 +225,17 @@ struct himax_hid_desc { /** * struct himax_platform_data - Platform data holder + * @is_panel_follower: Is panel follower enabled * @vccd_supply: VCCD supply + * @panel_follower: DRM panel follower * @gpiod_rst: GPIO reset * * This structure is used to hold the platform related data. */ struct himax_platform_data { + bool is_panel_follower; struct regulator *vccd_supply; + struct drm_panel_follower panel_follower; struct gpio_desc *gpiod_rst; }; -- 2.55.0